Codebase list facter / 6b0daf9
New upstream version 4.2.13 Jérôme Charaoui 1 year, 6 months ago
2616 changed file(s) with 85638 addition(s) and 49681 deletion(s). Raw diff Collapse all Expand all
0 # frozen_string_literal: true
1
2 require 'open3'
3 require 'fileutils'
4
5 def if_no_env_vars_set_defaults
6 ENV['FACTER_ROOT'] = __dir__.gsub('/.github/actions', '') unless ENV['FACTER_ROOT']
7 ENV['SHA'] = 'latest' unless ENV['SHA']
8 ENV['RELEASE_STREAM'] = 'puppet7' unless ENV['RELEASE_STREAM']
9 end
10
11 def install_bundler
12 message('INSTALL BUNDLER')
13 run('gem install bundler')
14 end
15
16 def install_facter_acceptance_dependencies
17 message('INSTALL FACTER ACCEPTANCE DEPENDENCIES')
18 run('bundle install')
19 end
20
21 def initialize_beaker
22 beaker_platform_with_options = platform_with_options(beaker_platform)
23
24 message('BEAKER INITIALIZE')
25 run("beaker init -h #{beaker_platform_with_options} -o #{File.join('config', 'aio', 'options.rb')}")
26
27 message('BEAKER PROVISION')
28 run('beaker provision')
29 end
30
31 def beaker_platform
32 {
33 'ubuntu-latest' => 'ubuntu2004-64a',
34 'macos-latest' => 'osx11-64a',
35 'windows-2016' => 'windows2016-64a',
36 'windows-2019' => 'windows2019-64a'
37 }[HOST_PLATFORM]
38 end
39
40 def platform_with_options(platform)
41 return "\"#{platform}{hypervisor=none,hostname=localhost,is_cygwin=false}\"" if platform.include? 'windows'
42
43 "#{platform}{hypervisor=none\\,hostname=localhost}"
44 end
45
46 def install_puppet_agent
47 message('INSTALL PUPPET AGENT')
48
49 beaker_puppet_root = run('bundle info beaker-puppet --path')
50 presuite_file_path = File.join(beaker_puppet_root.chomp, 'setup', 'aio', '010_Install_Puppet_Agent.rb')
51
52 run("beaker exec pre-suite --pre-suite #{presuite_file_path} --preserve-state", './', env_path_var)
53 end
54
55 def puppet_puppet_bin_dir
56 return '/opt/puppetlabs/puppet/bin' unless HOST_PLATFORM.include? 'windows'
57
58 'C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin'
59 end
60
61 def puppet_bin_dir
62 return '/opt/puppetlabs/puppet/bin' unless HOST_PLATFORM.include? 'windows'
63
64 'C:\\Program Files\\Puppet Labs\\Puppet\\bin'
65 end
66
67 def puppet_ruby
68 return '/opt/puppetlabs/puppet/bin/ruby' unless HOST_PLATFORM.include? 'windows'
69
70 'C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin\\ruby.exe'
71 end
72
73 def facter_lib_path
74 return '/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter' unless HOST_PLATFORM.include? 'windows'
75
76 'C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\lib\\ruby\\vendor_ruby\\facter'
77 end
78
79 def env_path_var
80 HOST_PLATFORM.include?('windows') ? { 'PATH' => "#{puppet_bin_dir};#{ENV['PATH']}" } : {}
81 end
82
83 def install_facter
84 message('OVERWRITE FACTER FROM PUPPET AGENT')
85
86 # clean facter directory
87 FileUtils.rm_r(facter_lib_path)
88 FileUtils.mkdir(facter_lib_path)
89
90 Dir.chdir('../') do
91 run("\'#{puppet_ruby}\' install.rb --bindir=\'#{puppet_puppet_bin_dir}\' " \
92 "--sitelibdir=\'#{facter_lib_path.gsub('facter', '')}\'")
93 end
94 end
95
96 def run_acceptance_tests
97 message('RUN ACCEPTANCE TESTS')
98
99 run('beaker exec tests --test-tag-exclude=server,facter_3 --test-tag-or=risk:high,audit:high', './', env_path_var)
100 end
101
102 def message(message)
103 message_length = message.length
104 total_length = 130
105 lines_length = (total_length - message_length) / 2
106 result = ('-' * lines_length + ' ' + message + ' ' + '-' * lines_length)[0, total_length]
107 puts "\n\n#{result}\n\n"
108 end
109
110 def run(command, dir = './', env = {})
111 puts command
112 output = ''
113 Open3.popen2e(env, command, chdir: dir) do |_stdin, stdout_and_err, wait_thr|
114 stdout_and_err.each do |line|
115 puts line
116 output += line
117 end
118 exit_status = wait_thr.value.exitstatus
119 exit(exit_status) if exit_status != 0
120 end
121 output
122 end
123
124 ENV['DEBIAN_DISABLE_RUBYGEMS_INTEGRATION'] = 'no_warnings'
125 if_no_env_vars_set_defaults
126 ACCEPTANCE_PATH = File.join(ENV['FACTER_ROOT'], 'acceptance')
127 HOST_PLATFORM = ARGV[0]
128
129 install_bundler
130
131 Dir.chdir(ACCEPTANCE_PATH) do
132 install_facter_acceptance_dependencies
133 initialize_beaker
134 install_puppet_agent
135 install_facter
136 run_acceptance_tests
137 end
0 ---
1 name: Acceptance tests
2
3 on:
4 push:
5 branches:
6 - main
7 pull_request:
8 branches:
9 - main
10
11 jobs:
12 acceptance_tests:
13 name: Platform
14 strategy:
15 matrix:
16 os: [ windows-2019, ubuntu-latest, macos-latest ]
17 runs-on: ${{ matrix.os }}
18 env:
19 BEAKER_debug: true
20 FACTER_ROOT: facter
21 SHA: latest
22 RELEASE_STREAM: puppet7
23
24 steps:
25 - name: Checkout current PR
26 uses: actions/checkout@v2
27 with:
28 path: facter
29
30 - name: Install Ruby 2.6
31 uses: ruby/setup-ruby@v1
32 with:
33 ruby-version: '2.6'
34
35 - name: Fix common Linux and macOS permissions
36 if: runner.os != 'Windows'
37 run: sudo chmod a-w /opt
38
39 - name: Fix Linux permissions
40 if: runner.os == 'Linux'
41 run: |
42 sudo chmod a-w /home/runner /usr/share &&
43 sudo chmod -R a-w /home/runner/.config /home/linuxbrew
44
45 - name: Install dhclient for Linux
46 if: runner.os == 'Linux'
47 run: |
48 sudo apt install dhcpcd5
49 sudo dhclient
50
51 # IPv6 is missing on the GitHub macOS image and we need it for the networking facts tests
52 # https://github.com/actions/runner-images/issues/668
53 - name: Add IPv6 on macOS
54 if: runner.os == 'macOS'
55 run: |
56 primary_interface=`route -n get default | awk '/interface: */{print $NF}'`
57 sudo ifconfig $primary_interface inet6 add ::1/64
58
59 - name: Run acceptance tests on Linux-like platform
60 if: runner.os != 'Windows'
61 run: sudo -E "PATH=$PATH" ruby $FACTER_ROOT/.github/actions/presuite.rb ${{ matrix.os }}
62
63 - name: Run acceptance tests on Windows-like platform
64 if: runner.os == 'Windows'
65 run: ruby $Env:FACTER_ROOT/.github/actions/presuite.rb ${{ matrix.os }}
0 ---
1 name: Checks
2
3 on:
4 push:
5 branches:
6 - main
7 pull_request:
8 branches:
9 - main
10
11 jobs:
12 rubocop_checks:
13 runs-on: ubuntu-latest
14 name: RuboCop
15 steps:
16 - name: Checkout current PR
17 uses: actions/checkout@v2
18
19 - name: Rubocop checks
20 uses: ruby/setup-ruby@v1
21 with:
22 ruby-version: 2.6
23 - run: bundle install --jobs 3 --retry 3
24 - run: bundle exec rubocop --parallel
25
26 rtc:
27 runs-on: ubuntu-latest
28 name: RuboCop TODO
29 steps:
30 - uses: actions/checkout@v1
31 - uses: gimmyxd/rtc-action@0.3.1
32 env:
33 RTC_TOKEN: ${{ secrets.RTC_TOKEN }}
34 UPDATE_PR: false
35
36 # forces the job to fail if there are any new offences
37 FORCE_ERROR_EXIT: true
38
39 commit_checks:
40 runs-on: ubuntu-latest
41 name: commit message
42 steps:
43 - name: Checkout current PR
44 uses: actions/checkout@v2
45 with:
46 fetch-depth: 2
47
48 - name: Commit message checks
49 uses: ruby/setup-ruby@v1
50 with:
51 ruby-version: 2.6
52 - run: bundle install --jobs 3 --retry 3
53 - run: bundle exec rake commits
0 ---
1 name: Coverage
2
3 on:
4 push:
5 branches:
6 - main
7
8 jobs:
9 coverage_checks:
10 runs-on: ubuntu-latest
11 name: coverage
12 steps:
13 - name: Checkout current PR
14 uses: actions/checkout@v2
15
16 - name: Setup Code Climate test-reporter
17 run: |
18 curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
19 chmod +x ./cc-test-reporter
20 ./cc-test-reporter before-build
21
22 - name: Generate coverage
23 uses: ruby/setup-ruby@v1
24 with:
25 ruby-version: 2.6
26 - run: bundle install --jobs 3 --retry 3
27 - run: bundle exec rake spec
28
29 - name: Publish code coverage
30 run: |
31 export GIT_BRANCH="${GITHUB_REF/refs\/heads\//}"
32 ./cc-test-reporter after-build -r ${{secrets.CC_TEST_REPORTER_ID}}
0 ---
1 name: Snyk Monitor
2 on:
3 push:
4 branches:
5 - main
6 jobs:
7 snyk_monitor:
8 if: ${{ github.repository_owner == 'puppetlabs' }}
9 runs-on: ubuntu-latest
10 name: Snyk Monitor
11 steps:
12 - name: Checkout current PR
13 uses: actions/checkout@v2
14 - name: Setup Ruby
15 uses: ruby/setup-ruby@v1
16 with:
17 ruby-version: 2.7
18 - name: Install dependencies
19 run: bundle install --jobs 3 --retry 3
20 - name: Run Snyk to check for vulnerabilities
21 uses: snyk/actions/ruby@master
22 env:
23 SNYK_TOKEN: ${{ secrets.SNYK_FOSS_KEY }}
24 with:
25 command: monitor
26 args: --org=puppet-foss --project-name=${{ github.repository }}
0 ---
1 name: Unit tests
2
3 on:
4 push:
5 branches:
6 - main
7 pull_request:
8 branches:
9 - main
10
11 env:
12 CI: true
13
14 jobs:
15 linux_unit_tests:
16 name: Ruby version
17 strategy:
18 matrix:
19 ruby:
20 - '2.3'
21 - '2.7'
22 - '3.0'
23 - '3.1'
24 - 'jruby-9.3.7.0'
25 runs-on: ubuntu-latest
26 steps:
27 - name: Checkout current PR
28 uses: actions/checkout@v2
29
30 - name: Rspec checks
31 uses: ruby/setup-ruby@v1
32 with:
33 ruby-version: ${{ matrix.ruby }}
34 - run: gem update bundler
35 - run: bundle install --jobs 3 --retry 3
36 - run: bundle exec rake spec_random
37 - run: bundle exec rake spec_integration
38
39 windows_unit_tests:
40 runs-on: windows-2019
41 steps:
42 - name: Checkout current PR
43 uses: actions/checkout@v2
44
45 - name: Rspec checks
46 uses: ruby/setup-ruby@v1
47 with:
48 ruby-version: 2.7
49 - run: gem update bundler
50 - run: bundle install --jobs 3 --retry 3
51 - run: bundle exec rake spec_random
52 - run: bundle exec rake spec_integration
0 *.gem
1 *.rbc
2 /.config
3 /coverage/
4 /InstalledFiles
5 /pkg/
6 /spec/reports/
7 /spec/examples.txt
8 /test/tmp/
9 /test/version_tmp/
10 /tmp/
11
12 # Used by dotenv library to load environment variables.
13 # .env
14
15 ## Specific to RubyMotion:
16 .dat*
17 .repl_history
18 build/
19 *.bridgesupport
20 build-iPhoneOS/
21 build-iPhoneSimulator/
22
23 ## Specific to RubyMotion (use of CocoaPods):
24 #
25 # We recommend against adding the Pods directory to your .gitignore. However
26 # you should judge for yourself, the pros and cons are mentioned at:
27 # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
28 #
29 # vendor/Pods/
30
31 ## Documentation cache and generated files:
32 /.yardoc/
33 /_yardoc/
34 /doc/
35 /rdoc/
36
37 ## Environment normalization:
38 /.bundle/
39 /vendor/bundle
40 /lib/bundler/man/
41
42 ## Logs
43 *.log
44
45 # for a library or gem, you might want to ignore these files since the code is
46 # intended to run in multiple environments; otherwise, check them in:
47 # Gemfile.lock
48 # .ruby-version
49 # .ruby-gemset
50
51 # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52 .rvmrc
53
54 .rspec_status
55 Gemfile.lock
56 Gemfile.local
57 acceptance/vendor
58 .beaker
0 cli.debug=true
1 debug.fullTrace=true
0 --color
1 --require spec_helper
0 ---
1 inherit_from: .rubocop_todo.yml
2
3 AllCops:
4 TargetRubyVersion: 2.3
5 Exclude:
6 - acceptance/**/*
7 - vendor/**/*
8
9 require:
10 - rubocop-performance
11 - rubocop-rspec
12
13 Layout/LineLength:
14 Max: 120
15
16 Lint/RaiseException:
17 Enabled: true
18
19 Lint/StructNewOverride:
20 Enabled: true
21
22 Metrics/MethodLength:
23 Max: 20
24 Exclude:
25 - 'lib/facter/custom_facts/util/values.rb'
26 - 'lib/facter/custom_facts/util/confine.rb'
27 - 'lib/facter/custom_facts/core/execution/windows.rb'
28 - 'lib/facter/custom_facts/core/execution/base.rb'
29 - 'lib/facter/framework/detector/os_detector.rb'
30 - 'lib/facter/resolvers/bsd/ffi/ffi_helper.rb'
31 - 'install.rb'
32 - 'scripts/generate_changelog.rb'
33 - 'lib/facter/resolvers/aix/ffi/ffi_helper.rb'
34
35
36 Metrics/ModuleLength:
37 Max: 100
38 Exclude:
39 - 'lib/facter.rb'
40 - 'agent/lib/facter-ng.rb'
41 - 'lib/facter/config.rb'
42 - 'lib/facter/resolvers/aix/ffi/ffi_helper.rb'
43
44 Metrics/BlockLength:
45 Exclude:
46 - !ruby/regexp /(?:(?!.+_spec.rb).)*$/
47
48 Naming/ClassAndModuleCamelCase:
49 Exclude:
50 - 'spec/mocks/**/*'
51
52 Metrics/AbcSize:
53 Max: 16
54 Exclude:
55 - 'spec/custom_facts/util/parser_spec.rb'
56 - 'spec/custom_facts/core/logging_spec.rb'
57 - 'lib/facter/custom_facts/util/values.rb'
58 - 'lib/facter/custom_facts/util/loader.rb'
59 - 'lib/facter/custom_facts/util/confine.rb'
60 - 'lib/facter/custom_facts/util/confine.rb'
61 - 'lib/facter/custom_facts/core/execution/windows.rb'
62 - 'lib/facter/custom_facts/core/execution/base.rb'
63 - 'lib/facter/custom_facts/core/resolvable.rb'
64 - 'lib/facter/resolvers/bsd/ffi/ffi_helper.rb'
65 - 'install.rb'
66 - 'scripts/generate_changelog.rb'
67 - 'lib/facter/resolvers/aix/ffi/ffi_helper.rb'
68 - 'lib/facter/custom_facts/core/execution/popen3.rb'
69 - 'lib/facter.rb'
70 - 'lib/facter/framework/parsers/query_parser.rb'
71 - 'lib/facter/framework/core/fact_manager.rb'
72
73 Metrics/PerceivedComplexity:
74 Exclude:
75 - 'lib/facter/custom_facts/util/values.rb'
76 - 'lib/facter/custom_facts/util/confine.rb'
77 - 'lib/facter/custom_facts/core/execution/windows.rb'
78 - 'lib/facter/custom_facts/core/execution/posix.rb'
79 - 'install.rb'
80 - 'lib/facter/resolvers/aix/ffi/ffi_helper.rb'
81
82 Metrics/CyclomaticComplexity:
83 Exclude:
84 - 'lib/facter/resolvers/windows/product_release.rb'
85 - 'lib/facter/custom_facts/util/values.rb'
86 - 'lib/facter/custom_facts/util/confine.rb'
87 - 'lib/facter/custom_facts/core/execution/windows.rb'
88 - 'lib/facter/custom_facts/core/execution/posix.rb'
89 - 'lib/facter/facts/linux/cloud/provider.rb'
90 - 'lib/facter/facts/windows/cloud/provider.rb'
91 - 'lib/facter/framework/detector/os_detector.rb'
92 - 'install.rb'
93 - 'scripts/generate_changelog.rb'
94 - 'lib/facter/resolvers/aix/ffi/ffi_helper.rb'
95
96 Metrics/ClassLength:
97 Exclude:
98 - 'lib/facter/resolvers/partitions.rb'
99 - 'lib/facter/custom_facts/core/execution/base.rb'
100 - 'lib/facter/custom_facts/util/fact.rb'
101 - 'lib/facter/resolvers/windows/networking.rb'
102 - 'lib/facter/custom_facts/util/collection.rb'
103 - 'lib/facter/framework/core/options/option_store.rb'
104 - 'lib/facter/framework/cli/cli.rb'
105 - 'lib/facter/framework/core/cache_manager.rb'
106 - 'install.rb'
107 - 'scripts/generate_changelog.rb'
108 - 'lib/facter/resolvers/solaris/networking.rb'
109 - 'lib/facter/framework/logging/logger.rb'
110 - 'lib/facter/framework/core/fact_manager.rb'
111
112 Naming/AccessorMethodName:
113 Exclude:
114 - 'lib/facter/custom_facts/core/suitable.rb'
115
116 Naming/MethodName:
117 Exclude:
118 - 'spec/mocks/**/*'
119
120 Naming/PredicateName:
121 Exclude:
122 - 'lib/facter/custom_facts/core/suitable.rb'
123
124 Naming/FileName:
125 Exclude:
126 - 'lib/facter.rb'
127 - 'agent/lib/facter-ng.rb'
128
129 RSpec/ExampleLength:
130 Enabled: false
131
132 RSpec/DescribedClass:
133 EnforcedStyle: explicit
134
135 RSpec/NestedGroups:
136 Max: 6
137
138 Style/Documentation:
139 Enabled: false
140
141 Style/ClassVars:
142 Exclude:
143 - !ruby/regexp /(?:(?!.+_resolver.rb).)*$/
144
145 Style/FrozenStringLiteralComment:
146 Exclude:
147 - 'spec/custom_facts/util/normalization_spec.rb'
148 - 'spec/custom_facts/core/execution/windows_spec.rb'
149 - 'spec/custom_facts/core/execution/posix_spec.rb'
150 - 'lib/facter/custom_facts/util/resolution.rb'
151 - 'lib/facter/custom_facts/core/execution/windows.rb'
152 - 'lib/facter/custom_facts/core/execution/posix.rb'
153
154 Style/TrivialAccessors:
155 AllowDSLWriters: true
156
157 Style/CaseEquality:
158 Exclude:
159 - 'spec/custom_facts/util/confine_spec.rb'
160 - 'lib/facter/custom_facts/util/confine.rb'
161
162 Style/DoubleNegation:
163 Exclude:
164 - 'lib/facter/custom_facts/util/confine.rb'
165 - 'lib/facter/custom_facts/util/confine.rb'
166 - 'lib/facter/custom_facts/core/execution/windows.rb'
167 - 'lib/facter/custom_facts/core/execution/posix.rb'
168
169 Style/MethodMissingSuper:
170 Exclude:
171 - 'lib/facter.rb'
172
173 Style/MissingRespondToMissing:
174 Exclude:
175 - 'lib/facter.rb'
176
177 Style/StderrPuts:
178 Exclude:
179 - 'lib/facter/custom_facts/core/logging.rb'
180
181 Style/ModuleFunction:
182 Exclude:
183 - 'lib/facter/custom_facts/core/logging.rb'
184
185 Style/HashEachMethods:
186 Enabled: false # not implemented in ruby 2.3
187
188 Style/HashTransformKeys:
189 Enabled: false # not implemented in ruby 2.3
190
191 Style/HashTransformValues:
192 Enabled: false # not implemented in ruby 2.3
0 # This configuration was generated by
1 # `rubocop --auto-gen-config`
2 # on 2021-09-27 16:56:42 +0300 using RuboCop version 0.81.0.
3 # The point is for the user to remove these configuration records
4 # one by one as the offenses are removed from the code base.
5 # Note that changes in the inspected code, or installation of new
6 # versions of RuboCop, may require this file to be generated again.
7
8 # Offense count: 3
9 # Cop supports --auto-correct.
10 # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
11 # URISchemes: http, https
12 Layout/LineLength:
13 Max: 164
14
15 # Offense count: 1
16 # Cop supports --auto-correct.
17 # Configuration parameters: AllowInHeredoc.
18 Layout/TrailingWhitespace:
19 Exclude:
20 - 'repro.rb'
21
22 # Offense count: 3
23 # Configuration parameters: IgnoredMethods.
24 Metrics/CyclomaticComplexity:
25 Max: 9
26
27 # Offense count: 2
28 # Configuration parameters: IgnoredMethods.
29 Metrics/PerceivedComplexity:
30 Max: 9
31
32 # Offense count: 68
33 # Configuration parameters: CustomTransform, IgnoreMethods, SpecSuffixOnly.
34 RSpec/FilePath:
35 Enabled: false
36
37 # Offense count: 15
38 # Configuration parameters: AssignmentOnly.
39 RSpec/InstanceVariable:
40 Exclude:
41 - 'spec/custom_facts/core/execution/fact_manager_spec.rb'
42 - 'spec/custom_facts/util/collection_spec.rb'
43 - 'spec/custom_facts/util/confine_spec.rb'
44
45 # Offense count: 8
46 RSpec/LeakyConstantDeclaration:
47 Exclude:
48 - 'spec/custom_facts/core/resolvable_spec.rb'
49 - 'spec/custom_facts/core/suitable_spec.rb'
50 - 'spec/custom_facts/util/collection_spec.rb'
51 - 'spec/facter/resolvers/macosx/mountpoints_spec.rb'
52 - 'spec/facter/util/windows/network_utils_spec.rb'
53
54 # Offense count: 75
55 # Configuration parameters: EnforcedStyle.
56 # SupportedStyles: have_received, receive
57 RSpec/MessageSpies:
58 Enabled: false
59
60 # Offense count: 6
61 RSpec/SubjectStub:
62 Exclude:
63 - 'spec/custom_facts/core/aggregate_spec.rb'
64 - 'spec/custom_facts/core/resolvable_spec.rb'
65 - 'spec/custom_facts/util/fact_spec.rb'
66 - 'spec/custom_facts/util/resolution_spec.rb'
67
68 # Offense count: 111
69 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
70 RSpec/VerifiedDoubles:
71 Enabled: false
72
73 # Offense count: 1
74 # Cop supports --auto-correct.
75 # Configuration parameters: EnforcedStyle.
76 # SupportedStyles: always, always_true, never
77 Style/FrozenStringLiteralComment:
78 Exclude:
79 - 'spec/custom_facts/util/normalization_spec.rb'
80 - 'spec/custom_facts/core/execution/windows_spec.rb'
81 - 'spec/custom_facts/core/execution/posix_spec.rb'
82 - 'lib/facter/custom_facts/util/resolution.rb'
83 - 'lib/facter/custom_facts/core/execution/windows.rb'
84 - 'lib/facter/custom_facts/core/execution/posix.rb'
85 - 'repro.rb'
0 paths: # Files to analyse.
1 - 'lib'
2 - 'bin'
3 - 'config'
0 ## Facter release notes are now provided as part of the [official Puppet documentation](https://puppet.com/docs/puppet/7/release_notes_facter.html) and are no longer tracked in this file.
1
2 # Previous versions
3
4 ## [4.0.44](https://github.com/puppetlabs/facter/tree/4.0.44) (2020-10-21)
5
6 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.43...4.0.44)
7
8 ### Added
9
10 - Added disk_type field to disk fact [#2145](https://github.com/puppetlabs/facter/pull/2145) ([kozl](https://github.com/kozl))
11
12 ### Fixed
13
14 - (FACT-2806) Fix os.release.minor on amazon 6 [#2133](https://github.com/puppetlabs/facter/pull/2133) ([florindragos](https://github.com/florindragos))
15 - (FACT-2832) Use full path for augparse command [#2135](https://github.com/puppetlabs/facter/pull/2135) ([oanatmaria](https://github.com/oanatmaria))
16 - (FACT-2815) Added timing for cached facts [#2134](https://github.com/puppetlabs/facter/pull/2134) ([sebastian-miclea](https://github.com/sebastian-miclea))
17 - (FACT-2834) Dinamically get AIX proc number [#2147](https://github.com/puppetlabs/facter/pull/2147) ([sebastian-miclea](https://github.com/sebastian-miclea))
18 - (FACT-2829) Fixed partitions and mount points facts [#2146](https://github.com/puppetlabs/facter/pull/2146) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
19 - (maint) Use strings instead of symbols for os names. [#2149](https://github.com/puppetlabs/facter/pull/2149) ([IrimieBogdan](https://github.com/IrimieBogdan))
20
21
22 ## [4.0.43](https://github.com/puppetlabs/facter/tree/4.0.43) (2020-10-12)
23
24 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.42...4.0.43)
25
26 ### Fixed
27
28 - (FACT-2810) Fix dmi.board_asset_tag and dhcp [#2125](https://github.com/puppetlabs/facter/pull/2125) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
29 - (FACT-2817) Only invalidate session cache on clear and reset. [#2121](https://github.com/puppetlabs/facter/pull/2121) ([IrimieBogdan](https://github.com/IrimieBogdan))
30 - (maint) Fix virtual_detector [#2128](https://github.com/puppetlabs/facter/pull/2128) ([IrimieBogdan](https://github.com/IrimieBogdan))
31 - (FACT-2806) Fix physicalprocessorcount [#2127](https://github.com/puppetlabs/facter/pull/2127) ([florindragos](https://github.com/florindragos))
32 - (FACT-2809) Fixed output differences on solaris [#2116](https://github.com/puppetlabs/facter/pull/2116) ([sebastian-miclea](https://github.com/sebastian-miclea))
33
34
35 ## [4.0.42](https://github.com/puppetlabs/facter/tree/4.0.42) (2020-10-07)
36
37 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.41...4.0.42)
38
39 ### Added
40
41 - (FACT-2792) Show not supported message for facter -p [#2119](https://github.com/puppetlabs/facter/pull/2119) ([IrimieBogdan](https://github.com/IrimieBogdan))
42
43 ### Fixed
44
45 - (FACT-2805) Read available memory from MemAvailable [#2109](https://github.com/puppetlabs/facter/pull/2109) ([florindragos](https://github.com/florindragos))
46 - (maint) Avoid deadlock of Facter::Core::Execution.execute [#2114](https://github.com/puppetlabs/facter/pull/2114) ([oanatmaria](https://github.com/oanatmaria))
47 - (maint) Fix external fact cache [#2123](https://github.com/puppetlabs/facter/pull/2123) ([florindragos](https://github.com/florindragos))
48
49
50 ## [4.0.41](https://github.com/puppetlabs/facter/tree/4.0.41) (2020-10-01)
51
52 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.40...4.0.41)
53
54 ### Fixed
55
56 - (FACT-2824) Facter make ec2 metadata requests when on gce [#2113](https://github.com/puppetlabs/facter/pull/2113) ([IrimieBogdan](https://github.com/IrimieBogdan))
57
58
59 ## [4.0.40](https://github.com/puppetlabs/facter/tree/4.0.40) (2020-09-30)
60
61 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.39...4.0.40)
62
63 ### Added
64
65 - (FACT-2774) Extend facter API with resolve. [#2054](https://github.com/puppetlabs/facter/pull/2054) ([IrimieBogdan](https://github.com/IrimieBogdan))
66
67 ### Fixed
68
69 - (FACT-2798) Set color to true, fix Facter.log_exception [#2105](https://github.com/puppetlabs/facter/pull/2105) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
70 - (FACT-2816) - Fix ec2 fact issues when on non ec2 systems [#2106](https://github.com/puppetlabs/facter/pull/2106) ([logicminds](https://github.com/logicminds))
71 - (FACT-2799) Fix fact loading for nested fact calls [#2108](https://github.com/puppetlabs/facter/pull/2108) ([IrimieBogdan](https://github.com/IrimieBogdan))
72 - (FACT-2786) Fix fact caching if fact is defined in multiple groups [#2089](https://github.com/puppetlabs/facter/pull/2089) ([florindragos](https://github.com/florindragos))
73 - (maint) Fix for blockdevice_*_size legacy fact on Aix and Solaris [#2111](https://github.com/puppetlabs/facter/pull/2111) ([sebastian-miclea](https://github.com/sebastian-miclea))
74
75
76 ## [4.0.39](https://github.com/puppetlabs/facter/tree/4.0.39) (2020-09-23)
77
78 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.38...4.0.39)
79
80 ### Added
81
82 - (FACT-2746) Added cloud resolver [#2082](https://github.com/puppetlabs/facter/pull/2082) ([sebastian-miclea](https://github.com/sebastian-miclea))
83 - (FACT-2317) Add Facter.define_fact method [#2102](https://github.com/puppetlabs/facter/pull/2102) ([oanatmaria](https://github.com/oanatmaria))
84 - FACT(2326) Add Facter.each method [#2100](https://github.com/puppetlabs/facter/pull/2100) ([florindragos](https://github.com/florindragos))
85 - (FACT-2324) Add loadfacts API method [#2103](https://github.com/puppetlabs/facter/pull/2103) ([sebastian-miclea](https://github.com/sebastian-miclea))
86
87 ### Fixed
88
89 - (FACT-2802) Fix Cloud resolver [#2093](https://github.com/puppetlabs/facter/pull/2093) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
90 - (FACT-2803) Detect hypervisors as amazon if virtwhat detects aws. [#2095](https://github.com/puppetlabs/facter/pull/2095) ([IrimieBogdan](https://github.com/IrimieBogdan))
91 - (FACT-2748) Fixed type for blockdevice_*_size [#2098](https://github.com/puppetlabs/facter/pull/2098) ([sebastian-miclea](https://github.com/sebastian-miclea))
92 - (FACT-2793) Time limit for Facter::Core::Execute [#2080](https://github.com/puppetlabs/facter/pull/2080) ([oanatmaria](https://github.com/oanatmaria))
93
94
95 ## [4.0.38](https://github.com/puppetlabs/facter/tree/4.0.38) (2020-09-16)
96
97 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.37...4.0.38)
98
99 ### Added
100
101 - (FACT-2319) Added debugonce method [#2085](https://github.com/puppetlabs/facter/pull/2085) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
102 - (FACT-2327) added list method [#2088](https://github.com/puppetlabs/facter/pull/2088) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
103 - (FACT-2320) Added warnonce method [#2084](https://github.com/puppetlabs/facter/pull/2084) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
104 - (FACT-2315) Added warn method to facter api [#2083](https://github.com/puppetlabs/facter/pull/2083) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
105
106 ### Fixed
107
108 - (FACT-2784) Fixed rhel os release fact [#2086](https://github.com/puppetlabs/facter/pull/2086) ([sebastian-miclea](https://github.com/sebastian-miclea))
109
110
111 ## [4.0.37](https://github.com/puppetlabs/facter/tree/4.0.37) (2020-09-09)
112
113 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.36-fixed...4.0.37)
114
115 ### Added
116
117 - (FACT-1380) Restore --timing option to native facter [#2061](https://github.com/puppetlabs/facter/pull/2061) ([IrimieBogdan](https://github.com/IrimieBogdan))
118
119 ### Fixed
120
121 - (FACT-2781) Fix filesystems on osx [#2065](https://github.com/puppetlabs/facter/pull/2065) ([florindragos](https://github.com/florindragos))
122 - (FACT-2777) Fix lsbdist facts on ubuntu [#2063](https://github.com/puppetlabs/facter/pull/2063) ([florindragos](https://github.com/florindragos))
123 - (FACT-2783) Updated how osx mountpoints are calculated [#2072](https://github.com/puppetlabs/facter/pull/2072) ([sebastian-miclea](https://github.com/sebastian-miclea))
124 - (FACT-2776) Fix Linux partitions fact [#2076](https://github.com/puppetlabs/facter/pull/2076) ([oanatmaria](https://github.com/oanatmaria))
125 - (FACT-2785) partitions.<partition_name>.mount has wrong value on sles15-64 [#2077](https://github.com/puppetlabs/facter/pull/2077) ([IrimieBogdan](https://github.com/IrimieBogdan))
126
127
128 ## [4.0.36](https://github.com/puppetlabs/facter/tree/4.0.36) (2020-09-02)
129
130 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.35...4.0.36-fixed)
131
132 ### Added
133
134 - (FACT-2747) Add missing legacy facts on all platforms [#2034](https://github.com/puppetlabs/facter/pull/2034) ([IrimieBogdan](https://github.com/IrimieBogdan))
135 - (FACT-2721) Added Solaris virtual fact [#2033](https://github.com/puppetlabs/facter/pull/2033) ([sebastian-miclea](https://github.com/sebastian-miclea))
136 - (FACT-2745) Add Linux xen fact [#2040](https://github.com/puppetlabs/facter/pull/2040) ([oanatmaria](https://github.com/oanatmaria))
137 - (FACT-2740) Add Gce fact [#2035](https://github.com/puppetlabs/facter/pull/2035) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
138 - (FACT-2743) Added LDom fact for solaris [#2041](https://github.com/puppetlabs/facter/pull/2041) ([sebastian-miclea](https://github.com/sebastian-miclea))
139 - (FACT-2296) Added fact group for legacy facts [#2047](https://github.com/puppetlabs/facter/pull/2047) ([sebastian-miclea](https://github.com/sebastian-miclea))
140 - (FACT-2753) Resolve facts sequentially. [#2050](https://github.com/puppetlabs/facter/pull/2050) ([IrimieBogdan](https://github.com/IrimieBogdan))
141 - (FACT-2728) Added hypervisors fact for Solaris [#2045](https://github.com/puppetlabs/facter/pull/2045) ([sebastian-miclea](https://github.com/sebastian-miclea))
142 - (FACT-2752) Added serialnumber fact for AIX [#2052](https://github.com/puppetlabs/facter/pull/2052) ([sebastian-miclea](https://github.com/sebastian-miclea))
143 - (FACT-2729) Add Solaris is_virtual fact [#2056](https://github.com/puppetlabs/facter/pull/2056) ([oanatmaria](https://github.com/oanatmaria))
144 - (FACT-2773) Added board_asset_tag fact for linux [#2059](https://github.com/puppetlabs/facter/pull/2059) ([sebastian-miclea](https://github.com/sebastian-miclea))
145
146 ### Fixed
147
148 - (FACT-2454) fix how used memory is calculated [#2038](https://github.com/puppetlabs/facter/pull/2038) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
149 - (FACT-2747-scope6) Allow scope6 to be blocked on all platforms [#2037](https://github.com/puppetlabs/facter/pull/2037) ([IrimieBogdan](https://github.com/IrimieBogdan))
150 - (maint) Add nil check for ec2 facts. [#2042](https://github.com/puppetlabs/facter/pull/2042) ([IrimieBogdan](https://github.com/IrimieBogdan))
151 - (maint) Correctly initialise logger. [#2043](https://github.com/puppetlabs/facter/pull/2043) ([IrimieBogdan](https://github.com/IrimieBogdan))
152 - (FACT-2747) Add ssh legacy facts. [#2044](https://github.com/puppetlabs/facter/pull/2044) ([IrimieBogdan](https://github.com/IrimieBogdan))
153 - (FACT-2561) Fix blocking mechanism [#2046](https://github.com/puppetlabs/facter/pull/2046) ([oanatmaria](https://github.com/oanatmaria))
154 - (FACT-2741) Fix double quotes for numbers in yaml formatter [#2053](https://github.com/puppetlabs/facter/pull/2053) ([florindragos](https://github.com/florindragos))
155 - (FACT-2754) Add os.distro release legacy facts [#2055](https://github.com/puppetlabs/facter/pull/2055) ([oanatmaria](https://github.com/oanatmaria))
156 - (FACT-2771) Fix Solaris kernelmajversion fact [#2057](https://github.com/puppetlabs/facter/pull/2057) ([oanatmaria](https://github.com/oanatmaria))
157 - (FACT-2457) Display newlines in values [#2058](https://github.com/puppetlabs/facter/pull/2058) ([florindragos](https://github.com/florindragos))
158
159
160 ## [4.0.35](https://github.com/puppetlabs/facter/tree/4.0.35) (2020-08-19)
161
162 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.34...4.0.35)
163
164 ### Added
165
166 - (FACT-2726) Add solaris dmi facts [#2025](https://github.com/puppetlabs/facter/pull/2025) ([florindragos](https://github.com/florindragos))
167 - (FACT-2722) Add disks fact for Solaris [#2027](https://github.com/puppetlabs/facter/pull/2027) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
168
169 ### Fixed
170
171 - (FACT-2723) --list-*-groups also displays external facts [#2024](https://github.com/puppetlabs/facter/pull/2024) ([sebastian-miclea](https://github.com/sebastian-miclea))
172 - (FACT-2742) Exclude net/https when running on jruby FIPS [#2030](https://github.com/puppetlabs/facter/pull/2030) ([IrimieBogdan](https://github.com/IrimieBogdan))
173 - (FACT-2737) facter uptime shows host uptime inside docker container [#2031](https://github.com/puppetlabs/facter/pull/2031) ([IrimieBogdan](https://github.com/IrimieBogdan))
174 - (FACT-2672) Fix ssh fact output [#2029](https://github.com/puppetlabs/facter/pull/2029) ([oanatmaria](https://github.com/oanatmaria))
175 - (FACT-2402) Exclude fuseblk from filesystems [#2032](https://github.com/puppetlabs/facter/pull/2032) ([oanatmaria](https://github.com/oanatmaria))
176
177
178 ## [4.0.34](https://github.com/puppetlabs/facter/tree/4.0.34) (2020-08-12)
179
180 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.33...4.0.34)
181
182 ### Added
183
184 - (FACT-2739) Extend os hierarchy to consider multiple os families [#2016](https://github.com/puppetlabs/facter/pull/2016) ([IrimieBogdan](https://github.com/IrimieBogdan))
185 - Add FreeBSD memory facts [#2020](https://github.com/puppetlabs/facter/pull/2020) ([smortex](https://github.com/smortex))
186 - Add FreeBSD dmi facts [#2021](https://github.com/puppetlabs/facter/pull/2021) ([smortex](https://github.com/smortex))
187 - (FACT-2727) add load averages for Solaris [#2023](https://github.com/puppetlabs/facter/pull/2023) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
188
189 ### Fixed
190
191 - (FACT-2714) Fix dhcp on solaris 10 [#2013](https://github.com/puppetlabs/facter/pull/2013) ([IrimieBogdan](https://github.com/IrimieBogdan))
192 - (FACT-2732) OracleLinux 7 and Scientific Linux 7 OS facts incorrect in Facter 4.0.30 [#2014](https://github.com/puppetlabs/facter/pull/2014) ([IrimieBogdan](https://github.com/IrimieBogdan))
193
194
195 ## [4.0.33](https://github.com/puppetlabs/facter/tree/4.0.33) (2020-08-05)
196
197 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.32...4.0.33)
198
199 ### Added
200 - \(FACT-2040\) Added solaris memory resolver [\#1999](https://github.com/puppetlabs/facter/pull/1999) ([sebastian-miclea](https://github.com/sebastian-miclea))
201
202 ### Fixed
203 - \(FACT-2735\) virtual not working on EXADATA baremetal [\#2004](https://github.com/puppetlabs/facter/pull/2004) ([IrimieBogdan](https://github.com/IrimieBogdan))
204 - \(FACT-2736\) networking facts don't work on EXADATA baremetal [\#2008](https://github.com/puppetlabs/facter/pull/2008) ([IrimieBogdan](https://github.com/IrimieBogdan))
205 - \(FACT-2724\) Confine blocks behave differently with Facter 4, causing spec tests to suddenly fail [\#2010](https://github.com/puppetlabs/facter/pull/2010) ([IrimieBogdan](https://github.com/IrimieBogdan))
206
207 ## [4.0.32](https://github.com/puppetlabs/facter/tree/4.0.32) (2020-07-30)
208
209 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.31...4.0.32)
210
211 ### Added
212 - \(FACT-2717\) Block external facts [\#1998](https://github.com/puppetlabs/facter/pull/1998) ([florindragos](https://github.com/florindragos))
213
214 ### Fixed
215 - \(FACT-2733\) Fix networking on Fedora 32 [\#2002](https://github.com/puppetlabs/facter/pull/2002) ([oanatmaria](https://github.com/oanatmaria))
216 - \(FACT-2734\) Return nil codename if we cannot determine it from /etc/redhat-release [\#2003](https://github.com/puppetlabs/facter/pull/2003) ([IrimieBogdan](https://github.com/IrimieBogdan))
217 - \(FACT-2699\) Detect augeas from gem if augparse is not available. [\#1993](https://github.com/puppetlabs/facter/pull/1993) ([IrimieBogdan](https://github.com/IrimieBogdan))
218
219
220 ## [4.0.31](https://github.com/puppetlabs/facter/tree/4.0.31) (2020-07-29)
221
222 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.30...4.0.31)
223
224 ### Added
225 - \(FACT-2718\) Block custom facts [\#1996](https://github.com/puppetlabs/facter/pull/1996) ([IrimieBogdan](https://github.com/IrimieBogdan))
226 - \(FACT-2230\) Add Aix memory facts [\#1994](https://github.com/puppetlabs/facter/pull/1994) ([oanatmaria](https://github.com/oanatmaria))
227 - \(FACT-2220\) Add Aix disks fact [\#1987](https://github.com/puppetlabs/facter/pull/1987) ([oanatmaria](https://github.com/oanatmaria))
228 - \(FACT-2708\) Add man pages [\#1984 ](https://github.com/puppetlabs/facter/pull/1984) ([florindragos](https://github.com/florindragos))
229
230 ### Fixed
231 - \(FACT-2710\) Correctly display vmware info [\#1988](https://github.com/puppetlabs/facter/pull/1987) ([oanatmaria](https://github.com/oanatmaria))
232 - \(FACT-2702\) Fix system_profiler legacy facts [\#1982](https://github.com/puppetlabs/facter/pull/1982) ([oanatmaria](https://github.com/oanatmaria))
233 - Handle Time and Symbol in executable facts [\#1977](https://github.com/puppetlabs/facter/pull/1977) ([gimmyxd](https://github.com/gimmyxd))
234
235 ## [4.0.30](https://github.com/puppetlabs/facter/tree/4.0.30) (2020-07-15)
236
237 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.29...4.0.30)
238
239 ### Added
240 - \(FACT-2690\) Added Hyper-V fact for Linux [\#1968](https://github.com/puppetlabs/facter/pull/1968) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
241 - \(FACT-2694\) Add linux openvz fact [\#1970](https://github.com/puppetlabs/facter/pull/1970) ([oanatmaria](https://github.com/oanatmaria))
242 - \(FACT-2656\) Add solaris networking facts [\#1947](https://github.com/puppetlabs/facter/pull/1947) ([sebastian-miclea](https://github.com/sebastian-miclea))
243 - \(FACT-2689\) Add hypervisors docker fact [\#1950](https://github.com/puppetlabs/facter/pull/1950) ([oanatmaria](https://github.com/oanatmaria))
244 - \(FACT-2683\) Added remaining legacy networking facts for OSX [\#1952](https://github.com/puppetlabs/facter/pull/1952) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
245 - \(FACT-2692\) Add hypervisors lxc fact [\#1953](https://github.com/puppetlabs/facter/pull/1953) ([oanatmaria](https://github.com/oanatmaria))
246 - \(FACT-2691\) Add kvm fact on linux [\#1955](https://github.com/puppetlabs/facter/pull/1955) ([IrimieBogdan](https://github.com/IrimieBogdan))
247 - \(FACT-2697\) Add Xen fact [\#1957](https://github.com/puppetlabs/facter/pull/1957) ([IrimieBogdan](https://github.com/IrimieBogdan))
248 - \(FACT-2695\) implementation for virtualbox hypervisor fact [\#1956](https://github.com/puppetlabs/facter/pull/1956) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
249 - \(FACT-2693\) Add systemd_nspawn fact [\#1958](https://github.com/puppetlabs/facter/pull/1958) ([oanatmaria](https://github.com/oanatmaria))
250 - \(FACT-2696\) Add vmware fact [\#1963](https://github.com/puppetlabs/facter/pull/1963) ([IrimieBogdan](https://github.com/IrimieBogdan))
251
252 ### Fixed
253 - \(FACT-2673\) Fix mountpoints logic for osx [\#1971](https://github.com/puppetlabs/facter/pull/1971) ([oanatmaria](https://github.com/oanatmaria))
254 - \(maint\) Silent solaris_zones facts on FreeBSD [\#1954](https://github.com/puppetlabs/facter/pull/1954) ([smortex](https://github.com/smortex))
255
256 ## [4.0.29](https://github.com/puppetlabs/facter/tree/4.0.29) (2020-07-01)
257
258 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.28...4.0.29)
259
260 ### Added
261 - \(FACT-2218\) virtual fact for OSX [\#1945](https://github.com/puppetlabs/facter/pull/1945) ([IrimieBogdan](https://github.com/IrimieBogdan))
262 - \(FACT-2232\) Add Aix networking facts [\#1937](https://github.com/puppetlabs/facter/pull/1937) ([oanatmaria](https://github.com/oanatmaria))
263
264 ### Fixed
265 - \(FACT-2676\) fix os identifier for opensuse-leap [\#1944](https://github.com/puppetlabs/facter/pull/1944) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
266 - FACT-2679 Get DHCP for all interfaces on OSX [\#1940](https://github.com/puppetlabs/facter/pull/1940) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
267
268 ## [4.0.28](https://github.com/puppetlabs/facter/tree/4.0.28) (2020-06-25)
269
270 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.27...4.0.28)
271
272 ### Fixed
273 - \(maint\) Fix aio_agent_version on non AIO node [\#1938](https://github.com/puppetlabs/facter/pull/1938) ([smortex](https://github.com/smortex))
274
275 ## [4.0.27](https://github.com/puppetlabs/facter/tree/4.0.27) (2020-06-24)
276
277 [Full Changelog](https://github.com/puppetlabs/facter/compare/4.0.26...4.0.27)
278
279 ### Added
280 - \(FACT-2212\) Networking facts for OSX [\#1929](https://github.com/puppetlabs/facter/pull/1929) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
281 - \(maint\) Add FreeBSD disks and partitions facts [\#553](https://github.com/puppetlabs/facter-ng/pull/553) ([smortex](https://github.com/smortex))
282 - \(FACT-2638\) Use puppet AIO VERSION file to specify AIO version [\#549](https://github.com/puppetlabs/facter-ng/pull/549) ([IrimieBogdan](https://github.com/IrimieBogdan))
283 - \(FACT-2654\) Add ec2 facts for Windows [\#546](https://github.com/puppetlabs/facter-ng/pull/546) ([oanatmaria](https://github.com/oanatmaria))
284 - \(FACT-2620\) Add EC2 facts for linux [\#544](https://github.com/puppetlabs/facter-ng/pull/544) ([oanatmaria](https://github.com/oanatmaria))
285 - \(FACT-2619\) External facts cache [\#541](https://github.com/puppetlabs/facter-ng/pull/541) ([florindragos](https://github.com/florindragos))
286 - Add support for processors facts on \*BSD [\#489](https://github.com/puppetlabs/facter-ng/pull/489) ([smortex](https://github.com/smortex))
287
288 ### Fixed
289 - \(FACT-2668\) Networking fact on linux should have logic for selecting IPs [\#1928](https://github.com/puppetlabs/facter/pull/1928) ([IrimieBogdan](https://github.com/IrimieBogdan))
290 - \(FACT-2678\) Facter sometimes pollutes the calling processes environment (race condition) [\#1932](https://github.com/puppetlabs/facter/pull/1932) ([IrimieBogdan](https://github.com/IrimieBogdan))
291
292 ## [4.0.26](https://github.com/puppetlabs/facter-ng/tree/4.0.26) (2020-06-11)
293
294 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.25...4.0.26)
295
296 ### Added
297
298 - \(FACT-2608\) Add is\_virtual fact [\#535](https://github.com/puppetlabs/facter-ng/pull/535) ([oanatmaria](https://github.com/oanatmaria))
299 - \(FACT-2609\) Add lspci resolver [\#534](https://github.com/puppetlabs/facter-ng/pull/534) ([oanatmaria](https://github.com/oanatmaria))
300 - \(FACT-2245\) Add xen resolver [\#532](https://github.com/puppetlabs/facter-ng/pull/532) ([oanatmaria](https://github.com/oanatmaria))
301 - \(FACT-2607\) Add Openvz detector [\#531](https://github.com/puppetlabs/facter-ng/pull/531) ([oanatmaria](https://github.com/oanatmaria))
302 - \(FACT-2600\) Run acceptance tests on Windows [\#519](https://github.com/puppetlabs/facter-ng/pull/519) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
303
304 ### Fixed
305
306 - \(FACT-2651\) Fix --list-cache-groups when there are multiple arguments before it [\#545](https://github.com/puppetlabs/facter-ng/pull/545) ([IrimieBogdan](https://github.com/IrimieBogdan))
307 - FACT-2650 Fix bug when loading external facts [\#543](https://github.com/puppetlabs/facter-ng/pull/543) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
308 - Use proper encoding [\#539](https://github.com/puppetlabs/facter-ng/pull/539) ([faucct](https://github.com/faucct))
309 - \(FACT-2635\) Incorrect output for non existing fact [\#536](https://github.com/puppetlabs/facter-ng/pull/536) ([IrimieBogdan](https://github.com/IrimieBogdan))
310
311
312
313 ## [4.0.25](https://github.com/puppetlabs/facter-ng/tree/4.0.25) (2020-05-29)
314
315 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.24...4.0.25)
316
317 ### Fixed
318
319 - \(FACT-2636\) Set external as fact\_type for environment variable facts. [\#537](https://github.com/puppetlabs/facter-ng/pull/537) ([IrimieBogdan](https://github.com/IrimieBogdan))
320
321
322
323 ## [4.0.24](https://github.com/puppetlabs/facter-ng/tree/4.0.24) (2020-05-26)
324
325 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.23...4.0.24)
326
327 ### Added
328
329 - \(FACT-2605\) Add vmware resolver [\#525](https://github.com/puppetlabs/facter-ng/pull/525) ([oanatmaria](https://github.com/oanatmaria))
330 - \(FACT-2604\) Add virt-what resolver [\#523](https://github.com/puppetlabs/facter-ng/pull/523) ([oanatmaria](https://github.com/oanatmaria))
331
332
333
334 ## [4.0.23](https://github.com/puppetlabs/facter-ng/tree/4.0.23) (2020-05-22)
335
336 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.22...4.0.23)
337
338 ### Fixed
339
340 - \(FACT-2632\) Log error message if we encounter exceptions while loading custom facts files [\#528](https://github.com/puppetlabs/facter-ng/pull/528) ([IrimieBogdan](https://github.com/IrimieBogdan))
341 - \(FACT-2631\) Trace is not working as expected [\#527](https://github.com/puppetlabs/facter-ng/pull/527) ([IrimieBogdan](https://github.com/IrimieBogdan))
342
343
344
345 ## [4.0.22](https://github.com/puppetlabs/facter-ng/tree/4.0.22) (2020-05-20)
346
347 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.21...4.0.22)
348
349 ### Added
350
351 - \(FACT-2603\) Detect virtual on GCE vms [\#521](https://github.com/puppetlabs/facter-ng/pull/521) ([oanatmaria](https://github.com/oanatmaria))
352 - \(FACT-2602\) Add docker/Lxc resolver for Linux [\#520](https://github.com/puppetlabs/facter-ng/pull/520) ([oanatmaria](https://github.com/oanatmaria))
353 - \(FACT-2615\) Add Solaris mountpoints fact [\#515](https://github.com/puppetlabs/facter-ng/pull/515) ([oanatmaria](https://github.com/oanatmaria))
354 - \(FACT-2532\) Add Aix nim\_type fact [\#513](https://github.com/puppetlabs/facter-ng/pull/513) ([oanatmaria](https://github.com/oanatmaria))
355 - \(FACT-2183\) Add Solaris's uptime legacy facts [\#511](https://github.com/puppetlabs/facter-ng/pull/511) ([oanatmaria](https://github.com/oanatmaria))
356
357 ### Fixed
358
359 - \(FACT-2617\) Fix for tests/external\_facts/external\_fact\_stderr\_messages\_output\_to\_stderr.rb [\#522](https://github.com/puppetlabs/facter-ng/pull/522) ([IrimieBogdan](https://github.com/IrimieBogdan))
360 - \(FACT-2523\) Fix for tests/external\_facts/non\_root\_users\_default\_external\_fact\_directory.rb [\#518](https://github.com/puppetlabs/facter-ng/pull/518) ([IrimieBogdan](https://github.com/IrimieBogdan))
361 - \(FACT-2522\) Fix for tests/external\_facts/fact\_directory\_precedence.rb [\#517](https://github.com/puppetlabs/facter-ng/pull/517) ([IrimieBogdan](https://github.com/IrimieBogdan))
362 - \(FACT-2521\) Fix for tests/external\_facts/external\_fact\_overrides\_custom\_fact\_with\_10000\_weight\_or\_less.rb [\#514](https://github.com/puppetlabs/facter-ng/pull/514) ([IrimieBogdan](https://github.com/IrimieBogdan))
363 - \(FACT-2525\) Fix for tests/options/color.rb [\#512](https://github.com/puppetlabs/facter-ng/pull/512) ([IrimieBogdan](https://github.com/IrimieBogdan))
364
365
366
367 ## [4.0.21](https://github.com/puppetlabs/facter-ng/tree/4.0.21) (2020-05-13)
368
369 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.20...4.0.21)
370
371 ### Added
372
373 - \(FACT-2599\) Run GitHub Actions on Ubuntu 16 and Osx 10 [\#497](https://github.com/puppetlabs/facter-ng/pull/497) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
374 - \(FACT-2247\) Add networking fact for linux [\#496](https://github.com/puppetlabs/facter-ng/pull/496) ([oanatmaria](https://github.com/oanatmaria))
375 - \(FACT-2515\) Define custom fact groups in facter.conf [\#491](https://github.com/puppetlabs/facter-ng/pull/491) ([florindragos](https://github.com/florindragos))
376 - \(FACT-2557\) Add rake task for generating list of facts for specified OS [\#488](https://github.com/puppetlabs/facter-ng/pull/488) ([IrimieBogdan](https://github.com/IrimieBogdan))
377 - Add os.release facts on FreeBSD [\#485](https://github.com/puppetlabs/facter-ng/pull/485) ([smortex](https://github.com/smortex))
378 - \(FACT-2235\) Add Aix processors fact [\#483](https://github.com/puppetlabs/facter-ng/pull/483) ([oanatmaria](https://github.com/oanatmaria))
379 - \(FACT-2569\) Run acceptance tests on Ubuntu GitHub actions [\#477](https://github.com/puppetlabs/facter-ng/pull/477) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
380 - \(FACT-2553\) Quote special string in YAML format [\#471](https://github.com/puppetlabs/facter-ng/pull/471) ([oanatmaria](https://github.com/oanatmaria))
381 - \(FACT-2517\) Open3 wrapper for executing system calls [\#469](https://github.com/puppetlabs/facter-ng/pull/469) ([oanatmaria](https://github.com/oanatmaria))
382
383 ### Fixed
384
385 - \(FACT-2533\) Fix for tests/facts/partitions.rb [\#507](https://github.com/puppetlabs/facter-ng/pull/507) ([oanatmaria](https://github.com/oanatmaria))
386 - \(FACT-2531\) Fix for tests/facts/validate\_file\_system\_size\_bytes.rb [\#500](https://github.com/puppetlabs/facter-ng/pull/500) ([oanatmaria](https://github.com/oanatmaria))
387 - \(FACT-2582\) Date and Time in external YAML fact is not loaded [\#499](https://github.com/puppetlabs/facter-ng/pull/499) ([IrimieBogdan](https://github.com/IrimieBogdan))
388 - \(FACT-2556\) Refactor existing facts to use the new OS hierarchy [\#486](https://github.com/puppetlabs/facter-ng/pull/486) ([IrimieBogdan](https://github.com/IrimieBogdan))
389
390
391
392 ## [4.0.20](https://github.com/puppetlabs/facter-ng/tree/4.0.20) (2020-05-06)
393
394 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.19...4.0.20)
395
396 ### Added
397
398 - Add \*BSD kernelversion and kernelmajversion facts [\#462](https://github.com/puppetlabs/facter-ng/pull/462) ([smortex](https://github.com/smortex))
399 - Fix os.family fact on \*BSD [\#461](https://github.com/puppetlabs/facter-ng/pull/461) ([smortex](https://github.com/smortex))
400 - Add support for \*BSD load averages [\#460](https://github.com/puppetlabs/facter-ng/pull/460) ([smortex](https://github.com/smortex))
401
402 ### Fixed
403
404 - \(FACT-2590\) No facts are displayed on Redhat 5 and Centos6 [\#484](https://github.com/puppetlabs/facter-ng/pull/484) ([IrimieBogdan](https://github.com/IrimieBogdan))
405 - \(FACT-2530\) Fix for tests/facts/os\_processors\_and\_kernel.rb [\#449](https://github.com/puppetlabs/facter-ng/pull/449) ([oanatmaria](https://github.com/oanatmaria))
406
407
408
409 ## [4.0.19](https://github.com/puppetlabs/facter-ng/tree/4.0.19) (2020-04-29)
410
411 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.18...4.0.19)
412
413 ### Added
414
415 - \(FACT-2555\)Create OS hierarchy and mechanism for loading it [\#470](https://github.com/puppetlabs/facter-ng/pull/470) ([IrimieBogdan](https://github.com/IrimieBogdan))
416 - \(FACT-2552\) Add Solaris processors facts [\#451](https://github.com/puppetlabs/facter-ng/pull/451) ([oanatmaria](https://github.com/oanatmaria))
417 - \(Fact 2486\) Add facts cache [\#430](https://github.com/puppetlabs/facter-ng/pull/430) ([florindragos](https://github.com/florindragos))
418
419 ### Fixed
420
421 - \(FACT-2585\) Mountpoints fact returns ASCI-8BIT instead of UTF-8 in some cases [\#472](https://github.com/puppetlabs/facter-ng/pull/472) ([IrimieBogdan](https://github.com/IrimieBogdan))
422 - \(FACT-2570\) Use Facter options to store custom and external facts [\#467](https://github.com/puppetlabs/facter-ng/pull/467) ([IrimieBogdan](https://github.com/IrimieBogdan))
423 - \(FACT-2565\) Debian development versions causes fatal error when resolving os.release [\#466](https://github.com/puppetlabs/facter-ng/pull/466) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
424
425
426
427 ## [4.0.18](https://github.com/puppetlabs/facter-ng/tree/4.0.18) (2020-04-24)
428
429 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.17...4.0.18)
430
431 ### Added
432
433 - \(FACT-2564\) Add support for zpool\_featureflags and fix zpool\_version [\#443](https://github.com/puppetlabs/facter-ng/pull/443) ([smortex](https://github.com/smortex))
434
435 ### Fixed
436
437 - \(FACT-2553\) remove double backslashes from windows path [\#456](https://github.com/puppetlabs/facter-ng/pull/456) ([oanatmaria](https://github.com/oanatmaria))
438 - \(FACT-2559\) Fix Facter.debugging? call when Facter not fully loaded [\#455](https://github.com/puppetlabs/facter-ng/pull/455) ([Filipovici-Andrei](https://github.com/Filipovici-Andrei))
439
440
441
442 ## [4.0.17](https://github.com/puppetlabs/facter-ng/tree/4.0.17) (2020-04-21)
443
444 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.16...4.0.17)
445
446 ### Fixed
447
448 - \(FACT-2562\) Correctly load custom and external fact directories [\#458](https://github.com/puppetlabs/facter-ng/pull/458) ([IrimieBogdan](https://github.com/IrimieBogdan))
449
450
451
452 ## [4.0.16](https://github.com/puppetlabs/facter-ng/tree/4.0.16) (2020-04-15)
453
454 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.15...4.0.16)
455
456 ### Added
457
458 - \(FACT-2233\) Add AIX partitons fact [\#433](https://github.com/puppetlabs/facter-ng/pull/433) ([oanatmaria](https://github.com/oanatmaria))
459 - \(FACT-2330\) Add ssh fact for Windows OpenSSH feature [\#424](https://github.com/puppetlabs/facter-ng/pull/424) ([oanatmaria](https://github.com/oanatmaria))
460
461 ### Fixed
462
463 - \(FACT-2528\) Fix for tests/facts/ssh\_key.rb [\#442](https://github.com/puppetlabs/facter-ng/pull/442) ([oanatmaria](https://github.com/oanatmaria))
464 - \(FACT-2538\) Don't save core and legacy facts in collection if they have no value [\#441](https://github.com/puppetlabs/facter-ng/pull/441) ([IrimieBogdan](https://github.com/IrimieBogdan))
465
466
467
468 ## [4.0.15](https://github.com/puppetlabs/facter-ng/tree/4.0.15) (2020-04-08)
469
470 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.14...4.0.15)
471
472 ### Added
473
474 - \(FACT-2541\) Add TYPE for legacy facts [\#439](https://github.com/puppetlabs/facter-ng/pull/439) ([oanatmaria](https://github.com/oanatmaria))
475 - \(FACT-2535\) Allow interpolation of Facter.fact\('fact\_name'\) [\#435](https://github.com/puppetlabs/facter-ng/pull/435) ([sebastian-miclea](https://github.com/sebastian-miclea))
476 - \(FACT-2477\) Collect facts from alternative sources [\#422](https://github.com/puppetlabs/facter-ng/pull/422) ([oanatmaria](https://github.com/oanatmaria))
477
478 ### Fixed
479
480 - \(FACT-2513\) Updated how option aliases are displayed [\#434](https://github.com/puppetlabs/facter-ng/pull/434) ([sebastian-miclea](https://github.com/sebastian-miclea))
481 - \(FACT-2499\) Facts with aliases are resolved only once [\#429](https://github.com/puppetlabs/facter-ng/pull/429) ([IrimieBogdan](https://github.com/IrimieBogdan))
482
483
484
485 ## [4.0.14](https://github.com/puppetlabs/facter-ng/tree/4.0.14) (2020-04-01)
486
487 [Full Changelog](https://github.com/puppetlabs/facter-ng/compare/4.0.13...4.0.14)
488
489 ### Added
490
491 - \(FACT-2512\) Handle Raspbian as Debian [\#421](https://github.com/puppetlabs/facter-ng/pull/421) ([mlove-au](https://github.com/mlove-au))
492 - \(FACT-2231\) Add AIX mountpoints fact [\#398](https://github.com/puppetlabs/facter-ng/pull/398) ([oanatmaria](https://github.com/oanatmaria))
493 - \(FACT-2471\) Add Linux partitions fact [\#393](https://github.com/puppetlabs/facter-ng/pull/393) ([oanatmaria](https://github.com/oanatmaria))
494 - Debugger tool [\#391](https://github.com/puppetlabs/facter-ng/pull/391) ([sebastian-miclea](https://github.com/sebastian-miclea))
495 - \(FACT-2435\) Expose :expand as an option to execute command [\#342](https://github.com/puppetlabs/facter-ng/pull/342) ([florindragos](https://github.com/florindragos))
496
497 ### Fixed
498
499 - \(FACT-2511\) Remove file logger [\#425](https://github.com/puppetlabs/facter-ng/pull/425) ([IrimieBogdan](https://github.com/IrimieBogdan))
500 - \(FACT-2498\) Internal fact loader should only load facts once [\#420](https://github.com/puppetlabs/facter-ng/pull/420) ([IrimieBogdan](https://github.com/IrimieBogdan))
501 - Avoid exceptions for zone facts on FreeBSD [\#412](https://github.com/puppetlabs/facter-ng/pull/412) ([smortex](https://github.com/smortex))
502 - \(FACT-2475\) Fix os.release on Debian [\#410](https://github.com/puppetlabs/facter-ng/pull/410) ([oanatmaria](https://github.com/oanatmaria))
503
504
505
506 \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
+0
-248
CMakeLists.txt less more
0 cmake_minimum_required(VERSION 3.2.2)
1 project(FACTER VERSION 3.14.12)
2
3 # Set this early, so it's available. AIX gets weird, man.
4 if("${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
5 set(AIX TRUE)
6 endif()
7
8 if (NOT CMAKE_BUILD_TYPE)
9 message(STATUS "Defaulting to a release build.")
10 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE)
11 endif()
12
13 include(CheckIncludeFile)
14 CHECK_INCLUDE_FILE(utmpx.h HAVE_UTMPX_H -DHAVE_UTMPX_H)
15
16 option(YAMLCPP_STATIC "Use yaml-cpp's static libraries" OFF)
17 option(BUILD_SHARED_LIBS "Build libfacter as a shared library" ON)
18
19 set(FACTER_PATH "" CACHE PATH "Specify the location to look for specific binaries before trying PATH.")
20 if (FACTER_PATH)
21 # Specify a preferred location for binary lookup that will be prioritized over PATH.
22 file(TO_CMAKE_PATH ${FACTER_PATH} FACTER_PATH_FIXED)
23 add_definitions(-DFACTER_PATH="${FACTER_PATH_FIXED}")
24 message(STATUS "Prioritizing binary lookup in ${FACTER_PATH_FIXED}")
25 endif()
26
27 set(FACTER_RUBY "" CACHE FILEPATH "Specify the location of libruby at compile-time, bypassing dynamic lookup.")
28 if (FACTER_RUBY)
29 file(TO_CMAKE_PATH ${FACTER_RUBY} FACTER_RUBY_PATH)
30 add_definitions(-DFACTER_RUBY="${FACTER_RUBY_PATH}")
31 message(STATUS "Fixing lookup for libruby to ${FACTER_RUBY_PATH}")
32 endif()
33
34 set(AIO_AGENT_VERSION "" CACHE STRING "Creates an aio_agent_version fact with the specified value.")
35 if (AIO_AGENT_VERSION)
36 add_definitions(-DAIO_AGENT_VERSION="${AIO_AGENT_VERSION}")
37 message(STATUS "Adding fact aio_agent_version=${AIO_AGENT_VERSION}")
38 endif()
39
40 enable_testing()
41
42 list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
43
44 if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
45 # Allow searching in boxen installed homebrew directories
46 # http://stackoverflow.com/questions/1487752/how-do-i-instruct-cmake-to-look-for-libraries-installed-by-macports
47 set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/boxen/homebrew/lib)
48 set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/boxen/homebrew/include)
49 endif()
50
51 include(FeatureSummary)
52
53 SET(LEATHERMAN_COMPONENTS locale catch nowide logging util file_util dynamic_library execution ruby rapidjson)
54
55 # We look for curl early, because whether or not we link to the leatherman curl library
56 # is dependant on whether or not we find curl on the system.
57 if ((("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD|Linux|OpenBSD") OR WIN32) AND NOT WITHOUT_CURL)
58 find_package(CURL)
59 if (CURL_FOUND)
60 add_definitions(-DUSE_CURL)
61 list(APPEND LEATHERMAN_COMPONENTS curl)
62 endif()
63 set_package_properties(CURL PROPERTIES DESCRIPTION "A free and easy-to-use client-side URL transfer library" URL "http://curl.haxx.se/libcurl/")
64 set_package_properties(CURL PROPERTIES TYPE OPTIONAL PURPOSE "Enables facts that require HTTP.")
65 endif()
66
67 if (WIN32)
68 list(APPEND LEATHERMAN_COMPONENTS windows)
69 endif()
70
71 find_package(Leatherman REQUIRED COMPONENTS ${LEATHERMAN_COMPONENTS})
72
73 # Now that we have leatherman, we can pulll in its options file, which
74 # we need for finding all our other libraries.
75 include(options)
76
77 if (LEATHERMAN_USE_LOCALES)
78 add_definitions(-DLEATHERMAN_I18N)
79 SET(BOOST_COMPONENTS locale)
80 endif()
81
82 # We use program_options, system, filesystem, date_time, and regex directly. Testing uses thread and chrono.
83 list(APPEND BOOST_COMPONENTS program_options system filesystem date_time regex thread chrono)
84
85 find_package(Boost 1.54 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
86 # date_time and regex need threads on some platforms, and find_package Boost only includes
87 # pthreads if you require the Boost.Thread component.
88 find_package(Threads)
89
90 find_package(Ruby 1.9)
91
92 find_package(YAMLCPP REQUIRED)
93 if (NOT WITHOUT_OPENSSL)
94 find_package(OPENSSL)
95 endif()
96 if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux" AND NOT WITHOUT_BLKID)
97 find_package(BLKID)
98 endif()
99
100 find_package(CPPHOCON REQUIRED)
101
102 if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
103 find_package(UDEV)
104 endif()
105
106 find_package(WHEREAMI)
107 if (WHEREAMI_FOUND)
108 add_definitions(-DUSE_WHEREAMI)
109 endif()
110
111 if (NOT WITHOUT_JRUBY AND NOT WIN32)
112 find_package(JNI)
113 set_package_properties(JNI PROPERTIES DESCRIPTION "Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by native applications.")
114 set_package_properties(JNI PROPERTIES TYPE OPTIONAL PURPOSE "Enables JRuby support in Facter.")
115
116 if (JNI_FOUND)
117 find_package(Java)
118 set_package_properties(Java PROPERTIES DESCRIPTION "Java compiler for JNI.")
119 set_package_properties(Java PROPERTIES TYPE OPTIONAL PURPOSE "Enables JRuby support in Facter.")
120
121 if (Java_JAVAC_EXECUTABLE)
122 set(JRUBY_SUPPORT TRUE)
123 set(CMAKE_JAVA_COMPILE_FLAGS -source 1.6 -target 1.6)
124 add_definitions(-DUSE_JRUBY_SUPPORT)
125 endif()
126 endif()
127 endif()
128
129 # Display a summary of the features
130 include(FeatureSummary)
131 feature_summary(WHAT ALL)
132
133 # Set RPATH if not installing to a system library directory
134 list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" INSTALL_IS_SYSTEM_DIR)
135 if ("${INSTALL_IS_SYSTEM_DIR}" STREQUAL "-1")
136 set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
137 endif()
138
139 # Pull in common cflags setting from leatherman
140 include(cflags)
141 set(FACTER_CXX_FLAGS "${LEATHERMAN_CXX_FLAGS}")
142 add_definitions(${LEATHERMAN_DEFINITIONS})
143
144 option(DYNAMICBASE "Add dynamicbase linker option" ON)
145 # Enable DEP/ASLR switches on windows versions
146 if (WIN32)
147 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat")
148 if (DYNAMICBASE)
149 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase")
150 endif()
151 endif()
152
153 #
154 # Add cpplint and cppcheck targets
155 #
156 file(GLOB_RECURSE ALL_SOURCES lib/src/*.cc lib/inc/*.hpp lib/inc/*.h exe/*.cc exe/*.hpp exe/*.h)
157 add_cpplint_files(${ALL_SOURCES})
158 enable_cpplint()
159
160 add_cppcheck_dirs("${PROJECT_SOURCE_DIR}/lib" "${PROJECT_SOURCE_DIR}/exe")
161 enable_cppcheck()
162
163 # Pull in helper macros for working with leatherman libraries
164 include(leatherman)
165
166 set(CMAKE_REQUIRED_FLAGS "-std=c++11")
167 set(CMAKE_REQUIRED_LIBRARIES ${LEATHERMAN_LIBRARIES})
168 set(CMAKE_REQUIRED_INCLUDES ${LEATHERMAN_INCLUDE_DIRS})
169
170 # Guards to make sure we don't compile what we don't have in Leatherman
171 CHECK_CXX_SOURCE_COMPILES("
172 #include <leatherman/util/environment.hpp>
173 int main() {
174 int a = leatherman::util::environment::get_int(\"A\", 1000);
175 return 0;
176 }
177 " HAS_LTH_GET_INT)
178
179 if (HAS_LTH_GET_INT)
180 add_definitions(-DHAS_LTH_GET_INT)
181 endif()
182
183 CHECK_CXX_SOURCE_COMPILES("
184 #include \"leatherman/execution/execution.hpp\"
185
186 using namespace leatherman::execution;
187 using namespace std;
188 int main() {
189 vector<string> paths;
190 expand_command(\"command\", paths, false);
191 return 0;
192 }
193 " HAS_LTH_EXPAND)
194
195 if (HAS_LTH_EXPAND)
196 add_definitions(-DHAS_LTH_EXPAND)
197 endif()
198 if (WIN32)
199 CHECK_CXX_SOURCE_COMPILES("
200 #include \"leatherman/windows/registry.hpp\"
201
202 using namespace leatherman::windows;
203 int main() {
204 int test = 0;
205 try {
206 test = registry::get_registry_dword(registry::HKEY::LOCAL_MACHINE, \"RandomSubkey\", \"RandomValue\");
207 } catch (registry_exception &e) {
208 return test;
209 }
210 }
211 " HAS_LTH_GET_DWORD)
212
213 if (HAS_LTH_GET_DWORD)
214 add_definitions(-DHAS_LTH_GET_DWORD)
215 endif()
216 endif()
217
218 add_subdirectory(lib)
219 add_subdirectory(exe)
220 add_subdirectory(locales)
221
222 # Add test executables for unit testing
223 add_test(NAME "libfacter\\ tests" COMMAND libfacter_test)
224 if (RUBY_FOUND)
225 find_program(BUNDLER_PATH NAMES bundle.bat bundle)
226 if (BUNDLER_PATH)
227 message(STATUS "Bundler found, installing dependencies for Ruby tests...")
228 execute_process(COMMAND ${BUNDLER_PATH} install --retry 2 --path=vendor/bundle WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
229 add_test(NAME "libfacter\\ specs" COMMAND ${BUNDLER_PATH} exec rspec WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
230 endif()
231 endif()
232 add_test(NAME "facter\\ smoke" COMMAND facter)
233
234 # Install the man page
235 if ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD|FreeBSD")
236 set(MANDIR man/man8/)
237 else()
238 set(MANDIR share/man/man8/)
239 endif()
240
241 option(INSTALL_BATCH_FILES "Generate .bat files during installation" ON)
242 install(FILES ${PROJECT_SOURCE_DIR}/man/man8/facter.8 DESTINATION ${MANDIR})
243 if (WIN32 AND INSTALL_BATCH_FILES)
244 install(FILES ${PROJECT_SOURCE_DIR}/ext/windows/facter.bat DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
245 install(FILES ${PROJECT_SOURCE_DIR}/ext/windows/facter_interactive.bat DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
246 install(FILES ${PROJECT_SOURCE_DIR}/ext/windows/run_facter_interactive.bat DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
247 endif()
0 * @puppetlabs/night-s-watch
0 # Default to Ghost team
1 * @puppetlabs/phoenix
0
01 # How to contribute
12
23 Third-party patches are essential for keeping facter great. We simply can't
34 access the huge number of platforms and myriad configurations for running
4 facter. We want to keep it as easy as possible to contribute changes that
5 Facter. We want to keep it as easy as possible to contribute changes that
56 get things working in your environment. There are a few guidelines that we
67 need contributors to follow so that we can have a chance of keeping on
78 top of things.
1314 * Submit a Jira ticket for your issue if one does not already exist.
1415 * Clearly describe the issue including steps to reproduce when it is a bug.
1516 * Make sure you fill in the earliest version that you know has the issue.
16 * A ticket is not necessary for [trivial changes](https://puppet.com/community/trivial-patch-exemption-policy)
17 * A ticket is not necessary for trivial changes
1718 * Fork the repository on GitHub.
18
19 ## New Facts
20
21 All new facts should also be included in `lib/schema/facter.yaml`. Without this
22 facts won't pass acceptance tests.
2319
2420 ## Making Changes
2521
2622 * Create a topic branch from where you want to base your work.
27 * This is usually the master branch.
28 * Only target release branches if you are certain your fix must be on that
29 branch.
30 * If you use facter as part of the [puppet-agent](https://github.com/puppetlabs/puppet-agent),
31 double-check the base branch to make sure your fix gets in the correct
32 stream.
33
34 | facter | puppet-agent |
35 |--------|--------------|
36 | 3.11.x | 5.5.x |
37 | 3.12.x | 6.0.x |
38 | 3.13.x | 6.4.x |
39 | master | master |
40
41 For example, if you use puppet5 you will want to base your work on top of
42 the `3.11.x` branch, so your fix would end up in a subsequent release of
43 puppet-agent `5.5.x`.
44
45 Once merged, your work will be automatically promoted to the other release
23 * This is usually the `main` branch.
24 * Once merged, your work will be automatically promoted to the other release
4625 streams when our internal CI passes.
47 * To quickly create a topic branch based on master, run `git checkout -b
48 fix/master/my_contribution master`. Please avoid working directly on the
49 `master` branch.
26 * To quickly create a topic branch based on main, run `git checkout -b
27 fix/main/my_contribution main`. Please avoid working directly on the
28 `main` branch.
5029 * Make commits of logical units.
5130 * Check for unnecessary whitespace with `git diff --check` before committing.
52 * If you have python 2 in your path you can run `make cpplint` to ensure your
53 code formatting is clean. The linter runs as part of Travis CI and could fail
54 the CI build.
55 * If you have cppcheck in your path you can run `make cppcheck` to ensure your
56 code passes static analysis. cppcheck runs as part of Travis CI and could
57 fail the CI build.
58 * Make sure your commit messages are in the proper format. If the commit
59 addresses an issue filed in the
60 [Facter Jira project](https://tickets.puppetlabs.com/browse/FACT), start
61 the first line of the commit with the issue number in parentheses.
31 * Make sure your commit messages are in the proper format. We use the [50/72 rule](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project) in commit messages.
32 * If the commit addresses an issue filed in the [Facter Jira project](https://tickets.puppetlabs.com/browse/FACT), start the first line of the commit with the issue number in parentheses.
6233
6334 ````
6435 (FACT-1234) Make the example in CONTRIBUTING imperative and concrete
7647
7748 * Make sure you have added the necessary tests for your changes.
7849 * Run _all_ the tests to assure nothing else was accidentally broken.
50 * We recommend running the follwing rake tasks:
51 * `bundle exec rake rubocop` - runs rubocop cheks
52 * `bundle exec rake spec_random` - runs unit tests
53 * `bundle exec rake spec_integration` - runs integration tests. Note that tests with the `skip_outside_ci` tag are excluded outside of CI, you can run them with `CI=true bundle exec rake spec_integration`
54 * Or you can use `bundle exec rake check` to run all the above tasks in one command
7955
8056 ## Making Trivial Changes
8157
82 For [changes of a trivial nature](https://puppet.com/community/trivial-patch-exemption-policy), it is not always necessary to create a new
58 For changes of a trivial nature, it is not always necessary to create a new
8359 ticket in Jira. In this case, it is appropriate to start the first line of a
84 commit with one of `(docs)`, `(maint)`, or `(packaging)` instead of a ticket
85 number.
60 commit with one of `(docs)` or `(maint)` instead of a ticket number.
8661
8762 If a Jira ticket exists for the documentation commit, you can include it
8863 after the `(docs)` token.
10075 the new documentation or comments added.
10176 ```
10277
103 For commits that address trivial repository maintenance tasks or packaging
104 issues, start the first line of the commit with `(maint)` or `(packaging)`,
105 respectively.
78 For commits that address trivial repository maintenance tasks start the first line of the commit with `(maint)`
10679
10780 ## Submitting Changes
10881
11487
11588 # Additional Resources
11689
117 * [Puppet community guidelines](https://docs.puppet.com/community/community_guidelines.html)
90 * [Puppet community guidelines](https://puppet.com/community/community-guidelines/)
11891 * [Bug tracker (Jira)](https://tickets.puppetlabs.com/browse/FACT)
11992 * [Contributor License Agreement](http://links.puppet.com/cla)
12093 * [General GitHub documentation](http://help.github.com/)
121 * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
122 * #puppet-dev IRC channel on freenode.org
94 * [GitHub pull request documentation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
12395 * [puppet-dev mailing list](https://groups.google.com/forum/#!forum/puppet-dev)
96 * [Puppet-dev Slack channel](https://puppetcommunity.slack.com/archives/C0W1X7ZAL)
00 Extensibility
11 =============
22
3 Native Facter has the following extensibility goals:
3 Facter 4 has the following extensibility goals:
44
5 * Clear guidelines for what facts are supported in-project
6 * Compatibility with 90+% of existing Facter custom facts
5 * Compatibility with 100% of existing Facter custom facts
76 * Compatibility with 100% of existing Facter external facts
8 * New features for external facts, including depends/requires logic
97
108 The following sections discuss those goals in more depth.
119
1210 Note that this doc is work-in-progress and should be updated as extensibility features are implemented, refined or ruled out.
1311
14 Built-in facts
15 --------------
16
17 These are the criteria for Native Facter pull requests:
18
19 * Additional resolutions for existing built-in facts (e.g. the operatingsystem facts for new Linux Distros) must conform to the facter.json schema.
20 * New facts must be accompanied by support in the facter.json schema.
21 * For [SemVer](http://semver.org) purposes, a change in fact value is only considered breaking if a) the fact is documented in the schema, and b) the fact is applicable on a platform we test against in [Puppet CI](http://jenkins.puppetlabs.com).
22
2312 Custom Facts Compatibility
2413 --------------------------
2514
26 Ruby Facter supports "custom facts" that are facts implemented using the public Facter API.
27
28 Native Facter implements a Ruby compatibility layer to support simple and aggregate fact resolutions.
29 Native Facter searches for a MRI Ruby library to load and initializes a Ruby VM as needed
30 to resolve custom facts.
31
32 The environment variable `LEATHERMAN_RUBY` can be used to explicitly instruct native Facter to use
33 a particular Ruby library (e.g. `LEATHERMAN_RUBY=/usr/local/lib/libruby.so.1.9.3`). If not set, native Facter will search for and load the highest
34 version Ruby library on the system.
35
36 **Ruby 1.8.x is not supported by native Facter. Please use Ruby 1.9.3 or later.**
37
38 Native Facter will load custom facts from the following locations:
15 Facter 4 will load custom facts from the following locations:
3916
4017 * Any Ruby source file in a `facter` subdirectory on the Ruby load path.
4118 * Any Ruby source file in a directory specified by the `FACTERLIB` environment variable (delimited by the platform PATH separator).
4219 * Any Ruby source file in a directory specified by the `--custom-dir` option to facter.
4320
44 The following methods from the Facter API are currently supported by native Facter:
21 The following methods from the Facter API are currently supported by Facter 4:
4522
4623 From the `Facter` module:
4724
5835 * loadfacts
5936 * log_exception
6037 * reset
38 * resolve
6139 * search
6240 * search_path
6341 * search_external
10280 * name
10381 * on_flush
10482
105 Please note: the timeout option is not currently supported on resolutions. Setting a timeout will result in a warning
106 and the timeout will be ignored.
107
108 Please see the [Facter Custom Facts Walkthrough](https://docs.puppetlabs.com/facter/2.2/custom_facts.html) for more information on using the Facter API.
83 Please see the [Facter Custom Facts Walkthrough](https://puppet.com/docs/puppet/latest/custom_facts.html) for more information on using the Facter API.
10984
11085 External Facts Compatiblity
11186 ---------------------------
11287
113 Native Facter will support all 4 forms of "external facts" which Ruby Facter supports:
88 Facter 4 supports all 4 forms of "external facts" which Facter 3 supports:
11489 * JSON files with the .json extension whose key-value pairs will be mapped to fact-value pairs.
11590 * YAML files with the .yaml extension whose key-value pairs will be mapped to fact-value pairs.
11691 * Text files with the .txt extension containing `fact=some_value` strings
11792 * Executable files returning `fact=some_value` strings
11893
119 New Features for External Facts
120 -------------------------------
94 Enable conversion of dotted facts to structured
95 ---------------------------
12196
122 Caveat: It is TBD which of these features will be implemented in what releases.
97 By default Facter 4 treats the `.` in custom or external fact names as part of the fact name and not a delimiter for structured facts.
12398
124 * Executable external facts can take a json object on stdin describing all known facts. This will allow logic like the confine/value methods provide in the custom facts API.
99 If you want to enable the new behaviour, that converts dotted facts to structured you need to set the following config:
125100
126 * To support the use case of facts which depend on other facts: executable external facts can describe what facts they depend on and what facts they provide, via a json schema. (This schema could either be a parallel file distributed with the external fact following some naming convention (e.g. foo.schema for an external fact called foo.sh or foo.py), or the schema could be provided on stdout when executing the external fact with some parameter (e.g. foo.sh --show-schema).)
127
128 * Native Facter might add a weight indicator for returned facts, to support fact precedence for external facts, along the lines of the `has_weight` functionality.
129
130 * Native Facter might add a volatility indicator, such as a ttl field, to the json schema provided by external facts. This would allow facter in a long-running process to avoid unnecessary refreshes of non-volatile facts.
131
132 * Native Facter might include some language-specific shims for its external facts support, most likely for Ruby and Python, to ease writing new external facts (including ports of legacy custom facts).
101 ```
102 global : {
103 force-dot-resolution : true
104 }
105 ```
0 source ENV['GEM_SOURCE'] || 'https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/'
0 # frozen_string_literal: true
11
2 def location_for(place, fake_version = nil)
3 if place =~ /^(git[:@][^#]*)#(.*)/
4 [fake_version, { :git => $1, :branch => $2, :require => false }].compact
5 elsif place =~ /^file:\/\/(.*)/
6 ['>= 0', { :path => File.expand_path($1), :require => false }]
7 else
8 [place, { :require => false }]
9 end
2 source ENV['GEM_SOURCE'] || 'https://rubygems.org'
3
4 gemspec name: 'facter'
5
6 group(:release, optional: true) do
7 gem 'octokit', '~> 4.18.0'
108 end
119
12 gem 'packaging', *location_for(ENV['PACKAGING_LOCATION'] || '~> 0.99')
10 gem 'packaging', require: false
11
12 local_gemfile = File.expand_path('Gemfile.local', __dir__)
13 eval_gemfile(local_gemfile) if File.exist?(local_gemfile)
14
15 group(:documentation) do
16 gem 'ronn', '~> 0.7.3', require: false, platforms: [:ruby]
17 end
+0
-23
Gemfile.lock less more
0 GEM
1 remote: https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/
2 specs:
3 artifactory (2.8.2)
4 csv (3.1.5)
5 docopt (0.6.1)
6 packaging (0.99.66)
7 artifactory (~> 2)
8 rake (>= 12.3)
9 release-metrics
10 rake (13.0.1)
11 release-metrics (1.1.0)
12 csv
13 docopt
14
15 PLATFORMS
16 ruby
17
18 DEPENDENCIES
19 packaging (~> 0.99)
20
21 BUNDLED WITH
22 1.17.3
0 facter - Tool for collecting system facts.
1
2 Copyright (C) 2014 Puppet Labs Inc
3
4 Puppet Labs can be contacted at: info@puppetlabs.com
0
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176 END OF TERMS AND CONDITIONS
177
178 APPENDIX: How to apply the Apache License to your work.
179
180 To apply the Apache License to your work, attach the following
181 boilerplate notice, with the fields enclosed by brackets "{}"
182 replaced with your own identifying information. (Don't include
183 the brackets!) The text should be enclosed in the appropriate
184 comment syntax for the file format. We also recommend that a
185 file or class name and description of purpose be included on the
186 same "printed page" as the copyright notice for easier
187 identification within third-party archives.
188
189 Copyright {yyyy} {name of copyright owner}
5190
6191 Licensed under the Apache License, Version 2.0 (the "License");
7192 you may not use this file except in compliance with the License.
0 Facter 4
1 ============
2 Facter 4 is the newest version of Facter that is written in Ruby and compatible with Facter 3. Currenly Facter 4 is maintained on [4.x branch]( https://github.com/puppetlabs/facter/tree/4.x)
0 # facter
31
4 Native Facter
5 =============
2 [![Gem Version](https://badge.fury.io/rb/facter.svg)](https://badge.fury.io/rb/facter)
3 [<img src="https://img.shields.io/badge/slack-puppet--dev-brightgreen?logo=slack">](https://puppetcommunity.slack.com/messages/C0W1X7ZAL)
64
7 An implementation of facter functionality in C++11, providing:
5 [![Modules Status](https://github.com/puppetlabs/facter/workflows/Acceptance%20tests/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions)
6 [![Modules Status](https://github.com/puppetlabs/facter/workflows/Unit%20tests/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions)
7 [![Modules Status](https://github.com/puppetlabs/facter/workflows/Checks/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions)
8 [![Test Coverage](https://api.codeclimate.com/v1/badges/3bd4be86f4b0b49bc0ca/test_coverage)](https://codeclimate.com/github/puppetlabs/facter/test_coverage)
9 [![Maintainability](https://api.codeclimate.com/v1/badges/3bd4be86f4b0b49bc0ca/maintainability)](https://codeclimate.com/github/puppetlabs/facter/maintainability)
810
9 * a shared library which gather facts about the system
10 * an executable for standalone command line usage
11 * a ruby file to enable `require 'facter'`.
1211
13 Please see our [extensibility document](Extensibility.md) to learn more
14 about extending native facter using custom and external facts.
12 Facter is a command-line tool that gathers basic facts about nodes (systems)
13 such as hardware details, network settings, OS type and version, and more.
14 These facts are made available as variables in your Puppet manifests and can be
15 used to inform conditional expressions in Puppet.
1516
16 Build Requirements
17 ------------------
17 ## Documentation
1818
19 * GCC 4.8+ or Clang 5.0+ (OSX)
20 * CMake >= 3.2.2
21 * Boost C++ Libraries >= 1.54
22 * yaml-cpp >= 0.5.1
23 * [leatherman](https://github.com/puppetlabs/leatherman) >= 1.4.3
24 * [cpp-hocon](https://github.com/puppetlabs/cpp-hocon) >= 0.1.0
19 Documentation for the Facter project can be found on the [Puppet Docs
20 site](https://puppet.com/docs/puppet/latest/facter.html).
2521
26 Optional Build Libraries
27 ------------------------
22 ## Supported platforms
23 * Linux
24 * macOS
25 * Windows
26 * Solaris
27 * AIX
2828
29 * OpenSSL - enables SSH fingerprinting facts.
30 * libblkid (Linux only) - enables the partitions fact.
31 * libcurl >= 7.18.0 - enables facts that perform HTTP requests.
29 ## Requirements
30 * Ruby 2.3+
31 * FFI (for facts like `mountpoints` which are resolved using C API calls)
3232
33 Initial Setup
34 -------------
33 ## Basic concepts
34 The project has three main parts, the framework, facts and resolvers.
35 In the framework we implement functionality that is agnostic of specific facts like parsing user input, formatting output, etc.
3536
36 Note: Testing custom facts requires Ruby 1.9+ with libruby built as a dynamic library; that often implies development builds of Ruby.
37 Facts are the nuggets of information that will be provided by facter e.g. `os.name`, `networking.interfaces`, etc.
3738
38 ### Setup on Fedora 23
39 Resolvers have the role of gathering data from the system.
40 For example a resolver can execute a command on the system, can read a file or any operation that retrieves some data from a single source on the system.
3941
40 The following will install all required tools and libraries:
42 ![Facter user interaction](docs/diagrams/facter_user_interaction.png?raw=true)
4143
42 dnf install boost-devel openssl-devel yaml-cpp-devel libblkid-devel libcurl-devel gcc-c++ make wget tar cmake
44 ## Getting started
45 After cloning the project, run `bundle install` to install all dependencies.
4346
44 ### Setup on Mac OSX El Capitan (homebrew)
47 You can run facter by executing `./bin/facter`.
48 The command will output all the facts that facter detected for the current OS.
4549
46 This assumes Clang is installed and the system OpenSSL libraries will be used.
50 The implementation can be validated locally by running `bundle exec rake check`.
4751
48 The following will install all required libraries:
52 ## Goals - fast, easy, compatible
53 * Gain performance similar to the C++ version of Facter. We plan to achieve this goal by gathering multiple facts with only one call and by using the faster Win32 API rather than WMI for the Windows implementation.
54 * Facilitate community contribution. At the moment, C++ presents a possible impediment for community contributions.
55 * Enable native integration with other Ruby-based projects such as Bolt and puppet.
56 * Enable native integration for custom facts.
57 * Provide 100% compatibility with C++ Facter (drop-in replacement).
4958
50 brew install cmake boost yaml-cpp
51
52 ### Setup on Ubuntu 15.10 (Trusty)
53
54 The following will install most required tools and libraries:
55
56 apt-get install build-essential libboost-all-dev libssl-dev libyaml-cpp-dev libblkid-dev libcurl4-openssl-dev wget tar cmake
57
58 ### Setup on FreeBSD 10
59
60 The following will install most required tools and libraries:
61
62 pkg install git ruby21 cmake boost-all yaml-cpp gcc49
63
64 ### Setup on Windows
65
66 [MinGW-w64](http://mingw-w64.sourceforge.net/) is used for full C++11 support, and [Chocolatey](https://chocolatey.org) can be used to install. You should have at least 2GB of memory for compilation.
67
68 * install [CMake](https://chocolatey.org/packages/cmake)
69 * install [MinGW-w64](https://chocolatey.org/packages/mingw)
70
71 choco install mingw --params "/threads:win32"
72
73 For the remaining tasks, build commands can be executed in the shell from Start > MinGW-w64 project > Run Terminal
74
75 * select an install location for dependencies, such as C:\\tools or cmake\\release\\ext; we'll refer to it as $install
76
77 * build [Boost](http://sourceforge.net/projects/boost/files/latest/download)
78
79 .\bootstrap mingw
80 .\b2 toolset=gcc --build-type=minimal install --prefix=$install --with-program_options --with-system --with-filesystem --with-date_time --with-thread --with-regex --with-log --with-locale --with-chrono boost.locale.iconv=off
81
82 * build [yaml-cpp](https://github.com/jbeder/yaml-cpp)
83
84 cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=$install -DCMAKE_INSTALL_PREFIX=$install .
85 mingw32-make install
86
87 * build [libcurl](http://curl.haxx.se/download)
88
89 mingw32-make mingw32
90 cp -r include\curl $install\include
91 cp -r lib\libcurl.a $install\lib
92
93 In Powershell:
94
95 choco install cmake 7zip.commandline -y
96 $env:PATH = "C:\Program Files\CMake\bin;$env:PATH"
97 choco install mingw --params "/threads:win32" -y
98 $env:PATH = "C:\tools\mingw64\bin;$env:PATH"
99 $install = "C:\tools"
100
101 (New-Object Net.WebClient).DownloadFile("https://downloads.sourceforge.net/boost/boost_1_54_0.7z", "$pwd/boost_1_54_0.7z")
102 7za x boost_1_54_0.7z
103 pushd boost_1_54_0
104 .\bootstrap mingw
105 .\b2 toolset=gcc --build-type=minimal install --prefix=$install --with-program_options --with-system --with-filesystem --with-date_time --with-thread --with-regex --with-log --with-locale --with-chrono boost.locale.iconv=off
106 popd
107
108 (New-Object Net.WebClient).DownloadFile("https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.5.3.tar.gz", "$pwd/yaml-cpp-0.5.3.tar.gz")
109 7za x yaml-cpp-0.5.3.tar.gz
110 7za x yaml-cpp-0.5.3.tar
111 pushd yaml-cpp-yaml-cpp-0.5.3
112 cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH="$install" -DCMAKE_INSTALL_PREFIX="$install" .
113 mingw32-make install
114 popd
115
116 (New-Object Net.WebClient).DownloadFile("http://curl.haxx.se/download/curl-7.42.1.zip", "$pwd/curl-7.42.1.zip")
117 7za x curl-7.42.1.zip
118 pushd curl-7.42.1
119 mingw32-make mingw32
120 cp -r include\curl $install\include
121 cp -r lib\libcurl.a $install\lib
122 popd
123
124 Note that OpenSSL isn't needed on Windows.
125
126 ### Setup on OpenBSD 6.0
127
128 The following will install all required tools and libraries:
129
130 pkg_add boost cmake curl g++ ruby yaml-cpp
131
132 Optionally `leatherman` can be installed from packages too if not
133 built locally.
134
135 ### Build and install Leatherman and cpp-hocon
136
137 [Leatherman](https://github.com/puppetlabs/leatherman) and [cpp-hocon](https://github.com/puppetlabs/cpp-hocon) are built similar to the Pre-Build instructions below. If building on Windows, install to the same `$install` location used for other dependencies.
138
139 Pre-Build
140 ---------
141
142 All of the following examples start by assuming the current directory is the root of the repo.
143
144 On Windows, add `-G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=\<binary install path\> -DBOOST_STATIC=ON` to the `cmake` invocation.
145
146 Before building facter, use `cmake` to generate build files:
147
148 $ mkdir release
149 $ cd release
150 $ cmake ..
151
152 To generate build files with debug information:
153
154 $ mkdir debug
155 $ cd debug
156 $ cmake -DCMAKE_BUILD_TYPE=Debug ..
157
158 Build
159 -----
160
161 To build facter, use 'make':
162
163 $ cd release
164 $ make
165
166 To build facter with debug information:
167
168 $ cd debug
169 $ make
170
171 Run
172 ---
173
174 You can run facter from where it was built:
175
176 `$ release/bin/facter`
177
178 For a debug build:
179
180 `$ debug/bin/facter`
181
182 Test
183 ----
184
185 If a ruby was found during configuration, execute the following command before running tests:
186
187 $ bundle install --gemfile lib/Gemfile
188
189 You can run facter tests using the test target:
190
191 $ cd release
192 $ make test
193
194 For a debug build:
195
196 $ cd debug
197 $ make test
198
199 For verbose test output, run `ctest` instead of using the test target:
200
201 $ cd release
202 $ ctest -V
203
204 Install
205 -------
206
207 You can install facter into your system:
208
209 $ cd release
210 $ make && sudo make install
211
212 By default, facter will install files into `/usr/local/bin`, `/usr/local/lib`, and `/usr/local/include`.
213 If the project is configured with Ruby in the PATH, *facter.rb* will be installed to that Ruby's vendor dir.
214 The install location for *facter.rb* can be overridden using by setting RUBY_LIB_INSTALL.
215
216 To install to a different location, set the install prefix:
217
218 $ cd release
219 $ cmake -DCMAKE_INSTALL_PREFIX=~ ..
220 $ make clean install
221
222 This would install facter into `~/bin`, `~/lib`, and `~/include`.
223
224 Ruby Usage
225 ----------
226
227 Using the Ruby API requires that facter.rb is installed into the Ruby load path, as done in the previous install steps.
228
229 ```ruby
230 require 'facter'
231
232 # Use the Facter API...
233 puts "kernel: #{Facter.value(:kernel)}"
234 ```
235
236 Configuration
237 -------------
238
239 Facter can be configured via a HOCON config file of the following format:
240
241 ```
242 global : {
243 external-dir : [ "path1", "path2" ],
244 custom-dir : [ "custom/path" ],
245 no-exernal-facts : false,
246 no-custom-facts : false,
247 no-ruby : false
248 }
249 cli : {
250 debug : false,
251 trace : true,
252 verbose : false,
253 log-level : "warn"
254 }
255 facts : {
256 blocklist : [ "file system", "EC2" ]
257 ttls : [
258 { "timezone" : 30 days },
259 ]
260 }
261 ```
262 All options are respected when running Facter standalone, while calling Facter from Ruby will only load `external-dir`, `custom-dir`, and the fact-specific configuration.
263
264 The file will be loaded by default from `/etc/puppetlabs/facter/facter.conf` on Unix and `C:\ProgramData\PuppetLabs\facter\etc\facter.conf` on Windows. A different location can be specified using the `--config` command line option.
265
266 Elements in the blocklist are fact groupings which will not be resolved when Facter runs. Use the `--list-block-group` command line option to see valid blockable groups.
267
268 Elements in the ttls section are key-value pairs of fact groupings that will be cached with the duration for which to cache them. Cached facts are stored as JSON in `/opt/puppetlabs/facter/cache/cached_facts` on Unix and `C:\ProgramData\PuppetLabs\facter\cache\cached_facts` on Windows. Use the `--list-cache-groups` command line option to see valid cacheable groups.
269
270 Uninstall
271 ---------
272
273 Run the following command to remove files that were previously installed:
274
275 $ sudo xargs rm < release/install_manifest.txt
276
277 Documentation
278 -------------
279
280 To generate API documentation, install doxygen 1.8.7 or later.
281
282 $ cd lib
283 $ doxygen
284
285 To view the documentation, open `lib/html/index.html` in a web browser.
286
287 Debugging
288 ---------
289
290 If when running the tests you encounter this error message:
291
292 "could not locate a ruby library"
293
294 You may need to use a different shared ruby library in Leatherman. To do
295 this, run this command, where the location below is the default for a
296 puppet agent installation:
297
298 $ export LEATHERMAN_RUBY=/opt/puppetlabs/puppet/lib/libruby.dylib
59 ## Licensing
60 See [LICENSE](https://github.com/puppetlabs/facter/blob/main/LICENSE) file. Puppet is licensed by Puppet, Inc. under the Apache license. Puppet, Inc. can be contacted at: info@puppet.com
0 RAKE_ROOT = File.expand_path(File.dirname(__FILE__))
0 # frozen_string_literal: true
11
2 $LOAD_PATH << File.join(RAKE_ROOT, 'tasks')
3 require 'rake'
4 Dir['tasks/**/*.rake'].each { |t| load t }
2 require 'bundler/gem_tasks'
3 require 'rspec/core/rake_task'
4 require 'facter/version'
55
6 require 'packaging'
7 Pkg::Util::RakeUtils.load_packaging_tasks
6 Dir.glob(File.join('tasks/**/*.rake')).each { |file| load file }
87
9 namespace :package do
10 task :bootstrap do
11 puts 'Bootstrap is no longer needed, using packaging-as-a-gem'
12 end
13 task :implode do
14 puts 'Implode is no longer needed, using packaging-as-a-gem'
8 task default: :spec
9
10 desc 'Generate changelog'
11 task :changelog, [:version] do |_t, args|
12 sh "./scripts/generate_changelog.rb #{args[:version]}"
13 end
14
15 if Rake.application.top_level_tasks.grep(/^(pl:|package:)/).any?
16 begin
17 require 'packaging'
18 Pkg::Util::RakeUtils.load_packaging_tasks
19 rescue LoadError => e
20 puts "Error loading packaging rake tasks: #{e}"
1521 end
1622 end
0 ---
1 ssh:
2 keys:
3 - id_rsa_acceptance
4 - ~/.ssh/id_rsa-acceptance
5 xml: true
6 timesync: false
7 repo_proxy: true
8 add_el_extras: false
9 forge_host: api-forge-aio02-petest.puppet.com
10 'master-start-curl-retries': 30
11 log_level: debug
12 preserve_hosts: onfail
13 helper: ./lib/helper.rb
14 options_file: ./config/aio/options.rb
0 .vagrant
1 local_options.rb
2 repos.tar
3 repo-configs
4 log
5 id_rsa-acceptance
6 id_rsa-acceptance.pub
7 preserved_config.yaml
8 merged_options.rb
9 junit
10 tmp
11 .bundle
0 source ENV['GEM_SOURCE'] || "https://rubygems.org"
1
2 def location_for(place, fake_version = nil)
3 if place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/
4 [fake_version, { :git => $1, :branch => $2, :require => false }].compact
5 elsif place =~ /^file:\/\/(.*)/
6 ['>= 0', { :path => File.expand_path($1), :require => false }]
7 else
8 [place, { :require => false }]
9 end
10 end
11
12 gem "beaker", *location_for(ENV['BEAKER_VERSION'] || "~> 4.24")
13 gem 'beaker-puppet', *location_for(ENV['BEAKER_PUPPET_VERSION'] || '~> 1')
14 gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || "~> 1.1")
15 gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || "~> 0.5")
16 gem "beaker-vagrant", *location_for(ENV['BEAKER_VAGRANT_VERSION'] || "~> 0")
17 gem "beaker-vmpooler", *location_for(ENV['BEAKER_VMPOOLER_VERSION'] || "~> 1")
18 gem "rake", ">= 12.3.3"
19 gem "multi_json", "~> 1.8"
20
21 if File.exists? "#{__FILE__}.local"
22 eval(File.read("#{__FILE__}.local"), binding)
23 end
0 require 'beaker-puppet'
1 Beaker::DSL::Helpers::RakeHelpers.load_tasks
0 #! /usr/bin/env bash
1
2 ###############################################################################
3 # Initial preparation for a ci acceptance job in Jenkins. Crucially, it
4 # handles the untarring of the build artifact and bundle install, getting us to
5 # a state where we can then bundle exec rake the particular ci:test we want to
6 # run.
7 #
8 # Having this checked in in a script makes it much easier to have multiple
9 # acceptance jobs. It must be kept agnostic between Linux/Solaris/Windows
10 # builds, however.
11
12 set -x
13
14 # If $GEM_SOURCE is not set, fall back to rubygems.org
15 if [ -z $GEM_SOURCE ]; then
16 export GEM_SOURCE='https://rubygems.org'
17 fi
18
19 echo "SHA: ${SHA}"
20 echo "FORK: ${FORK}"
21 echo "BUILD_SELECTOR: ${BUILD_SELECTOR}"
22 echo "PACKAGE_BUILD_STATUS: ${PACKAGE_BUILD_STATUS}"
23
24 rm -rf acceptance
25 tar -xzf acceptance-artifacts.tar.gz
26 cd acceptance
27
28 mkdir -p log/latest
29
30 echo "===== This artifact is from ====="
31 cat creator.txt
32
33 bundle install --without=development --path=.bundle/gems
34
35 if [[ "${platform}" =~ 'solaris' ]]; then
36 repo_proxy=" :repo_proxy => false,"
37 fi
38
39 # If the platform is Windows, append $ruby_arch
40 if [[ "${platform}" =~ 'win' ]]; then
41 platform="${platform}-${ruby_arch}"
42 fi
43
44 cat > local_options.rb <<-EOF
45 {
46 :hosts_file => 'config/nodes/${platform}.yaml',
47 :ssh => {
48 :keys => ["${HOME}/.ssh/id_rsa-acceptance"],
49 },
50 ${repo_proxy}
51 }
52 EOF
53
54 [[ (-z "${PACKAGE_BUILD_STATUS}") || ("${PACKAGE_BUILD_STATUS}" = "success") ]] || exit 1
0 {
1 :type => 'aio',
2 }
0 {
1 :type => :git,
2 }
0 ---
1 HOSTS:
2 pe-aix-53-acceptance:
3 roles:
4 - agent
5 platform: aix-5.3-power
6 hypervisor: none
0 ---
1 HOSTS:
2 pe-aix-61-acceptance:
3 roles:
4 - agent
5 platform: aix-6.1-power
6 hypervisor: none
0 ---
1 HOSTS:
2 pe-aix-71-acceptance:
3 roles:
4 - agent
5 platform: aix-7.1-power
6 hypervisor: none
0 ---
1 HOSTS:
2 huawei-ce6850-2-debian-vm-eth0.ops.puppetlabs.net:
3 roles:
4 - agent
5 platform: huaweios-6-powerpc
6 hypervisor: none
0 ---
1 HOSTS:
2 solaris-10-sparc:
3 roles:
4 - agent
5 platform: solaris-10-sparc
6 hypervisor: none
7 ip: 10.32.121.124
8 vmhostname: sol10-1.delivery.puppetlabs.net
0 ---
1 HOSTS:
2 solaris-11-sparc:
3 roles:
4 - agent
5 platform: solaris-11-sparc
6 hypervisor: none
7 ip: 10.32.114.245
8 vmhostname: sol11-1.delivery.puppetlabs.net
0 module Facter
1 module Acceptance
2 module ApiUtils
3 # create a facter_run.rb file that loads facter,
4 # register a teardown that will clear the executable dir
5 # set different config options(debug, custom/external dir)
6 # print the result of Facter.value
7 def facter_value_rb(agent, fact_name, options = {})
8 dir = agent.tmpdir('executables')
9
10 teardown do
11 agent.rm_rf(dir)
12 end
13
14 rb_file = File.join(dir, 'facter_run.rb')
15
16 file_content = <<-RUBY
17 require 'facter'
18
19 Facter.debugging(#{options.fetch(:debug, false)})
20 Facter.search('#{options.fetch(:custom_dir, '')}')
21 Facter.search_external(['#{options.fetch(:external_dir, '')}'])
22
23 puts Facter.value('#{fact_name}')
24 RUBY
25
26 create_remote_file(agent, rb_file, file_content)
27 rb_file
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Acceptance
4 module BaseFactUtils
5 # return a hash of expected facts for os, processors, and kernel facts
6 #
7 def os_processors_and_kernel_expected_facts(agent)
8 if agent['platform'] =~ /aix-/
9 aix_expected_facts(agent)
10 elsif agent['platform'] =~ /debian-/
11 debian_expected_facts(agent)
12 elsif agent['platform'] =~ /el-|centos-/
13 el_expected_facts(agent)
14 elsif agent['platform'] =~ /fedora-/
15 fedora_expected_facts(agent)
16 elsif agent['platform'] =~ /osx-/
17 osx_expected_facts(agent)
18 elsif agent['platform'] =~ /sles-/
19 sles_expected_facts(agent)
20 elsif agent['platform'] =~ /solaris-/
21 solaris_expected_facts(agent)
22 elsif agent['platform'] =~ /ubuntu-/
23 ubuntu_expected_facts(agent)
24 elsif agent['platform'] =~ /windows-/
25 windows_expected_facts(agent)
26 else
27 fail_test("unknown agent type being tested #{agent['platform']}")
28 end
29 end
30
31 # return the value from the facter json parsed set of results using the fact path separated using '.'s
32 #
33 def json_result_fact_by_key_path(results, fact)
34 fact.split('.').each do |key|
35 results = results[key]
36 end
37 results
38 end
39
40 # AIX
41 def aix_expected_facts(agent)
42 version = agent['platform'].match(/aix-(\d)\.(\d)/)
43 kernel_major_version = if version.nil?
44 /\d\d00/
45 else
46 /#{version[1]}#{version[2]}00/
47 end
48 kernel_release = /^#{kernel_major_version}-\d+-\d+-\d+/
49 os_arch = /[Pp]ower[Pp][Cc]/
50 os_hardware = /IBM/
51
52 expected_facts = {
53 'os.architecture' => os_arch,
54 'os.family' => 'AIX',
55 'os.hardware' => os_hardware,
56 'os.name' => 'AIX',
57 'os.release.full' => kernel_release,
58 'os.release.major' => kernel_major_version,
59 'processors.count' => /[1-9]+/,
60 'processors.isa' => os_arch,
61 'processors.models' => os_arch,
62 'kernel' => 'AIX',
63 'kernelrelease' => kernel_release,
64 'kernelversion' => kernel_major_version,
65 'kernelmajversion' => kernel_major_version
66 }
67 expected_facts
68 end
69
70 # Debian
71 def debian_expected_facts(agent)
72 version = agent['platform'].match(/debian-(\d{1,2})/)
73 os_version = if version.nil?
74 /\d+/
75 else
76 /#{version[1]}/
77 end
78 if agent['platform'] =~ /amd64/
79 os_arch = 'amd64'
80 os_hardware = 'x86_64'
81 else
82 os_arch = 'i386'
83 os_hardware = 'i686'
84 end
85
86 expected_facts = {
87 'os.architecture' => os_arch,
88 'os.distro.codename' => /\w+/,
89 'os.distro.description' => %r{Debian GNU/Linux #{os_version}(\.\d)?},
90 'os.distro.id' => 'Debian',
91 'os.distro.release.full' => /#{os_version}\.\d+/,
92 'os.distro.release.major' => os_version,
93 'os.distro.release.minor' => /\d/,
94 'os.family' => 'Debian',
95 'os.hardware' => os_hardware,
96 'os.name' => 'Debian',
97 'os.release.full' => /#{os_version}\.\d+/,
98 'os.release.major' => os_version,
99 'os.release.minor' => /\d/,
100 'processors.count' => /[1-9]/,
101 'processors.physicalcount' => /[1-9]/,
102 'processors.isa' => /unknown|#{os_hardware}/,
103 'processors.models' => /(Intel\(R\).*)|(AMD.*)/,
104 'kernel' => 'Linux',
105 'kernelrelease' => /\d+\.\d+\.\d+/,
106 'kernelversion' => /\d+\.\d+/,
107 'kernelmajversion' => /\d+\.\d+/
108 }
109 expected_facts
110 end
111
112 # el (RedHat, Centos)
113 def el_expected_facts(agent)
114 version = agent['platform'].match(/(el|centos)-(\d)/)
115 os_version = if version.nil?
116 /\d+/
117 else
118 /#{version[2]}/
119 end
120 release_string = on(agent, 'cat /etc/*-release').stdout.downcase
121 case release_string
122 when /almalinux/
123 os_name = 'AlmaLinux'
124 os_distro_description = /AlmaLinux release #{os_version}\.\d+ \(.+\)/
125 os_distro_id = 'AlmaLinux'
126 os_distro_release_full = /#{os_version}\.\d+/
127 when /amazon/
128 os_name = 'Amazon'
129 # This parses: VERSION_ID="2017.09"
130 os_version = on(agent, 'grep VERSION_ID /etc/os-release | cut --delimiter=\" --fields=2 | cut --delimiter=. --fields=1').stdout.chomp
131 os_distro_description = /Amazon Linux( AMI)? release (\d )?(\()?#{os_version}(\))?/
132 os_distro_id = /^Amazon(AMI)?$/
133 os_distro_release_full = /#{os_version}(\.\d+)?/
134 when /rocky/
135 os_name = 'Rocky'
136 os_distro_description = /Rocky Linux release #{os_version}\.\d+ \(.+\)/
137 os_distro_id = 'Rocky'
138 os_distro_release_full = /#{os_version}\.\d+/
139 when /centos/
140 os_name = 'CentOS'
141 os_distro_description = /CentOS( Linux)? release #{os_version}\.\d+(\.\d+)? \(\w+\)/
142 os_distro_id = 'CentOS'
143 os_distro_release_full = /#{os_version}\.\d+/
144 else
145 os_name = 'RedHat'
146 if '9'.match?(os_version) # FIXME: special case to be removed when ISO is updated to release ISO
147 os_distro_description = /Red Hat Enterprise Linux( Server)? release #{os_version}\.\d+ Beta \(\w+\)/
148 else
149 os_distro_description = /Red Hat Enterprise Linux( Server)? release #{os_version}\.\d+ \(\w+\)/
150 end
151 os_distro_id = /^RedHatEnterprise(Server)?$/
152 os_distro_release_full = /#{os_version}\.\d+/
153 end
154 if agent['platform'] =~ /x86_64/
155 os_arch = 'x86_64'
156 os_hardware = 'x86_64'
157 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
158 elsif agent['platform'] =~ /aarch64/
159 os_arch = 'aarch64'
160 os_hardware = 'aarch64'
161 processor_model_pattern = // # aarch64 does not populate a model value in /proc/cpuinfo
162 elsif agent['platform'] =~ /ppc64le/
163 os_arch = 'ppc64le'
164 os_hardware = 'ppc64le'
165 processor_model_pattern = /(POWER.*)/
166 else
167 os_arch = 'i386'
168 os_hardware = 'i686'
169 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
170 end
171
172 {}.tap do |expected_facts|
173 expected_facts['os.architecture'] = os_arch
174 expected_facts['os.distro.codename'] = /\w+/
175 expected_facts['os.distro.description'] = os_distro_description
176 expected_facts['os.distro.id'] = os_distro_id
177 expected_facts['os.distro.release.full'] = os_distro_release_full
178 expected_facts['os.distro.release.major'] = os_version
179 expected_facts['os.distro.release.minor'] = /\d/ if os_version != '2' # Amazon Linux 2
180 expected_facts['os.family'] = 'RedHat'
181 expected_facts['os.hardware'] = os_hardware
182 expected_facts['os.name'] = os_name
183 expected_facts['os.release.full'] = /#{os_version}(\.\d+)?(\.\d+)?/
184 expected_facts['os.release.major'] = os_version
185 expected_facts['os.release.minor'] = /(\d+)?/
186 expected_facts['processors.count'] = /[1-9]/
187 expected_facts['processors.physicalcount'] = /[1-9]/
188 expected_facts['processors.isa'] = os_hardware
189 expected_facts['processors.models'] = processor_model_pattern
190 expected_facts['kernel'] = 'Linux'
191 expected_facts['kernelrelease'] = /\d+\.\d+\.\d+/
192 expected_facts['kernelversion'] = /\d+\.\d+/
193 expected_facts['kernelmajversion'] = /\d+\.\d+/
194 end
195 end
196
197 # fedora
198 def fedora_expected_facts(agent)
199 version = agent['platform'].match(/fedora-(\d\d)-/)
200 os_version = if version.nil?
201 /\d+/
202 else
203 /#{version[1]}/
204 end
205 if agent['platform'] =~ /x86_64/
206 os_arch = 'x86_64'
207 os_hardware = 'x86_64'
208 else
209 os_arch = 'i386'
210 os_hardware = 'i686'
211 end
212
213 expected_facts = {
214 'os.architecture' => os_arch,
215 'os.distro.codename' => /\w+/,
216 'os.distro.description' => /Fedora release #{os_version} \(\w+( \w+)?\)/,
217 'os.distro.id' => 'Fedora',
218 'os.distro.release.full' => os_version,
219 'os.distro.release.major' => os_version,
220 'os.family' => 'RedHat',
221 'os.hardware' => os_hardware,
222 'os.name' => 'Fedora',
223 'os.release.full' => os_version,
224 'os.release.major' => os_version,
225 'processors.count' => /[1-9]/,
226 'processors.physicalcount' => /[1-9]/,
227 'processors.isa' => os_hardware,
228 'processors.models' => /(Intel\(R\).*)|(AMD.*)/,
229 'kernel' => 'Linux',
230 'kernelrelease' => /\d+\.\d+\.\d+/,
231 'kernelversion' => /\d+\.\d+\.\d+/,
232 'kernelmajversion' => /\d+\.\d+/
233 }
234 expected_facts
235 end
236
237 # osx
238 def osx_expected_facts(agent)
239 version = agent['platform'].match(/osx-(\d+)\.?(\d+)?/)
240 major_version = /#{Regexp.escape(version.captures.compact.join('.'))}/
241 if agent['platform'] =~ /x86_64/
242 os_arch = 'x86_64'
243 os_hardware = 'x86_64'
244 processors_isa = 'i386'
245 processors_models = /"Intel\(R\).*"/
246 elsif agent['platform'] =~ /arm64/
247 os_arch = 'arm64'
248 os_hardware = 'arm64'
249 processors_isa = 'arm'
250 processors_models = /"Apple M1.*"/
251 end
252 expected_facts = {
253 'os.architecture' => os_arch,
254 'os.family' => 'Darwin',
255 'os.hardware' => os_hardware,
256 'os.name' => 'Darwin',
257 'os.macosx.build' => /\d+[A-Z]\d{1,4}\w?/,
258 'os.macosx.product' => agent['platform'] =~ /osx-10/ ? 'Mac OS X' : 'macOS',
259 'os.macosx.version.major' => major_version,
260 'os.macosx.version.minor' => /\d+/,
261 'os.release.full' => /\d+\.\d+\.\d+/,
262 'os.release.major' => /\d+/,
263 'os.release.minor' => /\d+/,
264 'processors.count' => /[1-9]/,
265 'processors.physicalcount' => /[1-9]/,
266 'processors.isa' => processors_isa,
267 'processors.models' => processors_models,
268 'kernel' => 'Darwin',
269 'kernelrelease' => /\d+\.\d+\.\d+/,
270 'kernelversion' => /\d+\.\d+\.\d+/,
271 'kernelmajversion' => /\d+\.\d+/
272 }
273
274 if agent['platform'] =~ /osx-10/
275 expected_facts['os.macosx.version.full'] = /#{expected_facts['os.macosx.version.major']}\.#{expected_facts['os.macosx.version.minor']}/
276 else
277 expected_facts['os.macosx.version.patch'] = /\d+/
278 if agent['platform'] =~ /arm64/
279 expected_facts['os.macosx.version.full'] = /^#{expected_facts['os.macosx.version.major']}\.#{expected_facts['os.macosx.version.minor']}$/
280 else
281 expected_facts['os.macosx.version.full'] = /^#{expected_facts['os.macosx.version.major']}\.#{expected_facts['os.macosx.version.minor']}\.*#{expected_facts['os.macosx.version.patch']}*$/
282 end
283 end
284 expected_facts
285 end
286
287 # SLES
288 def sles_expected_facts(agent)
289 version = agent['platform'].match(/sles-(\d\d)/)
290 os_version = if version.nil?
291 /\d+/
292 else
293 /#{version[1]}/
294 end
295 if agent['platform'] =~ /x86_64/
296 os_arch = 'x86_64'
297 os_hardware = 'x86_64'
298 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
299 else
300 os_arch = 'i386'
301 os_hardware = 'i686'
302 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
303 end
304
305 expected_facts = {
306 'os.architecture' => os_arch,
307 'os.distro.codename' => /\w+/,
308 'os.distro.description' => /SUSE Linux Enterprise Server/,
309 'os.distro.id' => /^SUSE( LINUX)?$/,
310 'os.distro.release.full' => /#{os_version}\.\d+/,
311 'os.distro.release.major' => os_version,
312 'os.distro.release.minor' => /\d/,
313 'os.family' => 'Suse',
314 'os.hardware' => os_hardware,
315 'os.name' => 'SLES',
316 'os.release.full' => /#{os_version}\.\d+(\.\d+)?/,
317 'os.release.major' => os_version,
318 'os.release.minor' => /\d+/,
319 'processors.count' => /[1-9]/,
320 'processors.physicalcount' => /[1-9]/,
321 'processors.isa' => os_hardware,
322 'processors.models' => processor_model_pattern,
323 'kernel' => 'Linux',
324 'kernelrelease' => /\d+\.\d+\.\d+/,
325 'kernelversion' => /\d+\.\d+\.\d+/,
326 'kernelmajversion' => /\d+\.\d+/
327 }
328 expected_facts
329 end
330
331 # Solaris
332 def solaris_expected_facts(agent)
333 version = agent['platform'].match(/solaris-(\d\d)/)
334 os_version = if version.nil?
335 /\d+/
336 else
337 /#{version[1]}/
338 end
339 case agent[:platform]
340 when /solaris-11/
341 os_release_full = /#{os_version}\.\d+/
342 os_kernel = os_release_full
343 os_kernel_major = os_version
344 else
345 fail_test("Unknown Solaris version #{agent['platform']}")
346 end
347 case agent[:platform]
348 when /sparc/
349 os_architecture = 'sun4v'
350 proc_models = /.*SPARC.*/
351 proc_isa = /sparc/
352 else
353 os_architecture = 'i86pc'
354 proc_models = /(Intel.*)|(AMD.*)/
355 proc_isa = /i386/
356 end
357
358 expected_facts = {
359 'os.architecture' => os_architecture,
360 'os.family' => 'Solaris',
361 'os.hardware' => os_architecture,
362 'os.name' => 'Solaris',
363 'os.release.full' => os_release_full,
364 'os.release.major' => os_version,
365 'os.release.minor' => /\d+/,
366 'processors.count' => /[1-9]/,
367 'processors.physicalcount' => /[1-9]/,
368 'processors.isa' => proc_isa,
369 'processors.models' => proc_models,
370 'kernel' => 'SunOS',
371 'kernelrelease' => /5\.#{os_version}/,
372 'kernelversion' => os_kernel,
373 'kernelmajversion' => os_kernel_major
374 }
375 expected_facts
376 end
377
378 # Ubuntu
379 def ubuntu_expected_facts(agent)
380 version = agent['platform'].match(/ubuntu-(\d\d\.\d\d)/)
381 os_version = if version.nil?
382 /\d+\.\d+/
383 else
384 /#{version[1]}/
385 end
386 if agent['platform'] =~ /x86_64|amd64/
387 os_arch = 'amd64'
388 os_hardware = 'x86_64'
389 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
390 elsif agent['platform'] =~ /aarch64/
391 os_arch = 'aarch64'
392 os_hardware = 'aarch64'
393 processor_model_pattern = // # facter doesn't figure out the processor type on these machines
394 else
395 os_arch = 'i386'
396 os_hardware = 'i686'
397 processor_model_pattern = /(Intel\(R\).*)|(AMD.*)/
398 end
399
400 expected_facts = {
401 'os.architecture' => os_arch,
402 'os.distro.codename' => /\w+/,
403 'os.distro.description' => /Ubuntu #{os_version}/,
404 'os.distro.id' => 'Ubuntu',
405 'os.distro.release.full' => os_version,
406 'os.distro.release.major' => os_version,
407 'os.family' => 'Debian',
408 'os.hardware' => os_hardware,
409 'os.name' => 'Ubuntu',
410 'os.release.full' => os_version,
411 'os.release.major' => os_version,
412 'processors.count' => /[1-9]/,
413 'processors.physicalcount' => /[1-9]/,
414 'processors.isa' => os_hardware,
415 'processors.models' => processor_model_pattern,
416 'kernel' => 'Linux',
417 'kernelrelease' => /\d+\.\d+\.\d+/,
418 'kernelversion' => /\d+\.\d+\.\d+/,
419 'kernelmajversion' => /\d+\.\d+/
420 }
421 expected_facts
422 end
423
424 # Windows
425 def windows_expected_facts(agent)
426 # Get expected values based on platform name
427 if agent['platform'] =~ /2012/
428 os_version = agent['platform'] =~ /R2/ ? '2012 R2' : '2012'
429 elsif agent['platform'] =~ /-10/
430 os_version = '10'
431 elsif agent['platform'] =~ /-11/
432 os_version = '11'
433 elsif agent['platform'] =~ /2016/
434 os_version = '2016'
435 elsif agent['platform'] =~ /2019/
436 os_version = '2019'
437 elsif agent['platform'] =~ /2022/
438 os_version = '2022'
439 else
440 fail_test "Unknown Windows version #{agent['platform']}"
441 end
442 if agent['platform'] =~ /64/
443 os_arch = 'x64'
444 os_hardware = 'x86_64'
445 else
446 os_arch = 'x86'
447 os_hardware = 'i686'
448 end
449
450 expected_facts = {
451 'os.architecture' => os_arch,
452 'os.family' => 'windows',
453 'os.hardware' => os_hardware,
454 'os.name' => 'windows',
455 'os.release.full' => os_version,
456 'os.release.major' => os_version,
457 'os.windows.system32' => /C:\\(WINDOWS|[Ww]indows)\\(system32|sysnative)/,
458 'processors.count' => /[1-9]/,
459 'processors.physicalcount' => /[1-9]/,
460 'processors.isa' => /x86|x64/,
461 'processors.models' => /(Intel\(R\).*)|(AMD.*)/,
462 'kernel' => 'windows',
463 'kernelrelease' => /\d+\.\d+/,
464 'kernelversion' => /\d+\.\d+/,
465 'kernelmajversion' => /\d+\.\d+/
466 }
467 expected_facts
468 end
469 end
470 end
471 end
0 module Facter
1 module Acceptance
2 module UserFactUtils
3
4 # Determine paths for testing custom and external facts.
5 # Paths vary by platform.
6
7 # Retrieve the path of a non-standard directory for custom or external facts.
8 #
9 def get_user_fact_dir(platform, version)
10 if platform =~ /windows/
11 if version < 6.0
12 File.join('C:', 'Documents and Settings', 'All Users', 'Application Data', 'PuppetLabs', 'facter', 'custom')
13 else
14 File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'custom')
15 end
16 else
17 File.join('/', 'opt', 'puppetlabs', 'facter', 'custom')
18 end
19 end
20
21 # Retrieve the path of the standard facts.d directory.
22 #
23 def get_factsd_dir(platform, version)
24 if platform =~ /windows/
25 if version < 6.0
26 File.join('C:', 'Documents and Settings', 'All Users', 'Application Data', 'PuppetLabs', 'facter', 'facts.d')
27 else
28 File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'facts.d')
29 end
30 else
31 File.join('/', 'opt', 'puppetlabs', 'facter', 'facts.d')
32 end
33 end
34
35 # Retrieve the path of the standard cached facts directory.
36 #
37 def get_cached_facts_dir(platform, version)
38 if platform =~ /windows/
39 if version < 6.0
40 File.join('C:', 'Documents and Settings', 'All Users', 'Application Data', 'PuppetLabs', 'facter', 'cache', 'cached_facts')
41 else
42 File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'cache', 'cached_facts')
43 end
44 else
45 File.join('/', 'opt', 'puppetlabs', 'facter', 'cache', 'cached_facts')
46 end
47 end
48
49 # Retrieve the path of the facts.d directory in /etc/facter on Unix systems
50 #
51 def get_etc_factsd_dir(platform)
52 if platform =~ /windows/
53 File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'facts.d')
54 else
55 File.join('/', 'etc', 'facter', 'facts.d')
56 end
57 end
58
59 # Retrieve the path of the facts.d diretory in /etc/puppetlabs/facter on Unix systems
60 #
61 def get_etc_puppetlabs_factsd_dir(platform)
62 if platform =~ /windows/
63 raise "get_etc_puppetlabs_factsd_dir: not a supported directory on Windows"
64 else
65 File.join('/', 'etc', 'puppetlabs', 'facter', 'facts.d')
66 end
67 end
68
69 # Retrieve the extension to use for an external fact script.
70 # Windows uses '.bat' and everything else uses '.sh'
71 def get_external_fact_script_extension(platform)
72 if platform =~ /windows/
73 '.bat'
74 else
75 '.sh'
76 end
77 end
78
79 # Retrieve the path to default location of facter.conf file.
80 #
81 def get_default_fact_dir(platform, version)
82 if platform =~ /windows/
83 File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'etc')
84 else
85 File.join('/', 'etc', 'puppetlabs', 'facter')
86 end
87 end
88
89 # Return the content for an external fact based on the platform supplied
90 #
91 def external_fact_content(platform, key='external_fact', value='test_value')
92 unix_content = <<EOM
93 #!/bin/sh
94 echo "#{key}=#{value}"
95 EOM
96
97 win_content = <<EOM
98 @echo off
99 echo #{key}=#{value}
100 EOM
101
102 if platform =~ /windows/
103 win_content
104 else
105 unix_content
106 end
107 end
108
109 # Return the content for a custom fact
110 #
111 def custom_fact_content(key='custom_fact', value='custom_value', *args)
112 <<-EOM
113 Facter.add('#{key}') do
114 setcode {'#{value}'}
115 #{args.empty? ? '' : args.join('\n')}
116 end
117 EOM
118 end
119
120 # Return the correct shell path for Unix system under test
121 #
122 def user_shell(agent)
123 if agent['platform'] =~ /aix/
124 '/usr/bin/bash'
125 else
126 '/bin/bash'
127 end
128 end
129
130 def escape_paths(host, str)
131 if host['platform'] =~ /windows/ && !host.is_cygwin?
132 str.gsub('\\') { '\\\\' }
133 else
134 str
135 end
136 end
137 end
138 end
139 end
0 $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
1
2 require 'beaker-puppet'
0 module Puppet
1 module Acceptance
2 module CommandUtils
3 def ruby_command(host)
4 if host['platform'] =~ /windows/ && !host.is_cygwin?
5 "cmd /V /C \"set PATH=#{host['privatebindir']};!PATH! && ruby\""
6 else
7 "env PATH=\"#{host['privatebindir']}:${PATH}\" ruby"
8 end
9 end
10 module_function :ruby_command
11 end
12 end
13 end
0 module Puppet
1 module Acceptance
2 module GitUtils
3 def lookup_in_env(env_variable_name, project_name, default)
4 project_specific_name = "#{project_name.upcase.gsub("-","_")}_#{env_variable_name}"
5 ENV[project_specific_name] || ENV[env_variable_name] || default
6 end
7
8 def build_giturl(project_name, git_fork = nil, git_server = nil)
9 git_fork ||= lookup_in_env('FORK', project_name, 'puppetlabs')
10 git_server ||= lookup_in_env('GIT_SERVER', project_name, 'github.com')
11 repo = (git_server == 'github.com') ?
12 "#{git_fork}/#{project_name}.git" :
13 "#{git_fork}-#{project_name}.git"
14 "git://#{git_server}/#{repo}"
15 end
16 end
17 end
18 end
0 require 'open-uri'
1 require 'open3'
2 require 'uri'
3 require 'puppet/acceptance/common_utils'
4
5 module Puppet
6 module Acceptance
7 module InstallUtils
8 PLATFORM_PATTERNS = {
9 :redhat => /fedora|el-|centos/,
10 :debian => /debian|ubuntu/,
11 :debian_ruby18 => /debian|ubuntu-lucid|ubuntu-precise/,
12 :solaris => /solaris/,
13 :windows => /windows/,
14 }.freeze
15
16 # Installs packages on the hosts.
17 #
18 # @param hosts [Array<Host>] Array of hosts to install packages to.
19 # @param package_hash [Hash{Symbol=>Array<String,Array<String,String>>}]
20 # Keys should be a symbol for a platform in PLATFORM_PATTERNS. Values
21 # should be an array of package names to install, or of two element
22 # arrays where a[0] is the command we expect to find on the platform
23 # and a[1] is the package name (when they are different).
24 # @param options [Hash{Symbol=>Boolean}]
25 # @option options [Boolean] :check_if_exists First check to see if
26 # command is present before installing package. (Default false)
27 # @return true
28 def install_packages_on(hosts, package_hash, options = {})
29 check_if_exists = options[:check_if_exists]
30 hosts = [hosts] unless hosts.kind_of?(Array)
31 hosts.each do |host|
32 package_hash.each do |platform_key,package_list|
33 if pattern = PLATFORM_PATTERNS[platform_key]
34 if pattern.match(host['platform'])
35 package_list.each do |cmd_pkg|
36 if cmd_pkg.kind_of?(Array)
37 command, package = cmd_pkg
38 else
39 command = package = cmd_pkg
40 end
41 if !check_if_exists || !host.check_for_package(command)
42 host.logger.notify("Installing #{package}")
43 additional_switches = '--allow-unauthenticated' if platform_key == :debian
44 host.install_package(package, additional_switches)
45 end
46 end
47 end
48 else
49 raise("Unknown platform '#{platform_key}' in package_hash")
50 end
51 end
52 end
53 return true
54 end
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(core_fact) when custom fact is defined' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 fact_name = 'os.name'
13 core_fact_value = os_processors_and_kernel_expected_facts(agent)[fact_name]
14
15 step 'in different file than fact name' do
16 facts_dir = agent.tmpdir('facts')
17
18 teardown do
19 agent.rm_rf(facts_dir)
20 end
21
22 fact_file = File.join(facts_dir, 'test_fact.rb')
23 fact_content = <<-RUBY
24 Facter.add('#{fact_name}') do
25 has_weight(100)
26 setcode { 'custom_fact' }
27 end
28 RUBY
29
30 create_remote_file(agent, fact_file, fact_content)
31
32 step 'returns core fact value' do
33 facter_rb = facter_value_rb(agent, fact_name, custom_dir: facts_dir)
34 fact_value = on(agent, "#{ruby_command(agent)} #{facter_rb}").stdout&.strip
35
36 assert_match(fact_value, core_fact_value, 'Incorrect fact value for os.name')
37 end
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(core_fact) when custom fact is defined and overwrites core' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 fact_name = 'os.name'
13
14 step 'in the same file as fact name' do
15 facts_dir = agent.tmpdir('facts')
16
17 teardown do
18 agent.rm_rf(facts_dir)
19 end
20
21 fact_file = File.join(facts_dir, 'os.name.rb')
22 fact_content = <<-RUBY
23 Facter.add('#{fact_name}') do
24 has_weight(100)
25 setcode { 'custom_fact' }
26 end
27 RUBY
28
29 create_remote_file(agent, fact_file, fact_content)
30
31 step 'returns custom fact value' do
32 facter_rb = facter_value_rb(agent, fact_name, custom_dir: facts_dir)
33 fact_value = on(agent, "#{ruby_command(agent)} #{facter_rb}").stdout&.strip
34
35 assert_match(fact_value, 'custom_fact', 'Incorrect fact value for os.name')
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(core_fact)' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 fact_name = 'os.name'
13 core_fact_value = os_processors_and_kernel_expected_facts(agent)[fact_name]
14
15 step 'returns core fact value' do
16 facter_rb = facter_value_rb(agent, fact_name)
17 fact_value = on(agent, "#{ruby_command(agent)} #{facter_rb}").stdout&.strip
18
19 assert_match(fact_value, core_fact_value, 'Incorrect fact value for os.name')
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(custom_fact) in different file' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 facts_dir = agent.tmpdir('facts')
13 fact_name = 'single_custom_fact'
14
15 teardown do
16 agent.rm_rf(facts_dir)
17 end
18
19 fact_file = File.join(facts_dir, 'another_fact.rb')
20 fact_content = <<-RUBY
21 Facter.add('#{fact_name}') do
22 setcode { 'single_custom_fact' }
23 end
24 RUBY
25
26 create_remote_file(agent, fact_file, fact_content)
27
28 step 'returns custom_fact fact value after loading all custom facts' do
29 facter_rb = facter_value_rb(agent, fact_name, custom_dir: facts_dir, debug: true)
30 on(agent, "#{ruby_command(agent)} #{facter_rb}") do |result|
31 output = result.stdout.strip
32 assert_match(/has resolved to: #{fact_name}/, output, 'Incorrect fact value for custom fact')
33 assert_match(/Searching fact: #{fact_name} in all custom facts/, output, 'Loaded all custom facts')
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(custom_fact) in the same file as fact name' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 facts_dir = agent.tmpdir('facts')
13 fact_name = 'single_custom_fact'
14
15 teardown do
16 agent.rm_rf(facts_dir)
17 end
18
19 fact_file = File.join(facts_dir, "#{fact_name}.rb")
20 fact_content = <<-RUBY
21 Facter.add('#{fact_name}') do
22 setcode { 'single_custom_fact' }
23 end
24 RUBY
25
26 create_remote_file(agent, fact_file, fact_content)
27
28 step 'returns custom_fact fact value without loading all custom facts' do
29 facter_rb = facter_value_rb(agent, fact_name, custom_dir: facts_dir, debug: true)
30 on(agent, "#{ruby_command(agent)} #{facter_rb}") do |result|
31 output = result.stdout.strip
32 assert_match(/has resolved to: #{fact_name}/, output, 'Incorrect fact value for custom fact')
33 assert_no_match(/Searching fact: #{fact_name} in all custom facts/, output, 'Loaded all custom facts')
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(env_fact)' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 fact_name = 'env_fact'
13 fact_value = 'env_value'
14
15 step 'resolves the fact with the correct value' do
16 facter_rb = facter_value_rb(agent, fact_name)
17
18 env = { "FACTER_#{fact_name}" => fact_value }
19
20 on(agent, "#{ruby_command(agent)} #{facter_rb}", environment: env) do |result|
21 assert_match(fact_value, result.stdout.chomp, 'Incorrect fact value for env fact')
22 end
23 end
24
25 step 'resolves the fact with the correct value if the env fact is upcased' do
26 facter_rb = facter_value_rb(agent, fact_name)
27
28 env = { "FACTER_#{fact_name.upcase}" => fact_value }
29
30 on(agent, "#{ruby_command(agent)} #{facter_rb}", environment: env) do |result|
31 assert_match(fact_value, result.stdout.chomp, 'Incorrect fact value for env fact')
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(external_fact)' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 facts_dir = agent.tmpdir('facts')
13 fact_name = 'my_external_fact'
14
15 teardown do
16 agent.rm_rf(facts_dir)
17 end
18
19 fact_file = File.join(facts_dir, 'external.txt')
20 fact_content = 'my_external_fact=123'
21
22 create_remote_file(agent, fact_file, fact_content)
23
24 step 'returns external fact without loading custom facts' do
25 facter_rb = facter_value_rb(agent, fact_name, external_dir: facts_dir, debug: true)
26 on(agent, "#{ruby_command(agent)} #{facter_rb}") do |result|
27 output = result.stdout.strip
28 assert_match(/has resolved to: 123/, output, 'Incorrect fact value for external fact')
29 assert_no_match(/in all custom facts/, output, 'Loaded all custom facts')
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 test_name 'Facter.value(not_existent)' do
3 confine :to, platform: 'ubuntu'
4 tag 'audit:high'
5
6 require 'facter/acceptance/base_fact_utils'
7 require 'facter/acceptance/api_utils'
8 extend Facter::Acceptance::BaseFactUtils
9 extend Facter::Acceptance::ApiUtils
10
11 agents.each do |agent|
12 fact_name = 'non_existent'
13 facts_dir = agent.tmpdir('facts')
14
15 teardown do
16 agent.rm_rf(facts_dir)
17 end
18
19 step 'it loads facts in the correct order' do
20 facter_rb = facter_value_rb(
21 agent, fact_name,
22 external_dir: facts_dir,
23 custom_dir: facts_dir,
24 debug: true
25 )
26
27 on(agent, "#{ruby_command(agent)} #{facter_rb}") do |result|
28 output = result.stdout.strip
29 assert_no_match(/has resolved to: /, output, 'Fact was found')
30 assert_match(
31 /Searching fact: #{fact_name} in file: #{fact_name}.rb/,
32 output,
33 'Did not load fact name file'
34 )
35 assert_match(
36 /Searching fact: #{fact_name} in core facts and external facts/,
37 output,
38 'Did not load core and external'
39 )
40 assert_match(
41 /Searching fact: #{fact_name} in all custom facts/,
42 output, '
43 Did not load all custom facts'
44 )
45 end
46 end
47 end
48 end
0 test_name 'custom facts included in blocklist will not be displayed' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = "my_custom_fact"
8 custom_fact_value = "custom_fact_value"
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{custom_fact_name}) do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 config_data = <<~FACTER_CONF
19 facts : {
20 blocklist : [ "#{custom_fact_name}" ],
21 }
22 FACTER_CONF
23
24 agents.each do |agent|
25 fact_dir = agent.tmpdir('custom_facts')
26 fact_file = File.join(fact_dir, custom_fact_file)
27
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, 'facter.conf')
30
31 agent.mkdir_p(config_dir)
32 create_remote_file(agent, fact_file, fact_content)
33 create_remote_file(agent, config_file, config_data)
34
35 teardown do
36 agent.rm_rf(fact_dir)
37 agent.rm_rf(config_dir)
38 end
39
40 step "Facter: Verify that the blocked custom fact is not displayed" do
41 on(agent, facter("--custom-dir=#{fact_dir} my_custom_fact")) do |facter_output|
42 assert_equal("", facter_output.stdout.chomp)
43 end
44 end
45 end
46 end
0 test_name 'ttls configured custom facts files creates cache file and reads cache file' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = 'random_custom_fact'
8 custom_fact_value = 'custom fact value'
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{custom_fact_name}) do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 cached_file_content = <<~CACHED_FILE
19 {
20 "#{custom_fact_name}": "#{custom_fact_value}",
21 "cache_format_version": 1
22 }
23 CACHED_FILE
24
25 config_data = <<~FACTER_CONF
26 facts : {
27 ttls : [
28 { "cached-custom-facts" : 3 days }
29 ]
30 }
31 fact-groups : {
32 cached-custom-facts : ["#{custom_fact_name}"],
33 }
34 FACTER_CONF
35
36 agents.each do |agent|
37 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
38 fact_dir = agent.tmpdir('facter')
39 env = { 'FACTERLIB' => fact_dir }
40
41 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
42 config_file = File.join(config_dir, 'facter.conf')
43
44 step "Agent #{agent}: create config file" do
45 agent.mkdir_p(config_dir)
46 create_remote_file(agent, config_file, config_data)
47 fact_file = File.join(fact_dir, custom_fact_file)
48 create_remote_file(agent, fact_file, fact_content)
49 end
50
51 teardown do
52 agent.rm_rf(fact_dir)
53 agent.rm_rf("#{cache_folder}/*")
54 agent.rm_rf(config_file)
55 end
56
57 step "should log that it creates cache file and it caches custom facts found in facter.conf" do
58 on(agent, facter("#{custom_fact_name} --debug", environment: env)) do |facter_result|
59 assert_equal(custom_fact_value, facter_result.stdout.chomp, "#{custom_fact_name} value changed")
60 assert_match(/facts cache file expired, missing or is corrupt/, facter_result.stderr,
61 'Expected debug message to state that custom facts cache file is missing or expired')
62 assert_match(/Saving cached custom facts to ".+"|caching values for cached-custom-facts facts/, facter_result.stderr,
63 'Expected debug message to state that custom facts will be cached')
64 end
65 end
66
67 step "should create a cached-custom-facts cache file that containt fact information" do
68 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
69 assert_equal(true, result)
70 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
71 assert_match(cached_file_content.chomp, cat_output.strip, 'Expected cached custom fact file to contain fact information')
72 end
73
74 step 'should read from the cached file for a custom fact that has been cached' do
75 on(agent, facter("#{custom_fact_name} --debug", environment: env)) do |facter_result|
76 assert_match(/Loading cached custom facts from file ".+"|loading cached values for random_custom_fact facts/, facter_result.stderr,
77 'Expected debug message to state that cached custom facts are read from file')
78 end
79 end
80 end
81 end
0 test_name 'dependant custom facts are cached correctly' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'dependant_custom_facts.rb'
7 depending_fact_name = 'depending_fact_name'
8
9 simple_fact_name = 'simple_fact_name'
10 simple_fact_value = '["a","b","c","d"]'
11
12 fact_content = <<-CUSTOM_FACT
13 Facter.add(:#{simple_fact_name}) do
14 confine osfamily: Facter.value('osfamily').downcase
15 setcode do
16 array_value = #{simple_fact_value}
17 array_value
18 end
19 end
20
21 Facter.add(:#{depending_fact_name}) do
22 confine osfamily: Facter.value('osfamily').downcase
23 setcode do
24 Facter.value(:#{simple_fact_name}).length
25 end
26 end
27 CUSTOM_FACT
28
29 cached_file_content = <<~CACHED_FILE
30 {
31 "#{depending_fact_name}": 4,
32 "cache_format_version": 1
33 }
34 CACHED_FILE
35
36 config_data = <<~FACTER_CONF
37 facts : {
38 ttls : [
39 { "cached-dependant-custom-facts" : 3 days }
40 ]
41 }
42 fact-groups : {
43 cached-dependant-custom-facts : ["#{depending_fact_name}","#{simple_fact_name}"],
44 }
45 FACTER_CONF
46
47 agents.each do |agent|
48 facter_version = fact_on(agent, 'facterversion')
49
50 if facter_version.start_with?("3")
51 skip_test 'Test only viable on Facter 4'
52 end
53
54 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
55 fact_dir = agent.tmpdir('facter')
56 env = { 'FACTERLIB' => fact_dir }
57
58 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
59 config_file = File.join(config_dir, 'facter.conf')
60
61 step "Agent #{agent}: create config file" do
62 agent.mkdir_p(config_dir)
63 create_remote_file(agent, config_file, config_data)
64 fact_file = File.join(fact_dir, custom_fact_file)
65 create_remote_file(agent, fact_file, fact_content)
66 end
67
68 teardown do
69 agent.rm_rf(fact_dir)
70 agent.rm_rf("#{cache_folder}/*")
71 agent.rm_rf(config_file)
72 end
73
74 step "should log that it creates cache file and it caches custom facts found in facter.conf" do
75 on(agent, facter("#{depending_fact_name} --debug", environment: env)) do |facter_result|
76 assert_equal("4", facter_result.stdout.chomp, "#{depending_fact_name} value changed")
77 assert_match(/facts cache file expired, missing or is corrupt/, facter_result.stderr,
78 'Expected debug message to state that depending custom facts cache file is missing or expired')
79 assert_match(/caching values for cached-dependant-custom-facts facts/, facter_result.stderr,
80 'Expected debug message to state that depending custom facts will be cached')
81 end
82 end
83
84 step "should create a cached-dependant-custom-facts cache file that containt fact information" do
85 result = agent.file_exist?("#{cache_folder}/cached-dependant-custom-facts")
86 assert_equal(true, result)
87 cat_output = agent.cat("#{cache_folder}/cached-dependant-custom-facts")
88 assert_match(cached_file_content.chomp, cat_output.strip, 'Expected cached dependant custom fact file to contain fact information')
89 end
90
91 step 'should read from the cached file for a custom fact that has been cached' do
92 on(agent, facter("#{depending_fact_name} --debug", environment: env)) do |facter_result|
93 assert_match(/loading cached values for #{depending_fact_name} facts/, facter_result.stderr,
94 'Expected debug message to state that cached custom facts are read from file')
95 end
96 end
97 end
98 end
0 test_name 'Facter should appropriately resolve a custom fact when it conflicts with a builtin fact' do
1 tag 'risk:high'
2
3 def create_custom_fact_on(host, custom_fact_dir, fact_file_name, fact)
4 fact_file_contents = <<-CUSTOM_FACT
5 Facter.add(:#{fact[:name]}) do
6 has_weight #{fact[:weight]}
7 setcode do
8 #{fact[:value]}
9 end
10 end
11 CUSTOM_FACT
12
13 fact_file_path = File.join(custom_fact_dir, fact_file_name)
14 create_remote_file(host, fact_file_path, fact_file_contents)
15 end
16
17 def clear_custom_facts_on(host, custom_fact_dir)
18 step "Clean-up the previous test's custom facts" do
19 host.rm_rf("#{custom_fact_dir}/*")
20 end
21 end
22
23 agents.each do |agent|
24 custom_fact_dir = agent.tmpdir('facter')
25 teardown do
26 agent.rm_rf(custom_fact_dir)
27 end
28
29 fact_name = 'timezone'
30 builtin_value = on(agent, facter('timezone')).stdout.chomp
31
32 step "Verify that Facter uses the custom fact's value when its weight is > 0" do
33 custom_fact_value = "custom_timezone"
34 create_custom_fact_on(
35 agent,
36 custom_fact_dir,
37 'custom_timezone.rb',
38 name: fact_name,
39 weight: 10,
40 value: "'#{custom_fact_value}'"
41 )
42
43 on(agent, facter("--custom-dir \"#{custom_fact_dir}\" timezone")) do |result|
44 assert_match(/#{custom_fact_value}/, result.stdout.chomp, "Facter does not use the custom fact's value when its weight is > 0")
45 end
46 end
47
48 clear_custom_facts_on(agent, custom_fact_dir)
49
50 step "Verify that Facter uses the builtin fact's value when all conflicting custom facts fail to resolve" do
51 [ 'timezone_one.rb', 'timezone_two.rb'].each do |fact_file|
52 create_custom_fact_on(
53 agent,
54 custom_fact_dir,
55 fact_file,
56 { name: fact_name, weight: 10, value: nil }
57 )
58 end
59
60 on(agent, facter("--custom-dir \"#{custom_fact_dir}\" timezone")) do |result|
61 assert_match(/#{builtin_value}/, result.stdout.chomp, "Facter does not use the builtin fact's value when all conflicting custom facts fail to resolve")
62 end
63 end
64
65 step "Verify that Facter gives precedence to the builtin fact over zero weight custom facts" do
66 step "when all custom facts have zero weight" do
67 {
68 'timezone_one.rb' => "'timezone_one'",
69 'timezone_two.rb' => "'timezone_two'"
70 }.each do |fact_file, fact_value|
71 create_custom_fact_on(
72 agent,
73 custom_fact_dir,
74 fact_file,
75 { name: fact_name, weight: 0, value: fact_value }
76 )
77 end
78
79 on(agent, facter("--custom-dir \"#{custom_fact_dir}\" timezone")) do |result|
80 assert_match(/#{builtin_value}/, result.stdout.chomp, "Facter does not give precedence to the builtin fact when all custom facts have zero weight")
81 end
82 end
83
84 clear_custom_facts_on(agent, custom_fact_dir)
85
86 step "when some custom facts have zero weight" do
87 {
88 'timezone_one.rb' => { weight: 10, value: nil },
89 'timezone_two.rb' => { weight: 0, value: "'timezone_two'" }
90 }.each do |fact_file, fact|
91 create_custom_fact_on(
92 agent,
93 custom_fact_dir,
94 fact_file,
95 fact.merge(name: fact_name)
96 )
97 end
98
99 on(agent, facter("--custom-dir \"#{custom_fact_dir}\" timezone")) do |result|
100 assert_match(/#{builtin_value}/, result.stdout.chomp, "Facter does not give precedence to the builtin fact when only some custom facts have zero weight")
101 end
102 end
103 end
104 end
105 end
0 test_name "C100153: custom fact with weight of >= 10001 overrides an external fact" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_name = 'test'
7 # Use a static external fact
8 ext_fact = "#{fact_name}: 'EXTERNAL'"
9
10 agents.each do |agent|
11 facts_dir = agent.tmpdir('facts.d')
12 ext_fact_path = "#{facts_dir}/test.yaml"
13 cust_fact_path = "#{facts_dir}/test.rb"
14 create_remote_file(agent, ext_fact_path, ext_fact)
15 create_remote_file(agent, cust_fact_path, custom_fact_content(fact_name, 'CUSTOM', "has_weight 10001"))
16
17 teardown do
18 agent.rm_rf(facts_dir)
19 end
20
21 # Custom fact with weight >= 10001 should override an external fact
22 step "Agent #{agent}: resolve a custom fact with weight of 10001 overriding the external fact" do
23 on(agent, facter("--external-dir \"#{facts_dir}\" --custom-dir=#{facts_dir} test")) do |facter_output|
24 assert_equal("CUSTOM", facter_output.stdout.chomp)
25 end
26 end
27 end
28 end
29
0 test_name "Can resolve custom facts that call legacy facts" do
1 tag 'risk:high'
2
3 fact_content = <<-RUBY
4 Facter.add(:test_fact_with_legacy) do
5 setcode do
6 Facter.value('osfamily').downcase
7 'resolved'
8 end
9 end
10 RUBY
11
12 agents.each do |agent|
13 fact_dir = agent.tmpdir('fact')
14 fact_file = File.join(fact_dir, 'test_fact.rb')
15 create_remote_file(agent, fact_file, fact_content)
16
17 teardown do
18 agent.rm_rf(fact_dir)
19 end
20
21 step 'it resolves the fact without errors' do
22 on(agent, facter("--custom-dir #{fact_dir} --json")) do |facter_result|
23 assert_equal(
24 'resolved',
25 JSON.parse(facter_result.stdout.chomp)['test_fact_with_legacy'],
26 'test_fact_with_legacy value is wrong'
27 )
28 assert_empty(facter_result.stderr.chomp, "Expected no errors from facter")
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 test_name 'Facter should handle aggregated custom facts' do
3 tag 'risk:high'
4
5 fact_content1 = <<-RUBY
6 Facter.add(:test_fact) do
7 has_weight 100000
8 setcode do
9 "test fact"
10 end
11 end
12
13 Facter.add(:test_fact, :type => :aggregate) do
14 has_weight 10000
15 chunk(:one) do
16 'aggregate'
17 end
18
19 chunk(:two) do
20 'fact'
21 end
22
23 aggregate do |chunks|
24 result = ''
25
26 chunks.each_value do |str|
27 result += str
28 end
29
30 result
31 end
32 end
33 RUBY
34
35 fact_content2 = <<-RUBY
36 Facter.add(:test_fact) do
37 has_weight 10000
38 setcode do
39 "test fact"
40 end
41 end
42
43 Facter.add(:test_fact, :type => :aggregate) do
44 has_weight 100000
45 chunk(:one) do
46 'aggregate'
47 end
48
49 chunk(:two) do
50 'fact'
51 end
52
53 aggregate do |chunks|
54 result = ''
55
56 chunks.each_value do |str|
57 result += str
58 end
59
60 result
61 end
62 end
63 RUBY
64
65 fact_content3 = <<-RUBY
66 Facter.add(:test_array_fact, :type => :aggregate) do
67 has_weight 100000
68 chunk(:one) do
69 ['foo']
70 end
71
72 chunk(:two) do
73 ['bar']
74 end
75 end
76
77 Facter.add(:test_hash_fact, :type => :aggregate) do
78 chunk :first do
79 { foo: 'aggregate' }
80 end
81
82 chunk :second do
83 { bar: 'fact' }
84 end
85 end
86 RUBY
87
88 agents.each do |agent|
89 fact_dir1 = agent.tmpdir('fact1')
90 fact_file1 = File.join(fact_dir1, 'test_facts.rb')
91 create_remote_file(agent, fact_file1, fact_content1)
92
93 fact_dir2 = agent.tmpdir('fact2')
94 fact_file2 = File.join(fact_dir2, 'test_facts.rb')
95 create_remote_file(agent, fact_file2, fact_content2)
96
97 fact_dir3 = agent.tmpdir('fact3')
98 fact_file3 = File.join(fact_dir3, 'no_aggregate_block.rb')
99 create_remote_file(agent, fact_file3, fact_content3)
100
101 teardown do
102 agent.rm_rf(fact_dir1)
103 agent.rm_rf(fact_dir2)
104 agent.rm_rf(fact_dir3)
105 end
106
107 step "Agent: Verify test_fact from #{fact_file1}" do
108 on(agent, facter("--custom-dir #{fact_dir1} test_fact")) do |facter_result|
109 assert_equal('test fact', facter_result.stdout.chomp, 'test_fact value is wrong')
110 end
111 end
112
113 step "Agent: Verify test_fact from #{fact_file2} with aggregate fact overwriting the custom one" do
114 on(agent, facter("--custom-dir #{fact_dir2} test_fact")) do |facter_result|
115 assert_equal('aggregatefact', facter_result.stdout.chomp, 'test_fact value is wrong')
116 end
117 end
118
119 step "Agent: Verify aggregate facts with no aggregate block from #{fact_file3}" do
120 on(agent, facter("--custom-dir #{fact_dir3} test_array_fact --debug --json")) do |facter_result|
121 assert_equal(
122 { 'test_array_fact' => %w[foo bar] },
123 JSON.parse(facter_result.stdout.chomp), '
124 test_array_fact value is wrong'
125 )
126 assert_match(
127 /custom fact test_array_fact got resolved from.*no_aggregate_block\.rb\", 1\]/,
128 facter_result.stderr.chomp,
129 'resolution location not found on debug'
130 )
131 end
132
133 on(agent, facter("--custom-dir #{fact_dir3} test_hash_fact --debug --json")) do |facter_result|
134 assert_equal(
135 { 'test_hash_fact' => { 'bar' => 'fact', 'foo' => 'aggregate' } },
136 JSON.parse(facter_result.stdout.chomp),
137 'test_hash_fact value is wrong'
138 )
139 assert_match(
140 /custom fact test_hash_fact got resolved from.*no_aggregate_block\.rb\", 12\]/,
141 facter_result.stderr.chomp,
142 'resolution location not found on debug'
143 )
144 end
145 end
146 end
147 end
0 test_name 'Facter api works when there is an error inside a custom fact file' do
1 tag 'risk:high'
2
3 first_file_content = <<-EOM
4 Facter.add(:custom_fact_1) do
5 setcode do
6 'custom_fact_1_value'
7 end
8 end
9
10 # some error
11 nill.size
12
13 Facter.add(:custom_fact_2) do
14 setcode do
15 'custom_fact_2_value'
16 end
17 end
18
19 Facter.add(:custom_fact_3) do
20 setcode do
21 'custom_fact_3_value'
22 end
23 end
24 EOM
25
26 second_file_content = <<~EOM
27 Facter.add(:custom_fact_4) do
28 setcode do
29 'custom_fact_4_value'
30 end
31 end
32 EOM
33
34 def create_custom_fact_file(file_name, file_content, agent, folder)
35 fact_file = File.join(folder, file_name)
36 create_remote_file(agent, fact_file, file_content)
37 end
38
39 def create_api_call_file(test_vars, facter_querry)
40 file_content = <<-EOM
41 require 'facter'
42 Facter.search(\'#{test_vars[:facts_dir]}\')
43 #{facter_querry}
44 EOM
45 create_custom_fact_file(test_vars[:test_script_name], file_content, test_vars[:agent], test_vars[:script_dir])
46 end
47
48 agents.each do |agent|
49 test_vars = {}
50 test_vars[:facts_dir] = agent.tmpdir('facts_dir')
51 test_vars[:script_dir] = agent.tmpdir('script_dir')
52 test_vars[:agent] = agent
53 test_vars[:test_script_name] = 'test_custom_facts.rb'
54 test_vars[:test_script_path] = File.join(test_vars[:script_dir], test_vars[:test_script_name])
55
56 create_custom_fact_file('file1.rb', first_file_content, test_vars[:agent], test_vars[:facts_dir])
57 create_custom_fact_file('file2.rb', second_file_content, test_vars[:agent], test_vars[:facts_dir])
58
59 teardown do
60 agent.rm_rf(test_vars[:facts_dir])
61 agent.rm_rf(test_vars[:script_dir])
62 end
63
64 step "Agent #{agent}: Verify that custom fact 1 is available" do
65 create_api_call_file(test_vars, "puts Facter.value('custom_fact_1')")
66 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
67 assert_match(/custom_fact_1_value/, ruby_result.stdout.chomp)
68 end
69 end
70
71 step "Agent #{agent}: Verify that custom fact 2 is missing" do
72 create_api_call_file(test_vars, "puts Facter.value('custom_fact_2')")
73 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
74 assert_no_match(/custom_fact_2_value/, ruby_result.stdout.chomp)
75 end
76 end
77
78 step "Agent #{agent}: Verify that custom fact 3 is missing" do
79 create_api_call_file(test_vars, "puts Facter.value('custom_fact_3')")
80 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
81 assert_no_match(/custom_fact_3_value/, ruby_result.stdout.chomp)
82 end
83 end
84
85 step "Agent #{agent}: Verify that custom fact 4 is available" do
86 create_api_call_file(test_vars, "puts Facter.value('custom_fact_4')")
87 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
88 assert_match(/custom_fact_4_value/, ruby_result.stdout.chomp)
89 end
90 end
91
92 step "Agent #{agent}: Verify that a core fact is still available" do
93 os_name = on(agent, facter('os.name')).stdout.chomp
94 create_api_call_file(test_vars, "puts Facter.value('os.name')")
95 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
96 assert_match(/#{os_name}/, ruby_result.stdout)
97 end
98 end
99
100 step "Agent #{agent}: Verify that an error is outputted when custom fact file has an error" do
101 create_api_call_file(test_vars, "Facter.value('custom_fact_1')")
102 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
103 assert_match(/Facter.*error while resolving custom facts in .*file1.rb undefined local variable or method `nill'/,
104 ruby_result.stdout)
105 end
106 end
107
108 step "Agent #{agent}: Verify that Fact.to_hash still works" do
109 create_api_call_file(test_vars, "puts Facter.to_hash")
110 on(agent, "#{ruby_command(agent)} #{test_vars[:test_script_path]}") do |ruby_result|
111 assert_match(/os.name/, ruby_result.stdout)
112 end
113 end
114 end
115 end
116
0 test_name 'Facter cli works when there is an error inside a custom fact file' do
1 tag 'risk:high'
2
3 first_file_content = <<-EOM
4 Facter.add(:custom_fact_1) do
5 setcode do
6 'custom_fact_1_value'
7 end
8 end
9
10 # some error
11 nill.size
12
13 Facter.add(:custom_fact_2) do
14 setcode do
15 'custom_fact_2_value'
16 end
17 end
18
19 Facter.add(:custom_fact_3) do
20 setcode do
21 'custom_fact_3_value'
22 end
23 end
24 EOM
25
26 second_file_content = <<~EOM
27 Facter.add(:custom_fact_4) do
28 setcode do
29 'custom_fact_4_value'
30 end
31 end
32 EOM
33
34 def create_custom_fact_file(file_name, file_content, fact_dir, agent)
35 fact_file = File.join(fact_dir, file_name)
36 create_remote_file(agent, fact_file, file_content)
37 end
38
39 agents.each do |agent|
40 custom_facts = agent.tmpdir('custom_facts_dir')
41
42 os_name = on(agent, facter('os.name')).stdout.chomp
43
44 create_custom_fact_file('file1.rb', first_file_content, custom_facts, agent)
45 create_custom_fact_file('file2.rb', second_file_content, custom_facts, agent)
46 env = {'FACTERLIB' => custom_facts}
47
48 teardown do
49 agent.rm_rf(custom_facts)
50 end
51
52 step "Agent #{agent}: Verify that custom fact 1 is available" do
53 on(agent, facter('custom_fact_1', environment: env), acceptable_exit_codes: [1]) do |facter_output|
54 assert_equal('custom_fact_1_value', facter_output.stdout.chomp)
55 end
56 end
57
58 step "Agent #{agent}: Verify that custom fact 2 is not available" do
59 on(agent, facter('custom_fact_2', environment: env), acceptable_exit_codes: [1]) do |facter_output|
60 assert_equal('', facter_output.stdout.chomp)
61 end
62 end
63
64 step "Agent #{agent}: Verify that custom fact 3 is not available" do
65 on(agent, facter('custom_fact_3', environment: env), acceptable_exit_codes: [1]) do |facter_output|
66 assert_equal('', facter_output.stdout.chomp)
67 end
68 end
69
70 step "Agent #{agent}: Verify that custom fact 4 is available" do
71 on(agent, facter('custom_fact_4', environment: env), acceptable_exit_codes: [1]) do |facter_output|
72 assert_equal('custom_fact_4_value', facter_output.stdout.chomp)
73 end
74 end
75
76 step "Agent #{agent}: Verify that a core fact is still available" do
77 on(agent, facter('os.name', environment: env), acceptable_exit_codes: [1]) do |facter_output|
78 assert_equal(os_name, facter_output.stdout.chomp)
79 end
80 end
81
82 step "Agent #{agent}: Verify that an error is outputted when custom fact file has an error" do
83 on(agent, facter('custom_fact_4', environment: env), acceptable_exit_codes: [1]) do |facter_output|
84 assert_match(/ERROR Facter.*error while resolving custom facts in .*file1.rb undefined local variable or method `nill'/,
85 facter_output.stderr.chomp)
86 end
87 end
88
89 step "Agent #{agent}: Verify that most core facts are available" do
90 on(agent, facter('--json')) do |facter_output|
91 expected_keys = %w[identity memory os ruby networking system_uptime processors]
92 actual_keys = JSON.parse(facter_output.stdout).keys
93
94 assert_equal(true, (expected_keys - actual_keys).empty?)
95 end
96 end
97 end
98 end
99
0 test_name "(FACT-2934) Facter::Core::Execution sets $?" do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 command = if agent['platform'] =~ /windows/
5 'cmd /c exit 1'
6 elsif agent['platform'] =~ /solaris/ || agent['platform'] =~ /osx/
7 `which bash`.chomp + " -c 'exit 1'"
8 else
9 `which false`.chomp
10 end
11
12 content = <<-EOM
13 Facter.add(:foo) do
14 setcode do
15 Facter::Util::Resolution.exec("#{command}")
16 "#{command} exited with code: %{status}" % {status: $?.exitstatus}
17 end
18 end
19 EOM
20
21 fact_dir = agent.tmpdir('facter')
22 fact_file = File.join(fact_dir, 'test_facts.rb')
23 create_remote_file(agent, fact_file, content)
24
25 teardown do
26 agent.rm_rf(fact_dir)
27 end
28
29 step "Facter: should resolve the custom fact" do
30 on(agent, facter('--custom-dir', fact_dir, 'foo')) do |facter_output|
31 assert_match(/exited with code: 1/, facter_output.stdout.chomp)
32 end
33 end
34 end
35 end
36
37
0 test_name 'FACT-2054: Custom facts that execute a shell command should expand it' do
1 tag 'risk:high'
2
3 confine :to, :platform => /el-7/
4
5 content = <<-EOM
6 Facter.add(:foo) do
7 setcode do
8 Facter::Core::Execution.execute("cd /opt/puppetlabs && pwd")
9 end
10 end
11 EOM
12
13 agents.each do |agent|
14 fact_dir = agent.tmpdir('facter')
15 fact_file = File.join(fact_dir, 'test_facts.rb')
16 create_remote_file(agent, fact_file, content)
17 env = {'FACTERLIB' => fact_dir}
18
19 teardown do
20 agent.rm_rf(fact_dir)
21 end
22
23 step "Agent: Verify that command is expanded" do
24 on(agent, facter('foo', :environment => env)) do |facter_result|
25 refute_equal('/opt/puppetlabs', facter_result.stdout.chomp, 'command was not expanded')
26 end
27 end
28 end
29 end
0 test_name 'C14893: Facter should handle multiple facts in a single file' do
1 tag 'risk:high'
2
3 fact_content = <<-EOM
4 Facter.add(:test_fact1) do
5 setcode do
6 "test fact 1"
7 end
8 end
9
10 Facter.add(:test_fact2) do
11 setcode do
12 "test fact 2"
13 end
14 end
15 EOM
16
17 agents.each do |agent|
18 fact_dir = agent.tmpdir('facter')
19 fact_file = File.join(fact_dir, 'test_facts.rb')
20 create_remote_file(agent, fact_file, fact_content)
21 env = {'FACTERLIB' => fact_dir}
22
23 teardown do
24 agent.rm_rf(fact_dir)
25 end
26
27 step "Agent: Verify test_fact1 from #{fact_file}" do
28 on(agent, facter('test_fact1', :environment => env)) do |facter_result|
29 assert_equal('test fact 1', facter_result.stdout.chomp, 'test_fact1 is not the correct value')
30 end
31 end
32
33 step "Agent: Verify test_fact2 from #{fact_file}" do
34 on(agent, facter('test_fact2', :environment => env)) do |facter_result|
35 assert_equal('test fact 2', facter_result.stdout.chomp, 'test_fact2 is not the correct value')
36 end
37 end
38 end
39 end
0 test_name 'Facter should load facts that are placed in facter directory from all load path locations ' do
1 tag 'risk:high'
2
3 content = <<-EOM
4 Facter.add(:my_custom_fact) do
5 setcode do
6 'before_update'
7 end
8 end
9 EOM
10
11 script_content = <<-EOM
12 #!/usr/bin/env ruby
13
14 require 'facter'
15
16 file_name = 'custom_fact.rb'
17
18 path = ARGV[0]
19 $LOAD_PATH.unshift(path)
20
21 puts Facter.value('my_custom_fact')
22
23 # change custom fact value
24 text = File.read(path + '/facter/' + file_name)
25 new_contents = text.gsub(/before_update/, "after_update")
26
27 # Write changes to the file
28 File.open(path + '/facter/' + file_name, "w") {|file| file.puts new_contents }
29
30 # if we don't reset, the fact is cached and the old value is displayed
31 Facter.reset
32
33 puts Facter.value('my_custom_fact')
34 EOM
35
36
37 agents.each do |agent|
38 fact_temp_dir = agent.tmpdir('custom_facts')
39 test_script_dir = agent.tmpdir('test_script')
40 fact_dir = Pathname.new("#{fact_temp_dir}/lib/facter")
41
42 agent.mkdir_p(fact_dir.to_s)
43
44 fact_file = File.join(fact_dir, 'custom_fact.rb')
45 test_script_file = File.join(test_script_dir, 'test_script.rb')
46
47 create_remote_file(agent, fact_file, content)
48 create_remote_file(agent, test_script_file, script_content)
49
50 teardown do
51 agent.rm_rf(fact_temp_dir)
52 agent.rm_rf(test_script_dir)
53 end
54
55 step "Facter: Verify that custom fact updates it's value after updating the fact and reseting facter" do
56 # use ruby provided by puppet
57 on(agent, "#{ruby_command(agent)} #{test_script_file} #{fact_temp_dir}/lib") do |script_output|
58 assert_match(/before_update\nafter_update/, script_output.stdout, 'Expected custom facts value before and after fact value update')
59 end
60 end
61 end
62 end
0 test_name "Facter::Core::Execution doesn't kill process with long stderr message" do
1 tag 'risk:high'
2
3 confine :except, :platform => /windows/
4
5 long_output = "This is a very long error message. " * 4096
6 file_content = <<-EOM
7 #!/bin/sh
8 echo 'newfact=value_of_fact'
9 1>&2 echo #{long_output}
10 exit 1
11 EOM
12
13
14 agents.each do |agent|
15
16 external_dir = agent.tmpdir('external_dir')
17 fact_file = File.join(external_dir, 'test.sh')
18 create_remote_file(agent, fact_file, file_content)
19 agent.chmod('+x', fact_file)
20
21 teardown do
22 agent.rm_rf(external_dir)
23 end
24
25 step "Facter: should resolve the external fact and print as warning script's stderr message" do
26 on agent, facter('--external-dir', external_dir, 'newfact') do |facter_output|
27 assert_match(/value_of_fact/, facter_output.stdout.chomp)
28 assert_match(/WARN test.sh .*test.sh completed with the following stderr message: This is a very long error message./, facter_output.stderr.chomp)
29 end
30 end
31 end
32 end
33
0 test_name 'FACT-2054: Custom facts that execute a shell command should not expand it' do
1 tag 'risk:high'
2
3 confine :to, :platform => /el-7/
4
5 content = <<-EOM
6 Facter.add(:foo) do
7 setcode do
8 Facter::Core::Execution.execute("cd /opt/puppetlabs && pwd", {:expand => false})
9 end
10 end
11 EOM
12
13 agents.each do |agent|
14 fact_dir = agent.tmpdir('facter')
15 fact_file = File.join(fact_dir, 'test_facts.rb')
16 create_remote_file(agent, fact_file, content)
17 env = {'FACTERLIB' => fact_dir}
18
19 teardown do
20 agent.rm_rf(fact_dir)
21 end
22
23 step "Agent: Verify that command is not expanded" do
24 on(agent, facter('foo', :environment => env)) do |facter_result|
25 assert_equal('/opt/puppetlabs', facter_result.stdout.chomp, 'command was expanded')
26 end
27 end
28 end
29 end
0 test_name "Facter::Util::Resolution accepts timeout option" do
1 tag 'risk:high'
2
3 file_content = <<-EOM
4 Facter.add(:foo, {timeout: 0.2}) do
5 setcode do
6 Facter::Core::Execution.execute("sleep 1")
7 end
8 end
9 EOM
10
11
12 agents.each do |agent|
13
14 custom_dir = agent.tmpdir('arbitrary_dir')
15 fact_file = File.join(custom_dir, 'fact.rb')
16 create_remote_file(agent, fact_file, file_content)
17
18 teardown do
19 agent.rm_rf(custom_dir)
20 end
21
22 step "Facter: Errors that the custom fact reached the timeout" do
23 on(agent, facter('--custom-dir', custom_dir, 'foo'), acceptable_exit_codes: 1) do |output|
24 assert_match(/ERROR .*Timed out after 0.2 seconds while resolving fact='foo', resolution=.*/,
25 output.stderr.chomp)
26 end
27 end
28 end
29 end
30
0 # frozen_string_literal: true
1
2 test_name 'strucutured custom facts can be blocked' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_file = 'custom_fact.rb'
9 fact_1_name = 'key1.key2'
10 fact_2_name = 'key1.key3'
11 fact_1_value = 'test1'
12 fact_2_value = 'test2'
13
14 fact_content = <<-RUBY
15 Facter.add('#{fact_1_name}') do
16 setcode do
17 "#{fact_1_value}"
18 end
19 end
20
21 Facter.add('#{fact_2_name}') do
22 setcode do
23 "#{fact_2_value}"
24 end
25 end
26 RUBY
27
28 config_data = <<~HOCON
29 facts : {
30 blocklist : [ "#{fact_1_name}" ],
31 }
32 global : {
33 force-dot-resolution : true
34 }
35 HOCON
36
37 agents.each do |agent|
38 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
39 config_file = File.join(config_dir, 'facter.conf')
40 agent.mkdir_p(config_dir)
41 create_remote_file(agent, config_file, config_data)
42
43 fact_dir = agent.tmpdir('custom_facts')
44 fact_file = File.join(fact_dir, fact_file)
45 create_remote_file(agent, fact_file, fact_content)
46
47 teardown do
48 agent.rm_rf(fact_dir)
49 agent.rm_rf(config_dir)
50 end
51
52 step 'blocked structured custom fact is not displayed' do
53 on(agent, facter("--custom-dir=#{fact_dir} key1.key2")) do |facter_output|
54 assert_equal('', facter_output.stdout.chomp)
55 end
56 end
57
58 step 'the remaining structured fact is displayed' do
59 on(agent, facter("--custom-dir=#{fact_dir} key1.key3")) do |facter_output|
60 assert_equal(fact_2_value, facter_output.stdout.chomp)
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 test_name 'structured custom facts can be cached' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_file = 'custom_fact.rb'
9 fact_1_name = 'key1.key2'
10 fact_2_name = 'key1.key3'
11 fact_1_value = 'test1'
12 fact_2_value = 'test2'
13
14 fact_content = <<-RUBY
15 Facter.add('#{fact_1_name}') do
16 setcode do
17 "#{fact_1_value}"
18 end
19 end
20
21 Facter.add('#{fact_2_name}') do
22 setcode do
23 "#{fact_2_value}"
24 end
25 end
26 RUBY
27
28 cached_file_content = <<~RUBY
29 {
30 "#{fact_1_name}": "#{fact_1_value}",
31 "#{fact_2_name}": "#{fact_2_value}",
32 "cache_format_version": 1
33 }
34 RUBY
35
36 partial_file_content = <<~RUBY
37 {
38 "#{fact_2_name}": "#{fact_2_value}",
39 "cache_format_version": 1
40 }
41 RUBY
42
43 config_data = <<~HOCON
44 facts : {
45 ttls : [
46 { "cached-custom-facts" : 3 days }
47 ]
48 }
49 fact-groups : {
50 cached-custom-facts : ["key1"],
51 }
52
53 global : {
54 force-dot-resolution : true
55 }
56 HOCON
57
58 agents.each do |agent|
59 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
60
61 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
62 config_file = File.join(config_dir, 'facter.conf')
63 agent.mkdir_p(config_dir)
64 create_remote_file(agent, config_file, config_data)
65
66 fact_dir = agent.tmpdir('custom_facts')
67 fact_file = File.join(fact_dir, fact_file)
68 create_remote_file(agent, fact_file, fact_content)
69
70 teardown do
71 agent.rm_rf(fact_dir)
72 agent.rm_rf(config_dir)
73 agent.rm_rf(cache_folder)
74 end
75
76 step 'creates the cache for part of the fact' do
77 on(agent, facter("--custom-dir=#{fact_dir} key1.key3"))
78
79 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
80 assert_equal(true, result)
81
82 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
83 assert_match(
84 partial_file_content.chomp,
85 cat_output.strip,
86 'Expected cached custom fact file to contain fact information'
87 )
88 end
89
90 step 'creates the cache for full fact' do
91 on(agent, facter("--custom-dir=#{fact_dir} key1"))
92
93 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
94 assert_equal(true, result)
95
96 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
97 assert_match(
98 cached_file_content.chomp,
99 cat_output.strip,
100 'Expected cached custom fact file to contain fact information'
101 )
102 end
103
104 step 'resolves the fact' do
105 on(agent, facter("--custom-dir=#{fact_dir} key1 --json")) do |facter_output|
106 assert_equal(
107 {
108 'key1' => {
109 'key2' => fact_1_value,
110 'key3' => fact_2_value
111 }
112 },
113 JSON.parse(facter_output.stdout.chomp)
114 )
115 end
116 end
117 end
118 end
0 # frozen_string_literal: true
1
2 test_name 'custom facts can be defined structured' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_file = 'custom_fact.rb'
9 fact_name = 'key1.key2'
10 fact_value = 'test'
11
12 fact_content = <<-RUBY
13 Facter.add('#{fact_name}') do
14 setcode do
15 "#{fact_value}"
16 end
17 end
18 RUBY
19
20 config_data = <<~HOCON
21 global : {
22 force-dot-resolution : true
23 }
24 HOCON
25
26 agents.each do |agent|
27 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
28 config_file = File.join(config_dir, 'facter.conf')
29 agent.mkdir_p(config_dir)
30 create_remote_file(agent, config_file, config_data)
31
32 fact_dir = agent.tmpdir('custom_facts')
33 fact_file = File.join(fact_dir, fact_file)
34 create_remote_file(agent, fact_file, fact_content)
35
36 teardown do
37 agent.rm_rf(fact_dir)
38 agent.rm_rf(config_dir)
39 end
40
41 step 'access fact with dot' do
42 on(agent, facter("--custom-dir=#{fact_dir} key1.key2")) do |facter_output|
43 assert_equal(fact_value, facter_output.stdout.chomp)
44 end
45
46 on(agent, facter("--custom-dir=#{fact_dir} key1 --json")) do |facter_output|
47 assert_equal(
48 { 'key1' => { 'key2' => fact_value } },
49 JSON.parse(facter_output.stdout.chomp)
50 )
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 test_name 'custom structured facts can extend core facts' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 core_fact = 'os'
9 fact_file = 'custom_fact.rb'
10 fact_name = 'extension'
11 fact_value = 'test'
12
13 fact_content = <<-RUBY
14 Facter.add('#{core_fact}.#{fact_name}') do
15 setcode do
16 "#{fact_value}"
17 end
18 end
19 RUBY
20
21 config_data = <<~HOCON
22 global : {
23 force-dot-resolution : true
24 }
25 HOCON
26
27 agents.each do |agent|
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, 'facter.conf')
30 agent.mkdir_p(config_dir)
31 create_remote_file(agent, config_file, config_data)
32
33 builtin_value = JSON.parse(on(agent, facter('os --json')).stdout.chomp)
34 builtin_value['os'][fact_name] = fact_value
35 expected_value = builtin_value
36
37 fact_dir = agent.tmpdir('custom_facts')
38 fact_file = File.join(fact_dir, fact_file)
39 create_remote_file(agent, fact_file, fact_content)
40
41 teardown do
42 agent.rm_rf(fact_dir)
43 agent.rm_rf(config_dir)
44 end
45
46 step 'check that core fact is extended' do
47 on(agent, facter("os --custom-dir=#{fact_dir} --json")) do |facter_output|
48 assert_equal(
49 expected_value,
50 JSON.parse(facter_output.stdout.chomp)
51 )
52 end
53 end
54 end
55 end
0 # frozen_string_literal: true
1
2 test_name 'custom structured facts can override parts of core facts' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 core_fact = 'os'
9 fact_file = 'custom_fact.rb'
10 fact_name = 'name'
11 fact_value = 'test'
12
13 fact_content = <<-RUBY
14 Facter.add('#{core_fact}.#{fact_name}', weight: 999) do
15 setcode do
16 "#{fact_value}"
17 end
18 end
19 RUBY
20
21 config_data = <<~HOCON
22 global : {
23 force-dot-resolution : true
24 }
25 HOCON
26
27 agents.each do |agent|
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, 'facter.conf')
30 agent.mkdir_p(config_dir)
31 create_remote_file(agent, config_file, config_data)
32
33 builtin_value = JSON.parse(on(agent, facter('os --json')).stdout.chomp)
34 builtin_value['os'][fact_name] = fact_value
35 expected_value = builtin_value
36
37 fact_dir = agent.tmpdir('custom_facts')
38 fact_file = File.join(fact_dir, fact_file)
39 create_remote_file(agent, fact_file, fact_content)
40
41 teardown do
42 agent.rm_rf(fact_dir)
43 agent.rm_rf(config_dir)
44 end
45
46 step 'check that core fact is extended' do
47 on(agent, facter("os --custom-dir=#{fact_dir} --json")) do |facter_output|
48 assert_equal(
49 expected_value,
50 JSON.parse(facter_output.stdout.chomp)
51 )
52 end
53
54 on(agent, facter("os.name --custom-dir=#{fact_dir}")) do |facter_output|
55 assert_equal(
56 fact_value,
57 facter_output.stdout.chomp
58 )
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 test_name 'structured custom facts can be granually cached' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_file = 'custom_fact.rb'
9 fact_1_name = 'key1.key2'
10 fact_2_name = 'key1.key3'
11 fact_1_value = 'test1'
12 fact_2_value = 'test2'
13
14 fact_content = <<-RUBY
15 Facter.add('#{fact_1_name}') do
16 setcode do
17 "#{fact_1_value}"
18 end
19 end
20
21 Facter.add('#{fact_2_name}') do
22 setcode do
23 "#{fact_2_value}"
24 end
25 end
26 RUBY
27
28 cached_file_content = <<~RUBY
29 {
30 "#{fact_1_name}": "#{fact_1_value}",
31 "cache_format_version": 1
32 }
33 RUBY
34
35 config_data = <<~HOCON
36 facts : {
37 ttls : [
38 { "cached-custom-facts" : 3 days }
39 ]
40 }
41 fact-groups : {
42 cached-custom-facts : ["#{fact_1_name}"],
43 }
44 global : {
45 force-dot-resolution : true
46 }
47 HOCON
48
49 agents.each do |agent|
50 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
51
52 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
53 config_file = File.join(config_dir, 'facter.conf')
54 agent.mkdir_p(config_dir)
55 create_remote_file(agent, config_file, config_data)
56
57 fact_dir = agent.tmpdir('custom_facts')
58 fact_file = File.join(fact_dir, fact_file)
59 create_remote_file(agent, fact_file, fact_content)
60
61 teardown do
62 agent.rm_rf(fact_dir)
63 agent.rm_rf(config_dir)
64 agent.rm_rf(cache_folder)
65 end
66
67 step 'does not create cache of part of the fact that is not in ttls' do
68 on(agent, facter("--custom-dir=#{fact_dir} key1.key3"))
69
70 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
71 assert_equal(false, result)
72 end
73
74 step 'creates a cached-custom-facts cache file that contains fact information' do
75 on(agent, facter("--custom-dir=#{fact_dir} key1.key2"))
76
77 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
78 assert_equal(true, result)
79
80 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
81 assert_match(
82 cached_file_content.chomp,
83 cat_output.strip,
84 'Expected cached custom fact file to contain fact information'
85 )
86 end
87
88 step 'resolves the fact' do
89 on(agent, facter("--custom-dir=#{fact_dir} key1 --json")) do |facter_output|
90 assert_equal(
91 {
92 'key1' => {
93 'key2' => fact_1_value,
94 'key3' => fact_2_value
95 }
96 },
97 JSON.parse(facter_output.stdout.chomp)
98 )
99 end
100 end
101 end
102 end
0 test_name 'Facter::Core::Execution accepts and correctly sets a time limit option' do
1 tag 'risk:high'
2
3 first_file_content = <<-EOM
4 Facter.add(:foo) do
5 setcode do
6 Facter::Core::Execution.execute("sleep 3", {:timeout => 2, :on_fail => :not_raise})
7 end
8 end
9 EOM
10
11 second_file_content = <<-EOM
12 Facter.add(:custom_fact) do
13 setcode do
14 Facter::Core::Execution.execute("sleep 2", {:timeout => 1})
15 end
16 end
17 EOM
18
19
20 agents.each do |agent|
21
22 custom_dir = agent.tmpdir('arbitrary_dir')
23 fact_file1 = File.join(custom_dir, 'file1.rb')
24 fact_file2 = File.join(custom_dir, 'file2.rb')
25 create_remote_file(agent, fact_file1, first_file_content)
26 create_remote_file(agent, fact_file2, second_file_content)
27
28 teardown do
29 agent.rm_rf(custom_dir)
30 end
31
32 step "Facter: Logs that command of the first custom fact had timeout after setted time limit" do
33 on agent, facter('--custom-dir', custom_dir, 'foo --debug') do |output|
34 assert_match(/DEBUG Facter::Core::Execution.*Timeout encounter after 2s, killing process with pid:/,
35 output.stderr.chomp)
36 end
37 end
38
39 step "Facter: Logs an error stating that the command of the second custom fact had timeout" do
40 on(agent, facter('--custom-dir', custom_dir, 'custom_fact --debug'), acceptable_exit_codes: 1) do |output|
41 assert_match(/ERROR\s+.*Failed while executing '.*sleep.*2': Timeout encounter after 1s, killing process/,
42 output.stderr.chomp)
43 end
44 end
45 end
46 end
0 test_name 'C92060: Custom facts should not hang Facter when using win32ole' do
1 tag 'risk:high'
2
3 confine :to, :platform => /windows/
4 require 'timeout'
5
6 content = <<-EOM
7 Facter.add('custom_fact_ole') do
8 setcode do
9 require 'win32ole'
10 locator = WIN32OLE.new('WbemScripting.SWbemLocator')
11 locator.ConnectServer('', "root/CIMV2", '', '', nil, nil, nil, nil).to_s
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 custom_dir = agent.tmpdir('arbitrary_dir')
18 custom_fact = File.join(custom_dir, 'custom_fact.rb')
19 create_remote_file(agent, custom_fact, content)
20
21 teardown do
22 agent.rm_rf(custom_dir)
23 end
24
25 # Test is assumed to have hung if it takes longer than 5 seconds.
26 Timeout::timeout(5) do
27 on agent, facter('--custom-dir', custom_dir, 'custom_fact_ole') do |facter_result|
28 assert_match(/#<WIN32OLE:0x[0-9a-f]+>/, facter_result.stdout.chomp, 'Custom fact output does not match expected output')
29 end
30 end
31 end
32 end
0 test_name 'ttls configured weighted custom facts files creates cache file and reads cache file depending on weight' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 duplicate_custom_fact_name = 'random_custom_fact'
8 custom_fact_value = 'custom fact value with weight:'
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{duplicate_custom_fact_name}) do
12 has_weight 90
13 setcode do
14 "#{custom_fact_value} 90"
15 end
16 end
17 Facter.add(:#{duplicate_custom_fact_name}) do
18 has_weight 100
19 setcode do
20 "#{custom_fact_value} 100"
21 end
22 end
23 Facter.add(:#{duplicate_custom_fact_name}) do
24 has_weight 110
25 setcode do
26 "#{custom_fact_value} 110"
27 end
28 end
29 CUSTOM_FACT
30
31 cached_file_content_highest_weight = <<~CACHED_FILE
32 {
33 "#{duplicate_custom_fact_name}": "#{custom_fact_value} 110",
34 "cache_format_version": 1
35 }
36 CACHED_FILE
37
38 config_data = <<~FACTER_CONF
39 facts : {
40 ttls : [
41 { "cached-custom-facts" : 3 days }
42 ]
43 }
44 fact-groups : {
45 cached-custom-facts : [ "#{duplicate_custom_fact_name}" ]
46 }
47 FACTER_CONF
48
49 agents.each do |agent|
50 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
51 fact_dir = agent.tmpdir('facter')
52 env = { 'FACTERLIB' => fact_dir }
53 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
54 config_file = File.join(config_dir, 'facter.conf')
55
56 step "Agent #{agent}: create config file" do
57 agent.mkdir_p(config_dir)
58 create_remote_file(agent, config_file, config_data)
59 fact_file = File.join(fact_dir, custom_fact_file)
60 create_remote_file(agent, fact_file, fact_content)
61 end
62
63 teardown do
64 agent.rm_rf(fact_dir)
65 agent.rm_rf("#{cache_folder}/*")
66 agent.rm_rf("#{config_dir}/facter.conf")
67 end
68
69 step "should log that it creates cache file and it caches custom facts found in facter.conf with the highest weight" do
70 on(agent, facter("#{duplicate_custom_fact_name} --debug", environment: env)) do |facter_result|
71 assert_equal(custom_fact_value + " 110", facter_result.stdout.chomp, "#{duplicate_custom_fact_name} value changed")
72 assert_match(/facts cache file expired, missing or is corrupt/, facter_result.stderr,
73 'Expected debug message to state that custom facts cache file is missing or expired')
74 assert_match(/Saving cached custom facts to ".+"|caching values for cached-custom-facts facts/, facter_result.stderr,
75 'Expected debug message to state that custom facts will be cached')
76 end
77 end
78
79 step "should create a cached-custom-facts cache file that containt fact information from the highest weight fact" do
80 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
81 assert_equal(true, result)
82 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
83 assert_match(cached_file_content_highest_weight.chomp, cat_output.strip, 'Expected cached custom fact file to contain fact information from the highest weight fact')
84 end
85
86 step 'should read from the cached file for a custom fact that has been cached' do
87 on(agent, facter("#{duplicate_custom_fact_name} --debug", environment: env)) do |facter_result|
88 assert_match(/Loading cached custom facts from file ".+"|loading cached values for random_custom_fact facts/, facter_result.stderr,
89 'Expected debug message to state that cached custom facts are read from file')
90 end
91 end
92 end
93 end
0 test_name 'FACT-2054: Execute on Windows with expand => false should raise an error' do
1 tag 'risk:high'
2
3 confine :to, :platform => /windows/
4
5 content = <<-EOM
6 Facter.add(:foo) do
7 setcode do
8 Facter::Core::Execution.execute("cmd", {:expand => false})
9 end
10 end
11 EOM
12
13 agents.each do |agent|
14 fact_dir = agent.tmpdir('facter')
15 fact_file = File.join(fact_dir, 'test_facts.rb')
16 create_remote_file(agent, fact_file, content)
17 env = {'FACTERLIB' => fact_dir}
18
19 teardown do
20 agent.rm_rf(fact_dir)
21 end
22
23 step "Agent: Verify that exception is raised" do
24 on(agent, facter('foo', :environment => env), :acceptable_exit_codes => [0, 1]) do |output|
25 assert_match(/Unsupported argument on Windows/, output.stderr, '{:expand => false} on Windows should raise error')
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 test_name 'Can use environment facts' do
3 step 'FACTER_ env fact is correctly resolved' do
4 fact_name = 'env_name'
5 fact_value = 'env_value'
6
7 on(agent, facter(fact_name, environment: { "FACTER_#{fact_name}" => fact_value })) do |facter_output|
8 assert_equal(
9 fact_value,
10 facter_output.stdout.chomp,
11 'Expected `FACTER_` to be resolved from environment'
12 )
13 end
14 end
15
16 step 'FACTER_ env fact is correctly resolved when the fact name is upcased' do
17 fact_name = 'env_name'
18 fact_value = 'env_value'
19
20 on(agent, facter(fact_name, environment: { "FACTER_#{fact_name.upcase}" => fact_value })) do |facter_output|
21 assert_equal(
22 fact_value,
23 facter_output.stdout.chomp,
24 'Expected `FACTER_` to be resolved from environment'
25 )
26 end
27 end
28 end
0 test_name 'custom facts included in blocklist will not be displayed' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 agents.each do |agent|
7 ext = get_external_fact_script_extension(agent['platform'])
8 facts_dir = agent.tmpdir('facts.d')
9 fact_file = File.join(facts_dir, "external_fact_1#{ext}")
10 content = external_fact_content(agent['platform'], 'external_fact', 'external_value')
11
12 config_dir = agent.tmpdir("config_dir")
13 config_file = File.join(config_dir, "facter.conf")
14
15 teardown do
16 agent.rm_rf(facts_dir)
17 end
18
19 create_remote_file(agent, config_file, <<-FILE)
20 facts : { blocklist : [ "external_fact_1#{ext}" ] }
21 FILE
22
23 step "Agent #{agent}: setup default external facts directory and fact" do
24 agent.mkdir_p(facts_dir)
25 create_remote_file(agent, fact_file, content)
26 agent.chmod('+x', fact_file)
27 end
28
29 step "agent #{agent}: resolve the external fact" do
30 on(agent, facter("--debug --external-dir \"#{facts_dir}\" --config \"#{config_file}\"")) do |facter_output|
31 assert_match(/External fact file external_fact_1#{ext} blocked./, facter_output.stderr.chomp, 'Expected to block the external_fact')
32 assert_no_match(/external_fact => external_value/, stdout, 'Expected fact not to match fact')
33 end
34 end
35 end
36 end
0 test_name 'C100537: FACTER_ env var should override external fact' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 agents.each do |agent|
7 fact_name = 'external_fact'
8 fact_value = 'from_script'
9 override_value = 'override_fact'
10 external_dir = agent.tmpdir('facts.d')
11 fact_file = File.join(external_dir,
12 "#{fact_name}#{get_external_fact_script_extension(agent['platform'])}")
13
14 teardown do
15 agent.rm_rf(external_dir)
16 end
17
18 step "Agent #{agent}: setup external fact" do
19 agent.mkdir_p(external_dir)
20 create_remote_file(agent,
21 fact_file,
22 external_fact_content(agent['platform'], fact_name, fact_value))
23 agent.chmod('+x', fact_file)
24 end
25
26 step "Agent: #{agent}: ensure external fact resolves correctly" do
27 on(agent, facter("--external-dir \"#{external_dir}\" #{fact_name}")) do |facter_output|
28 assert_equal(fact_value,
29 facter_output.stdout.chomp,
30 'Expected external fact to resolve as defined in script')
31 end
32 end
33
34 step "Agent #{agent}: the fact value from FACTER_ env var should override the external fact value" do
35 on(agent, facter("--external-dir \"#{external_dir}\" #{fact_name}",
36 :environment => { "FACTER_#{fact_name}" => override_value })) do |facter_output|
37 assert_equal(override_value,
38 facter_output.stdout.chomp,
39 'Expected `FACTER_` fact value to override external fact')
40 end
41 end
42
43 end
44
45 end
0 test_name "C100154: --external-dir fact overrides fact in default facts.d directory" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 agents.each do |agent|
7 os_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
8 ext = get_external_fact_script_extension(agent['platform'])
9 factsd = get_factsd_dir(agent['platform'], os_version)
10 external_dir = agent.tmpdir('facts.d')
11 fact_file = File.join(factsd, "external_fact#{ext}")
12 content = external_fact_content(agent['platform'], 'external_fact', 'value')
13 override_fact_file = File.join(external_dir, "external_fact#{ext}")
14 override_content = external_fact_content(agent['platform'], 'external_fact', 'OVERRIDE_value')
15
16 teardown do
17 agent.rm_rf(fact_file)
18 agent.rm_rf(override_fact_file)
19 end
20
21 step "Agent #{agent}: setup default external facts directories and the test facts" do
22 agent.mkdir_p(factsd)
23 create_remote_file(agent, fact_file, content)
24 create_remote_file(agent, override_fact_file, override_content)
25 agent.chmod('+x', fact_file)
26 agent.chmod('+x', override_fact_file)
27 end
28
29 step "Agent #{agent}: the fact value from the custom external dir should override that of facts.d" do
30 on(agent, facter("--external-dir \"#{external_dir}\" external_fact")) do |facter_output|
31 assert_equal('OVERRIDE_value', facter_output.stdout.chomp, 'Expected to resolve override version of the external_fact')
32 end
33 end
34 end
35 end
0 test_name "C100150: external fact overrides custom fact without a weight" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_name = 'test'
7 # Use a static external fact
8 ext_fact = "#{fact_name}: 'EXTERNAL'"
9
10 agents.each do |agent|
11 facts_dir = agent.tmpdir('facts.d')
12 ext_fact_path = "#{facts_dir}/test.yaml"
13 cust_fact_path = "#{facts_dir}/test.rb"
14 create_remote_file(agent, cust_fact_path, custom_fact_content(fact_name, 'CUSTOM'))
15 create_remote_file(agent, ext_fact_path, ext_fact)
16
17 teardown do
18 agent.rm_rf(facts_dir)
19 end
20
21 step "Agent #{agent}: resolve an external fact over a custom fact" do
22 on(agent, facter("--external-dir \"#{facts_dir}\" --custom-dir=#{facts_dir} #{fact_name}")) do |facter_output|
23 assert_equal("EXTERNAL", facter_output.stdout.chomp)
24 end
25 end
26 end
27 end
28
0 test_name "C100151: external fact overrides a custom fact of weight 10000 or less" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_name = 'test'
7 # Use a static external fact
8 ext_fact = "#{fact_name}: 'EXTERNAL'"
9
10 agents.each do |agent|
11 facts_dir = agent.tmpdir('facts.d')
12 ext_fact_path = "#{facts_dir}/test.yaml"
13 cust_fact_path = "#{facts_dir}/test.rb"
14 create_remote_file(agent, ext_fact_path, ext_fact)
15 create_remote_file(agent, cust_fact_path, custom_fact_content(fact_name, 'CUSTOM', "has_weight 10000"))
16
17 teardown do
18 agent.rm_rf(facts_dir)
19 end
20
21 # Custom fact with weight <= 10000 should give precedence to the EXTERNAL fact
22 step "Agent #{agent}: resolve an external fact over the custom fact with a weight of 10000" do
23 on(agent, facter("--external-dir \"#{facts_dir}\" --custom-dir \"#{facts_dir}\" #{fact_name}")) do |facter_output|
24 assert_equal("EXTERNAL", facter_output.stdout.chomp)
25 end
26 end
27 end
28 end
29
0 test_name "C100152: external fact overrides a custom fact with a confine" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_name = "test"
7 # Use a static external fact
8 ext_fact = "#{fact_name}: 'EXTERNAL'"
9
10 agents.each do |agent|
11 # Shared directory for external and custom facts
12 facts_dir = agent.tmpdir('facts.d')
13 ext_fact_path = "#{facts_dir}/test.yaml"
14 cust_fact_path = "#{facts_dir}/test.rb"
15 create_remote_file(agent, ext_fact_path, ext_fact)
16
17 agent_kernel = on(agent, facter('kernel')).stdout.chomp
18 create_remote_file(agent, cust_fact_path,
19 custom_fact_content(fact_name, 'CUSTOM', "confine :kernel=>'#{agent_kernel}'"))
20
21 teardown do
22 agent.rm_rf(facts_dir)
23 end
24
25 # External fact should take precedence over a custom fact with a confine
26 # (from FACT-1413)
27 step "Agent #{agent}: resolve external fact over a custom fact with a confine" do
28 on(agent, facter("--external-dir \"#{facts_dir}\" --custom-dir \"#{facts_dir}\" test")) do |facter_output|
29 assert_equal("EXTERNAL", facter_output.stdout.chomp)
30 end
31 end
32 end
33 end
34
0 test_name "C64315: external facts that print messages to stderr should be seen on stderr" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 agents.each do |agent|
7 factsd = get_factsd_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
8 ext = get_external_fact_script_extension(agent['platform'])
9 ext_fact = File.join(factsd, "external_fact#{ext}")
10
11 if agent['platform'] =~ /windows/
12 content = <<EOM
13 echo "SCRIPT STDERR" >&2
14 echo "test=value"
15 EOM
16 else
17 content = <<EOM
18 #!/bin/sh
19 echo "SCRIPT STDERR" >&2
20 echo "test=value"
21 EOM
22 end
23
24 teardown do
25 agent.rm_rf(ext_fact)
26 end
27
28 step "Agent #{agent}: create facts.d directory and fact" do
29 agent.mkdir_p(factsd)
30 create_remote_file(agent, ext_fact, content)
31 agent.chmod('+x', ext_fact)
32 end
33
34 step "Agent #{agent}: external fact stderr messages should appear on stderr from facter" do
35 on(agent, facter) do |facter_output|
36 assert_match(/WARN.*SCRIPT STDERR/, facter_output.stderr,
37 "Expected facter to output a warning message with the stderr string from the external fact")
38 end
39 end
40
41 step "Agent #{agent}: external fact stderr messages should appear on stderr from puppet facts" do
42 on(agent, puppet("facts")) do |puppet_output|
43 assert_match(/Warning.*SCRIPT STDERR/, puppet_output.stderr,
44 "Expected puppet facts to output a warning message with the stderr string from the external fact")
45 end
46 end
47 end
48 end
0 test_name "C14892: external facts should only be run once" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 agents.each do |agent|
7 factsd = get_factsd_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
8 ext = get_external_fact_script_extension(agent['platform'])
9 ext_fact = File.join(factsd, "external_fact#{ext}")
10
11 if agent['platform'] =~ /windows/
12 content = <<EOM
13 echo "SCRIPT CALLED" >&2
14 echo "test=value"
15 EOM
16 else
17 content = <<EOM
18 #!/bin/sh
19 echo "SCRIPT CALLED" >&2
20 echo "test=value"
21 EOM
22 end
23
24 teardown do
25 agent.rm_rf(ext_fact)
26 end
27
28 step "Agent #{agent}: create facts.d directory and fact" do
29 agent.mkdir_p(factsd)
30 create_remote_file(agent, ext_fact, content)
31 agent.chmod('+x', ext_fact)
32 end
33
34 step "Agent #{agent}: ensure the fact is only executed once" do
35 on(agent, facter) do |facter_output|
36 lines = facter_output.stderr.split('\n')
37 times = lines.count { |line| line =~ /SCRIPT CALLED/ }
38 assert_equal(1, times, "External fact should only execute once: #{facter_output.stderr}")
39 end
40 end
41 end
42 end
0 # Verify that facter uses the new AIO default paths for external facts
1 #
2 # On Unix/Linux/OS X, there are three directories:
3 # /opt/puppetlabs/facter/facts.d/
4 # /etc/puppetlabs/facter/facts.d/
5 # /etc/facter/facts.d/
6 test_name "C59201: Fact directory precedence and resolution order for facts" do
7 tag 'risk:high'
8
9 confine :except, :platform => 'windows' # windows only supports 1 directory instead of 3 on unix
10
11 require 'facter/acceptance/user_fact_utils'
12 extend Facter::Acceptance::UserFactUtils
13
14 # Generate an external fact dynamically
15 def ext_fact(value='BASIC')
16 "test: '#{value}'"
17 end
18
19 agents.each do |agent|
20 # The directories that facter processes facts
21 os_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
22 factsd_dir = get_factsd_dir(agent['platform'], os_version)
23 etc_factsd_dir = get_etc_factsd_dir(agent['platform'])
24 etc_puppetlabs_factsd_dir = get_etc_puppetlabs_factsd_dir(agent['platform'])
25 factsd_path = "#{factsd_dir}/test.yaml"
26 etc_factsd_path = "#{etc_factsd_dir}/test.yaml"
27 etc_puppetlabs_factsd_path = "#{etc_puppetlabs_factsd_dir}/test.yaml"
28
29 teardown do
30 agent.rm_rf(factsd_dir)
31 agent.rm_rf(etc_factsd_dir)
32 agent.rm_rf(etc_puppetlabs_factsd_dir)
33 end
34
35 # ensure the fact directory we want to use exists
36 step "Agent #{agent}: create facts directory (#{etc_puppetlabs_factsd_dir})" do
37 agent.rm_rf(etc_puppetlabs_factsd_dir)
38 agent.mkdir_p(etc_puppetlabs_factsd_dir)
39 end
40
41 # A fact in the etc_puppetlabs_factsd_dir directory should resolve to the fact
42 step "Agent #{agent}: create and resolve a custom fact in #{etc_puppetlabs_factsd_dir}" do
43 create_remote_file(agent, etc_puppetlabs_factsd_path, ext_fact('etc_puppetlabs_path'))
44 on(agent, facter("test")) do |facter_output|
45 assert_match(/etc_puppetlabs_path/, facter_output.stdout, "Fact from #{etc_puppetlabs_factsd_dir} did not resolve correctly")
46 end
47 end
48
49 # remove the fact
50 step "Agent #{agent}: remove the fact in #{etc_puppetlabs_factsd_dir}" do
51 agent.rm_rf(etc_puppetlabs_factsd_path)
52 end
53
54 # ensure the fact directory we want to use exists
55 step "Agent #{agent}: create facts directory (#{etc_factsd_dir})" do
56 agent.rm_rf(etc_factsd_dir)
57 agent.mkdir_p(etc_factsd_dir)
58 end
59
60 # A fact in the etc_factsd_dir directory should resolve to the fact
61 step "Agent #{agent}: create and resolve a custom fact in #{etc_factsd_dir}" do
62 create_remote_file(agent, etc_factsd_path, ext_fact('etc_path'))
63 on(agent, facter("test")) do |facter_output|
64 assert_match(/etc_path/, facter_output.stdout, "Fact from #{etc_factsd_dir} did not resolve correctly")
65 end
66 end
67
68 # remove the fact
69 step "Agent #{agent}: remove the fact in #{etc_factsd_dir}" do
70 agent.rm_rf(etc_factsd_path)
71 end
72
73 # ensure the fact directory we want to use exists
74 step "Agent #{agent}: create facts directory (#{factsd_dir})" do
75 agent.rm_rf(factsd_dir)
76 agent.mkdir_p(factsd_dir)
77 end
78
79 # A fact in the factsd_dir directory should resolve to the fact
80 step "Agent #{agent}: create and resolve a custom fact in #{factsd_dir}" do
81 create_remote_file(agent, factsd_path, ext_fact('default_factsd'))
82 on(agent, facter("test")) do |facter_output|
83 assert_match(/default_factsd/, facter_output.stdout, "Fact from #{factsd_dir} did not resolve correctly")
84 end
85 end
86
87 # remove the fact
88 step "Agent #{agent}: remove the fact in #{factsd_dir}" do
89 agent.rm_rf(factsd_path)
90 end
91
92 # A fact in the etc_factsd_dir directory should take precedence over the same fact in factsd_dir
93 step "Agent #{agent}: create and resolve 2 facts of the same name between #{factsd_dir} and #{etc_factsd_dir}" do
94 create_remote_file(agent, factsd_path, ext_fact('BASE'))
95 create_remote_file(agent, etc_factsd_path, ext_fact('ETC_FACTS'))
96 on(agent, facter("test")) do |facter_output|
97 assert_match(/ETC_FACTS/, facter_output.stdout, "Fact from #{etc_factsd_dir} should take precedence over #{factsd_dir}")
98 end
99 end
100
101 # A fact in the etc_puppetlabs_factsd_dir should take precedence over the same fact in etc_factsd_dir
102 step "Agent #{agent}: create and resolve 2 facts of the same name between #{etc_factsd_dir} and #{etc_puppetlabs_factsd_dir}" do
103 create_remote_file(agent, etc_factsd_path, ext_fact('ETC_FACTS'))
104 create_remote_file(agent, etc_puppetlabs_factsd_path, ext_fact('ETC_PUPPETLABS_FACTS'))
105 on(agent, facter("test")) do |facter_output|
106 assert_match(/ETC_PUPPETLABS_FACTS/, facter_output.stdout, "Fact from #{etc_puppetlabs_factsd_dir} should take precedence over #{etc_factsd_dir}")
107 end
108 end
109 end
110 end
0 test_name "FACT-2874: file containing external facts are loaded in lexicographical order" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_name = 'test'
7 # Use a static external fact
8 ext_fact1 = "#{fact_name}: 'EXTERNAL1'"
9 ext_fact2 = "#{fact_name}: 'EXTERNAL2'"
10
11 agents.each do |agent|
12 facts_dir = agent.tmpdir('facts.d')
13 ext_fact_path1 = "#{facts_dir}/a_test.yaml"
14 ext_fact_path2 = "#{facts_dir}/b_test.yaml"
15 create_remote_file(agent, ext_fact_path1, ext_fact1)
16 create_remote_file(agent, ext_fact_path2, ext_fact2)
17
18 teardown do
19 agent.rm_rf(facts_dir)
20 end
21
22 step "Agent #{agent}: resolve external fact with the last value it resolves to" do
23 on(agent, facter("--external-dir \"#{facts_dir}\" #{fact_name}")) do |facter_output|
24 assert_equal("EXTERNAL2", facter_output.stdout.chomp)
25 end
26 end
27 end
28 end
29
30
0 # Verify how facter handles same external facts filename in different directories:
1 #
2 # - in case ttl not enabled, will accept same filename in two external directories
3 # - in case ttl enabled on filename, will throw error and exit 1
4 #
5
6 test_name 'Should handle same filename in two external directories only if ttl is not enabled' do
7 tag 'risk:high'
8
9 require 'facter/acceptance/user_fact_utils'
10 extend Facter::Acceptance::UserFactUtils
11
12 agents.each do |agent|
13 fact1 = 'fact1'
14 fact2 = 'fact2'
15 fact1_value = 'fact1_value'
16 fact2_value = 'fact2_value'
17 external_filename = 'text.yaml'
18 external_dir1 = agent.tmpdir('external_dir1')
19 external_fact_file1 = File.join(external_dir1, external_filename)
20 external_dir2 = agent.tmpdir('external_dir2')
21 external_fact_file2 = File.join(external_dir2, external_filename)
22 create_remote_file(agent, external_fact_file1, "#{fact1}: #{fact1_value}")
23 create_remote_file(agent, external_fact_file2, "#{fact2}: #{fact2_value}")
24
25 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
26 config_file = File.join(config_dir, 'facter.conf')
27
28 teardown do
29 agent.rm_rf(external_dir1)
30 agent.rm_rf(external_dir2)
31 agent.rm_rf(config_file)
32 end
33
34 step 'works if ttl is not enabled' do
35 on(agent, facter("--external-dir \"#{external_dir1}\" --external-dir \"#{external_dir2}\" --debug #{fact1} #{fact2}")) do |facter_output|
36 assert_match(/#{fact1} => #{fact1_value}/, stdout, 'Expected fact to match first fact')
37 assert_match(/#{fact2} => #{fact2_value}/, stdout, 'Expected fact to match second fact')
38 end
39 end
40
41 step 'does not work if ttl is enabled' do
42 config = <<EOM
43 facts : {
44 ttls : [
45 { "#{external_filename}" : 30 days }
46 ]
47 }
48 EOM
49 agent.mkdir_p(config_dir)
50 create_remote_file(agent, config_file, config)
51 on(agent, facter("--external-dir \"#{external_dir1}\" --external-dir \"#{external_dir2}\" --debug #{fact1} #{fact2}"), :acceptable_exit_codes => 1) do |facter_output|
52 assert_match(/ERROR.*Caching is enabled for group "#{external_filename}" while there are at least two external facts files with the same filename/, stderr, 'Expected error message')
53 assert_match(/#{fact1} => #{fact1_value}/, stdout, 'Expected fact to match first fact')
54 assert_not_match(/#{fact2} => #{fact2_value}/, stdout, 'Expected fact not to match second fact')
55 end
56 end
57
58
59 end
60 end
0 # verify that facter run as a non-root user honor facts in the users home directory:
1 # ~/.facter/facts.d
2 # ~/.puppetlabs/opt/facter/facts.d
3 test_name "C64580: Non-root default user external facts directory is searched for facts" do
4 tag 'risk:high'
5
6 confine :except, :platform => 'aix' # bug FACT-1586
7
8 confine :except, :platform => 'windows' # this test currently only supported on unix systems FACT-1647
9 confine :except, :platform => 'osx' # does not support managehome
10 confine :except, :platform => 'solaris' # does not work with managehome on solaris boxes
11 confine :except, :platform => 'eos-' # does not support user creation ARISTA-37
12
13 require 'facter/acceptance/user_fact_utils'
14 extend Facter::Acceptance::UserFactUtils
15
16 # Generate an external fact dynamically
17 def ext_user_fact(value='BASIC')
18 "test: '#{value}'"
19 end
20
21 # Retrieve a specific user's home directory $HOME/.facter
22 #
23 def get_user_facter_dir(user_home, platform)
24 File.join(user_home, '.facter')
25 end
26
27 # Retrieve a specific user's home facts directory $HOME/.facter/facts.d
28 #
29 def get_user_facts_dir(user_home, platform)
30 File.join(get_user_facter_dir(user_home, platform), 'facts.d')
31 end
32
33 # Retreive a specific user's home puppetlabs directory $HOME/.puppetlabs
34 #
35 def get_user_puppetlabs_dir(user_home, platform)
36 File.join(user_home, '.puppetlabs')
37 end
38
39 # Retreive a specific user's home puppetlabs facts directory $HOME/.puppetlabs/opt/facter/facts.d
40 #
41 def get_user_puppetlabs_facts_dir(user_home, platform)
42 File.join(get_user_puppetlabs_dir(user_home, platform), 'opt', 'facter', 'facts.d')
43 end
44
45 # retrieve the user's home directory for a host and user
46 #
47 def get_home_dir(host, user_name)
48 home_dir = nil
49 on host, puppet_resource('user', user_name) do |result|
50 home_dir = result.stdout.match(/home\s*=>\s*'([^']+)'/m)[1]
51 end
52 home_dir
53 end
54
55 agents.each do |agent|
56 non_root_user = "nonroot"
57
58 step "Agent #{agent}: create a #{non_root_user} to run facter with" do
59 on(agent, "puppet resource user #{non_root_user} ensure=present managehome=true shell='#{user_shell(agent)}'")
60 end
61
62 user_home = get_home_dir(agent, non_root_user)
63
64 # The directories that facter processes facts for a user from
65 user_base_facts_dir = get_user_facter_dir(user_home, agent['platform'])
66 user_facts_dir = get_user_facts_dir(user_home, agent['platform'])
67 user_facts_path = "#{user_facts_dir}/test.yaml"
68
69 user_base_puppetlabs_dir = get_user_puppetlabs_dir(user_home, agent['platform'])
70 user_puppetlabs_facts_dir = get_user_puppetlabs_facts_dir(user_home, agent['platform'])
71 user_puppetlabs_facts_path = "#{user_puppetlabs_facts_dir}/test.yaml"
72
73 step "Agent #{agent}: figure out facter program location"
74 facter_path = agent.which('facter').chomp
75
76 teardown do
77 agent.rm_rf(user_base_facts_dir)
78 agent.rm_rf(user_base_puppetlabs_dir)
79 on(agent, puppet("resource user #{non_root_user} ensure=absent managehome=true"))
80 end
81
82 step "Agent #{agent}: create facts directory (#{user_facts_dir})" do
83 agent.rm_rf(user_facts_dir)
84 agent.mkdir_p(user_facts_dir)
85 end
86
87 step "Agent #{agent}: create and resolve a custom fact in #{user_facts_dir}" do
88 create_remote_file(agent, user_facts_path, ext_user_fact('USER_TEST_FACTER'))
89 end
90
91 step "Agent #{agent}: chown and chmod the facts to the user #{non_root_user}" do
92 agent.chown(non_root_user, user_base_facts_dir, true)
93 agent.chmod('a+rx', user_base_facts_dir, true)
94 end
95
96 step "Agent #{agent}: run facter as #{non_root_user} and make sure we get the fact" do
97 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}' test"]) do |facter_result|
98 assert_match(/USER_TEST_FACTER/, facter_result.stdout, "Fact from #{user_facts_dir} did not resolve correctly")
99 end
100 end
101
102 step "Agent #{agent}: remove #{user_facts_path}" do
103 agent.rm_rf(user_facts_path)
104 end
105
106 step "Agent #{agent}: create facts directory (#{user_puppetlabs_facts_dir})" do
107 agent.rm_rf(user_puppetlabs_facts_dir)
108 agent.mkdir_p(user_puppetlabs_facts_dir)
109 end
110
111 step "Agent #{agent}: create and resolve a custom fact in #{user_puppetlabs_facts_dir}" do
112 create_remote_file(agent, user_puppetlabs_facts_path, ext_user_fact('USER_TEST_PUPPETLABS'))
113 end
114
115 step "Agent #{agent}: chown and chmod the facts to the user #{non_root_user}" do
116 agent.chown(non_root_user, user_base_puppetlabs_dir, true)
117 agent.chmod('a+rx', user_base_puppetlabs_dir, true)
118 end
119
120 step "Agent #{agent}: run facter as #{non_root_user} and make sure we get the fact" do
121 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}' test"]) do |facter_result|
122 assert_match(/USER_TEST_PUPPETLABS/, facter_result.stdout, "Fact from #{user_puppetlabs_facts_dir} did not resolve correctly")
123 end
124 end
125
126 step "Agent #{agent}: create and resolve a custom fact in #{user_puppetlabs_facts_dir}" do
127 create_remote_file(agent, user_facts_path, ext_user_fact('USER_PRECEDENCE_FACTER'))
128 create_remote_file(agent, user_puppetlabs_facts_path, ext_user_fact('USER_PRECEDENCE_PUPPETLABS'))
129 end
130
131 step "Agent #{agent}: chown and chmod the facts to the user #{non_root_user}" do
132 agent.chown(non_root_user, user_base_facts_dir, true)
133 agent.chown(non_root_user, user_base_puppetlabs_dir, true)
134 agent.chmod('a+rx', user_base_facts_dir, true)
135 agent.chmod('a+rx', user_base_puppetlabs_dir, true)
136 end
137
138 step "Agent #{agent}: run facter as #{non_root_user} and .facter will take precedence over .puppetlabs" do
139 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}' test"]) do |facter_result|
140 assert_match(/USER_PRECEDENCE_FACTER/, facter_result.stdout, "Fact from #{user_puppetlabs_facts_dir} did not resolve correctly")
141 end
142 end
143 end
144 end
0 # facter resolves facts from the default facts.d directory
1 # on Unix this can be 3 directories (See fact_directory_precedence.rb)
2 # Unix - /opt/puppetlabs/facter/facts.d/
3 # Windows - C:\ProgramData\PuppetLabs\facter\facts.d\
4 test_name "C87571: facter resolves facts in the default facts.d directory" do
5 tag 'risk:high'
6
7 require 'facter/acceptance/user_fact_utils'
8 extend Facter::Acceptance::UserFactUtils
9
10 agents.each do |agent|
11 os_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
12 ext = get_external_fact_script_extension(agent['platform'])
13 factsd = get_factsd_dir(agent['platform'], os_version)
14 fact_file = File.join(factsd, "external_fact_1#{ext}")
15 content = external_fact_content(agent['platform'], 'external_fact', 'external_value')
16
17 teardown do
18 agent.rm_rf(fact_file)
19 end
20
21 step "Agent #{agent}: setup default external facts directory and fact" do
22 agent.mkdir_p(factsd)
23 create_remote_file(agent, fact_file, content)
24 agent.chmod('+x', fact_file)
25 end
26
27 step "agent #{agent}: resolve the external fact" do
28 on(agent, facter('external_fact')) do |facter_output|
29 assert_equal('external_value', facter_output.stdout.chomp, 'Expected to resolve the external_fact')
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 test_name 'strucutured external facts can be blocked' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_1_name = 'key1.key2'
9 fact_2_name = 'key1.key3'
10 fact_1_value = 'test1'
11 fact_2_value = 'test2'
12 fact_1_content = "#{fact_1_name}=#{fact_1_value}"
13 fact_2_content = "#{fact_2_name}=#{fact_2_value}"
14
15 config_data = <<~HOCON
16 facts : {
17 blocklist : [ "#{fact_1_name}" ],
18 }
19
20 global : {
21 force-dot-resolution : true
22 }
23 HOCON
24
25 agents.each do |agent|
26 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
27 config_file = File.join(config_dir, 'facter.conf')
28 agent.mkdir_p(config_dir)
29 create_remote_file(agent, config_file, config_data)
30
31 external_dir = agent.tmpdir('facts.d')
32 agent.mkdir_p(external_dir)
33 create_remote_file(agent, File.join(external_dir, 'fact_1.txt'), fact_1_content)
34 create_remote_file(agent, File.join(external_dir, 'fact_2.txt'), fact_2_content)
35
36 teardown do
37 agent.rm_rf(external_dir)
38 agent.rm_rf(config_dir)
39 end
40
41 step 'blocked structured external fact is not displayed' do
42 on(agent, facter("--external-dir \"#{external_dir}\" key1.key2")) do |facter_output|
43 assert_equal('', facter_output.stdout.chomp)
44 end
45 end
46
47 step 'the remaining structured fact is displayed' do
48 on(agent, facter("--external-dir \"#{external_dir}\" key1.key3")) do |facter_output|
49 assert_equal(fact_2_value, facter_output.stdout.chomp)
50 end
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 test_name 'strucutured external facts can be cached' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_1_name = 'key1.key2'
9 fact_2_name = 'key1.key3'
10 fact_1_value = 'test1'
11 fact_2_value = 'test2'
12 fact_1_content = "#{fact_1_name}=#{fact_1_value}"
13 fact_2_content = "#{fact_2_name}=#{fact_2_value}"
14
15 cached_file_1_content = <<~RUBY
16 {
17 "#{fact_1_name}": "#{fact_1_value}",
18 "cache_format_version": 1
19 }
20 RUBY
21
22 cached_file_2_content = <<~RUBY
23 {
24 "#{fact_2_name}": "#{fact_2_value}",
25 "cache_format_version": 1
26 }
27 RUBY
28
29 config_data = <<~HOCON
30 facts : {
31 ttls : [
32 { "fact_1.txt" : 3 days },
33 { "fact_2.txt" : 3 days }
34 ]
35 }
36 global : {
37 force-dot-resolution : true
38 }
39 HOCON
40
41 agents.each do |agent|
42 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
43
44 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
45 config_file = File.join(config_dir, 'facter.conf')
46 agent.mkdir_p(config_dir)
47 create_remote_file(agent, config_file, config_data)
48
49 external_dir = agent.tmpdir('facts.d')
50 agent.mkdir_p(external_dir)
51 create_remote_file(agent, File.join(external_dir, 'fact_1.txt'), fact_1_content)
52 create_remote_file(agent, File.join(external_dir, 'fact_2.txt'), fact_2_content)
53
54 teardown do
55 agent.rm_rf(external_dir)
56 agent.rm_rf(config_dir)
57 agent.rm_rf(cache_folder)
58 end
59
60 step 'creates a fact_1.txt and fact_2.txt cache file that contains fact information' do
61 on(agent, facter("--external-dir \"#{external_dir}\" key1 --json")) do |facter_output|
62 assert_equal(
63 {
64 'key1' => {
65 'key2' => fact_1_value,
66 'key3' => fact_2_value
67 }
68 },
69 JSON.parse(facter_output.stdout.chomp)
70 )
71 end
72
73 assert_equal(true, agent.file_exist?("#{cache_folder}/fact_1.txt"))
74 assert_equal(true, agent.file_exist?("#{cache_folder}/fact_2.txt"))
75
76 assert_match(
77 cached_file_1_content.chomp,
78 agent.cat("#{cache_folder}/fact_1.txt").strip,
79 'Expected cached external fact file to contain fact information for fact_1'
80 )
81
82 assert_match(
83 cached_file_2_content.chomp,
84 agent.cat("#{cache_folder}/fact_2.txt").strip,
85 'Expected cached external fact file to contain fact information for fact_2'
86 )
87 end
88 end
89 end
0 # frozen_string_literal: true
1
2 test_name 'external facts can be defined as structured' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_name = 'key1.key2'
9 fact_value = 'EXTERNAL'
10 ext_fact_content = "#{fact_name}: '#{fact_value}'"
11
12 config_data = <<~HOCON
13 global : {
14 force-dot-resolution : true
15 }
16 HOCON
17
18 agents.each do |agent|
19 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
20 config_file = File.join(config_dir, 'facter.conf')
21 agent.mkdir_p(config_dir)
22 create_remote_file(agent, config_file, config_data)
23
24 external_dir = agent.tmpdir('facts.d')
25 agent.mkdir_p(external_dir)
26
27 ext_fact_path = File.join(external_dir, 'test.yaml')
28 create_remote_file(agent, ext_fact_path, ext_fact_content)
29
30 teardown do
31 agent.rm_rf(external_dir)
32 agent.rm_rf(config_dir)
33 end
34
35 step 'resolve an external structured fact' do
36 on(agent, facter("--external-dir \"#{external_dir}\" #{fact_name}")) do |facter_output|
37 assert_equal(fact_value, facter_output.stdout.chomp)
38 end
39
40 on(agent, facter("--external-dir \"#{external_dir}\" key1 --json")) do |facter_output|
41 assert_equal(
42 { 'key1' => { 'key2' => fact_value } },
43 JSON.parse(facter_output.stdout.chomp)
44 )
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 test_name 'strucutured external facts can be cached' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 fact_1_name = 'key1.key2'
9 fact_2_name = 'key1.key3'
10 fact_1_value = 'test1'
11 fact_2_value = 'test2'
12 fact_1_content = "#{fact_1_name}=#{fact_1_value}"
13 fact_2_content = "#{fact_2_name}=#{fact_2_value}"
14
15 cached_file_content = <<~RUBY
16 {
17 "#{fact_1_name}": "#{fact_1_value}",
18 "cache_format_version": 1
19 }
20 RUBY
21
22 config_data = <<~HOCON
23 facts : {
24 ttls : [
25 { "fact_1.txt" : 3 days }
26 ]
27 }
28 global : {
29 force-dot-resolution : true
30 }
31 HOCON
32
33 agents.each do |agent|
34 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
35
36 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
37 config_file = File.join(config_dir, 'facter.conf')
38 agent.mkdir_p(config_dir)
39 create_remote_file(agent, config_file, config_data)
40
41 external_dir = agent.tmpdir('facts.d')
42 agent.mkdir_p(external_dir)
43 create_remote_file(agent, File.join(external_dir, 'fact_1.txt'), fact_1_content)
44 create_remote_file(agent, File.join(external_dir, 'fact_2.txt'), fact_2_content)
45
46 teardown do
47 agent.rm_rf(external_dir)
48 agent.rm_rf(config_dir)
49 agent.rm_rf(cache_folder)
50 end
51
52 step 'does not create cache for part of the fact that is not in ttls' do
53 on(agent, facter("--external-dir \"#{external_dir}\" key1.key3"))
54 assert_equal(false, agent.file_exist?("#{cache_folder}/fact_2.txt"))
55 end
56
57 step 'creates a fact_1.txt cache file that contains fact information' do
58 on(agent, facter("--external-dir \"#{external_dir}\" key1.key2"))
59
60 result = agent.file_exist?("#{cache_folder}/fact_1.txt")
61 assert_equal(true, result)
62
63 cat_output = agent.cat("#{cache_folder}/fact_1.txt")
64 assert_match(
65 cached_file_content.chomp,
66 cat_output.strip,
67 'Expected cached custom fact file to contain fact information'
68 )
69 end
70
71 step 'resolves the entire fact' do
72 on(agent, facter("--external-dir \"#{external_dir}\" key1 --json")) do |facter_output|
73 assert_equal(
74 {
75 'key1' => {
76 'key2' => fact_1_value,
77 'key3' => fact_2_value
78 }
79 },
80 JSON.parse(facter_output.stdout.chomp)
81 )
82 end
83 end
84 end
85 end
0 # frozen_string_literal: true
1
2 test_name 'external facts override parts of custom_facts' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 ext_fact_name = 'key1.key2'
9 ext_fact_value = 'EXTERNAL'
10 ext_fact_content = "#{ext_fact_name}: '#{ext_fact_value}'"
11 custom_fact_file = 'custom_fact.rb'
12
13 fact_content = <<-RUBY
14 Facter.add('ke1.key12') do
15 setcode do
16 "custom1"
17 end
18 end
19
20 Facter.add('key1.key3') do
21 setcode do
22 "custom2"
23 end
24 end
25 RUBY
26
27 config_data = <<~HOCON
28 global : {
29 force-dot-resolution : true
30 }
31 HOCON
32
33 agents.each do |agent|
34 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
35 config_file = File.join(config_dir, 'facter.conf')
36 agent.mkdir_p(config_dir)
37 create_remote_file(agent, config_file, config_data)
38
39 external_dir = agent.tmpdir('facts.d')
40 agent.mkdir_p(external_dir)
41 ext_fact_path = File.join(external_dir, 'test.yaml')
42 create_remote_file(agent, ext_fact_path, ext_fact_content)
43
44 custom_facts_dir = agent.tmpdir('custom_facts')
45 custom_fact_file = File.join(custom_facts_dir, custom_fact_file)
46 create_remote_file(agent, custom_fact_file, fact_content)
47
48 teardown do
49 agent.rm_rf(external_dir)
50 agent.rm_rf(config_dir)
51 agent.rm_rf(custom_facts_dir)
52 end
53
54 step 'overwtites part of the custom fact' do
55 on(
56 agent,
57 facter("--external-dir \"#{external_dir}\" --custom-dir \"#{custom_facts_dir}\" key1 --json")
58 ) do |facter_output|
59 assert_equal(
60 { 'key1' =>
61 {
62 'key2' => ext_fact_value,
63 'key3' => 'custom2'
64 } },
65 JSON.parse(facter_output.stdout.chomp)
66 )
67 end
68 end
69 end
70 end
0 # This test is intended to demonstrate that executable external facts can return
1 # YAML or JSON data, in addition to plain key-value pairs. If the output cannot be
2 # parsed as YAML, it will fall back to key-value pair parsing, and only fail if
3 # this is also invalid.
4 test_name "executable external facts can return structured data" do
5
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 unix_fact_yaml = <<EOM
10 #!/bin/sh
11 echo "yaml_fact: [ 'one', 'two', 'three' ]"
12 EOM
13
14 unix_fact_json = <<EOM
15 #!/bin/sh
16 echo "{ json_fact: { element: 1 } }"
17 EOM
18
19 unix_fact_kv = <<EOM
20 #!/bin/sh
21 echo "kv_fact=one"
22 EOM
23
24 unix_fact_bad = <<EOM
25 #!/bin/sh
26 echo "bad_fact : : not, json"
27 EOM
28
29 win_fact_yaml = <<EOM
30 @echo off
31 echo yaml_fact: [ 'one', 'two', 'three' ]
32 EOM
33
34 win_fact_json = <<EOM
35 @echo off
36 echo { json_fact: { element: 1 } }
37 EOM
38
39 win_fact_kv = <<EOM
40 @echo off
41 echo kv_fact=one
42 EOM
43
44 win_fact_bad = <<EOM
45 @echo off
46 echo bad_fact : : not, json
47 EOM
48
49 yaml_structured_output = <<EOM
50 [
51 "one",
52 "two",
53 "three"
54 ]
55 EOM
56
57 json_structured_output = <<EOM
58 {
59 element => 1
60 }
61 EOM
62
63 kv_output = 'one'
64
65 agents.each do |agent|
66 os_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
67 factsd = get_factsd_dir(agent['platform'], os_version)
68 ext = get_external_fact_script_extension(agent['platform'])
69
70 if agent['platform'] =~ /windows/
71 yaml_content = win_fact_yaml
72 json_content = win_fact_json
73 kv_content = win_fact_kv
74 bad_fact_content = win_fact_bad
75 else
76 yaml_content = unix_fact_yaml
77 json_content = unix_fact_json
78 kv_content = unix_fact_kv
79 bad_fact_content = unix_fact_bad
80 end
81
82 step "Agent #{agent}: setup default external facts directory (facts.d)" do
83 agent.mkdir_p(factsd)
84 end
85
86 teardown do
87 agent.rm_rf(factsd)
88 end
89
90 step "Agent #{agent}: create an executable yaml fact in default facts.d" do
91 yaml_fact = File.join(factsd, "yaml_fact#{ext}")
92 create_remote_file(agent, yaml_fact, yaml_content)
93 agent.chmod('+x', yaml_fact)
94
95 step "YAML output should produce a structured fact" do
96 on(agent, facter("yaml_fact")) do
97 assert_match(/#{yaml_structured_output}/, stdout, "Expected properly structured fact")
98 end
99 end
100 end
101
102 step "Agent #{agent}: create an executable json fact in default facts.d" do
103 json_fact = File.join(factsd, "json_fact#{ext}")
104 create_remote_file(agent, json_fact, json_content)
105 agent.chmod('+x', json_fact)
106
107 step "JSON output should produce a structured fact" do
108 on(agent, facter("json_fact")) do
109 assert_match(/#{json_structured_output}/, stdout, "Expected properly structured fact")
110 end
111 end
112 end
113
114 step "Agent #{agent}: create an executable key-value fact in default facts.d" do
115 kv_fact = File.join(factsd, "kv_fact#{ext}")
116 create_remote_file(agent, kv_fact, kv_content)
117 agent.chmod('+x', kv_fact)
118
119 step "output that is neither yaml nor json should not produce a structured fact" do
120 on(agent, facter("kv_fact")) do
121 assert_match(/#{kv_output}/, stdout, "Expected a simple key-value fact")
122 end
123 end
124 end
125
126 step "Agent #{agent}: create a malformed executable fact in default facts.d" do
127 bad_fact = File.join(factsd, "bad_fact#{ext}")
128 create_remote_file(agent, bad_fact, bad_fact_content)
129 agent.chmod('+x', bad_fact)
130
131 step "should error when output is not in a supported format" do
132 on(agent, facter("bad_fact --debug")) do
133 assert_match(/Could not parse executable fact/, stderr, "Expected parsing the malformed fact to fail")
134 end
135 end
136 end
137 end
138 end
0 test_name 'facter should flush fact values' do
1 tag 'risk:high'
2
3 fact_content1 = <<-EOM
4 require 'facter'
5
6 Facter.add(:fact1) do
7 'this should be flushed'
8 end
9
10 Facter.flush
11
12 puts "Fact1: \#\{Facter.value(:fact1)\}"
13 EOM
14
15 fact_content2 = <<-EOM
16 require 'facter'
17
18 Facter.add(:fact2) do
19 on_flush do
20 puts 'before flush'
21 end
22 end
23
24 Facter.flush
25 EOM
26
27 agents.each do |agent|
28 fact_dir = agent.tmpdir('test_scripts')
29 script_path1 = File.join(fact_dir, 'flush_test1.rb')
30 script_path2 = File.join(fact_dir, 'flush_test2.rb')
31 create_remote_file(agent, script_path1, fact_content1)
32 create_remote_file(agent, script_path2, fact_content2)
33
34 teardown do
35 agent.rm_rf(script_path1)
36 agent.rm_rf(script_path2)
37 end
38
39 step 'fact value has been flushed' do
40 on(agent, "#{ruby_command(agent)} #{script_path1}") do |ruby_result|
41 assert_equal('Fact1: ', ruby_result.stdout.chomp)
42 end
43 end
44
45 step 'prints on_flush block gets called' do
46 on(agent, "#{ruby_command(agent)} #{script_path2}") do |ruby_result|
47 assert_equal('before flush', ruby_result.stdout.chomp)
48 end
49 end
50 end
51 end
0 test_name "Facter should not display external facts when 'load_external' method is called with false" do
1 tag 'risk:high'
2
3 confine :except, :platform => 'windows'
4
5 require 'facter/acceptance/user_fact_utils'
6 require "puppet/acceptance/common_utils"
7 extend Facter::Acceptance::UserFactUtils
8
9 script_contents1 = <<-NO_EXTERNAL
10 require 'facter'
11 Facter.load_external(false)
12 output = Facter.to_hash
13 exit output["my_external_fact"] == nil
14 NO_EXTERNAL
15
16 script_contents2 = <<-WITH_EXTERNAL
17 require 'facter'
18 output = Facter.to_hash
19 exit output["my_external_fact"] == nil
20 WITH_EXTERNAL
21
22 agents.each do |agent|
23 factsd = get_factsd_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
24 ext = get_external_fact_script_extension(agent['platform'])
25 ext_fact = File.join(factsd, "external_fact#{ext}")
26
27 if agent['platform'] =~ /windows/
28 content = <<EOM
29 echo "my_external_fact=value"
30 EOM
31 else
32 content = <<EOM
33 #!/bin/sh
34 echo "my_external_fact=value"
35 EOM
36 end
37
38 script_dir = agent.tmpdir('scripts')
39 script_name1 = File.join(script_dir, "script_without_ext_facts")
40 script_name2 = File.join(script_dir, "script_with_ext_facts")
41 create_remote_file(agent, script_name1, script_contents1)
42 create_remote_file(agent, script_name2, script_contents2)
43
44 teardown do
45 agent.rm_rf(ext_fact)
46 agent.rm_rf(script_dir)
47 end
48
49 step "Agent #{agent}: create facts.d directory and fact" do
50 agent.mkdir_p(factsd)
51 create_remote_file(agent, ext_fact, content)
52 agent.chmod('+x', ext_fact)
53 end
54
55 step "Agent #{agent}: ensure that external fact is loaded and resolved" do
56 on(agent, "#{ruby_command(agent)} #{script_name2}", :acceptable_exit_codes => 1)
57 end
58
59 step "Agent #{agent}: ensure that external fact is not displayed when load_external method was called with false" do
60 on(agent, "#{ruby_command(agent)} #{script_name1}", :acceptable_exit_codes => 0)
61 end
62 end
63 end
64
0 test_name 'C100160: facter exits with success when asked for a non-existent fact' do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 step 'facter should return exit code 0 for querying non-existing-fact without --strict flag' do
5 on(agent, facter('non-existing-fact'), :acceptable_exit_codes => 0)
6 end
7 end
8 end
0 test_name "C96148: verify dmi facts" do
1 tag 'risk:high'
2
3 confine :except, :platform => 'aix' # no dmi support
4 confine :except, :platform => 'huawei' # no dmi support
5 confine :except, :platform => 'osx' # no dmi support
6 confine :except, :platform => 'sparc' # no dmi support
7 confine :except, :platform => 'ppc64' # no dmi support on linux on powerpc
8
9 require 'json'
10 require 'facter/acceptance/base_fact_utils'
11 extend Facter::Acceptance::BaseFactUtils
12
13 agents.each do |agent|
14 expected_facts = {
15 'dmi.manufacturer' => /\w+/,
16 'dmi.product.name' => /\w+/,
17 'dmi.product.uuid' => /[-0-9A-Fa-f]+/,
18 }
19 unless agent['platform'] =~ /windows/
20 expected_facts.merge!({'dmi.bios.release_date' => /\d+\/\d+\/\d+/,
21 'dmi.bios.vendor' => /\w+/,
22 'dmi.bios.version' => /(\d+|Google)/,
23 'dmi.chassis.type' => /\w+/,
24 })
25 end
26 ## gce does not set the dmi.chassis.asset_flag
27 unless agent['platform'] =~ /windows|cisco|aarch64|el-/ || on(agent, facter('virtual')).stdout.chomp =~ /gce/
28 expected_facts.merge!({'dmi.chassis.asset_tag' => /\w+/})
29 end
30 unless agent['platform'] =~ /cisco|aarch64|el-/
31 expected_facts.merge!({'dmi.product.serial_number' => /\w+/})
32 end
33 unless agent['platform'] =~ /windows|cisco|solaris|aarch64|el-/
34 expected_facts.merge!({'dmi.board.asset_tag' => /\w+|/,
35 'dmi.board.manufacturer' => /\w+/,
36 'dmi.board.product' => /\w+/,
37 'dmi.board.serial_number' => /None|\w+/
38 })
39 end
40
41 step("verify that dmi structured fact contains facts") do
42 on(agent, facter("--json dmi")) do |facter_results|
43 json_facts = JSON.parse(facter_results.stdout)
44 expected_facts.each do |fact, value|
45 actual_fact = json_result_fact_by_key_path(json_facts, fact)
46 assert_match(value, actual_fact.to_s, "Incorrect fact pattern for '#{fact}'")
47 end
48 end
49 end
50 end
51 end
0 test_name "C15286: verify facterversion fact" do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 step("verify that fact facterversion is a version string") do
5 on(agent, facter("facterversion")) do |facter_result|
6 assert_match(/\d+\.\d+\.\d+/, facter_result.stdout.chomp, "Expected 'facterversion' fact to be a version string")
7 end
8 end
9 end
10 end
0 test_name 'C100202: Facter identity facts resolve on all platforms' do
1 tag 'risk:high'
2
3 require 'json'
4
5 agents.each do |agent|
6 step 'Ensure the identity fact resolves as expected' do
7 if agent['platform'] =~ /windows/
8 # Regular expression to validate the username from facter in the form of '<domain>\<username>'
9 # Reference - https://msdn.microsoft.com/en-us/library/bb726984.aspx
10 # - The domain name can be any character or empty
11 # - Must contain a backslash between the domain and username
12 # - Username must be at least one character and not contain the following charaters; " / \ [ ] : ; | = , + * ? < >
13 expected_identity = {
14 'user' => /.*\\[^\\\/\"\[\]:|<>+=;,?*@]+$/,
15 'privileged' => 'true'
16 }
17 elsif agent['platform'] =~ /aix-/
18 expected_identity = {
19 'gid' => '0',
20 'group' => 'system',
21 'uid' => '0',
22 'user' => 'root',
23 'privileged' => 'true'
24 }
25 elsif agent['platform'] =~ /osx-/
26 expected_identity = {
27 'gid' => '0',
28 'group' => 'wheel',
29 'uid' => '0',
30 'user' => 'root',
31 'privileged' => 'true'
32 }
33 else
34 expected_identity = {
35 'gid' => '0',
36 'group' => 'root',
37 'uid' => '0',
38 'user' => 'root',
39 'privileged' => 'true'
40 }
41 end
42
43 on(agent, facter('--json')) do |facter_result|
44 results = JSON.parse(facter_result.stdout)
45 expected_identity.each do |fact, value|
46 assert_match(value, results['identity'][fact].to_s, "Incorrect fact value for identity.#{fact}")
47 end
48 end
49 end
50 end
51 end
0 test_name '(FACT-1964) Facter mountpoints does not show rootfs as type for root directory' do
1
2 confine :to, :platform => /el/
3
4 agents.each do |agent|
5 step 'Ensure that mountpoints does not contain rootfs as type for root directory' do
6 on(agent, facter("mountpoints")) do |facter_result|
7 assert_no_match(/rootfs/, facter_result.stdout.chomp, "Expected mountpoint with rootfs type to not be present")
8 end
9 end
10 end
11 end
0 test_name 'C59029: networking facts should be fully populated' do
1 tag 'risk:high'
2
3 #
4 # This test is intended to ensure that networking facts resolve
5 # as expected across supported platforms.
6 #
7
8 @ip_regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
9 @netmask_regex = /^(((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(0|128|192|224|240|248|252|254)\.0\.0)|(255\.255\.(0|128|192|224|240|248|252|254)\.0)|(255\.255\.255\.(0|128|192|224|240|248|252|254|255)))$/
10
11 agents.each do |agent|
12 expected_networking = {
13 %w[networking dhcp] => agent['platform'] =~ /fedora-32|fedora-34|fedora-36|el-8-|el-9-/ ? '' : @ip_regex, # https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/426
14 %w[networking ip] => @ip_regex,
15 %w[networking ip6] => /[a-f0-9]+:+/,
16 %w[networking mac] => /[a-f0-9]{2}:/,
17 %w[networking mtu] => /\d+/,
18 %w[networking netmask] => @netmask_regex,
19 %w[networking netmask6] => /[a-f0-9]+:/,
20 %w[networking network] => @ip_regex,
21 %w[networking network6] => /([a-f0-9]+)?:([a-f0-9]+)?/,
22 %w[networking scope6] => /link|host|site|global|compat/
23 }
24
25 primary_interface = fact_on(agent, 'networking.primary')
26 refute_empty(primary_interface)
27
28 expected_bindings = {
29 ['networking', 'interfaces', primary_interface, 'bindings', 0, 'address'] => @ip_regex,
30 ['networking', 'interfaces', primary_interface, 'bindings', 0, 'netmask'] => @netmask_regex,
31 ['networking', 'interfaces', primary_interface, 'bindings', 0, 'network'] => @ip_regex,
32 ['networking', 'interfaces', primary_interface, 'bindings6', 0, 'address'] => /[a-f0-9:]+/,
33 ['networking', 'interfaces', primary_interface, 'bindings6', 0, 'netmask'] => /[a-f0-9:]+/,
34 ['networking', 'interfaces', primary_interface, 'bindings6', 0, 'network'] => /[a-f0-9:]+/,
35 ['networking', 'interfaces', primary_interface, 'bindings6', 0, 'scope6'] => /link|host|site|global|compat/
36 }
37
38 if agent['platform'] =~ /eos|solaris|aix|cisco/
39 #remove the invalid networking facts on eccentric platforms
40 expected_networking.delete(%w[networking ip6])
41 expected_networking.delete(%w[networking netmask6])
42 expected_networking.delete(%w[networking network6])
43 expected_networking.delete(%w[networking scope6])
44
45 #remove invalid bindings for the primary networking interface eccentric platforms
46 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings6', 0, 'address'])
47 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings6', 0, 'netmask'])
48 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings6', 0, 'network'])
49 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings6', 0, 'scope6'])
50 end
51
52 if agent['platform'] =~ /aix|sparc|cisco|huawei|sles|s390x/
53 # some of our testing platforms do not use DHCP
54 expected_networking.delete(%w[networking dhcp])
55 end
56
57 if agent['platform'] =~ /cisco/
58 # Cisco main interface does not define netmask or network
59 expected_networking.delete(%w[networking network])
60 expected_networking.delete(%w[networking netmask])
61
62 #remove invalid bindings for Cisco's primary networking interface
63 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings', 0, 'netmask'])
64 expected_bindings.delete(['networking', 'interfaces', primary_interface, 'bindings', 0, 'network'])
65 end
66
67 networking_facts = JSON.parse(on(agent, facter('networking --json')).stdout)
68
69 step "Ensure the Networking fact resolves with reasonable values for at least one interface" do
70 expected_networking.each do |fact_tokens, regex|
71 assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
72 end
73 end
74
75 step "Ensure bindings for the primary networking interface are present" do
76 expected_bindings.each do |fact_tokens, regex|
77 assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
78 end
79 end
80 end
81
82 # Verify that IP Address v6 and network v6 is retrieved correctly and does not contain the interface identifier
83 agents.each do |agent|
84 if agent['platform'] =~ /windows/
85 step "verify that ipaddress6 is retrieved correctly" do
86 on(agent, facter('ipaddress6')) do |facter_result|
87 assert_match(/^[a-fA-F0-9:]+$/, facter_result.stdout.chomp)
88 end
89 end
90
91 step "verify that network6 is retrieved correctly" do
92 on(agent, facter('network6')) do |facter_result|
93 assert_match(/([a-fA-F0-9:]+)?:([a-fA-F0-9:]+)?$/, facter_result.stdout.chomp)
94 end
95 end
96 end
97 end
98 end
0 test_name 'networking facts with secondary ip' do
1 tag 'risk:high'
2
3 confine :except, :platform => 'windows'
4 confine :except, :platform => 'aix'
5 confine :except, :platform => 'osx'
6 confine :except, :platform => 'solaris'
7 #
8 # This test is intended to ensure that networking facts resolve secondary ips
9 # as expected across supported platforms.
10 #
11
12 @ip_regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
13 @netmask_regex = /^(((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(0|128|192|224|240|248|252|254)\.0\.0)|(255\.255\.(0|128|192|224|240|248|252|254)\.0)|(255\.255\.255\.(0|128|192|224|240|248|252|254|255)))$/
14
15 def expected_interfaces(interface, count)
16 expected_interfaces = {}
17 (1..count).each do |index|
18 expected_interfaces.merge!(expected_bindings(ip_name(interface, index), 1))
19 expected_interfaces.merge!(
20 {
21 ['networking', 'interfaces', "#{ip_name(interface, index)}", 'ip'] => @ip_regex,
22 ['networking', 'interfaces', "#{ip_name(interface, index)}", 'netmask'] => @netmask_regex,
23 ['networking', 'interfaces', "#{ip_name(interface, index)}", 'network'] => @ip_regex
24 }
25 )
26 end
27 expected_interfaces
28 end
29
30 def expected_bindings(interface, count)
31 expected_bindings = {}
32 (1..count).each do |index|
33 expected_bindings.merge!(
34 {
35 ['networking', 'interfaces', "#{interface}", 'bindings', index - 1, 'address'] => @ip_regex,
36 ['networking', 'interfaces', "#{interface}", 'bindings', index - 1, 'netmask'] => @netmask_regex,
37 ['networking', 'interfaces', "#{interface}", 'bindings', index - 1, 'network'] => @ip_regex
38 }
39 )
40 end
41 expected_bindings
42 end
43
44 def ip_name(interface, index)
45 "#{interface}:#{index}"
46 end
47
48 agents.each do |agent|
49 interface = fact_on(agent, 'networking.primary')
50
51 step "Add secondary ip without labels" do
52 on(agent, "ip addr add 11.0.0.0/24 dev #{interface}")
53 end
54
55 step "Add two secondary ips with label" do
56 on(agent, "ip addr add 11.0.0.1/24 dev #{interface} label #{ip_name(interface, 1)}")
57 on(agent, "ip addr add 11.0.0.2/24 dev #{interface} label #{ip_name(interface, 2)}")
58 end
59
60 networking_facts = JSON.parse(on(agent, facter('networking --json')).stdout)
61
62 step "Check labeled secondary interfaces are found" do
63 expected_interfaces(interface, 2).each do |fact_tokens, regex|
64 assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
65 end
66 end
67
68 step "Check unlabeled secondary interface is inside the bindings of the primary interface" do
69 expected_bindings(interface, 2).each do |fact_tokens, regex|
70 assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
71 end
72 end
73
74 teardown do
75 on(agent, "ip addr del 11.0.0.0/24 dev #{interface}")
76 # On Ubuntu 16, deleting the first secondary ip, deletes all of them.
77 # Next commands may fail, because the ips they attempt to delete, no longer exist.
78 on(agent, "ip addr del 11.0.0.1/24 dev #{interface}", :acceptable_exit_codes => [0, 2])
79 on(agent, "ip addr del 11.0.0.2/24 dev #{interface}", :acceptable_exit_codes => [0, 2])
80 end
81 end
82 end
0 test_name 'networking facts with vlans' do
1 tag 'risk:high'
2
3 confine :except, :platform => 'windows'
4 confine :except, :platform => 'aix'
5 confine :except, :platform => 'osx'
6 confine :except, :platform => 'solaris'
7
8 #
9 # This test is intended to ensure that networking facts resolve vlans
10 # as expected across supported platforms.
11 #
12
13 @ip_regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
14 @netmask_regex = /^(((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(0|128|192|224|240|248|252|254)\.0\.0)|(255\.255\.(0|128|192|224|240|248|252|254)\.0)|(255\.255\.255\.(0|128|192|224|240|248|252|254)))$/
15
16 def vlan(interface, index)
17 "#{interface}.#{index}"
18 end
19
20 def expected_bindings(interface, count)
21 expected_bindings = {}
22 (1..count).each do |index|
23 expected_bindings.merge!(
24 {
25 ['networking', 'interfaces', vlan(interface, index).to_s, 'bindings', 0, 'address'] => @ip_regex,
26 ['networking', 'interfaces', vlan(interface, index).to_s, 'bindings', 0, 'netmask'] => @netmask_regex,
27 ['networking', 'interfaces', vlan(interface, index).to_s, 'bindings', 0, 'network'] => @ip_regex,
28 %W[networking interfaces #{vlan(interface, index)} ip] => @ip_regex,
29 %W[networking interfaces #{vlan(interface, index)} mac] => /[a-f0-9]{2}:/,
30 %W[networking interfaces #{vlan(interface, index)} mtu] => /\d+/,
31 %W[networking interfaces #{vlan(interface, index)} netmask] => @netmask_regex,
32 %W[networking interfaces #{vlan(interface, index)} network] => @ip_regex
33 }
34 )
35 end
36 expected_bindings
37 end
38
39 agents.each do |agent|
40 operating_system = fact_on(agent, 'operatingsystem')
41 release = fact_on(agent, 'operatingsystemrelease')
42
43 if operating_system == 'Amazon' && release == '2017.03'
44 skip_test 'Not able to create VLANs on Amazon 6'
45 end
46
47 interface = fact_on(agent, 'networking.primary')
48
49 step "Add two vlans" do
50 on(agent, "ip link add link #{interface} name #{vlan(interface, 1)} type vlan id 1")
51 on(agent, "ip addr add 11.0.0.1/24 dev #{vlan(interface, 1)}")
52 on(agent, "ip link add link #{interface} name #{vlan(interface, 2)} type vlan id 2")
53 on(agent, "ip addr add 11.0.0.2/24 dev #{vlan(interface, 2)}")
54 end
55
56 step "Check vlans are found" do
57 networking_facts = JSON.parse(on(agent, facter('networking --json')).stdout)
58
59 expected_bindings(interface, 2).each do |fact_tokens, regex|
60 assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
61 end
62 end
63
64 teardown do
65 on(agent, "ip link delete #{vlan(interface, 1)}")
66 on(agent, "ip link delete #{vlan(interface, 2)}")
67 end
68 end
69 end
0 test_name "Test nim_type fact" do
1
2 confine :to, :platform => /aix/
3
4 agents.each do |agent|
5 step("verify that nim_type is retrieved correctly") do
6 on(agent, facter("nim_type")) do |facter_result|
7 assert_equal("standalone", facter_result.stdout.chomp)
8 end
9 end
10 end
11 end
0 test_name "C59196: running facter as a non-root user should not produce permission errors" do
1 tag 'risk:high'
2
3 confine :except, :platform => 'windows' # this test currently only supported on unix systems FACT-1647
4 confine :except, :platform => 'aix' # system su(1) command prints errors cannot access parent directories and ticket FACT-1586
5 confine :except, :platform => 'cisco' # system su(1) command prints errors cannot access parent directories
6 confine :except, :platform => 'osx' # system su(1) command prints errors cannot access parent directories
7 confine :except, :platform => 'solaris' # system su(1) command prints errors cannot access parent directories
8 confine :except, :platform => 'eos-' # does not support user creation ARISTA-37
9
10 require 'facter/acceptance/user_fact_utils'
11 extend Facter::Acceptance::UserFactUtils
12
13 agents.each do |agent|
14 non_root_user = "nonroot"
15 facter_path = agent.which('facter').chomp
16
17 step "Agent #{agent}: create a #{non_root_user} user to run facter with" do
18 on(agent, "puppet resource user #{non_root_user} ensure=present shell='#{user_shell(agent)}'")
19 end
20
21 teardown do
22 on(agent, puppet("resource user #{non_root_user} ensure=absent"))
23 end
24
25 step "Agent #{agent}: run facter as #{non_root_user} and get no errors" do
26 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}'"]) do |facter_results|
27 assert_empty(facter_results.stderr.chomp, "Expected no errors from facter when run as user #{non_root_user}")
28 end
29 end
30 end
31 end
0 test_name 'C14891: Facter should properly detect operatingsystem on Ubuntu after a Facter.clear' do
1 tag 'risk:high'
2
3 confine :to, :platform => /ubuntu/
4
5 require "puppet/acceptance/common_utils"
6
7 script_contents = <<-OS_DETECT
8 require 'facter'
9 Facter['operatingsystem'].value
10 Facter.clear
11 exit Facter['operatingsystem'].value == 'Ubuntu'
12 OS_DETECT
13
14 agents.each do |agent|
15 script_dir = agent.tmpdir('ubuntu')
16 script_name = File.join(script_dir, "facter_os_detection_test")
17 create_remote_file(agent, script_name, script_contents)
18
19 teardown do
20 agent.rm_rf(script_dir)
21 end
22
23 on(agent, "#{Puppet::Acceptance::CommandUtils.ruby_command(agent)} #{script_name}", :acceptable_exit_codes => 0)
24 end
25 end
0 test_name 'C100193: Facter os, processors, and kernel facts resolve on all platforms' do
1 tag 'risk:high'
2
3 require 'json'
4 require 'facter/acceptance/base_fact_utils'
5 extend Facter::Acceptance::BaseFactUtils
6
7 agents.each do |agent|
8 step 'Ensure the os, processors, and kernel fact resolves as expected' do
9 expected_facts = os_processors_and_kernel_expected_facts(agent)
10 on(agent, facter('--json')) do |facter_result|
11 results = JSON.parse(facter_result.stdout)
12 expected_facts.each do |fact, value|
13 actual_fact = json_result_fact_by_key_path(results, fact)
14 assert_match(value, actual_fact.to_s, "Incorrect fact value for #{fact}")
15 end
16 end
17 end
18 end
19 end
0 test_name 'Querying the fqdn with a numeric hostname should not fail' do
1
2 # calling getaddrinfo with a numeric value on OS X does not fill the
3 # ai_canonname field of the addrinfo structure
4 confine :to, :platform => /^osx-/
5
6 agents.each do |agent|
7 original_hostname = agent.hostname.split('.').first
8 numeric_hostname = 42
9
10 teardown do
11 on(agent, "scutil --set HostName #{original_hostname}")
12 end
13
14 step "Change hostname from '#{original_hostname}' to '#{numeric_hostname}'" do
15 on(agent, "scutil --set HostName #{numeric_hostname}")
16 end
17
18 step 'Verify fqdn fact does not fail' do
19 on(agent, facter('fqdn'))
20 end
21 end
22 end
0 test_name "C96148: verify partitions facts" do
1 tag 'risk:high'
2
3 confine :except, :platform => 'osx' # no partitions on osx
4 confine :except, :platform => 'windows' # no partitions on windows
5 confine :except, :platform => 'solaris' # no partitions on solaris
6
7 require 'json'
8
9 possible_facts = [
10 ['backing_file', /^\/.*/],
11 ['filesystem', /\w/],
12 ['uuid', /^[-a-zA-Z0-9]+$/],
13 ['partuuid', /^[-a-f0-9]+$/],
14 ['mount', /^\/.*/],
15 ['label', /.*/],
16 ['partlabel', /\w+/],
17 ]
18
19 agents.each do |agent|
20 step("verify that partitions contain facts") do
21 on(agent, facter("--json partitions")) do |facter_output|
22 facter_results = JSON.parse(facter_output.stdout)
23 facter_results['partitions'].each_key do |partition_name|
24 partition_facts = facter_results['partitions'][partition_name]
25 assert_match(/\d+\.\d+ [TGMK]iB/, partition_facts['size'], "Expected partition '#{partition_name}' fact 'size' to match expression")
26 assert(partition_facts['size_bytes'] >= 0, "Expected partition '#{partition_name}' fact 'size_bytes' to be positive")
27 possible_facts.each do |fact, expression|
28 unless partition_facts[fact].nil?
29 assert_match(expression, partition_facts[fact], "Expected partition '#{partition_name}' fact '#{fact}' to match expression")
30 end
31 end
32 end
33 end
34 end
35 end
36 end
0 test_name "C89604: verify productname fact" do
1 tag 'risk:high'
2
3 confine :except, :platform => 'aix' # not supported on
4 confine :except, :platform => 'huawei' # not supported on
5 confine :except, :platform => 'ppc64' # not supported on linux on powerpc
6
7 agents.each do |agent|
8 step("verify the fact productname") do
9 on(agent, facter("productname")) do |facter_result|
10 assert_match(/\w+/, facter_result.stdout.chomp, "Expected fact 'productname' to be set")
11 end
12 end
13 end
14 end
0 test_name "C100305: The Ruby fact should resolve as expected in AIO" do
1 tag 'risk:high'
2
3 #
4 # This test is intended to ensure that the the ruby fact resolves
5 # as expected in AIO across supported platforms.
6 #
7 skip_test "Ruby fact test is confined to AIO" if @options[:type] != 'aio'
8
9 require 'json'
10 require 'facter/acceptance/base_fact_utils'
11 extend Facter::Acceptance::BaseFactUtils
12
13 agents.each do |agent|
14 step "Ensure the Ruby fact resolves as expected" do
15 case agent['platform']
16 when /windows/
17 ruby_platform = agent['ruby_arch'] == 'x64' ? 'x64-mingw32' : 'i386-mingw32'
18 when /osx/
19 ruby_platform = /(x86_64-darwin[\d.]+|aarch64-darwin)/
20 when /aix/
21 ruby_platform = /powerpc-aix[\d.]+/
22 when /solaris/
23 if agent['platform'] =~ /sparc/
24 ruby_platform = /sparc-solaris[\d.]+/
25 else
26 ruby_platform = /i386-solaris[\d.]+/
27 end
28 when /cisco_ios_xr/
29 ruby_platform = /x86_64-linux/
30 when /huaweios/
31 ruby_platform = /powerpc-linux/
32 else
33 if agent['ruby_arch']
34 ruby_platform = agent['ruby_arch'] == 'x64' ? /(x86_64|powerpc64le|aarch64)-linux/ : /(i486|i686|s390x)-linux/
35 else
36 ruby_platform = agent['platform'] =~ /64/ ? /(x86_64|powerpc64le|aarch64)-linux/ : /(i486|i686|s390x)-linux/
37 end
38 end
39
40 has_sitedir = !on(agent, 'ruby -e"puts RbConfig::CONFIG[\'sitedir\']"').output.chomp.empty?
41
42 ruby_version = /2\.\d+\.\d+/
43 expected_facts = {
44 'ruby.platform' => ruby_platform,
45 'ruby.version' => ruby_version
46 }
47 expected_facts['ruby.sitedir'] = /\/site_ruby/ if has_sitedir
48
49 step("verify that ruby structured fact contains facts") do
50 on(agent, facter("--json ruby")) do |facter_results|
51 json_facts = JSON.parse(facter_results.stdout)
52 expected_facts.each do |fact, value|
53 actual_fact = json_result_fact_by_key_path(json_facts, fact)
54 assert_match(value, actual_fact.to_s, "Incorrect fact pattern for '#{fact}'")
55 end
56 end
57 end
58 end
59 end
60 end
0 test_name 'SSH publick key' do
1 confine :except, :platform => 'windows'
2 confine :except, :platform => /solaris/
3
4 agents.each do |agent|
5 ssh_host_rsa_key_file = '/etc/ssh/ssh_host_rsa_key.pub'
6 ssh_tmp_host_rsa_key_file = '/tmp/ssh_host_rsa_key.pub'
7
8 # The 'cp' might fail because the source file doesn't exist
9 on(
10 agent,
11 "cp -fv #{ssh_host_rsa_key_file} #{ssh_tmp_host_rsa_key_file}",
12 acceptable_exit_codes: [0, 1]
13 )
14
15 key = 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDi8n9KzzF4tPIZsohBuyxFrLnkT5YbahpIjHvQZbQ9OwG3pOxTcQJjtS/gGMKJeRE2uaHaWb700rGlfGzhit7198FmjCeYdYLZvTH0q76mN9Ew1a8aesE46JMAmZijfehxzmlbyyQDamB0wSv3CbcpGccQ3cp/jBnnj54q9EJuEN+YU/uWVHK9IgNOAj9n7l7ZKKiDAFYlhg22sWIwX+8EyoAp+ewItLpO1BJe+NcnLzMoh71Qfb2Gm/yDPbKt/3N6CHp6JeHNbbPCL0hPkcbMdc/1+3ZuzM0yqt/Sq+6lz1tQBOeDp7UqZNT0t2I5bu0NNMphpBIAELpb4f6uuZ25'
16 rsa_pub_host_key_without_comment = 'ssh-rsa ' + key
17 rsa_pub_host_key_with_comment = rsa_pub_host_key_without_comment + ' root@ciprian.badescu-pf1s74sr\n'
18
19 teardown do
20 # Is it present?
21 rc = on(
22 agent,
23 "[ -e #{ssh_tmp_host_rsa_key_file} ]",
24 accept_all_exit_codes: true,
25 )
26 if rc.exit_code == 0
27 # It's present, so restore the original
28 agent.mv(ssh_tmp_host_rsa_key_file, ssh_host_rsa_key_file)
29 else
30 # It's missing, which means there wasn't one to backup; just
31 # delete the one we laid down
32 agent.rm_rf(ssh_host_rsa_key_file)
33 end
34 end
35
36 step 'SSH publick key with comment is printed' do
37 on(agent, "echo '#{rsa_pub_host_key_with_comment}' > #{ssh_host_rsa_key_file}")
38 on(agent, facter('ssh.rsa.key')) do |facter_output|
39 assert_equal(key, facter_output.stdout.chomp, 'Expected debug to contain key only')
40 end
41 end
42
43 step 'SSH publick key without comment is printed' do
44 on(agent, "echo '#{rsa_pub_host_key_without_comment}' > #{ssh_host_rsa_key_file}")
45 on(agent, facter('ssh.rsa.key')) do |facter_output|
46 assert_equal(key, facter_output.stdout.chomp, 'Expected debug to contain key only')
47 end
48 end
49 end
50 end
0 # This test verifies that the numbers for file systems sizes are positive from "facter" and "puppet facts"
1 # This is a regression test for FACT-1578
2 test_name "C100110: verify that file system sizes are positive" do
3 tag 'risk:high'
4
5 confine :except, :platform => 'windows' # Windows does not list mount points as facts like Unix
6 confine :to, :platform => /skip/
7
8 require 'json'
9
10 agents.each do |agent|
11 step("verify that facter returns positive numbers for the mount points byte fields") do
12
13 on(agent, facter("--json")) do |facter_output|
14 facter_results = JSON.parse(facter_output.stdout)
15 facter_results['mountpoints'].each_key do |mount_key|
16 ['available_bytes', 'size_bytes', 'used_bytes'].each do |sub_key|
17 assert_operator(facter_results['mountpoints'][mount_key][sub_key], :>=, 0,
18 "Expected the #{sub_key} from facter to be positive for #{mount_key}")
19 end
20 end
21 end
22 end
23
24 step("verify that puppet facts returns positive numbers for the mount points byte fields") do
25
26 on(agent, puppet("facts --render-as json")) do |puppet_facts|
27 puppet_results = JSON.parse(puppet_facts.stdout)
28 puppet_results['mountpoints'].each_key do |mount_key|
29 ['available_bytes', 'size_bytes', 'used_bytes'].each do |sub_key|
30 assert_operator(puppet_results['mountpoints'][mount_key][sub_key], :>=, 0,
31 "Expected the #{sub_key} from puppet facts to be positive for #{mount_key}")
32 end
33 end
34 end
35 end
36 end
37 end
0 # This test is intended to demonstrate that mount resource can mount tmpfs file systems
1 # and the mount facter mountpoints should show the mount as tmpfs
2 test_name 'C98163: mountpoints fact should show mounts on tmpfs' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8
9 confine :except, :platform => 'windows'
10 confine :except, :platform => /osx/ # See PUP-4823
11 confine :except, :platform => /solaris/ # See PUP-5201
12 confine :except, :platform => /aix/ # See PUP-6845
13 confine :except, :platform => /^eos-/ # Mount provider not supported on Arista EOS switches
14 confine :except, :platform => /^cisco_/ # See PUP-5826
15 confine :except, :platform => /^huawei/ # See PUP-6126
16
17 agents.each do |agent|
18 mount_point = '/tmp/mountdir'
19 manifest_dir = agent.tmpdir('tmpfs')
20 manifest = File.join(manifest_dir, 'mount_manifest.pp')
21 manifest_content = <<-FILE
22 mount {"#{mount_point}":
23 ensure => mounted,
24 options => 'noexec',
25 fstype => 'tmpfs',
26 device => 'tmpfs',
27 atboot => true,
28 }
29 FILE
30 agent.mkdir_p(mount_point)
31 create_remote_file(agent, manifest, manifest_content)
32
33 teardown do
34 on(agent, "umount #{mount_point}")
35 agent.rm_rf(mount_point)
36 agent.rm_rf(manifest_dir)
37 end
38
39 step "Apply the manifest to mount directory '#{mount_point}'" do
40 on(agent, puppet("apply #{manifest}"), :acceptable_exit_codes => [0,2]) do |puppet_apply|
41 assert_no_match(/Error/, puppet_apply.stdout, 'Unexpected error on stdout was detected!')
42 assert_no_match(/ERROR/, puppet_apply.stderr, 'Unexpected error on stderr was detected!')
43 end
44 end
45
46 step 'verify tmpfs mount point seen by facter' do
47 on(agent, facter("mountpoints.#{mount_point}")) do |facter_output|
48 assert_match(/filesystem\s+=>\s+\"tmpfs\"/, facter_output.stdout, 'filesystem is the wrong type')
49 assert_match(/device\s+=>\s+\"tmpfs\"/, facter_output.stdout, 'device is not a tmpfs')
50 assert_match(/noexec/, facter_output.stdout, 'expected to see noexec option')
51 end
52 end
53 end
54 end
0 test_name "Test facter os reports correct os.windows.system32" do
1
2 # disable this test until changes in beaker
3 # https://tickets.puppetlabs.com/browse/BKR-1627
4 # will allow this test to run in timely manner
5 confine :to, :platform => /no-platform/
6 # confine :to, :platform => /windows/
7 # confine :except, :platform => /2008/
8
9 agents.each do |agent|
10 os_type = on(agent, powershell("facter os.windows.installation_type")).stdout.chomp
11 domain_firewall_state = on(agent, powershell("'(Get-NetFirewallProfile -Profile Domain).Enabled'")).stdout.chomp
12 public_firewall_state = on(agent, powershell("'(Get-NetFirewallProfile -Profile Public).Enabled'")).stdout.chomp
13 private_firewall_state = on(agent, powershell("'(Get-NetFirewallProfile -Profile Private).Enabled'")).stdout.chomp
14 on(agent, powershell("'Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False'"))
15
16 teardown do
17 if os_type == "Server" && on(agent, powershell("'Import-module servermanager ; (Get-WindowsFeature -name RDS-RD-Server).Installed'")).stdout.chomp == "True"
18 on(agent, powershell("'Import-module servermanager ; Remove-WindowsFeature –Name RDS-RD-Server'"))
19 agent.reboot
20 begin
21 agent.down?
22 rescue
23 #TODO: Handle restart pending
24 puts 'Assuming that restart was not caught'
25 end
26 agent.wait_for_port(3389)
27 agent.wait_for_port(22)
28 on(agent, powershell("Set-NetFirewallProfile -Profile Domain -Enabled #{domain_firewall_state}"))
29 on(agent, powershell("Set-NetFirewallProfile -Profile Public -Enabled #{public_firewall_state}"))
30 on(agent, powershell("Set-NetFirewallProfile -Profile Private -Enabled #{private_firewall_state}"))
31 end
32 end
33
34 step "Install Windows remote desktop feature" do
35 if os_type == "Server"
36 on(agent, powershell("facter os.windows.system32")) do |facter_before_output|
37 assert_equal("C:\\Windows\\system32", facter_before_output.stdout.chomp, 'Before windows feature installation, should be C:\\Windows\\system32')
38 end
39 on(agent, powershell("Add-WindowsFeature –Name RDS-RD-Server –IncludeAllSubFeature"))
40 on(agent, powershell("'Import-module servermanager ; (Get-WindowsFeature -name RDS-RD-Server).Installed'")) do |installed_feature|
41 assert_equal("True", installed_feature.stdout.chomp, 'Result should be true for installed RDS-RD-Server feature')
42 end
43 agent.reboot
44 #assert_equal(true, agent.down?, 'Cound install RDS-RD-Server, failed to bring the host down')
45 begin
46 agent.down?
47 rescue
48 #TODO: Handle restart pending
49 puts 'Assuming that restart was not caught'
50 end
51 agent.wait_for_port(3389)
52 agent.wait_for_port(22)
53 end
54 end
55
56 step "Verify facter os reports correct system32 variable" do
57 on(agent, powershell("facter os.windows.system32")) do |facter_output|
58 assert_equal("C:\\Windows\\system32", facter_output.stdout.chomp, 'Result should be C:\\Windows\\system32')
59 end
60 end
61
62 end
63 end
0 # Verify that we can load the facter the way that mco does based on the platform
1 test_name 'C100161: Ruby can load libfacter without raising an error' do
2 tag 'risk:high'
3
4 require 'puppet/acceptance/common_utils'
5 extend Puppet::Acceptance::CommandUtils
6
7 def puppet_ruby_path_to_puppet_install_dir(puppet_ruby_path)
8 # find the "puppet" directory which should be the root of the install
9 puppet_dir = puppet_ruby_path
10 while File.basename(puppet_dir).downcase != 'puppet'
11 new_puppet_dir = File.dirname(puppet_dir)
12 if new_puppet_dir == puppet_dir
13 break
14 else
15 puppet_dir = new_puppet_dir
16 end
17 end
18 puppet_dir
19 end
20
21 agents.each do |agent|
22 # create a ruby program that will add a fact through Facter
23 fact_content = <<-EOM
24 require 'facter'
25 Facter.add('facter_loaded') do
26 setcode do
27 'FACTER_LOADED'
28 end
29 end
30
31 # print a value so that we know that facter loaded and is working
32 puts Facter.value('facter_loaded')
33 exit 0
34 EOM
35
36 fact_dir = agent.tmpdir('mco_test')
37 fact_program = File.join(fact_dir, 'loading_facter.rb')
38 create_remote_file(agent, fact_program, fact_content)
39
40 teardown do
41 agent.rm_rf(fact_dir)
42 end
43
44 if agent['platform'] =~ /windows/ && agent.is_cygwin?
45 # on Windows we have to figure out where facter.rb is so we can include the path
46 # figure out the root of the Puppet installation
47 puppet_ruby_path = on(agent, "env PATH=\"#{agent['privatebindir']}:${PATH}\" which ruby").stdout.chomp
48 cygwin_puppet_root = puppet_ruby_path_to_puppet_install_dir(puppet_ruby_path)
49 puppet_root = on(agent, "cygpath -w '#{cygwin_puppet_root}'").stdout.chomp
50 # on Windows mco uses -I to include the path to the facter.rb as its not in the
51 # default $LOAD_PATH for Puppets Ruby
52 include_facter_lib = "-I '#{puppet_root}/facter/lib'"
53 else
54 # On Unix systems facter.rb is already in the $LOAD_PATH for Puppets Ruby for mco
55 include_facter_lib = ''
56 end
57
58 # Run Puppet's ruby and load facter.rb
59 # if we fail to load the .jar or .so, ruby will raise an error for us to detect
60 on(agent, "#{ruby_command(agent)} #{include_facter_lib} #{fact_program}") do |ruby_result|
61 assert_equal('FACTER_LOADED', ruby_result.stdout.chomp, 'Expected the output to be only the value the added fact')
62 assert_empty(ruby_result.stderr, 'Expected libfacter to load without any output on stderr')
63 end
64 end
65 end
0 test_name 'C14514: Running facter should not output anything to stderr' do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 on(agent, facter) do |facter_output|
5 assert_match(/hostname\s*=>\s*\S*/, facter_output.stdout, 'Hostname fact is missing')
6 assert_empty(facter_output.stderr, 'Facter should not have written to stderr')
7 end
8 end
9 end
0 test_name 'blocking os fact does not block oss fact' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = "oss"
8 custom_fact_value = "custom_fact_value"
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{custom_fact_name}) do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 config_data = <<~FACTER_CONF
19 facts : {
20 blocklist : [ "os" ],
21 }
22 FACTER_CONF
23
24 agents.each do |agent|
25 fact_dir = agent.tmpdir('custom_facts')
26 fact_file = File.join(fact_dir, custom_fact_file)
27
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, 'facter.conf')
30
31 agent.mkdir_p(config_dir)
32 create_remote_file(agent, fact_file, fact_content)
33 create_remote_file(agent, config_file, config_data)
34
35 teardown do
36 agent.rm_rf(fact_dir)
37 agent.rm_rf(config_dir)
38 end
39
40 step "Facter: Verify that the blocked fact is not displayed" do
41 on(agent, facter("os")) do |facter_output|
42 assert_equal("", facter_output.stdout.chomp)
43 end
44 end
45
46 step "Facter: Verify that the custom fact is displayed" do
47 on(agent, facter("--custom-dir=#{fact_dir} oss")) do |facter_output|
48 assert_match(/#{custom_fact_value}/, facter_output.stdout.chomp)
49 end
50 end
51 end
52 end
0 test_name 'blocking facts using regex' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = "oss"
8 custom_fact_value = "custom_fact_value"
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{custom_fact_name}) do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 config_data = <<~FACTER_CONF
19 facts : {
20 blocklist : [ "os.*" ],
21 }
22 FACTER_CONF
23
24 agents.each do |agent|
25 fact_dir = agent.tmpdir('custom_facts')
26 fact_file = File.join(fact_dir, custom_fact_file)
27
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, 'facter.conf')
30
31 agent.mkdir_p(config_dir)
32 create_remote_file(agent, fact_file, fact_content)
33 create_remote_file(agent, config_file, config_data)
34
35 teardown do
36 agent.rm_rf(fact_dir)
37 agent.rm_rf(config_dir)
38 end
39
40 step "Facter: Verify that the blocked fact is not displayed" do
41 on(agent, facter("os")) do |facter_output|
42 assert_equal("", facter_output.stdout.chomp)
43 end
44 end
45
46 step "Facter: Verify that the blocked custom fact is not displayed" do
47 on(agent, facter("--custom-dir=#{fact_dir} oss")) do |facter_output|
48 assert_equal("", facter_output.stdout.chomp)
49 end
50 end
51 end
52 end
0 # This test is intended to ensure with --debug and --color, facter sends escape sequences to colorize the output
1 test_name "C86545: --debug and --color command-line options should print DEBUG messages with color escape sequences" do
2 tag 'risk:high'
3
4 confine :except, :platform => 'windows' # On windows we don't get an escape sequence to detect to color change
5
6 agents.each do |agent|
7 step "Agent #{agent}: retrieve debug info from stderr using --debug and --color option" do
8 # set the TERM type to be a color xterm to help ensure we emit the escape sequence to change the color
9 on(agent, facter('--debug --color'), :environment => { 'TERM' => 'xterm-256color' }) do |facter_output|
10 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
11 assert_match(
12 /\e\[(\d{2,3})?;?(\d{1})?;?(\d{2,3})?m/,
13 facter_output.stderr,
14 "Expected to see an escape sequence in the output"
15 )
16 end
17 end
18 end
19 end
0 # This test is intended to verify that the config file location can be specified
1 # via the `--config` flag on the command line.
2 test_name "C100014: --config command-line option designates the location of the config file" do
3 tag 'risk:high'
4
5 agents.each do |agent|
6 step "Agent #{agent}: create config file" do
7 config_dir = agent.tmpdir("config_dir")
8 config_file = File.join(config_dir, "facter.conf")
9 create_remote_file(agent, config_file, <<-FILE)
10 cli : {
11 debug : true
12 }
13 FILE
14
15 teardown do
16 agent.rm_rf(config_dir)
17 end
18
19 step "setting --config should cause the config file to be loaded from the specified location" do
20 on(agent, facter("--config \"#{config_file}\"")) do |facter_output|
21 assert_match(/DEBUG/, facter_output.stderr, "Expected debug output on stderr")
22 end
23 end
24 end
25 end
26 end
0 # This test verifies that when a fact group is blocked in the config file
1 # the corresponding facts do not resolve.
2 test_name "C99972: facts can be blocked via a blocklist in the config file" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create config file" do
10 custom_conf_dir = agent.tmpdir("config_dir")
11 config_file = File.join(custom_conf_dir, "facter.conf")
12 create_remote_file(agent, config_file, <<-FILE)
13 cli : { debug : true }
14 facts : { blocklist : [ "file system", "EC2" ] }
15 FILE
16
17 teardown do
18 agent.rm_rf(custom_conf_dir)
19 end
20
21 step "blocked facts should not be resolved" do
22 on(agent, facter("--config \"#{config_file}\"")) do |facter_output|
23 # every platform attempts to resolve at least EC2 facts
24 assert_match(/blocking collection of .+ facts/, facter_output.stderr, "Expected stderr to contain statement about blocking fact collection")
25
26 # on some platforms, file system facts are never resolved, so this will also be true in those cases
27 assert_no_match(/filesystems/, facter_output.stdout, "filesystems fact should have been blocked")
28 assert_no_match(/mountpoints/, facter_output.stdout, "mountpoints fact should have been blocked")
29 assert_no_match(/partitions/, facter_output.stdout, "partitions fact should have been blocked")
30 end
31 end
32 end
33 end
34 end
0 # This test verifies that when a fact group is blocked in the config file the
1 # corresponding facts do not resolve when being run from the puppet facts command.
2 test_name "C100036: when run from puppet facts, facts can be blocked via a list in the config file" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "facts should be blocked when Facter is run from Puppet with a configured blocklist" do
10 # default facter.conf
11 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
12 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
13
14 teardown do
15 agent.rm_rf(facter_conf_default_dir)
16 end
17
18 step "Agent #{agent}: create default config file" do
19 # create the directories
20 agent.mkdir_p(facter_conf_default_dir)
21 create_remote_file(agent, facter_conf_default_path, <<-FILE)
22 facts : { blocklist : [ "file system", "EC2" ] }
23 FILE
24 end
25
26 step "blocked facts should not be resolved" do
27 on(agent, puppet("facts --debug")) do |puppet_facts_output|
28 # every platform attempts to resolve at least EC2 facts
29 assert_match(/blocking collection of .+ facts/, puppet_facts_output.stdout, "Expected stderr to contain statement about blocking fact collection")
30
31 # on some platforms, file system facts are never resolved, so this will also be true in those cases
32 assert_no_match(/filesystems/, puppet_facts_output.stdout, "filesystems fact should have been blocked")
33 assert_no_match(/mountpoints/, puppet_facts_output.stdout, "mountpoints fact should have been blocked")
34 assert_no_match(/partitions/, puppet_facts_output.stdout, "partitions fact should have been blocked")
35 end
36 end
37 end
38 end
39 end
0 test_name 'migrating from facter 3 to facter 4 having cache enabled' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 fact_group_name = 'uptime'
7 config_data = <<~FACTER_CONF
8 facts : {
9 ttls : [
10 { "#{fact_group_name}" : 3 days }
11
12 ]
13 }
14 FACTER_CONF
15
16 f3_cache =
17 "{
18 \"system_uptime\": {
19 \"days\": 1,
20 \"hours\": 1,
21 \"seconds\": 1,
22 \"uptime\": \"1 day\"
23 },
24 \"uptime\": \"1 days\",
25 \"uptime_days\": 1,
26 \"uptime_hours\": 1,
27 \"uptime_seconds\": 1
28 }"
29
30 agents.each do |agent|
31 cache_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
32 f3_cache_file = File.join(cache_dir, fact_group_name)
33
34 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
35 config_file = File.join(config_dir, 'facter.conf')
36
37 step 'create cache file' do
38 agent.mkdir_p(cache_dir)
39 create_remote_file(agent, f3_cache_file, f3_cache)
40 end
41
42 teardown do
43 agent.rm_rf("#{cache_dir}/*")
44 agent.rm_rf(config_file)
45 end
46
47 step 'calling facter 4 without config won\'t modify the cache file' do
48 _output = on(agent, facter)
49 stdout = agent.cat("#{cache_dir}/#{fact_group_name}")
50
51 assert_equal(stdout.strip, f3_cache.strip)
52 end
53
54 step "Agent #{agent}: create config file" do
55 agent.mkdir_p(config_dir)
56 create_remote_file(agent, config_file, config_data)
57 end
58
59 step 'calling facter will invalidate old f3 cache and will overwrite' do
60 output = on(agent, facter('--json'))
61
62 step 'output should be different from f3 cache' do
63 parsed_output = JSON.parse(output.stdout)
64
65 cache_value_changed = f3_cache['system_uptime']['seconds'] != parsed_output['system_uptime']['seconds']
66 assert_equal(true, cache_value_changed, 'Cache value did not change')
67 end
68
69 step 'cache file should contain cache_format_version' do
70 stdout = agent.cat("#{cache_dir}/#{fact_group_name}")
71 cache_content = JSON.parse(stdout)
72
73 assert_equal(cache_content['cache_format_version'], 1)
74
75 step 'values should be read from cache' do
76 cached_value = cache_content['system_uptime.seconds']
77 sleep 1
78 output = on(agent, facter('system_uptime.seconds'))
79
80 assert_equal(cached_value.to_s, output.stdout.strip)
81 end
82 end
83 end
84 end
85 end
0 # This test verifies that the custom-dir specified in the configuration file can be overridden by using
1 # --custom-dir on the command line
2 test_name "C100015: config custom-dir overridden by command line --custom-dir" do
3 tag 'risk:high'
4
5 require 'json'
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 config_fact_content = <<EOM
10 Facter.add('config_fact') do
11 setcode do
12 "config_value"
13 end
14 end
15 EOM
16
17 cli_fact_content = <<EOM
18 Facter.add('cli_fact') do
19 setcode do
20 "cli_value"
21 end
22 end
23 EOM
24
25 agents.each do |agent|
26 step "Agent #{agent}: create 2 custom fact directories with facts and a config file pointing at 1 directory" do
27 custom_config_dir = agent.tmpdir('custom_dir')
28 custom_cli_dir = agent.tmpdir('cli_custom_dir')
29 custom_config_fact = File.join(custom_config_dir, 'custom_fact.rb')
30 custom_cli_fact = File.join(custom_cli_dir, 'custom_fact.rb')
31 create_remote_file(agent, custom_config_fact, config_fact_content)
32 create_remote_file(agent, custom_cli_fact, cli_fact_content)
33 config_dir = agent.tmpdir("config_dir")
34 config_file = File.join(config_dir, "facter.conf")
35 config_content = <<EOM
36 global : {
37 custom-dir : "#{custom_config_dir}",
38 }
39 EOM
40
41 config_content = escape_paths(agent, config_content)
42 create_remote_file(agent, config_file, config_content)
43
44 teardown do
45 agent.rm_rf(custom_config_dir)
46 agent.rm_rf(custom_cli_dir)
47 agent.rm_rf(config_dir)
48 end
49
50 step "Agent #{agent}: resolve a fact from the command line custom-dir and not the config file" do
51 on(agent, facter("--config \"#{config_file}\" --custom-dir \"#{custom_cli_dir}\" --json")) do |facter_output|
52 results = JSON.parse(facter_output.stdout)
53 assert_equal("cli_value", results['cli_fact'], "Incorrect custom fact value for cli_fact")
54 assert_nil(results['config_fact'], "Config fact should not resolve and be nil")
55 end
56 end
57 end
58 end
59 end
0 # This test verifies that facter can load facts from a single custom-dir specified
1 # in the configuration file
2 test_name "C98143: config custom-dir allows single custom fact directory" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content = <<EOM
9 Facter.add('config_fact') do
10 setcode do
11 "config_value"
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create custom fact directory and a custom fact and config file" do
18 custom_dir = agent.tmpdir('custom_dir')
19 custom_fact = File.join(custom_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21 config_dir = agent.tmpdir("config_dir")
22 config_file = File.join(config_dir, "facter.conf")
23 config_content = <<EOM
24 global : {
25 custom-dir : "#{custom_dir}",
26 }
27 EOM
28 config_content = escape_paths(agent, config_content)
29 create_remote_file(agent, config_file, config_content)
30
31 teardown do
32 agent.rm_rf(custom_dir)
33 agent.rm_rf(config_dir)
34 end
35
36 step "Agent #{agent}: resolve a fact from the configured custom-dir path" do
37 on(agent, facter("--config \"#{config_file}\" config_fact")) do |facter_output|
38 assert_equal("config_value", facter_output.stdout.chomp, "Incorrect custom fact value")
39 end
40 end
41 end
42 end
43 end
0 # This test verifies that facter can load facts from multiple custom-dir's specified
1 # in the configuration file
2 test_name "C99996: config custom-dir allows multiple custom fact directories" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content_1 = <<EOM
9 Facter.add('config_fact_1') do
10 setcode do
11 "config_value_1"
12 end
13 end
14 EOM
15
16 content_2 = <<EOM
17 Facter.add('config_fact_2') do
18 setcode do
19 "config_value_2"
20 end
21 end
22 EOM
23
24 agents.each do |agent|
25 step "Agent #{agent}: create custom fact directories and a custom fact in each and a config file" do
26 custom_dir_1 = agent.tmpdir('custom_dir_1')
27 custom_dir_2 = agent.tmpdir('custom_dir_2')
28 custom_fact_1 = File.join(custom_dir_1, 'custom_fact.rb')
29 custom_fact_2 = File.join(custom_dir_2, 'custom_fact.rb')
30 create_remote_file(agent, custom_fact_1, content_1)
31 create_remote_file(agent, custom_fact_2, content_2)
32
33 config_dir = agent.tmpdir("config_dir")
34 config_file = File.join(config_dir, "facter.conf")
35 config_content = <<EOM
36 global : {
37 custom-dir : [ "#{custom_dir_1}", "#{custom_dir_2}" ],
38 }
39 EOM
40 config_content = escape_paths(agent, config_content)
41 create_remote_file(agent, config_file, config_content)
42
43 teardown do
44 agent.rm_rf(custom_dir_1)
45 agent.rm_rf(custom_dir_2)
46 agent.rm_rf(config_dir)
47 end
48
49 step "Agent #{agent}: resolve a fact from each configured custom-dir path" do
50 on(agent, facter("--config \"#{config_file}\" --json")) do |facter_output|
51 results = JSON.parse(facter_output.stdout)
52 assert_equal("config_value_1", results['config_fact_1'], "Incorrect custom fact value for config_fact_1")
53 assert_equal("config_value_2", results['config_fact_2'], "Incorrect custom fact value for config_fact_2")
54 end
55 end
56 end
57 end
58 end
0 # This test is intended to demonstrate that setting the cli.debug field to true
1 # causes DEBUG information to be printed to stderr.
2 test_name "C99965: setting the debug config field to true prints debug info to stderr" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 cli : {
10 debug : true
11 }
12 EOM
13
14 agents.each do |agent|
15 step "Agent #{agent}: create config file" do
16 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
17 config_file = File.join(config_dir, "facter.conf")
18 agent.mkdir_p(config_dir)
19 create_remote_file(agent, config_file, config)
20
21 teardown do
22 agent.rm_rf(config_dir)
23 end
24
25 step "debug output should print when config file is loaded" do
26 on(agent, facter("")) do |facter_output|
27 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
28 end
29 end
30 end
31 end
32 end
33
0 # This test verified that having debug set to false in the config file can be
1 # overridden by the command line option --debug
2 test_name "C100044: flags set on the command line override config file settings" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 cli : {
10 debug : false
11 }
12 EOM
13
14 agents.each do |agent|
15 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
16 config_file = File.join(config_dir, "facter.conf")
17
18 teardown do
19 agent.rm_rf(config_dir)
20 end
21
22 step "Agent #{agent}: create config file in default location" do
23 agent.mkdir_p(config_dir)
24 create_remote_file(agent, config_file, config)
25 end
26
27 step "--debug flag should override debug=false in config file" do
28 on(agent, facter("--debug")) do |facter_output|
29 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
30 end
31 end
32 end
33 end
0 # This test is intended to demonstrate that Facter will load a config file
1 # saved at the default location without any special command line flags.
2 # On Unix, this location is /etc/puppetlabs/facter/facter.conf.
3 # On Windows, it is C:\ProgramData\PuppetLabs\facter\etc\facter.conf
4 test_name "C99991: config file is loaded from default location" do
5 tag 'risk:high'
6
7 config = <<EOM
8 cli : {
9 debug : true
10 }
11 EOM
12
13 agents.each do |agent|
14 step "Agent #{agent}: create config file" do
15 if agent['platform'] =~ /windows/
16 config_dir = 'C:\\ProgramData\\PuppetLabs\\facter\\etc'
17 config_file = "#{config_dir}\\facter.conf"
18 else
19 config_dir = '/etc/puppetlabs/facter'
20 config_file = "#{config_dir}/facter.conf"
21 end
22
23 agent.mkdir_p(config_dir)
24
25 create_remote_file(agent, config_file, config)
26
27 teardown do
28 agent.rm_rf(config_dir)
29 end
30
31 step "config file should be loaded automatically and turn DEBUG output on" do
32 on(agent, facter("")) do |facter_output|
33 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
34 end
35 end
36 end
37 end
38 end
0 # This test verifies that a configured external-dir conflicts with the command
1 # line option --no-external-facts
2 test_name "configured external-dir conflicts with command line --no-external-facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 global : {
10 external-dir : "config/file/dir"
11 }
12 cli : {
13 debug : false
14 }
15 EOM
16
17 agents.each do |agent|
18 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
19 config_file = File.join(config_dir, "facter.conf")
20
21 teardown do
22 agent.rm_rf(config_dir)
23 end
24
25 step "Agent #{agent}: create config file in default location" do
26 agent.mkdir_p(config_dir)
27 create_remote_file(agent, config_file, config)
28 end
29
30 step "conflict logic applies across settings sources" do
31 on(agent, facter("--no-external-facts"), :acceptable_exit_codes => [1]) do |facter_output|
32 assert_match(/no-external-facts and external-dir options conflict/, facter_output.stderr, "Facter should have warned about conflicting settings")
33 end
34 end
35 end
36 end
0 # This test verifies that the external-dir specified in the configuration file can be overridden by using
1 # --external-dir on the command line
2 test_name "C100016: config external-dir overridden by command line --external-dir" do
3 tag 'risk:high'
4
5 require 'json'
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 agents.each do |agent|
10 step "Agent #{agent}: create 2 custom fact directories with facts and a config file pointing at 1 directory" do
11 external_config_dir = agent.tmpdir('external_dir')
12 external_cli_dir = agent.tmpdir('cli_external_dir')
13 external_config_fact = File.join(external_config_dir, 'external.txt')
14 external_cli_fact = File.join(external_cli_dir, 'external.txt')
15 create_remote_file(agent, external_config_fact, "config_fact=config_value")
16 create_remote_file(agent, external_cli_fact, "cli_fact=cli_value")
17 config_dir = agent.tmpdir("config_dir")
18 config_file = File.join(config_dir, "facter.conf")
19 config_content = <<EOM
20 global : {
21 external-dir : "#{external_config_dir}",
22 }
23 EOM
24 config_content = escape_paths(agent, config_content)
25 create_remote_file(agent, config_file, config_content)
26
27 teardown do
28 agent.rm_rf(external_config_dir)
29 agent.rm_rf(external_cli_dir)
30 agent.rm_rf(config_dir)
31 end
32
33 step "Agent #{agent}: resolve a fact from the command line external-dir and not the config file" do
34 on(agent, facter("--config \"#{config_file}\" --external-dir \"#{external_cli_dir}\" --json")) do |facter_output|
35 results = JSON.parse(facter_output.stdout)
36 assert_equal("cli_value", results['cli_fact'], "Incorrect custom fact value for cli_fact")
37 assert_nil(results['config_fact'], "Config fact should not resolve and be nil")
38 end
39 end
40 end
41 end
42 end
0 # This test verifies that facter can load facts from a single external-dir specified
1 # in the configuration file
2 test_name "C98142: config external-dir allows single external fact directory" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create an external fact directory with an external fact and a config file" do
10 external_dir = agent.tmpdir('external_dir')
11 ext = get_external_fact_script_extension(agent['platform'])
12 external_fact = File.join(external_dir, "external_fact#{ext}")
13 create_remote_file(agent, external_fact, external_fact_content(agent['platform'], 'single_fact', 'external_value'))
14 agent.chmod('+x', external_fact)
15
16 config_dir = agent.tmpdir("config_dir")
17 config_file = File.join(config_dir, "facter.conf")
18 config_content = <<EOM
19 global : {
20 external-dir : "#{external_dir}",
21 }
22 EOM
23 config_content = escape_paths(agent, config_content)
24 create_remote_file(agent, config_file, config_content)
25
26 teardown do
27 agent.rm_rf(external_dir)
28 agent.rm_rf(config_dir)
29 end
30
31 step "Agent #{agent}: resolve a fact in the external-dir in the configuration file" do
32 on(agent, facter("--config \"#{config_file}\" single_fact")) do |facter_output|
33 assert_equal("external_value", facter_output.stdout.chomp, "Incorrect external fact value")
34 end
35 end
36 end
37 end
38 end
0 # facter should be able to load facts from multiple external-dir's specified
1 # in the configuration file
2 test_name "C99995: config file supports external-dir for multiple fact directories" do
3 tag 'risk:high'
4
5 require 'json'
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 agents.each do |agent|
10 step "Agent #{agent}: create external fact directories and a external fact in each and a config file" do
11 external_dir_1 = agent.tmpdir('external_dir_1')
12 external_dir_2 = agent.tmpdir('external_dir_2')
13 ext = get_external_fact_script_extension(agent['platform'])
14 external_fact_1 = File.join(external_dir_1, "external_fact#{ext}")
15 external_fact_2 = File.join(external_dir_2, "external_fact#{ext}")
16 create_remote_file(agent, external_fact_1, external_fact_content(agent['platform'], 'external_fact_1', 'external_value_1'))
17 create_remote_file(agent, external_fact_2, external_fact_content(agent['platform'], 'external_fact_2', 'external_value_2'))
18 agent.chmod('+x', external_fact_1)
19 agent.chmod('+x', external_fact_2)
20
21 config_dir = agent.tmpdir("config_dir")
22 config_file = File.join(config_dir, "facter.conf")
23 config_content = <<EOM
24 global : {
25 external-dir : [ "#{external_dir_1}", "#{external_dir_2}" ],
26 }
27 EOM
28 config_content = escape_paths(agent, config_content)
29 create_remote_file(agent, config_file, config_content)
30
31 teardown do
32 agent.rm_rf(external_dir_1)
33 agent.rm_rf(external_dir_2)
34 agent.rm_rf(config_dir)
35 end
36
37 step "Agent #{agent}: resolve a fact from each configured external-dir path" do
38 on(agent, facter("--config \"#{config_file}\" --json")) do |facter_output|
39 results = JSON.parse(facter_output.stdout)
40 assert_equal("external_value_1", results['external_fact_1'], "Incorrect external fact value for external_fact_1")
41 assert_equal("external_value_2", results['external_fact_2'], "Incorrect external fact value for external_fact_2")
42 end
43 end
44 end
45 end
46 end
0 # This test is intended to demonstrate that Facter's config file will
1 # load correctly from the default location when Facter is required from Ruby.
2 # On *nix, this location is /etc/puppetlabs/facter/facter.conf.
3 # On Windows, it is C:\ProgramData\PuppetLabs\facter\etc\facter.conf
4 #
5 # The test also verifies that facter will search for external facts and custom facts
6 # in the directory paths defined with external-dir and custom-dir in the facter.conf file
7 test_name "C98141: config file is loaded when Facter is run from Puppet" do
8 tag 'risk:high'
9
10 require 'facter/acceptance/user_fact_utils'
11 extend Facter::Acceptance::UserFactUtils
12
13 agents.each do |agent|
14 # create paths for default facter.conf, external-dir, and custom-dir
15
16 # default facter.conf
17 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
18 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
19
20 # external-dir
21 ext_fact_dir1 = agent.tmpdir('ext_fact_dir1')
22 ext_path1 = File.join(ext_fact_dir1, "test_ext_fact1.yaml")
23 ext_fact_dir2 = agent.tmpdir('ext_fact_dir2')
24 ext_path2 = File.join(ext_fact_dir2, "test_ext_fact2.yaml")
25
26 # custom-dir
27 cust_fact_dir = agent.tmpdir('custom_fact_dir')
28 cust_path = File.join(cust_fact_dir, "custom_fact.rb")
29
30 teardown do
31 agent.rm_rf(facter_conf_default_dir)
32 agent.rm_rf(ext_fact_dir1)
33 agent.rm_rf(ext_fact_dir2)
34 agent.rm_rf(cust_fact_dir)
35 end
36
37 # create the directories
38 [facter_conf_default_dir, ext_fact_dir1, ext_fact_dir2, cust_fact_dir].each do |dir|
39 agent.mkdir_p(dir)
40 end
41
42 step "Agent #{agent}: create facter.conf, external fact, and custom fact files" do
43 config_content = <<-FILE
44 global : {
45 external-dir : ["#{ext_fact_dir1}", "#{ext_fact_dir2}"],
46 custom-dir : ["#{cust_fact_dir}"]
47 }
48 FILE
49
50 config_content = escape_paths(agent, config_content)
51 create_remote_file(agent, facter_conf_default_path, config_content)
52
53 create_remote_file(agent, ext_path1, <<-FILE)
54 externalfact1: 'This is external fact 1 in #{ext_fact_dir1} directory'
55 FILE
56
57 create_remote_file(agent, ext_path2, <<-FILE)
58 externalfact2: 'This is external fact 2 in #{ext_fact_dir2} directory'
59 FILE
60
61 create_remote_file(agent, cust_path, <<-FILE)
62 Facter.add('customfact') do
63 setcode do
64 'This is a custom fact in #{cust_fact_dir} directory'
65 end
66 end
67 FILE
68 end
69
70 step "running `puppet facts` should load the config file automatically and search all external-dir and custom-dir paths" do
71 on(agent, puppet('facts')) do |puppet_facts_output|
72
73 assert_match(/This is external fact 1 in #{escape_paths(agent, ext_fact_dir1)} directory/, puppet_facts_output.stdout.gsub('\\\\', '\\'), "Expected external fact")
74 assert_match(/This is external fact 2 in #{escape_paths(agent, ext_fact_dir2)} directory/, puppet_facts_output.stdout.gsub('\\\\', '\\'), "Expected external fact")
75 assert_match(/This is a custom fact in #{escape_paths(agent, cust_fact_dir)} directory/, puppet_facts_output.stdout.gsub('\\\\', '\\'), "Expected custom fact")
76 end
77 end
78 end
79 end
0 # This test ensures that the cli.log-level config file setting works
1 # properly. The value of the setting should be a string indicating the
2 # logging level.
3 test_name "C99990: log-level setting can be used to specific logging level" do
4 tag 'risk:high'
5
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 config = <<EOM
10 cli : {
11 log-level : debug
12 }
13 EOM
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
18 config_file = File.join(config_dir, "facter.conf")
19 agent.mkdir_p(config_dir)
20 create_remote_file(agent, config_file, config)
21
22 teardown do
23 agent.rm_rf(config_dir)
24 end
25
26 step "log-level set to debug should print DEBUG output to stderr" do
27 on(agent, facter("")) do |facter_output|
28 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
29 end
30 end
31 end
32 end
33 end
34
0 # This test verifies that setting both custom-dir and no-custom-facts results in an error
1 test_name "C99994: config option no-custom-facts conflicts with custom-dir" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 content = <<EOM
8 Facter.add('custom_fact') do
9 setcode do
10 "testvalue"
11 end
12 end
13 EOM
14
15 agents.each do |agent|
16 step "Agent #{agent}: create a custom fact directory and fact and a config file" do
17 custom_dir = agent.tmpdir('custom_dir')
18 custom_fact = File.join(custom_dir, 'custom_fact.rb')
19 create_remote_file(agent, custom_fact, content)
20
21 config_dir = agent.tmpdir("config_dir")
22 config_file = File.join(config_dir, "facter.conf")
23 config_content = <<EOM
24 global : {
25 custom-dir : "#{custom_dir}"
26 no-custom-facts : true,
27 }
28 EOM
29 config_content = escape_paths(agent, config_content)
30 create_remote_file(agent, config_file, config_content)
31
32 teardown do
33 agent.rm_rf(custom_fact)
34 agent.rm_rf(config_dir)
35 end
36
37 step "Agent #{agent}: config option no-custom-facts : true and custom-dir should result in an options conflict error" do
38 on(agent, facter("--config \"#{config_file}\""), :acceptable_exit_codes => 1) do |facter_output|
39 assert_match(/options conflict/, facter_output.stderr, "Output does not contain error string")
40 end
41 end
42 end
43 end
44 end
0 # This test verifies that setting no-custom-facts in the config file disables
1 # finding facts under the environment variable FACTERLIB
2 test_name "C99997: config option no-custom-facts : true does not load facts from FACTERLIB" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content = <<EOM
9 Facter.add('custom_fact') do
10 setcode do
11 "testvalue"
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create a custom fact directory and fact and a config file" do
18 facterlib_dir = agent.tmpdir('facterlib')
19 custom_fact = File.join(facterlib_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21
22 config_dir = agent.tmpdir("config_dir")
23 config_file = File.join(config_dir, "facter.conf")
24 config_content = <<EOM
25 global : {
26 no-custom-facts : true,
27 }
28 EOM
29 create_remote_file(agent, config_file, config_content)
30
31 teardown do
32 agent.rm_rf(facterlib_dir)
33 agent.rm_rf(config_dir)
34 end
35
36 step "Agent #{agent}: no-custom-facts should ignore the FACTERLIB environment variable" do
37 on(agent, facter("--config \"#{config_file}\" custom_fact", :environment => {'FACTERLIB' => facterlib_dir})) do |facter_output|
38 assert_equal("", facter_output.stdout.chomp, "Custom fact in FACTERLIB should not have resolved")
39 end
40 end
41 end
42 end
43 end
0 # This test verifies that setting no-custom-facts in the config file disables the
1 # the loading of custom facts in facter directories under the $LOAD_PATH
2 test_name "C100004: config file option no-custom-facts : true does not load $LOAD_PATH facter directories" do
3 confine :except, :platform => 'cisco_nexus' # see BKR-749
4 tag 'risk:high'
5
6 require 'puppet/acceptance/common_utils'
7 extend Puppet::Acceptance::CommandUtils
8
9 require 'facter/acceptance/user_fact_utils'
10 extend Facter::Acceptance::UserFactUtils
11
12 content = <<EOM
13 Facter.add('custom_fact') do
14 setcode do
15 "testvalue"
16 end
17 end
18 EOM
19
20 agents.each do |agent|
21 step("Agent #{agent}: determine the load path and create a custom facter directory on it and a config file") do
22 ruby_path = on(agent, "#{ruby_command(agent)} -e 'puts $LOAD_PATH[0]'").stdout.chomp
23 load_path_facter_dir = File.join(ruby_path, 'facter')
24 agent.mkdir_p(load_path_facter_dir)
25 custom_fact = File.join(load_path_facter_dir, 'custom_fact.rb')
26 create_remote_file(agent, custom_fact, content)
27
28 config_dir = agent.tmpdir("config_dir")
29 config_file = File.join(config_dir, "facter.conf")
30 config_content = <<EOM
31 global : {
32 no-custom-facts : true,
33 }
34 EOM
35 create_remote_file(agent, config_file, config_content)
36
37 teardown do
38 load_path_facter_dir = "\"#{load_path_facter_dir}\"" if agent.is_cygwin?
39
40 agent.rm_rf(load_path_facter_dir)
41 agent.rm_rf(config_dir)
42 end
43
44 step("Agent #{agent}: using config no-custom-facts : true should not resolve facts in facter directories on the $LOAD_PATH") do
45 on(agent, facter("--config \"#{config_file}\" custom_fact")) do |facter_output|
46 assert_equal("", facter_output.stdout.chomp, "Custom fact in $LOAD_PATH/facter should not have resolved")
47 end
48 end
49 end
50 end
51 end
0 # This test verifies that no-external-facts : true set in the configuration file
1 # does not load external facts
2 test_name "C99962: config no-external-facts : true does not load external facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create external fact directory and external fact and a config file" do
10 external_dir = agent.tmpdir('external_dir')
11 ext = get_external_fact_script_extension(agent['platform'])
12 external_fact = File.join(external_dir, "external_fact#{ext}")
13 create_remote_file(agent, external_fact, external_fact_content(agent['platform'], 'external_fact', 'external_value'))
14 agent.chmod('+x', external_fact)
15
16 config_dir = agent.tmpdir("config_dir")
17 config_file = File.join(config_dir, "facter.conf")
18 config_content = <<EOM
19 global : {
20 no-external-facts : true,
21 }
22 EOM
23 create_remote_file(agent, config_file, config_content)
24
25 teardown do
26 agent.rm_rf(external_dir)
27 agent.rm_rf(config_dir)
28 end
29
30 step "Agent #{agent}: --no-external-facts option should not load external facts" do
31 on(agent, facter("--no-external-facts external_fact")) do |facter_output|
32 assert_equal("", facter_output.stdout.chomp, "External fact should not have resolved")
33 end
34 end
35 end
36 end
37 end
0 # This test verifies that setting external-dir and no-external-facts in the config file
1 # results in an error
2 test_name "C99993: config option no-external-facts conflicts with external-dir" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create the exernal-dir and a config file" do
10 external_dir = agent.tmpdir('external_dir')
11
12 config_dir = agent.tmpdir("config_dir")
13 config_file = File.join(config_dir, "facter.conf")
14 config_content = <<EOM
15 global : {
16 external-dir : "#{external_dir}"
17 no-external-facts : true,
18 }
19 EOM
20 config_content = escape_paths(agent, config_content)
21 create_remote_file(agent, config_file, config_content)
22
23 teardown do
24 agent.rm_rf(external_dir)
25 agent.rm_rf(config_dir)
26 end
27
28 step "Agent #{agent}: config option no-external-facts : true and external-dir should result in an options conflict error" do
29 on(agent, facter("--config \"#{config_file}\""), :acceptable_exit_codes => 1) do |facter_output|
30 assert_match(/options conflict/, facter_output.stderr, "Output does not contain error string")
31 end
32 end
33 end
34 end
35 end
0 # This test is intended to demonstrate that the global.no-ruby config file field
1 # disables custom fact lookup.
2 test_name "C100045: config no-ruby to true should disable custom facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 global : {
10 no-ruby : true
11 }
12 EOM
13
14 custom_fact_content = <<EOM
15 Facter.add('custom_fact') do
16 setcode do
17 "testvalue"
18 end
19 end
20 EOM
21
22 agents.each do |agent|
23 step "Agent #{agent}: create config file" do
24 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
25 config_file = File.join(config_dir, "facter.conf")
26 agent.mkdir_p(config_dir)
27 create_remote_file(agent, config_file, config)
28
29 teardown do
30 agent.rm_rf(config_dir)
31 end
32
33 step "no-ruby option should disable custom facts" do
34 step "Agent #{agent}: create custom fact directory and custom fact" do
35 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
36 agent.mkdir_p(custom_dir)
37 custom_fact = File.join(custom_dir, 'custom_fact.rb')
38 create_remote_file(agent, custom_fact, custom_fact_content)
39
40 teardown do
41 agent.rm_rf(custom_dir)
42 end
43
44 on(agent, facter("custom_fact", :environment => { 'FACTERLIB' => custom_dir })) do |facter_output|
45 assert_equal("", facter_output.stdout.chomp, "Expected custom fact to be disabled when no-ruby is true")
46 end
47 end
48 end
49 end
50 end
51 end
0 # This test verifies that the global.no-ruby config file field disables
1 # ruby facts
2 test_name "C99964: no-ruby config field flag disables requiring Ruby" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 global : {
10 no-ruby : true
11 }
12 EOM
13
14 agents.each do |agent|
15 step "Agent #{agent}: create config file" do
16 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
17 config_file = File.join(config_dir, "facter.conf")
18 agent.mkdir_p(config_dir)
19 create_remote_file(agent, config_file, config)
20
21 teardown do
22 agent.rm_rf(config_dir)
23 end
24
25 step "no-ruby option should disable Ruby and facts requiring Ruby" do
26 on(agent, facter("ruby")) do |facter_output|
27 assert_equal("", facter_output.stdout.chomp, "Expected Ruby and Ruby fact to be disabled")
28 assert_equal("", facter_output.stderr.chomp, "Expected no warnings about Ruby on stderr")
29 end
30 end
31 end
32 end
33 end
0 # This test is intended to demonstrate that setting the cli.trace field to true
1 # enables backtrace reporting for errors in custom facts.
2 test_name "C99988: trace config field enables backtraces for custom facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 erroring_custom_fact = <<EOM
9 Facter.add('custom_fact_with_trace') do
10 setcode do
11 non_existent_value
12 end
13 end
14 EOM
15
16 config = <<EOM
17 cli : {
18 trace : true
19 }
20 EOM
21
22 agents.each do |agent|
23 step "Agent #{agent}: create custom fact directory and executable custom fact" do
24 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
25 agent.mkdir_p(custom_dir)
26 custom_fact = File.join(custom_dir, "custom_fact.rb")
27 create_remote_file(agent, custom_fact, erroring_custom_fact)
28 agent.chmod('+x', custom_fact)
29
30 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31 config_file = File.join(config_dir, "facter.conf")
32 agent.mkdir_p(config_dir)
33 create_remote_file(agent, config_file, config)
34
35 teardown do
36 agent.rm_rf(custom_dir)
37 agent.rm_rf(config_dir)
38 end
39
40 step "trace setting should provide a backtrace for a custom fact with errors" do
41 on(agent, facter("--custom-dir \"#{custom_dir}\" custom_fact_with_trace"), :acceptable_exit_codes => [1]) do |facter_output|
42 assert_match(/backtrace:\s+#{custom_fact}/, facter_output.stderr, "Expected a backtrace for erroneous custom fact")
43 end
44 end
45 end
46 end
47 end
48
0 test_name 'ttls configured with all types of facts' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = 'random_custom_fact'
8 custom_fact_value = 'custom fact value'
9
10 custom_fact_content = <<-CUSTOM_FACT
11 Facter.add(:#{custom_fact_name}) do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 external_fact_name = 'external_fact'
19 external_fact_value = 'external_value'
20 external_fact_content = <<-EXTERNAL_FACT
21 #{external_fact_name}=#{external_fact_value}
22 EXTERNAL_FACT
23
24 cached_file_content = <<~CACHED_FILE
25 {
26 "#{custom_fact_name}": "#{custom_fact_value}",
27 "#{external_fact_name}": "#{external_fact_name}",
28 "cache_format_version": 1
29 }
30 CACHED_FILE
31
32 config_data = <<~FACTER_CONF
33 facts : {
34 ttls : [
35 { "cached-custom-facts" : 3 days },
36 { "#{external_fact_name}.txt": 3 days}
37 ]
38 }
39 fact-groups : {
40 cached-custom-facts : ["#{custom_fact_name}", "uptime"],
41 }
42 FACTER_CONF
43
44 agents.each do |agent|
45 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
46 fact_dir = agent.tmpdir('facter')
47 env = { 'FACTERLIB' => fact_dir }
48
49 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
50 config_file = File.join(config_dir, 'facter.conf')
51
52 step "Agent #{agent}: create config file" do
53 agent.mkdir_p(config_dir)
54 create_remote_file(agent, config_file, config_data)
55
56 fact_file = File.join(fact_dir, custom_fact_file)
57 create_remote_file(agent, fact_file, custom_fact_content)
58
59 external_dir = agent.tmpdir('external_dir')
60 external_fact = File.join(external_dir, "#{external_fact_name}.txt")
61 create_remote_file(agent, external_fact, external_fact_content)
62
63 teardown do
64 agent.rm_rf(fact_dir)
65 agent.rm_rf("#{cache_folder}/*")
66 agent.rm_rf(config_file)
67 agent.rm_rf(external_dir)
68 end
69
70 step "should log that it creates cache file and it caches custom facts found in facter.conf" do
71 on(agent, facter("--external-dir \"#{external_dir}\" --debug", environment: env)) do |facter_result|
72 assert_match(/#{custom_fact_value}/, facter_result.stdout.chomp, "#{custom_fact_name} value changed")
73 assert_match(/#{external_fact_value}/, facter_result.stdout.chomp, "#{external_fact_name} value changed")
74 assert_match(/caching values for cached-custom-facts facts/, facter_result.stderr,
75 'Expected debug message to state that custom facts will be cached')
76 assert_match(/caching values for #{external_fact_name}.txt facts/, facter_result.stderr,
77 'Expected debug message to state that custom facts will be cached')
78 end
79 end
80
81 step "should create a cached-custom-facts cache file that containt fact information" do
82 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
83 assert_equal(true, result)
84 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
85 cat_external = agent.cat("#{cache_folder}/#{external_fact_name}.txt")
86 assert_match(/#{custom_fact_value}/, cat_output.strip, 'Expected cached custom fact file to contain fact information')
87 assert_match(/uptime/, cat_output.strip, 'Expected cached custom fact file to contain fact information')
88 assert_match(/#{external_fact_value}/, cat_external.strip, 'Expected cached custom fact file to contain fact information')
89 end
90
91 step 'should read from the cached file for a custom fact that has been cached' do
92 on(agent, facter("--external-dir \"#{external_dir}\" --debug", environment: env)) do |facter_result|
93 assert_match(/loading cached values for #{custom_fact_name} facts/, facter_result.stderr,
94 'Expected debug message to state that cached custom facts are read from file')
95 assert_match(/loading cached values for #{external_fact_name}.txt facts/, facter_result.stderr,
96 'Expected debug message to state that cached custom facts are read from file')
97 assert_match(/loading cached values for uptime facts/, facter_result.stderr,
98 'Expected debug message to state that cached custom facts are read from file')
99 end
100 end
101 end
102 end
103 end
0 test_name 'ttls configured with custom group containing core and custom facts' do
1 tag 'risk:high'
2
3 skip_test "Known issue. Scenario does not work."
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 custom_fact_file = 'custom_facts.rb'
9 custom_fact_name = 'random_custom_fact'
10 uptime_seconds_value = ''
11
12 custom_fact_content = <<-CUSTOM_FACT
13 Facter.add(:#{custom_fact_name}) do
14 setcode do
15 Facter.value('system_uptime.seconds')
16 end
17 end
18 CUSTOM_FACT
19
20 config_data = <<~FACTER_CONF
21 facts : {
22 ttls : [
23 { "cached-custom-facts" : 3 days },
24 ]
25 }
26 fact-groups : {
27 cached-custom-facts : ["#{custom_fact_name}", "system_uptime"],
28 }
29 FACTER_CONF
30
31 agents.each do |agent|
32 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
33 fact_dir = agent.tmpdir('facter')
34 env = { 'FACTERLIB' => fact_dir }
35
36 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
37 config_file = File.join(config_dir, 'facter.conf')
38
39 step "Agent #{agent}: create config file" do
40 agent.mkdir_p(config_dir)
41 create_remote_file(agent, config_file, config_data)
42
43 fact_file = File.join(fact_dir, custom_fact_file)
44 create_remote_file(agent, fact_file, custom_fact_content)
45
46 teardown do
47 agent.rm_rf(fact_dir)
48 agent.rm_rf("#{cache_folder}/*")
49 agent.rm_rf(config_file)
50 end
51
52 step "should log that it creates cache file and it caches custom facts found in facter.conf" do
53 on(agent, facter("--debug --json", environment: env)) do |facter_result|
54 output_json = JSON.parse(facter_result.stdout.chomp)
55 uptime_seconds_value = output_json['system_uptime']['seconds']
56 assert_match(/caching values for cached-custom-facts facts/, facter_result.stderr,
57 'Expected debug message to state that custom facts will be cached')
58 end
59 end
60
61 step "should create a cached-custom-facts cache file that contains fact information" do
62 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
63 assert_equal(true, result)
64 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
65 output_json = JSON.parse(cat_output.chomp)
66 assert_match(output_json[custom_fact_name], uptime_seconds_value, 'Expected cached custom fact file to contain fact information')
67 assert_match(output_json['system_uptime.seconds'], uptime_seconds_value, 'Expected cached file to contain system_uptime information')
68 end
69
70 step 'should read from the cached file for a custom fact that has been cached' do
71 on(agent, facter("--debug", environment: env)) do |facter_result|
72 output_json = JSON.parse(facter_result.stdout.chomp)
73
74 assert_match(output_json[custom_fact_name], uptime_seconds_value, 'Expected cached custom fact file to contain fact information')
75 assert_match(output_json['system_uptime.seconds'], uptime_seconds_value, 'Expected cached file to contain system_uptime information')
76
77 assert_match(/caching values for cached-custom-facts facts/, facter_result.stderr,
78 'Expected debug message to state that custom facts will be cached')
79 assert_match(/loading cached values for #{custom_fact_name} facts/, facter_result.stderr,
80 'Expected debug message to state that cached custom facts are read from file')
81 assert_match(/loading cached values for system_uptime.seconds facts/, facter_result.stderr,
82 'Expected debug message to state that system_uptime facts are read from file')
83 end
84 end
85 end
86 end
87 end
0 test_name 'ttls configured nested custom facts files creates cache file and reads cache file' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7 custom_fact_name = 'random_custom.fact'
8 custom_fact_value = 'custom fact value'
9
10 fact_content = <<-CUSTOM_FACT
11 Facter.add('#{custom_fact_name}') do
12 setcode do
13 "#{custom_fact_value}"
14 end
15 end
16 CUSTOM_FACT
17
18 cached_file_content = <<~CACHED_FILE
19 {
20 "#{custom_fact_name}": "#{custom_fact_value}",
21 "cache_format_version": 1
22 }
23 CACHED_FILE
24
25 config_data = <<~FACTER_CONF
26 facts : {
27 ttls : [
28 { "cached-custom-facts" : 3 days }
29 ]
30 }
31 fact-groups : {
32 cached-custom-facts : ["#{custom_fact_name}"],
33 }
34 FACTER_CONF
35
36 agents.each do |agent|
37 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
38 fact_dir = agent.tmpdir('facter')
39 env = { 'FACTERLIB' => fact_dir }
40
41 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
42 config_file = File.join(config_dir, 'facter.conf')
43
44 step "Agent #{agent}: create config file" do
45 agent.mkdir_p(config_dir)
46 create_remote_file(agent, config_file, config_data)
47 fact_file = File.join(fact_dir, custom_fact_file)
48 create_remote_file(agent, fact_file, fact_content)
49 end
50
51 teardown do
52 agent.rm_rf(fact_dir)
53 agent.rm_rf("#{cache_folder}/*")
54 agent.rm_rf(config_file)
55 end
56
57 step "should log that it creates cache file and it caches custom facts found in facter.conf" do
58 on(agent, facter("#{custom_fact_name} --debug", environment: env)) do |facter_result|
59 assert_equal(custom_fact_value, facter_result.stdout.chomp, "#{custom_fact_name} value changed")
60 assert_match(/facts cache file expired, missing or is corrupt/, facter_result.stderr,
61 'Expected debug message to state that custom facts cache file is missing or expired')
62 assert_match(/Saving cached custom facts to ".+"|caching values for cached-custom-facts facts/, facter_result.stderr,
63 'Expected debug message to state that custom facts will be cached')
64 end
65 end
66
67 step "should create a cached-custom-facts cache file that containt fact information" do
68 result = agent.file_exist?("#{cache_folder}/cached-custom-facts")
69 assert_equal(true, result)
70 cat_output = agent.cat("#{cache_folder}/cached-custom-facts")
71 assert_match(cached_file_content.chomp, cat_output.strip, 'Expected cached custom fact file to contain fact information')
72 end
73
74 step 'should read from the cached file for a custom fact that has been cached' do
75 on(agent, facter("#{custom_fact_name} --debug", environment: env)) do |facter_result|
76 assert_match(/Loading cached custom facts from file ".+"|loading cached values for #{custom_fact_name} facts/, facter_result.stderr,
77 'Expected debug message to state that cached custom facts are read from file')
78 end
79 end
80 end
81 end
0 test_name "ttls configured cached nested external facts" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 # This fact must be resolvable on ALL platforms
7 # Do NOT use the 'kernel' fact as it is used to configure the tests
8 external_cachegroup = 'external_fact'
9 first_fact_name = 'fact.first'
10 second_fact_name = 'fact.second'
11 first_fact_value = 'value.first'
12 second_fact_value = 'value.second'
13 cached_fact_value = 'cached_external_value'
14
15 external_fact_content = <<EOM
16 #{first_fact_name}=#{first_fact_value}
17 #{second_fact_name}=#{second_fact_value}
18 EOM
19
20 agents.each do |agent|
21 step "Agent #{agent}: create config file" do
22 external_dir = agent.tmpdir('external_dir')
23 ext = '.txt'
24 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
25 create_remote_file(agent, external_fact, external_fact_content)
26
27 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
28 config_file = File.join(config_dir, "facter.conf")
29 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30
31 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
32
33 # Setup facter conf
34 agent.mkdir_p(config_dir)
35 cached_fact_content = <<EOM
36 {
37 "#{first_fact_name}": "#{cached_fact_value}",
38 "#{second_fact_name}": "#{second_fact_value}",
39 "cache_format_version": 1
40 }
41 EOM
42
43 config = <<EOM
44 facts : {
45 ttls : [
46 { "#{external_cachegroup}#{ext}" : 30 days }
47 ]
48 }
49 EOM
50 create_remote_file(agent, config_file, config)
51
52 teardown do
53 agent.rm_rf(config_dir)
54 agent.rm_rf(cached_facts_dir)
55 agent.rm_rf(external_dir)
56 end
57
58 step "should create a JSON file for a fact that is to be cached" do
59 agent.rm_rf(cached_facts_dir)
60 on(agent, facter("--external-dir \"#{external_dir}\" --debug")) do |facter_output|
61 assert_match(/caching values for #{external_cachegroup}#{ext} facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
62 end
63 cat_output = agent.cat(cached_fact_file)
64 assert_match(/#{first_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
65 assert_match(/#{second_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
66 end
67
68 step "should read from a cached JSON file for a fact that has been cached" do
69 agent.mkdir_p(cached_facts_dir)
70 create_remote_file(agent, cached_fact_file, cached_fact_content)
71
72 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{first_fact_name}")) do |facter_output|
73 assert_match(/loading cached values for #{external_cachegroup}#{ext} facts/, stderr, "Expected debug message to state that values are read from cache")
74 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
75 end
76 end
77 end
78 end
79 end
0 test_name "clearing ttls does not delete cache" do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 # This fact must be resolvable on ALL platforms
7 # Do NOT use the 'kernel' fact as it is used to configure the tests
8 cached_fact_name = 'uptime'
9 cached_fact_value = "CACHED_FACT_VALUE"
10 cached_fact_content = <<EOM
11 {
12 "#{cached_fact_name}": "#{cached_fact_value}"
13 }
14 EOM
15
16 custom_fact_file = 'custom_facts.rb'
17 custom_fact_name = 'random_custom_fact'
18 custom_fact_value = 'custom fact value'
19
20 custom_fact_content = <<-CUSTOM_FACT
21 Facter.add(:#{custom_fact_name}) do
22 setcode do
23 "#{custom_fact_value}"
24 end
25 end
26 CUSTOM_FACT
27
28 external_fact_name = 'external_fact'
29 external_fact_value = 'external_value'
30 external_fact_content = <<-EXTERNAL_FACT
31 #{external_fact_name}=#{external_fact_value}
32 EXTERNAL_FACT
33
34 config = <<EOM
35 facts : {
36 ttls : [
37 { "#{cached_fact_name}" : 3 days },
38 { "#{external_fact_name}.txt": 3 days}
39 { "cached-custom-facts": 3 days}
40 ]
41 }
42 fact-groups : {
43 cached-custom-facts : ["#{custom_fact_name}"],
44 }
45 EOM
46
47 config_no_cache = <<EOM
48 facts : {
49 ttls : [ ]
50 }
51 EOM
52
53 agents.each do |agent|
54 step "Agent #{agent}: create config file" do
55 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
56 config_file = File.join(config_dir, "facter.conf")
57 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
58
59 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
60
61 fact_dir = agent.tmpdir('facter')
62 env = { 'FACTERLIB' => fact_dir }
63
64 # Setup facter conf
65 agent.mkdir_p(config_dir)
66 create_remote_file(agent, config_file, config)
67
68 fact_file = File.join(fact_dir, custom_fact_file)
69 create_remote_file(agent, fact_file, custom_fact_content)
70
71 external_dir = agent.tmpdir('external_dir')
72 external_fact = File.join(external_dir, "#{external_fact_name}.txt")
73 create_remote_file(agent, external_fact, external_fact_content)
74
75 teardown do
76 agent.rm_rf(fact_dir)
77 agent.rm_rf("#{cached_facts_dir}/*")
78 agent.rm_rf(config_file)
79 agent.rm_rf(external_dir)
80 end
81
82 step "Agent #{agent}: run facter with cached facts" do
83 # Set up a known cached fact
84 agent.rm_rf(cached_facts_dir)
85 on(agent, facter("--external-dir \"#{external_dir}\"", environment: env))
86 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/cached-custom-facts"))
87 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/#{cached_fact_name}"))
88 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/#{external_fact_name}.txt"))
89 create_remote_file(agent, cached_fact_file, cached_fact_content)
90 end
91
92 step "Agent #{agent}: resolves fact after ttls was removed" do
93 # Create config file with no caching
94 no_cache_config_file = File.join(config_dir, "no-cache.conf")
95 create_remote_file(agent, no_cache_config_file, config_no_cache)
96
97 on(agent, facter("--config \"#{no_cache_config_file}\" --external-dir \"#{external_dir}\"", environment: env)) do |facter_output|
98 assert_match(/#{cached_fact_name}/, facter_output.stdout, "Expected to see the fact in output")
99 assert_no_match(/#{cached_fact_value}/, facter_output.stdout, "Expected to not see the cached fact value")
100 end
101
102 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/cached-custom-facts"))
103 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/#{cached_fact_name}"))
104 assert_equal(true, agent.file_exist?("#{cached_facts_dir}/#{external_fact_name}.txt"))
105
106 end
107 end
108 end
109 end
0 test_name 'missing facts should not invalidate cache' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_facts.rb'
7
8 fact_content = <<-CUSTOM_FACT
9 Facter.add("networking.custom_fact") do
10 setcode do
11 ''
12 end
13 end
14 CUSTOM_FACT
15
16
17 agents.each do |agent|
18 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
19 fact_dir = agent.tmpdir('facter')
20 env = { 'FACTERLIB' => fact_dir }
21
22 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
23 config_file = File.join(config_dir, 'facter.conf')
24
25 config_data = <<~FACTER_CONF
26 facts : {
27 ttls : [
28 { "networking" : 3 days }
29 ]
30 }
31 FACTER_CONF
32
33 step "Agent #{agent}: create config file" do
34 agent.mkdir_p(config_dir)
35 create_remote_file(agent, config_file, config_data)
36
37 fact_file = File.join(fact_dir, custom_fact_file)
38 create_remote_file(agent, fact_file, fact_content)
39 end
40
41 teardown do
42 agent.rm_rf(fact_dir)
43 agent.rm_rf("#{cache_folder}/*")
44 agent.rm_rf(config_file)
45 end
46
47 step "should create cache file once" do
48 on(agent, facter('', environment: env))
49 ls1 = agent.ls_ld("#{cache_folder}/networking")
50 sleep 1
51 on(agent, facter('', environment: env))
52 ls2 = agent.ls_ld("#{cache_folder}/networking")
53
54 assert_equal(ls1, ls2)
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 test_name 'ttls configured custom facts files creates cache file and reads cache file' do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config_data = <<~FACTER_CONF
9 facts : {
10 ttls : [
11 { "uptime" : 3 days }
12
13 ]
14 }
15 FACTER_CONF
16
17 agents.each do |agent|
18 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
19
20 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
21 config_file = File.join(config_dir, 'facter.conf')
22
23 step "Agent #{agent}: create config file" do
24 agent.mkdir_p(config_dir)
25 create_remote_file(agent, config_file, config_data)
26 end
27
28 teardown do
29 agent.rm_rf("#{cache_folder}/*")
30 agent.rm_rf(config_file)
31 end
32
33 step 'calling one fact from the cached group will cache only that fact' do
34 output = on(agent, facter('system_uptime.seconds'))
35
36 seconds = output.stdout.strip.to_i
37 expected = { 'system_uptime.seconds' => seconds, 'cache_format_version' => 1 }
38
39 stdout = agent.cat("#{cache_folder}/uptime")
40 cache_content = JSON.parse(stdout)
41
42 assert_equal(expected, cache_content)
43 end
44
45 step 'calling a different fact from the cached group will cache only that fact' do
46 output = on(agent, facter('system_uptime.days'))
47
48 days = output.stdout.strip.to_i
49 expected = { 'system_uptime.days' => days, 'cache_format_version' => 1 }
50
51 stdout = agent.cat("#{cache_folder}/uptime")
52 cache_content = JSON.parse(stdout)
53
54 assert_equal(expected, cache_content)
55 end
56
57 # TODO: This is a knoew issue and needs to be fixed
58 # Added this step just to have quick validation
59 #
60 # step "calling a fact with the same name as the group should work" do
61 # output = on(agent, "facter uptime")
62 # uptime = output.stdout.strip
63
64 # stdout = agent.cat("#{cache_folder}/uptime")
65 # cache_content = JSON.parse(stdout)
66
67 # expected = { "uptime" => uptime, "cache_format_version" => 1 }
68
69 # assert_equal(expected, cache_content)
70 # end
71
72 step 'calling facter without a query will cache the entire group' do
73 _output = on(agent, facter)
74
75 stdout = agent.cat("#{cache_folder}/uptime")
76 cache_content = JSON.parse(stdout)
77 ['system_uptime.days',
78 'uptime_days',
79 'system_uptime.hours',
80 'uptime_hours',
81 'system_uptime.seconds',
82 'uptime_seconds',
83 'system_uptime.uptime',
84 'uptime',
85 'cache_format_version'].each do |key|
86 assert_equal(true, cache_content.key?(key))
87 end
88 end
89
90 operating_system = fact_on(agent, 'operatingsystem')
91
92 # rubocop:disable Style/Next
93 if operating_system == 'Ubuntu'
94 step 'check file info to verify that it did not change' do
95 output1 = on(agent, "stat -c %Y #{cache_folder}/uptime")
96 on(agent, facter)
97 on(agent, facter('system_uptime'))
98 output2 = on(agent, "stat -c %Y #{cache_folder}/uptime")
99 assert_equal(output1.stdout, output2.stdout)
100 end
101 end
102 # rubocop:enable Style/Next
103 end
104 end
0 test_name 'missing facts should not invalidate cache' do
1 tag 'risk:high'
2
3 require 'facter/acceptance/user_fact_utils'
4 extend Facter::Acceptance::UserFactUtils
5
6 custom_fact_file = 'custom_fact.rb'
7 custom_fact_name = 'my_custom_fact'
8 custom_cache_group = 'my_custom_group'
9 custom_fact_value = 'banana'
10 core_fact_name = 'system_uptime.seconds'
11
12 fact_content = <<-CUSTOM_FACT
13 Facter.add('#{custom_fact_name}') do
14 setcode do
15 "#{custom_fact_value}"
16 end
17 end
18 CUSTOM_FACT
19
20
21 agents.each do |agent|
22 cache_folder = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
23 fact_dir = agent.tmpdir('facter')
24 env = { 'FACTERLIB' => fact_dir }
25
26 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
27 config_file = File.join(config_dir, 'facter.conf')
28
29 config_data = <<~FACTER_CONF
30 facts : {
31 ttls : [
32 { "#{custom_cache_group}" : 3 days }
33 ]
34 }
35
36 fact-groups : {
37 "#{custom_cache_group}" : ["#{custom_fact_name}", "#{core_fact_name}"],
38 }
39 FACTER_CONF
40
41 step "Agent #{agent}: create config file" do
42 agent.mkdir_p(config_dir)
43 create_remote_file(agent, config_file, config_data)
44
45 fact_file = File.join(fact_dir, custom_fact_file)
46 create_remote_file(agent, fact_file, fact_content)
47 end
48
49 teardown do
50 agent.rm_rf(fact_dir)
51 agent.rm_rf("#{cache_folder}/*")
52 agent.rm_rf(config_file)
53 end
54
55 step 'request the core fact' do
56 @core_value = on(agent, facter(core_fact_name, environment: env)).stdout.strip.to_i
57
58 cat_output = agent.cat("#{cache_folder}/#{custom_cache_group}")
59 cache = JSON.parse(cat_output)
60
61 step 'check if it is the only fact on cache' do
62 cached = { core_fact_name => @core_value, 'cache_format_version' => 1 }
63 assert_equal(cached, cache)
64 end
65
66 step 'check that it cached the value it printed' do
67 assert_equal(@core_value, cache[core_fact_name])
68 end
69 end
70
71 step 'request the core fact again' do
72 core_value = on(agent, facter(core_fact_name, environment: env)).stdout.strip.to_i
73
74 cat_output = agent.cat("#{cache_folder}/#{custom_cache_group}")
75 cache = JSON.parse(cat_output)
76
77 step 'check that it cached the value it printed' do
78 assert_equal(core_value, cache[core_fact_name])
79 end
80
81 step 'check that core value did not change' do
82 assert_equal(@core_value, core_value)
83 end
84 end
85
86 step 'request the custom fact' do
87 @custom_fact_value = on(agent, facter(custom_fact_name, environment: env)).stdout.strip
88
89 cat_output = agent.cat("#{cache_folder}/#{custom_cache_group}")
90 cache = JSON.parse(cat_output)
91
92 step 'check if it is the only fact on cache' do
93 cached = { custom_fact_name => @custom_fact_value, 'cache_format_version' => 1 }
94 assert_equal(cached, cache)
95 end
96
97 step 'check that it cached the value it printed' do
98 assert_equal(@custom_fact_value, cache[custom_fact_name].to_s)
99 end
100 end
101
102 step 'request the custom fact again' do
103 custom_fact_value = on(agent, facter(custom_fact_name, environment: env)).stdout.strip
104
105 cat_output = agent.cat("#{cache_folder}/#{custom_cache_group}")
106 cache = JSON.parse(cat_output)
107
108 step 'check that it cached the value it printed' do
109 assert_equal(custom_fact_value, cache[custom_fact_name].to_s)
110 end
111
112 step 'check that the value did not change' do
113 assert_equal(@custom_fact_value, custom_fact_value)
114 end
115 end
116
117 step "updates cache file with full group contents" do
118 on(agent, facter('', environment: env))
119 cat_output = agent.cat("#{cache_folder}/#{custom_cache_group}")
120 cache = JSON.parse(cat_output)
121
122 step 'cache contains core and custom fact' do
123 cache_keys = cache.keys - ['cache_format_version']
124 assert_equal([custom_fact_name, core_fact_name].sort, cache_keys.sort)
125 end
126
127 step 'reads the cache file' do
128 cache_hash = {
129 custom_fact_name => "pine apple",
130 "system_uptime.seconds" => 2,
131 "cache_format_version" => 1
132 }
133
134 create_remote_file(agent, "#{cache_folder}/#{custom_cache_group}", cache_hash.to_json)
135
136 step 'custom fact is read correctly' do
137 output = on(agent, facter(custom_fact_name, environment: env))
138 assert_equal(cache_hash[custom_fact_name], output.stdout.strip )
139 end
140
141 step 'core fact is read correctly' do
142 output = on(agent, facter(core_fact_name, environment: env))
143 assert_equal(cache_hash[core_fact_name].to_s, output.stdout.strip )
144 end
145 end
146 end
147 end
148 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external execution resolver with json output creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = get_external_fact_script_extension(agent['platform'])
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20 if agent['platform'] =~ /windows/
21 external_fact_content = <<EOM
22 @echo off
23 SetLocal EnableDelayedExpansion
24 echo {"#{cached_fact_name}": "#{initial_fact_value}"}
25
26 EOM
27 else
28 external_fact_content = <<EOM
29 #!/bin/sh
30 cat << EOF
31 {
32 "#{cached_fact_name}": "#{initial_fact_value}"
33 }
34 EOF
35 EOM
36 end
37 create_remote_file(agent, external_fact, external_fact_content)
38 agent.chmod('+x', external_fact)
39
40 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
41 config_file = File.join(config_dir, "facter.conf")
42 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
43
44 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
45
46 # Setup facter conf
47 agent.mkdir_p(config_dir)
48 cached_fact_content = <<EOM
49 {
50 "#{cached_fact_name}": "#{cached_fact_value}",
51 "cache_format_version": 1
52 }
53 EOM
54
55 config = <<EOM
56 facts : {
57 ttls : [
58 { "#{external_cachegroup}#{ext}" : 30 days }
59 ]
60 }
61 EOM
62 create_remote_file(agent, config_file, config)
63
64 teardown do
65 agent.rm_rf(config_dir)
66 agent.rm_rf(cached_facts_dir)
67 agent.rm_rf(external_dir)
68 end
69
70 step "should create a JSON file for a fact that is to be cached" do
71 agent.rm_rf(cached_facts_dir)
72 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
73 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
74 end
75 cat_output = agent.cat(cached_fact_file)
76 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
77 end
78
79 step "should read from a cached JSON file for a fact that has been cached" do
80 agent.rm_rf(cached_facts_dir)
81 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
82
83 create_remote_file(agent, cached_fact_file, cached_fact_content)
84
85 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
86 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
87 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
88 end
89 end
90 end
91 end
92 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external execution resolver with text output creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = get_external_fact_script_extension(agent['platform'])
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20 create_remote_file(agent, external_fact, external_fact_content(agent['platform'], cached_fact_name, initial_fact_value))
21 agent.chmod('+x', external_fact)
22
23 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
24 config_file = File.join(config_dir, "facter.conf")
25 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
26
27 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
28
29 # Setup facter conf
30 agent.mkdir_p(config_dir)
31 cached_fact_content = <<EOM
32 {
33 "#{cached_fact_name}": "#{cached_fact_value}",
34 "cache_format_version": 1
35 }
36 EOM
37
38 config = <<EOM
39 facts : {
40 ttls : [
41 { "#{external_cachegroup}#{ext}" : 30 days }
42 ]
43 }
44 EOM
45 create_remote_file(agent, config_file, config)
46
47 teardown do
48 agent.rm_rf(config_dir)
49 agent.rm_rf(cached_facts_dir)
50 agent.rm_rf(external_dir)
51 end
52
53 step "should create a JSON file for a fact that is to be cached" do
54 agent.rm_rf(cached_facts_dir)
55 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
56 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
57 end
58 cat_output = agent.cat(cached_fact_file)
59 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
60 end
61
62 step "should read from a cached JSON file for a fact that has been cached" do
63 agent.rm_rf(cached_facts_dir)
64 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
65
66 create_remote_file(agent, cached_fact_file, cached_fact_content)
67
68 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
69 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
70 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
71 end
72 end
73 end
74 end
75 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external execution resolver with yaml output creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = get_external_fact_script_extension(agent['platform'])
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20 if agent['platform'] =~ /windows/
21 external_fact_content = <<EOM
22 @echo off
23 SetLocal EnableDelayedExpansion
24 echo {"#{cached_fact_name}": "#{initial_fact_value}"}
25
26 EOM
27 else
28 external_fact_content = <<EOM
29 #!/bin/sh
30 cat << EOF
31 #{cached_fact_name}: "#{initial_fact_value}"
32 EOF
33 EOM
34 end
35 create_remote_file(agent, external_fact, external_fact_content)
36 agent.chmod('+x', external_fact)
37
38 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
39 config_file = File.join(config_dir, "facter.conf")
40 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
41
42 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
43
44 # Setup facter conf
45 agent.mkdir_p(config_dir)
46 cached_fact_content = <<EOM
47 {
48 "#{cached_fact_name}": "#{cached_fact_value}",
49 "cache_format_version": 1
50 }
51 EOM
52
53 config = <<EOM
54 facts : {
55 ttls : [
56 { "#{external_cachegroup}#{ext}" : 30 days }
57 ]
58 }
59 EOM
60 create_remote_file(agent, config_file, config)
61
62 teardown do
63 agent.rm_rf(config_dir)
64 agent.rm_rf(cached_facts_dir)
65 agent.rm_rf(external_dir)
66 end
67
68 step "should create a JSON file for a fact that is to be cached" do
69 agent.rm_rf(cached_facts_dir)
70 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
71 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
72 end
73 cat_output = agent.cat(cached_fact_file)
74 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
75 end
76
77 step "should read from a cached JSON file for a fact that has been cached" do
78 agent.rm_rf(cached_facts_dir)
79 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
80
81 create_remote_file(agent, cached_fact_file, cached_fact_content)
82
83 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
84 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
85 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
86 end
87 end
88 end
89 end
90 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external json resolver creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'initial_external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = '.json'
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20
21 external_fact_content = <<EOM
22 {
23 "#{cached_fact_name}": "#{initial_fact_value}",
24 "cache_format_version": 1
25 }
26 EOM
27 create_remote_file(agent, external_fact, external_fact_content)
28
29 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30 config_file = File.join(config_dir, "facter.conf")
31 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
32
33 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
34
35 # Setup facter conf
36 agent.mkdir_p(config_dir)
37 cached_fact_content = <<EOM
38 {
39 "#{cached_fact_name}": "#{cached_fact_value}"
40 }
41 EOM
42
43 config = <<EOM
44 facts : {
45 ttls : [
46 { "#{external_cachegroup}#{ext}" : 30 days }
47 ]
48 }
49 EOM
50 create_remote_file(agent, config_file, config)
51
52 teardown do
53 agent.rm_rf(config_dir)
54 agent.rm_rf(cached_facts_dir)
55 agent.rm_rf(external_dir)
56 end
57
58 step "should create a JSON file for a fact that is to be cached" do
59 agent.rm_rf(cached_facts_dir)
60 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
61 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
62 end
63 cat_output = agent.cat(cached_fact_file)
64 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
65 end
66
67 step "should read from a cached JSON file for a fact that has been cached" do
68 agent.rm_rf(cached_facts_dir)
69 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
70
71 create_remote_file(agent, cached_fact_file, cached_fact_content)
72
73 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
74 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
75 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
76 end
77 end
78 end
79 end
80 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external text resolver creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'initial_external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = '.txt'
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20
21 external_fact_content = <<EOM
22 #{cached_fact_name}=#{initial_fact_value}
23 EOM
24
25 create_remote_file(agent, external_fact, external_fact_content)
26
27 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
28 config_file = File.join(config_dir, "facter.conf")
29 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30
31 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
32
33 # Setup facter conf
34 agent.mkdir_p(config_dir)
35 cached_fact_content = <<EOM
36 {
37 "#{cached_fact_name}": "#{cached_fact_value}",
38 "cache_format_version": 1
39 }
40 EOM
41
42 config = <<EOM
43 facts : {
44 ttls : [
45 { "#{external_cachegroup}#{ext}" : 30 days }
46 ]
47 }
48 EOM
49 create_remote_file(agent, config_file, config)
50
51 teardown do
52 agent.rm_rf(config_dir)
53 agent.rm_rf(cached_facts_dir)
54 agent.rm_rf(external_dir)
55 end
56
57 step "should create a JSON file for a fact that is to be cached" do
58 agent.rm_rf(cached_facts_dir)
59 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
60 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
61 end
62 cat_output = agent.cat(cached_fact_file)
63 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
64 end
65
66 step "should read from a cached JSON file for a fact that has been cached" do
67 agent.rm_rf(cached_facts_dir)
68 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
69
70 create_remote_file(agent, cached_fact_file, cached_fact_content)
71
72 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
73 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
74 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
75 end
76 end
77 end
78 end
79 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "ttls configured cached external yaml resolver creates and read json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact'
11 cached_fact_name = 'single_fact'
12 initial_fact_value = 'initial_external_value'
13 cached_fact_value = 'cached_external_value'
14
15 agents.each do |agent|
16 step "Agent #{agent}: create config file" do
17 external_dir = agent.tmpdir('external_dir')
18 ext = '.yaml'
19 external_fact = File.join(external_dir, "#{external_cachegroup}#{ext}")
20
21 external_fact_content = <<EOM
22 #{cached_fact_name}: "#{initial_fact_value}"
23 EOM
24
25 create_remote_file(agent, external_fact, external_fact_content)
26
27 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
28 config_file = File.join(config_dir, "facter.conf")
29 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30
31 cached_fact_file = File.join(cached_facts_dir, "#{external_cachegroup}#{ext}")
32
33 # Setup facter conf
34 agent.mkdir_p(config_dir)
35 cached_fact_content = <<EOM
36 {
37 "#{cached_fact_name}": "#{cached_fact_value}",
38 "cache_format_version": 1
39 }
40 EOM
41
42 config = <<EOM
43 facts : {
44 ttls : [
45 { "#{external_cachegroup}#{ext}" : 30 days }
46 ]
47 }
48 EOM
49 create_remote_file(agent, config_file, config)
50
51 teardown do
52 agent.rm_rf(config_dir)
53 agent.rm_rf(cached_facts_dir)
54 agent.rm_rf(external_dir)
55 end
56
57 step "should create a JSON file for a fact that is to be cached" do
58 agent.rm_rf(cached_facts_dir)
59 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
60 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
61 end
62 cat_output = agent.cat(cached_fact_file)
63 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
64 end
65
66 step "should read from a cached JSON file for a fact that has been cached" do
67 agent.rm_rf(cached_facts_dir)
68 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"))
69
70 create_remote_file(agent, cached_fact_file, cached_fact_content)
71
72 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}")) do |facter_output|
73 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
74 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
75 end
76 end
77 end
78 end
79 end
0 # This test verifies that individual facts can be cached
1 test_name "ttls config with fact in multiple groups should not cache fact twice" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_fact_name = 'os.name'
10 first_fact_group = 'first'
11 second_fact_group = 'second'
12
13 config = <<EOM
14 facts : {
15 ttls : [
16 { "#{first_fact_group}" : 30 days },
17 { "#{second_fact_group}" : 1 days },
18 ]
19 }
20 fact-groups : {
21 #{first_fact_group}: [#{cached_fact_name}],
22 #{second_fact_group}: ["os"],
23 }
24 EOM
25
26 agents.each do |agent|
27 step "Agent #{agent}: create cache file with individual fact" do
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, "facter.conf")
30 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31
32 first_cached_fact_file = File.join(cached_facts_dir, first_fact_group)
33 second_cached_fact_file = File.join(cached_facts_dir, second_fact_group)
34
35 # Setup facter conf
36 agent.mkdir_p(config_dir)
37 create_remote_file(agent, config_file, config)
38
39 teardown do
40 agent.rm_rf(config_dir)
41 agent.rm_rf(cached_facts_dir)
42 end
43
44 step "should create a JSON file for a fact that is to be cached" do
45 agent.rm_rf(cached_facts_dir)
46 on(agent, facter("--debug")) do |facter_output|
47 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
48 end
49 first_cat_output = agent.cat(first_cached_fact_file)
50 assert_match(/#{cached_fact_name}/, first_cat_output.strip, "Expected cached fact file to contain fact information")
51 second_cat_output = agent.cat(second_cached_fact_file)
52 assert_not_match(/#{cached_fact_name}/, second_cat_output.strip, "Expected cached fact file to not contain fact information")
53 end
54 end
55 end
56 end
0 # This test verifies that a ttls configured cached facts when initially called
1 # create a json cache file
2 test_name "C99973: ttls configured cached facts create json cache files" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 cached_factname = 'uptime'
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 agents.each do |agent|
20 step "Agent #{agent}: create config file" do
21 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
22 config_file = File.join(config_dir, "facter.conf")
23 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
24
25 cached_fact_file = File.join(cached_facts_dir, cached_factname)
26
27 # Setup facter conf
28 agent.mkdir_p(config_dir)
29 create_remote_file(agent, config_file, config)
30
31 teardown do
32 agent.rm_rf(config_dir)
33 agent.rm_rf(cached_facts_dir)
34 end
35
36 step "should create a JSON file for a fact that is to be cached" do
37 agent.rm_rf(cached_facts_dir)
38 on(agent, facter("--debug")) do |facter_output|
39 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
40 end
41 cat_output = agent.cat(cached_fact_file)
42 assert_match(/#{cached_factname}/, cat_output.strip, "Expected cached fact file to contain fact information")
43 end
44 end
45 end
46 end
0 # This test verifies that expired cached facts are not used
1 test_name "C100037: ttls configured cached valued that are expired are not returned" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_factname = 'uptime'
10
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 cached_fact_value = "CACHED_FACT_VALUE"
20 cached_fact_content = <<EOM
21 {
22 "#{cached_factname}": "#{cached_fact_value}"
23 }
24 EOM
25
26 agents.each do |agent|
27 step "Agent #{agent}: create config file" do
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, "facter.conf")
30 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31
32 cached_fact_file = File.join(cached_facts_dir, cached_factname)
33
34 # Setup facter conf
35 agent.mkdir_p(config_dir)
36 create_remote_file(agent, config_file, config)
37
38 teardown do
39 agent.rm_rf(config_dir)
40 agent.rm_rf(cached_facts_dir)
41 end
42
43 step "should not read from a cached JSON file for a fact that has been cached but the TTL expired" do
44 # Setup a known cached fact
45 agent.rm_rf(cached_facts_dir)
46 on(agent, facter(""))
47 create_remote_file(agent, cached_fact_file, cached_fact_content)
48 # Change the modified date to sometime in the far distant past
49 agent.modified_at(cached_fact_file, '198001010000')
50
51 on(agent, facter("#{cached_factname}")) do |facter_output|
52 assert_not_match(/#{cached_fact_value}/, facter_output.stdout, "Expected fact to not match the cached fact file")
53 end
54 end
55 end
56 end
57 end
0 # This test verifies that cached facts that are expired are refreshed
1 test_name "C100040: ttls configured facts that are expired are refreshed" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_factname = 'uptime'
10
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 cached_fact_value = "EXPIRED_CACHED_FACT_VALUE"
20 cached_fact_content = <<EOM
21 {
22 "#{cached_factname}": "#{cached_fact_value}"
23 }
24 EOM
25
26 agents.each do |agent|
27 step "Agent #{agent}: create config file" do
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, "facter.conf")
30 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31
32 cached_fact_file = File.join(cached_facts_dir, cached_factname)
33
34 # Setup facter conf
35 agent.mkdir_p(config_dir)
36 create_remote_file(agent, config_file, config)
37
38 teardown do
39 agent.rm_rf(config_dir)
40 agent.rm_rf(cached_facts_dir)
41 end
42
43 step "should refresh an expired cached fact" do
44 # Setup a known cached fact
45 agent.rm_rf(cached_facts_dir)
46 on(agent, facter(""))
47 create_remote_file(agent, cached_fact_file, cached_fact_content)
48 # Change the modified date to sometime in the far distant past
49 agent.modified_at(cached_fact_file, '198001010000')
50 # Force facter to recache
51 on(agent, facter("#{cached_factname}"))
52
53 # Read cached fact file content
54 cat_output = agent.cat(cached_fact_file)
55 assert_no_match(/#{cached_fact_value}/, cat_output.strip, "Expected cached fact file to be refreshed")
56 end
57 end
58 end
59 end
0 # This test verifies that cached facts are used while still valid
1 test_name "C100041: ttls configured cached facts are used while still valid" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_factname = 'uptime'
10
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 cached_fact_value = "CACHED_FACT_VALUE"
20 cached_fact_content = <<EOM
21 {
22 "#{cached_factname}": "#{cached_fact_value}",
23 "cache_format_version": 1
24 }
25 EOM
26
27 agents.each do |agent|
28 step "Agent #{agent}: create config file" do
29 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30 config_file = File.join(config_dir, "facter.conf")
31 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
32
33 cached_fact_file = File.join(cached_facts_dir, cached_factname)
34
35 # Setup facter conf
36 agent.mkdir_p(config_dir)
37 create_remote_file(agent, config_file, config)
38
39 teardown do
40 agent.rm_rf(config_dir)
41 agent.rm_rf(cached_facts_dir)
42 end
43
44 step "should read from a cached JSON file for a fact that has been cached" do
45 # Setup a known cached fact
46 agent.rm_rf(cached_facts_dir)
47 on(agent, facter(""))
48 create_remote_file(agent, cached_fact_file, cached_fact_content)
49
50 on(agent, facter("#{cached_factname} --debug")) do
51 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
52 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
53 end
54 end
55 end
56 end
57 end
0 # This test verifies that a previouslt cached fact is being resolved correctly after it is not cached anymore
1 test_name "ttls configured cached fact is resolved after ttls is removed" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_fact_name = 'uptime'
10 cached_fact_value = "CACHED_FACT_VALUE"
11 cached_fact_content = <<EOM
12 {
13 "#{cached_fact_name}": "#{cached_fact_value}"
14 }
15 EOM
16
17 config = <<EOM
18 facts : {
19 ttls : [
20 { "#{cached_fact_name}" : 30 days }
21 ]
22 }
23 EOM
24
25 config_no_cache = <<EOM
26 facts : {
27 ttls : [ ]
28 }
29 EOM
30
31 agents.each do |agent|
32 step "Agent #{agent}: create config file" do
33 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
34 config_file = File.join(config_dir, "facter.conf")
35 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
36
37 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
38
39 # Setup facter conf
40 agent.mkdir_p(config_dir)
41 create_remote_file(agent, config_file, config)
42
43 teardown do
44 agent.rm_rf(config_dir)
45 agent.rm_rf(cached_facts_dir)
46 end
47
48 step "Agent #{agent}: create config file with no cached facts" do
49 # Set up a known cached fact
50 agent.rm_rf(cached_facts_dir)
51 on(agent, facter(""))
52 create_remote_file(agent, cached_fact_file, cached_fact_content)
53 end
54
55 step "Agent #{agent}: resolves fact after ttls was removed" do
56 # Create config file with no caching
57 no_cache_config_file = File.join(config_dir, "no-cache.conf")
58 create_remote_file(agent, no_cache_config_file, config_no_cache)
59
60 on(agent, facter("--config \"#{no_cache_config_file}\"")) do |facter_output|
61 assert_match(/#{cached_fact_name}/, facter_output.stdout, "Expected to see the fact in output")
62 assert_no_match(/#{cached_fact_value}/, facter_output.stdout, "Expected to not see the cached fact value")
63 end
64 end
65 end
66 end
67 end
0 # This test verifies that corrupt facts are refreshed with new values
1 test_name "C100042: ttls configured cached facts that are corrupt are refreshed with new values" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_factname = 'uptime'
10
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 agents.each do |agent|
20 step "Agent #{agent}: create config file" do
21 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
22 config_file = File.join(config_dir, "facter.conf")
23 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
24
25 cached_fact_file = File.join(cached_facts_dir, cached_factname)
26
27 # Setup facter conf
28 agent.mkdir_p(config_dir)
29 create_remote_file(agent, config_file, config)
30
31 teardown do
32 agent.rm_rf(config_dir)
33 agent.rm_rf(cached_facts_dir)
34 end
35
36 step "should refresh a cached fact if cache file is corrupt" do
37 # Setup a known cached fact
38 agent.rm_rf(cached_facts_dir)
39 on(agent, facter(""))
40 # Corrupt the cached fact file
41 create_remote_file(agent, cached_fact_file, 'ThisIsNotvalidJSON')
42
43 on(agent, facter("#{cached_factname}")) do
44 assert_match(/.+/, stdout, "Expected fact to be resolved")
45 end
46 cat_output = agent.cat(cached_fact_file)
47 assert_match(/#{cached_factname}/, cat_output.strip, "Expected cachced fact to contain the fact name")
48 end
49 end
50 end
51 end
0 # This test verifies that cached facts that are empty return an empty string
1 test_name "C100043: ttls configured cached facts that are empty, return an empty string" do
2 tag 'risk:high'
3
4 skip_test 'Facts should be resolved if values are not found in cache'
5
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 # This fact must be resolvable on ALL platforms
10 # Do NOT use the 'kernel' fact as it is used to configure the tests
11 cached_factname = 'uptime'
12
13 config = <<EOM
14 facts : {
15 ttls : [
16 { "#{cached_factname}" : 30 days }
17 ]
18 }
19 EOM
20
21 empty_cached_fact_content = <<EOM
22 { }
23 EOM
24
25 agents.each do |agent|
26 step "Agent #{agent}: create config file" do
27 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
28 config_file = File.join(config_dir, "facter.conf")
29 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
30
31 cached_fact_file = File.join(cached_facts_dir, cached_factname)
32
33 # Setup facter conf
34 agent.mkdir_p(config_dir)
35 create_remote_file(agent, config_file, config)
36
37 teardown do
38 agent.rm_rf(config_dir)
39 agent.rm_rf(cached_facts_dir)
40 end
41
42 step "should return an empty string for an empty JSON document" do
43 # Setup a known cached fact
44 agent.rm_rf(cached_facts_dir)
45 on(agent, facter(""))
46 create_remote_file(agent, cached_fact_file, empty_cached_fact_content)
47
48 on(agent, facter("#{cached_factname}")) do |facter_output|
49 assert_empty(facter_output.stdout.chomp, "Expected fact to be empty")
50 end
51 end
52 end
53 end
54 end
0 # This test verifies that individual facts can be cached
1 test_name "ttls config that contains fact name caches individual facts" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_fact_name = 'system_uptime.days'
10 cached_fact_value = "CACHED_FACT_VALUE"
11 cached_fact_content = <<EOM
12 {
13 "#{cached_fact_name}": "#{cached_fact_value}",
14 "cache_format_version": 1
15 }
16 EOM
17
18 config = <<EOM
19 facts : {
20 ttls : [
21 { "#{cached_fact_name}" : 30 days }
22 ]
23 }
24 EOM
25
26 agents.each do |agent|
27 step "Agent #{agent}: create cache file with individual fact" do
28 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 config_file = File.join(config_dir, "facter.conf")
30 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31
32 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
33
34 # Setup facter conf
35 agent.mkdir_p(config_dir)
36 create_remote_file(agent, config_file, config)
37
38 teardown do
39 agent.rm_rf(config_dir)
40 agent.rm_rf(cached_facts_dir)
41 end
42
43 step "should create a JSON file for a fact that is to be cached" do
44 agent.rm_rf(cached_facts_dir)
45 on(agent, facter("--debug")) do |facter_output|
46 assert_match(/caching values for .+ facts/, facter_output.stderr, "Expected debug message to state that values will be cached")
47 end
48 cat_output = agent.cat(cached_fact_file)
49 assert_match(/#{cached_fact_name}/, cat_output.strip, "Expected cached fact file to contain fact information")
50 end
51
52 step "should read from a cached JSON file for a fact that has been cached" do
53 agent.mkdir_p(cached_facts_dir)
54 create_remote_file(agent, cached_fact_file, cached_fact_content)
55
56 on(agent, facter("#{cached_fact_name} --debug")) do
57 assert_match(/loading cached values for .+ facts/, stderr, "Expected debug message to state that values are read from cache")
58 assert_match(/#{cached_fact_value}/, stdout, "Expected fact to match the cached fact file")
59 end
60 end
61 end
62 end
63 end
0 test_name "ttls configured cached with external in custom group prints error" do
1 tag 'risk:high'
2
3 skip_test "WIP"
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 external_cachegroup = 'external_fact_group'
11 cached_fact_name = 'external_fact'
12 fact_value = 'initial_external_value'
13
14 agents.each do |agent|
15 step "Agent #{agent}: create config file" do
16 external_dir = agent.tmpdir('external_dir')
17 ext = '.txt'
18 external_fact = File.join(external_dir, "#{cached_fact_name}#{ext}")
19
20 external_fact_content = <<EOM
21 #{cached_fact_name}=#{fact_value}
22 EOM
23
24 create_remote_file(agent, external_fact, external_fact_content)
25
26 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
27 config_file = File.join(config_dir, "facter.conf")
28
29 # Setup facter conf
30 agent.mkdir_p(config_dir)
31
32 config = <<EOM
33 facts : {
34 ttls : [
35 { "#{external_cachegroup}" : 30 days }
36 ]
37 }
38
39 fact-groups : {
40 "#{external_cachegroup}" : ["#{cached_fact_name}#{ext}"],
41 }
42 EOM
43 create_remote_file(agent, config_file, config)
44
45 teardown do
46 agent.rm_rf(config_dir)
47 agent.rm_rf(cached_facts_dir)
48 agent.rm_rf(external_dir)
49 end
50
51 step "should print error and not cache anything" do
52 on(agent, facter("--external-dir \"#{external_dir}\" --debug #{cached_fact_name}"), acceptable_exit_codes: [1]) do |facter_output|
53 assert_match(/Caching custom group is not supported for external facts/, facter_output.stderr, "Expected error message to state that external facts cannot be grouped")
54 end
55 end
56 end
57 end
58 end
0 # Verify that setting a ttls, creates a json file for the cached fact when run
1 # from puppet facts
2 test_name "C100038: with ttls configured create cached facts when run from puppet facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # This fact must be resolvable on ALL platforms
9 # Do NOT use the 'kernel' fact as it is used to configure the tests
10 cached_factname = 'uptime'
11 config = <<EOM
12 facts : {
13 ttls : [
14 { "#{cached_factname}" : 30 days }
15 ]
16 }
17 EOM
18
19 agents.each do |agent|
20 step "Agent #{agent}: create config file" do
21 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
22 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
23 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
24 cached_fact_file = File.join(cached_facts_dir, cached_factname)
25
26 agent.mkdir_p(facter_conf_default_dir)
27 create_remote_file(agent, facter_conf_default_path, config)
28
29 teardown do
30 agent.rm_rf(facter_conf_default_dir)
31 agent.rm_rf(cached_facts_dir)
32 end
33
34 step "should create a JSON file for a fact that is to be cached" do
35 agent.rm_rf(cached_facts_dir)
36 on(agent, puppet("facts --debug")) do |pupppet_fact_output|
37 assert_match(/caching values for .+ facts/, pupppet_fact_output.stdout, "Expected debug message to state that values will be cached")
38 end
39 cat_output = agent.cat(cached_fact_file)
40 assert_match(/#{cached_factname}/, cat_output.strip, "Expected cached fact file to contain fact information")
41 end
42 end
43 end
44 end
0 # Verify that setting a ttls, puppet facts returns the cached value of the fact
1 test_name "C100039: ttls configured cached facts run from puppet facts return cached facts" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 # This fact must be resolvable on ALL platforms
8 # Do NOT use the 'kernel' fact as it is used to configure the tests
9 cached_factname = 'system_uptime.uptime'
10 config = <<EOM
11 facts : {
12 ttls : [
13 { "#{cached_factname}" : 30 days }
14 ]
15 }
16 EOM
17
18 cached_value = "CACHED_FACT_VALUE"
19 cached_fact_content = <<EOM
20 {
21 "#{cached_factname}": "#{cached_value}",
22 "cache_format_version": 1
23 }
24 EOM
25
26 agents.each do |agent|
27 step "Agent #{agent}: create config file" do
28 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
30 cached_facts_dir = get_cached_facts_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
31 cached_fact_file = File.join(cached_facts_dir, cached_factname)
32
33 agent.mkdir_p(facter_conf_default_dir)
34 create_remote_file(agent, facter_conf_default_path, config)
35
36 teardown do
37 agent.rm_rf(cached_facts_dir)
38 agent.rm_rf(facter_conf_default_dir)
39 end
40
41 step "should read from a cached JSON file for a fact that has been cached" do
42 step "call puppet facts to setup the cached fact" do
43 agent.rm_rf(cached_facts_dir)
44 on(agent, puppet("facts"))
45 create_remote_file(agent, cached_fact_file, cached_fact_content)
46 end
47
48 on(agent, puppet("facts --debug")) do |puppet_facts_output|
49 assert_match(/loading cached values for .+ facts/, puppet_facts_output.stdout, "Expected debug message to state that values are read from cache")
50 assert_match(/#{cached_value}/, puppet_facts_output.stdout, "Expected fact to match the cached fact file")
51 end
52 end
53 end
54 end
55 end
0 test_name 'running facter 4 as non-root while facter 3 cache file owned by root exists(FACT-2961)' do
1 tag 'risk:high'
2
3 confine :except, :platform => 'windows' # this test currently only supported on unix systems FACT-1647
4 confine :except, :platform => 'aix' # system su(1) command prints errors cannot access parent directories and ticket FACT-1586
5 confine :except, :platform => 'osx' # system su(1) command prints errors cannot access parent directories
6 confine :except, :platform => 'solaris' # system su(1) command prints errors cannot access parent directories
7
8 require 'facter/acceptance/user_fact_utils'
9 extend Facter::Acceptance::UserFactUtils
10
11 fact_group_name = 'uptime'
12 config_data = <<~HOCON
13 facts : {
14 ttls : [
15 { "#{fact_group_name}" : 3 days }
16 ]
17 }
18 HOCON
19
20 f3_cache = <<~JSON
21 {
22 "system_uptime": {
23 "days": 1,
24 "hours": 1,
25 "seconds": 1,
26 "uptime": "1 day"
27 },
28 "uptime": "1 days",
29 "uptime_days": 1,
30 "uptime_hours": 1,
31 "uptime_seconds": 1
32 }
33 JSON
34
35 # since facter is using beaker on localhost, stdin is not connected
36 # and the only way to execute a manifest with puppet is by using
37 # `-e "<MANIFEST>"` argument in command line, needing extra-escape
38 # for quotes in manifests containing quotes
39 config_data.gsub!('"','\"')
40 f3_cache.gsub!('"','\"')
41
42 agents.each do |agent|
43 kernelmajversion = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
44
45 cache_dir = get_cached_facts_dir(agent['platform'], kernelmajversion)
46 f3_cache_file = File.join(cache_dir, fact_group_name)
47
48 f3_cache_file_manifest = <<-MANIFEST
49 file { '#{cache_dir}':
50 ensure => 'directory',
51 mode => '755'
52 }
53 file { '#{f3_cache_file}':
54 content => '#{f3_cache}'
55 }
56 MANIFEST
57
58 config_dir = get_default_fact_dir(agent['platform'], kernelmajversion)
59 config_file = File.join(config_dir, 'facter.conf')
60
61 config_file_manifest = <<-MANIFEST
62 file { '#{config_file}':
63 content => '#{config_data}'
64 }
65 MANIFEST
66
67 non_root_user = "nonroot"
68 facter_path = agent.which('facter').chomp
69
70 teardown do
71 agent.rm_rf(cache_dir)
72 agent.rm_rf(config_dir)
73 on(agent, puppet("resource user #{non_root_user} ensure=absent"))
74 end
75
76 step 'create cache file and the non-root account' do
77 agent.mkdir_p(cache_dir)
78 on(agent, puppet('apply', '--debug', "-e \" #{f3_cache_file_manifest} \""))
79 on(agent, "puppet resource user #{non_root_user} ensure=present shell='#{user_shell(agent)}'")
80 end
81
82 step 'calling facter 4 as non-root user without config will show no error' do
83 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}' uptime"]) do |facter_results|
84 assert_empty(facter_results.stderr.chomp, "Expected no errors from facter when run as user #{non_root_user}")
85 end
86 end
87
88 step "Agent #{agent}: create config file to enable cache" do
89 agent.mkdir_p(config_dir)
90 on(agent, puppet('apply', '--debug', "-e \" #{config_file_manifest} \""))
91 end
92
93 step 'calling facter 4 as non-root user with config will print warning that cannot update cache file' do
94 on(agent, %Q[su #{non_root_user} -c "'#{facter_path}' uptime"]) do |facter_results|
95 assert_match(/WARN.*Could not delete cache: Permission denied/, facter_results.stderr, "Expected cache related permission denied warning #{non_root_user}")
96 end
97 end
98 end
99 end
0 # This test is intended to demonstrate that setting cli.verbose to true in the
1 # config file causes INFO level logging to output to stderr.
2 test_name "C99989: verbose config field prints verbose information to stderr" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 config = <<EOM
9 cli : {
10 verbose : true
11 }
12 EOM
13
14 agents.each do |agent|
15 step "Agent #{agent}: create config file" do
16 config_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
17 config_file = File.join(config_dir, "facter.conf")
18 agent.mkdir_p(config_dir)
19 create_remote_file(agent, config_file, config)
20
21 teardown do
22 agent.rm_rf(config_dir)
23 end
24
25 step "debug output should print when config file is loaded" do
26 on(agent, facter("")) do |facter_output|
27 assert_match(/INFO/, facter_output.stderr, "Expected stderr to contain verbose (INFO) statements")
28 end
29 end
30 end
31 end
32 end
33
0 # This test checks that we can call facter with a --custom-dir and get a custom fact
1 # from that directory
2 test_name "C14905: custom fact command line option --custom-dir loads custom fact" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content = <<EOM
9 Facter.add('single_custom_fact') do
10 setcode do
11 "single_fact"
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create custom fact directory and a custom fact" do
18 custom_dir = agent.tmpdir('custom_dir')
19 custom_fact = File.join(custom_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21
22 teardown do
23 agent.rm_rf(custom_dir)
24 end
25
26 step "Agent #{agent}: --custom-dir option should resolve custom facts from the specific directory" do
27 on(agent, facter("--custom-dir \"#{custom_dir}\" single_custom_fact")) do |facter_output|
28 assert_equal("single_fact", facter_output.stdout.chomp, "Incorrect custom fact value")
29 end
30 end
31 end
32 end
33 end
0 # This test verifies that we can load a custom fact using the environment variable FACTERLIB
1 test_name "C14779: custom facts are loaded from the environment variable FACTERLIB path" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 content = <<EOM
8 Facter.add('custom_fact_facterlib') do
9 # has_weight 1
10 setcode do
11 "facterlib"
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create custom directory and fact" do
18 custom_dir = agent.tmpdir('facter_lib_dir')
19 custom_fact = File.join(custom_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21
22 teardown do
23 agent.rm_rf(custom_dir)
24 end
25
26 step "Agent #{agent}: facter should resolve a fact from the directory specified by the environment variable FACTERLIB" do
27 on(agent, facter('custom_fact_facterlib', :environment => { 'FACTERLIB' => custom_dir })) do |facter_output|
28 assert_equal("facterlib", facter_output.stdout.chomp, "Incorrect custom fact value for fact in FACTERLIB")
29 end
30 end
31 end
32 end
33 end
0 # facter should be able to be called with multiple --custom-dir's and find a fact in each
1 # directory specified
2 test_name "C99999: custom fact commandline option --custom-dir can be specified multiple times" do
3 tag 'risk:high'
4
5 require 'json'
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9 content_1 = <<EOM
10 Facter.add('custom_fact_1') do
11 setcode do
12 "testvalue_1"
13 end
14 end
15 EOM
16
17 content_2 = <<EOM
18 Facter.add('custom_fact_2') do
19 setcode do
20 "testvalue_2"
21 end
22 end
23 EOM
24
25 agents.each do |agent|
26 step "Agent #{agent}: create custom fact directory and a custom fact in each" do
27 custom_dir_1 = agent.tmpdir('custom_dir_1')
28 custom_dir_2 = agent.tmpdir('custom_dir_2')
29 custom_fact_1 = File.join(custom_dir_1, 'custom_fact.rb')
30 custom_fact_2 = File.join(custom_dir_2, 'custom_fact.rb')
31 create_remote_file(agent, custom_fact_1, content_1)
32 create_remote_file(agent, custom_fact_2, content_2)
33
34 teardown do
35 agent.rm_rf(custom_dir_1)
36 agent.rm_rf(custom_dir_2)
37 end
38
39 step "Agent #{agent}: resolve a fact from each specified --custom-dir option" do
40 on(agent, facter("--custom-dir \"#{custom_dir_1}\" --custom-dir \"#{custom_dir_2}\" --json")) do |facter_output|
41 results = JSON.parse(facter_output.stdout)
42 assert_equal("testvalue_1", results['custom_fact_1'], "Incorrect custom fact value for custom_fact_1")
43 assert_equal("testvalue_2", results['custom_fact_2'], "Incorrect custom fact value for custom_fact_2")
44 end
45 end
46 end
47 end
48 end
0 # This test verifies that we can load a custom fact using the ruby $LOAD_PATH variable
1 #
2 # Facter searches all directories in the Ruby $LOAD_PATH variable for subdirectories
3 # named ‘facter’, and loads all Ruby files in those directories.
4 test_name "C14777: custom facts loaded from facter subdirectory found in $LOAD_PATH directory" do
5 confine :except, :platform => 'cisco_nexus' # see BKR-749
6
7 tag 'risk:high'
8
9 require 'puppet/acceptance/common_utils'
10 extend Puppet::Acceptance::CommandUtils
11
12 require 'facter/acceptance/user_fact_utils'
13 extend Facter::Acceptance::UserFactUtils
14
15 content = <<EOM
16 Facter.add('custom_fact') do
17 setcode do
18 "load_path"
19 end
20 end
21 EOM
22
23 agents.each do |agent|
24 step "Agent #{agent}: determine $LOAD_PATH and create custom fact" do
25 on(agent, "#{ruby_command(agent)} -e 'puts $LOAD_PATH[0]'")
26 load_path_facter_dir = File.join(stdout.chomp, 'facter')
27 agent.mkdir_p(load_path_facter_dir)
28 custom_fact = File.join(load_path_facter_dir, 'custom_fact.rb')
29 create_remote_file(agent, custom_fact, content)
30
31 teardown do
32 load_path_facter_dir = "\"#{load_path_facter_dir}\"" if agent.is_cygwin?
33
34 agent.rm_rf(load_path_facter_dir)
35 end
36
37 step("Agent #{agent}: resolve the custom fact that is in a facter directory on the $LOAD_PATH")
38 on(agent, facter("custom_fact")) do |facter_output|
39 assert_equal("load_path", facter_output.stdout.chomp, "Incorrect custom fact value for fact in $LOAD_PATH/facter")
40 end
41 end
42 end
43 end
0 # This test is intended to ensure that the --debug command-line option works
1 # properly. This option prints debugging information to stderr.
2 test_name "C63191: --debug command-line option prints debugging information to stderr" do
3
4 agents.each do |agent|
5 step "Agent #{agent}: retrieve debug info from stderr using --debug option" do
6 on(agent, facter('--debug')) do
7 assert_match(/DEBUG/, stderr, "Expected DEBUG information in stderr")
8 end
9 end
10 end
11 end
0 # This tests is intended to verify that an empty string in blocklist will not block all facts
1 test_name "empty string in blocklist does not block facts" do
2 tag 'risk:high'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 agents.each do |agent|
8 # default facter.conf
9 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
10 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
11
12 teardown do
13 agent.rm_rf(facter_conf_default_dir)
14 end
15
16 # create the directories
17 agent.mkdir_p(facter_conf_default_dir)
18
19 step "Agent #{agent}: create config file" do
20 create_remote_file(agent, facter_conf_default_path, <<-FILE)
21 cli : { debug : true }
22 facts : { blocklist : [ "" ] }
23 FILE
24 end
25
26 step "no facts should be blocked is specified" do
27 on(agent, facter) do |facter_output|
28 assert_no_match(/blocking collection of .+ facts/, facter_output.stderr, "Expected no facts to be blocked")
29 end
30 end
31 end
32 end
0 # This tests checks that we can call facter with a --external-dir and get an external fact
1 # from that directory
2 test_name "C99974: external fact commandline options --external-dir resolves an external fact" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create an external fact directory with an external fact" do
10 external_dir = agent.tmpdir('external_dir')
11 ext = get_external_fact_script_extension(agent['platform'])
12 external_fact = File.join(external_dir, "external_fact#{ext}")
13 create_remote_file(agent, external_fact, external_fact_content(agent['platform'], 'single_fact', 'external_value'))
14 agent.chmod('+x', external_fact)
15
16 teardown do
17 agent.rm_rf(external_dir)
18 end
19
20 step "Agent #{agent}: resolve a fact from each specified --external_dir option" do
21 on(agent, facter("--external-dir \"#{external_dir}\" single_fact")) do |facter_output|
22 assert_equal("external_value", facter_output.stdout.chomp, "Incorrect external fact value")
23 end
24 end
25 end
26 end
27 end
0 # facter should be able to be called with multiple --external-dir's and find a fact in each
1 # directory specified
2
3 test_name "C99998: external fact commandline option --external-dir can be specified multiple times" do
4 tag 'risk:high'
5
6 require 'json'
7 require 'facter/acceptance/user_fact_utils'
8 extend Facter::Acceptance::UserFactUtils
9
10 agents.each do |agent|
11 step "Agent #{agent}: create external fact directories and a external fact in each" do
12 external_dir_1 = agent.tmpdir('external_dir_1')
13 external_dir_2 = agent.tmpdir('external_dir_2')
14 ext = get_external_fact_script_extension(agent['platform'])
15 external_fact_1 = File.join(external_dir_1, "external_fact#{ext}")
16 external_fact_2 = File.join(external_dir_2, "external_fact#{ext}")
17 create_remote_file(agent, external_fact_1, external_fact_content(agent['platform'], 'external_fact_1', 'external_value_1'))
18 create_remote_file(agent, external_fact_2, external_fact_content(agent['platform'], 'external_fact_2', 'external_value_2'))
19 agent.chmod('+x', external_fact_1)
20 agent.chmod('+x', external_fact_2)
21
22 teardown do
23 agent.rm_rf(external_dir_1)
24 agent.rm_rf(external_dir_2)
25 end
26
27 step "Agent #{agent}: resolve a fact from each specified --external_dir option" do
28 on(agent, facter("--external-dir \"#{external_dir_1}\" --external-dir \"#{external_dir_2}\" --json")) do |facter_output|
29 results = JSON.parse(facter_output.stdout)
30 assert_equal("external_value_1", results['external_fact_1'], "Incorrect external fact value for external_fact_1")
31 assert_equal("external_value_2", results['external_fact_2'], "Incorrect external fact value for external_fact_2")
32 end
33 end
34 end
35 end
36 end
0 # This test is intended to ensure that the --help command-line option works
1 # properly. This option prints usage information and available command-line
2 # options.
3 test_name 'C99984: --help command-line option prints usage information to stdout' do
4
5 agents.each do |agent|
6 step "Agent #{agent}: retrieve usage info from stdout using --help option" do
7 on(agent, facter('--help')) do
8 assert_match(/(facter|facter-ng) \[options\] \[query\] \[query\] \[...\]/, stdout, 'Expected stdout to contain usage information')
9 end
10 end
11
12 step 'Facter help should list options with minus sign' do
13 options = %w[
14 --no-color
15 --custom-dir
16 --external-dir
17 --log-level
18 --no-block
19 --no-cache
20 --no-custom-facts
21 --no-external-facts
22 --no-ruby
23 --show-legacy
24 --list-block-groups
25 --list-cache-groups
26 ]
27 on(agent, facter('--help')) do
28 options.each do |option|
29 assert_match(/#{option}/, stdout, "Key: #{option} not found in help description")
30 end
31 end
32 end
33 end
34 end
0 # This test is intended to ensure that the --hocon command-line option works
1 # properly. This option causes Facter to output facts in HOCON format.
2 test_name "--hocon command-line option results in valid HOCON output" do
3
4 require 'hocon/parser/config_document_factory'
5 require 'hocon/config_parse_options'
6 require 'facter/acceptance/user_fact_utils'
7 extend Facter::Acceptance::UserFactUtils
8
9
10 agents.each do |agent|
11 step "Agent #{agent}: retrieve os fact data using the --hocon option" do
12 on(agent, facter('--hocon os')) do
13 begin
14 parsing_successful = Hocon::Parser::ConfigDocumentFactory.parse_string(stdout.chomp, options =
15 Hocon::ConfigParseOptions.defaults) != nil
16 assert_equal(true, parsing_successful, "Output is not HOCON compatible.")
17 rescue
18 fail_test "Couldn't parse output as HOCON"
19 end
20 end
21 end
22 end
23 end
24
0 # This test is intended to ensure that the --json command-line option works
1 # properly. This option causes Facter to output facts in JSON format.
2 # A custom fact is used to test for parity between Facter's output and
3 # the expected JSON output.
4 test_name "C99966, C98083: --json command-line option results in valid JSON output" do
5
6 require 'json'
7 require 'facter/acceptance/user_fact_utils'
8 extend Facter::Acceptance::UserFactUtils
9
10 content = <<EOM
11 Facter.add('structured_fact') do
12 setcode do
13 { "foo" => {"nested" => "value1"}, "bar" => "value2", "baz" => "value3", "true" => true, "false" => false }
14 end
15 end
16 EOM
17
18 agents.each do |agent|
19 step "Agent #{agent}: create a structured custom fact" do
20 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
21 custom_fact = File.join(custom_dir, 'custom_fact.rb')
22 agent.mkdir_p(custom_dir)
23 create_remote_file(agent, custom_fact, content)
24 agent.chmod('+x', custom_fact)
25
26 teardown do
27 agent.rm_rf(custom_fact)
28 end
29
30 step "Agent #{agent}: retrieve output using the --json option" do
31 on(agent, facter("--custom-dir \"#{custom_dir}\" --json structured_fact")) do
32 begin
33 expected = {"structured_fact" => {"foo" => {"nested" => "value1"}, "bar" => "value2", "baz" => "value3", "true" => true, "false" => false}}
34 assert_equal(expected, JSON.parse(stdout.chomp), "JSON output does not match expected output")
35 rescue
36 fail_test "Couldn't parse output as JSON"
37 end
38 end
39 end
40 end
41 end
42 end
0 # This tests is intended to verify that passing the `--list-block-groups` flag
1 # will cause the names of blockable resolvers to be printed to stdout. It should not list
2 # any resolver name that is not blockable.
3 test_name "C99969: the `--list-block-groups` command line flag prints available block groups to stdout" do
4 tag 'risk:high'
5
6 agents.each do |agent|
7 step "the EC2 blockgroup should be listed" do
8 on(agent, facter("--list-block-groups")) do |facter_output|
9 assert_match(/EC2/, facter_output.stdout, "Expected the EC2 group to be listed")
10 assert_match(/ec2_metadata/, facter_output.stdout, "Expected the EC2 group's facts to be listed")
11 end
12 end
13 end
14 end
0 # This tests is intended to verify that passing the `--list-cache-groups` flag
1 # will cause the names of cacheable resolvers to be printed to stdout.
2 test_name "C99970: the `--list-cache-groups` command line flag prints available cache groups to stdout" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 external_dir = agent.tmpdir('external_dir')
10 etc_factsd_dir = get_etc_factsd_dir(agent['platform'])
11 filename = "test.yaml"
12 etc_factsd_path = "#{etc_factsd_dir}/#{filename}"
13
14 teardown do
15 agent.rm_rf(external_dir)
16 agent.rm_rf(etc_factsd_path)
17 end
18
19 step "the various cache groups should be listed" do
20 on(agent, facter("--list-cache-groups")) do |facter_output|
21 assert_match(/EC2/, facter_output.stdout, "EC2 group should be listed as cacheable")
22 assert_match(/ec2_metadata/, facter_output.stdout, "EC2 group's facts should be listed")
23 assert_match(/kernel/, facter_output.stdout, "kernel group should be listed as cacheable")
24 assert_match(/kernelversion/, facter_output.stdout, "kernel group's facts should be listed as cacheable")
25 end
26 end
27
28 step "the various external facts file should be visible as caching groups" do
29 external_filename = "external_facts_filename"
30 ext = get_external_fact_script_extension(agent['platform'])
31 external_fact_script = File.join(external_dir, "#{external_filename}#{ext}")
32 create_remote_file(agent, external_fact_script, external_fact_content(agent['platform'], "a", "b"))
33 agent.chmod('+x', external_fact_script)
34
35 external_fact_script_txt = File.join(external_dir, "#{external_filename}.txt")
36 create_remote_file(agent, external_fact_script_txt, '')
37
38 external_fact_script_json = File.join(external_dir, "#{external_filename}.json")
39 create_remote_file(agent, external_fact_script_json, '')
40
41 external_fact_script_yaml = File.join(external_dir, "#{external_filename}.yaml")
42 create_remote_file(agent, external_fact_script_yaml, '')
43
44 on(agent, facter("--external-dir \"#{external_dir}\" --list-cache-groups")) do |facter_output|
45 assert_match(/#{external_filename}#{ext}/, facter_output.stdout, "external facts script files should be listed as cacheable")
46 assert_match(/#{external_filename}.txt/, facter_output.stdout, "external facts txt files should be listed as cacheable")
47 assert_match(/#{external_filename}.json/, facter_output.stdout, "external facts json files should be listed as cacheable")
48 assert_match(/#{external_filename}.yaml/, facter_output.stdout, "external facts yaml files should be listed as cacheable")
49 end
50 agent.rm_rf(external_dir)
51 end
52
53 step "external facts groups should be listed only without --no-external-facts" do
54 agent.mkdir_p(etc_factsd_dir)
55 create_remote_file(agent, etc_factsd_path, 'test_fact: test_value')
56 on(agent, facter("--list-cache-groups")) do |facter_output|
57 assert_match(/#{filename}/, facter_output.stdout, "external facts script files should be listed as cacheable")
58 end
59 on(agent, facter("--list-cache-groups --no-external-facts")) do |facter_output|
60 assert_no_match(/#{filename}/, facter_output.stdout, "external facts script files should now be listed as cacheable when --no-external-facts is used")
61 end
62 agent.rm_rf(etc_factsd_path)
63 end
64 end
65 end
0 # This test is intended to ensure that the --log-level command-line option works
1 # properly. This option can be used with an argument to specify the level of logging
2 # which will present in Facter's output.
3 test_name "C99985: --log-level command-line option can be used to specify logging level" do
4
5 agents.each do |agent|
6 step "Agent #{agent}: retrieve debug info from stderr using `--log-level debug` option" do
7 on(agent, facter('--log-level debug')) do
8 assert_match(/DEBUG/, stderr, "Expected DEBUG information in stderr")
9 end
10 end
11 end
12 end
0 # This tests is intended to verify that passing the `--no-block` command to facter will prevent
1 # fact blocking, despite a blocklist being specified in the config file.
2 test_name "C99971: the `--no-block` command line flag prevents facts from being blocked" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 # default facter.conf
10 facter_conf_default_dir = get_default_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
11 facter_conf_default_path = File.join(facter_conf_default_dir, "facter.conf")
12
13 teardown do
14 agent.rm_rf(facter_conf_default_dir)
15 end
16
17 # create the directories
18 agent.mkdir_p(facter_conf_default_dir)
19
20 step "Agent #{agent}: create config file" do
21 create_remote_file(agent, facter_conf_default_path, <<-FILE)
22 cli : { debug : true }
23 facts : { blocklist : [ "EC2" ] }
24 FILE
25 end
26
27 step "no facts should be blocked when `--no-block` is specified" do
28 on(agent, facter("--no-block")) do |facter_output|
29 assert_no_match(/blocking collection of .+ facts/, facter_output.stderr, "Expected no facts to be blocked")
30 end
31 end
32 end
33 end
0 # This test is intended to verify that the `--no-cache` command line flag will
1 # cause facter to not do any caching of facts
2 test_name "C99968: --no-cache command-line option causes facter to not cache facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # the uptime fact should be resolvable on ALL systems
9 # Note: do NOT use the kernel fact, as it is used to configure the tests
10 cached_fact_name = "uptime"
11 config = <<-FILE
12 cli : { debug : true }
13 facts : { ttls : [ { "#{cached_fact_name}" : 30 minutes } ] }
14 FILE
15
16 agents.each do |agent|
17 kernel_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
18 config_dir = get_default_fact_dir(agent['platform'], kernel_version)
19 config_file = File.join(config_dir, "facter.conf")
20
21 cached_facts_dir = get_cached_facts_dir(agent['platform'], kernel_version)
22 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
23
24 agent.rm_rf(cached_fact_file)
25
26 teardown do
27 agent.rm_rf(config_dir)
28 agent.rm_rf(cached_facts_dir)
29 end
30
31 step "Agent #{agent}: create config file in default location" do
32 agent.mkdir_p(config_dir)
33 create_remote_file(agent, config_file, config)
34 end
35
36 step "facter should not cache facts when --no-cache is specified" do
37 on(agent, facter("--no-cache")) do |facter_output|
38 assert_no_match(/caching values for/, facter_output.stderr, "facter should not have tried to cache any facts")
39 end
40 end
41 end
42 end
0 # This test is intended to verify that the `--no-cache` command line flag will
1 # cause facter to not load already cached facts
2 test_name "C100123: --no-cache command-line option does not load facts from the cache" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # the uptime fact should be resolvable on ALL systems
9 # Note: do NOT use the kernel fact, as it is used to configure the tests
10 cached_fact_name = "uptime"
11 bad_cached_fact_value = "CACHED_FACT_VALUE"
12 bad_cached_content = <<EOM
13 {
14 "#{cached_fact_name}": "fake #{bad_cached_fact_value}"
15 }
16 EOM
17
18
19 config = <<-FILE
20 cli : { debug : true }
21 facts : { ttls : [ { "#{cached_fact_name}" : 30 minutes } ] }
22 FILE
23
24 agents.each do |agent|
25 kernel_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
26 config_dir = get_default_fact_dir(agent['platform'], kernel_version)
27 config_file = File.join(config_dir, "facter.conf")
28
29 cached_facts_dir = get_cached_facts_dir(agent['platform'], kernel_version)
30 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
31
32 teardown do
33 agent.rm_rf(config_dir)
34 agent.rm_rf(cached_facts_dir)
35 end
36
37 step "Agent #{agent}: create config file in default location" do
38 agent.mkdir_p(config_dir)
39 create_remote_file(agent, config_file, config)
40 end
41
42 step "facter should not load facts from the cache when --no-cache is specified" do
43 # clear the fact cache
44 agent.rm_rf(cached_facts_dir)
45
46 # run once to cache the uptime fact
47 on(agent, facter(""))
48 # override cached content
49 create_remote_file(agent, cached_fact_file, bad_cached_content)
50
51 on(agent, facter("--no-cache #{cached_fact_name}")) do |facter_output|
52 assert_no_match(/loading cached values for .+ fact/, facter_output.stderr, "facter should not have tried to load any cached facts")
53 assert_no_match(/#{bad_cached_fact_value}/, facter_output.stdout, "facter should not have loaded the cached value")
54 end
55 end
56 end
57 end
0 # This test is intended to verify that the `--no-cache` command line flag will
1 # cause facter to not refresh a cached fact that is expired
2 test_name "C100124: --no-cache does not refresh expired cached facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 # the uptime fact should be resolvable on ALL systems
9 # Note: do NOT use the kernel fact, as it is used to configure the tests
10 cached_fact_name = "uptime"
11 bad_cached_fact_value = "CACHED_FACT_VALUE"
12 bad_cached_content = <<EOM
13 {
14 "#{cached_fact_name}": "fake #{bad_cached_fact_value}"
15 }
16 EOM
17
18 config = <<-FILE
19 cli : { debug : true }
20 facts : { ttls : [ { "#{cached_fact_name}" : 30 minutes } ] }
21 FILE
22
23 agents.each do |agent|
24 kernel_version = on(agent, facter('kernelmajversion')).stdout.chomp.to_f
25 config_dir = get_default_fact_dir(agent['platform'], kernel_version)
26 config_file = File.join(config_dir, "facter.conf")
27
28 cached_facts_dir = get_cached_facts_dir(agent['platform'], kernel_version)
29 cached_fact_file = File.join(cached_facts_dir, cached_fact_name)
30
31 teardown do
32 agent.rm_rf(config_dir)
33 agent.rm_rf(cached_facts_dir)
34 end
35
36 step "Agent #{agent}: create config file in default location" do
37 agent.mkdir_p(config_dir)
38 create_remote_file(agent, config_file, config)
39 end
40
41 step "facter should not refresh an expired cache when --no-cache is specified" do
42 # clear the fact cache
43 agent.rm_rf(cached_facts_dir)
44
45 # run once to cache the uptime fact
46 on(agent, facter(""))
47
48 # override cached content
49 create_remote_file(agent, cached_fact_file, bad_cached_content)
50 # update the modify time on the new cached fact to prompt a refresh
51 agent.modified_at(cached_fact_file, '198001010000')
52
53 on(agent, facter("--no-cache")) do |facter_output|
54 assert_no_match(/caching values for/, facter_output.stderr, "facter should not have tried to refresh the cache")
55 end
56
57 cat_output = agent.cat(cached_fact_file)
58 assert_match(/#{bad_cached_content.chomp}/, cat_output.strip, "facter should not have updated the cached value")
59 end
60 end
61 end
0 # This test is intended to ensure with --debug and --no-color, facter does not send any escape sequences
1 # to colorize the output
2 test_name "C99975: --debug and --no-color command-line options should print DEBUG messages without color escape sequences" do
3 tag 'risk:high'
4
5 confine :except, :platform => 'windows' # On windows we don't get an escape sequence so we can't detect a color change
6
7 agents.each do |agent|
8 step "Agent #{agent}: retrieve debug info from stderr using --debug anod --no-color options" do
9 on(agent, facter('--debug --no-color')) do |facter_output|
10 assert_match(/DEBUG/, facter_output.stderr, "Expected DEBUG information in stderr")
11 refute_match(/\e\[0;/, facter_output.stderr, "Expected to output to not contain an escape sequence")
12 end
13 end
14 end
15 end
0 # This test verifies that --no-custom-facts does not load custom facts
1 test_name "C64171: custom fact command line option --no-custom-facts does not load custom facts" do
2 tag 'risk:med'
3
4 require 'facter/acceptance/user_fact_utils'
5 extend Facter::Acceptance::UserFactUtils
6
7 content = <<EOM
8 Facter.add('custom_fact') do
9 setcode do
10 "testvalue"
11 end
12 end
13 EOM
14
15 agents.each do |agent|
16 step "Agent #{agent}: create custom fact directory and custom fact" do
17 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
18 agent.mkdir_p(custom_dir)
19 custom_fact = File.join(custom_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21
22 teardown do
23 agent.rm_rf(custom_fact)
24 end
25
26 step "Agent #{agent}: --no-custom-facts option should not load custom facts" do
27 on(agent, facter("--no-custom-facts custom_fact")) do |facter_output|
28 assert_equal("", facter_output.stdout.chomp, "Custom fact should not have resolved")
29 end
30 end
31 end
32 end
33 end
0 # This test verifies that calling facter with both --no-custom-facts and --custom-dir results
1 # in an options conflict error
2 test_name "C100001: custom fact commandline options --no-custom-facts together with --custom-dir should produce an error" do
3 tag 'risk:high'
4
5 agents.each do |agent|
6 custom_dir = agent.tmpdir('custom_dir')
7
8 teardown do
9 agent.rm_rf(custom_dir)
10 end
11
12 step "Agent #{agent}: --no-custom-facts and --custom-dir options should result in a error" do
13 on(agent, facter("--no-custom-facts --custom-dir '#{custom_dir}'"), :acceptable_exit_codes => 1) do |facter_output|
14 assert_match(/options conflict/, facter_output.stderr.chomp, "Output does not contain error string")
15 end
16 end
17 end
18 end
0 # This test verifies that --no-custom-facts keeps facter from loading facts from the environment
1 # variable FACTERLIB
2 test_name "C100000: custom fact commandline options --no-custom-facts does not load from FACTERLIB" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content = <<EOM
9 Facter.add('custom_fact') do
10 setcode do
11 "testvalue"
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create a custom fact directory and fact" do
18 facterlib_dir = agent.tmpdir('facterlib')
19 custom_fact = File.join(facterlib_dir, 'custom_fact.rb')
20 create_remote_file(agent, custom_fact, content)
21
22 teardown do
23 agent.rm_rf(facterlib_dir)
24 end
25
26 step "Agent #{agent}: --no-custom-facts should ignore the FACTERLIB environment variable" do
27 on(agent, facter('--no-custom-facts custom_fact', :environment => { 'FACTERLIB' => facterlib_dir })) do |facter_output|
28 assert_equal("", facter_output.stdout.chomp, "Custom fact in FACTERLIB should not have resolved")
29 end
30 end
31 end
32 end
33 end
0 # This tests verifies that when --no-custom-facts is used we do not look for
1 # 'facter' subdirectories in the $LOAD_PATH
2 #
3 # Facter searches all directories in the Ruby $LOAD_PATH variable for subdirectories
4 # named ‘facter’, and loads all Ruby files in those directories.
5 test_name "C100003: custom fact commandline options --no-custom-facts does not load $LOAD_PATH facter directories" do
6 confine :except, :platform => 'cisco_nexus' # see BKR-749
7 tag 'risk:high'
8
9 require 'puppet/acceptance/common_utils'
10 extend Puppet::Acceptance::CommandUtils
11
12 require 'facter/acceptance/user_fact_utils'
13 extend Facter::Acceptance::UserFactUtils
14
15 content = <<EOM
16 Facter.add('custom_fact') do
17 setcode do
18 "testvalue"
19 end
20 end
21 EOM
22
23 agents.each do |agent|
24 step("Agent #{agent}: determine the load path and create a custom facter directory on it") do
25 on(agent, "#{ruby_command(agent)} -e 'puts $LOAD_PATH[0]'")
26 load_path_facter_dir = File.join(stdout.chomp, 'facter')
27 agent.mkdir_p(load_path_facter_dir)
28 custom_fact = File.join(load_path_facter_dir, 'custom_fact.rb')
29 create_remote_file(agent, custom_fact, content)
30
31 teardown do
32 load_path_facter_dir = "\"#{load_path_facter_dir}\"" if agent.is_cygwin?
33
34 agent.rm_rf(load_path_facter_dir)
35 end
36
37 step("Agent #{agent}: using --no-custom-facts should not resolve facts on the $LOAD_PATH") do
38 on(agent, facter("--no-custom-facts custom_fact")) do |facter_output|
39 assert_equal("", facter_output.stdout.chomp, "Custom fact in $LOAD_PATH/facter should not have resolved")
40 end
41 end
42 end
43 end
44 end
0 # This test verifies that --no-external-facts does not load external facts
1
2 test_name "C99961: external fact command line option --no-external-facts does not load external facts" do
3 tag 'risk:high'
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 agents.each do |agent|
9 step "Agent #{agent}: create external fact directory and external fact" do
10 external_dir = agent.tmpdir('external_dir')
11 ext = get_external_fact_script_extension(agent['platform'])
12 external_fact = File.join(external_dir, "external_fact#{ext}")
13 create_remote_file(agent, external_fact, external_fact_content(agent['platform'], 'external_fact', 'external_value'))
14 agent.chmod('+x', external_fact)
15
16 teardown do
17 agent.rm_rf(external_dir)
18 end
19
20 step "Agent #{agent}: --no-external-facts option should not load external facts" do
21 on(agent, facter("--no-external-facts external_fact")) do |facter_output|
22 assert_equal("", facter_output.stdout.chomp, "External fact should not have resolved")
23 end
24 end
25 end
26 end
27 end
0 # This test verifies that calling facter with both --no-external-facts and --external-dir results
1 # in an options conflict error
2 test_name "C100002: external fact commandline options --no-external-facts together with --external-dir should produce an error" do
3 tag 'risk:high'
4
5 agents.each do |agent|
6 external_dir = agent.tmpdir('external_dir')
7
8 teardown do
9 agent.rm_rf(external_dir)
10 end
11
12 step "Agent #{agent}: --no-external-facts and --external-dir options should result in a error" do
13 on(agent, facter("--no-external-facts --external-dir '#{external_dir}'"), :acceptable_exit_codes => 1) do |facter_output|
14 assert_match(/options conflict/, facter_output.stderr.chomp, "Output does not contain error string")
15 end
16 end
17 end
18 end
0 # These tests are intended to ensure that the --no-ruby command-line option
1 # works properly. The first ensures that the built in Ruby fact does not resolve
2 # when using the --no-ruby fact, and also checks that the 'No Ruby' warning does
3 # not appear in stderr. The second test ensures that custom facts are not resolved
4 # when the --no-ruby option is present.
5 test_name "C99987: --no-ruby commandline option" do
6
7 require 'facter/acceptance/user_fact_utils'
8 extend Facter::Acceptance::UserFactUtils
9
10 content = <<EOM
11 Facter.add('custom_fact') do
12 setcode do
13 "testvalue"
14 end
15 end
16 EOM
17
18 agents.each do |agent|
19 step "--no-ruby option should disable Ruby and facts requiring ruby from being loaded" do
20 on(agent, facter("--no-ruby ruby")) do
21 assert_equal("", stdout.chomp, "Expected Ruby and Ruby fact to be disabled, but got output: #{stdout.chomp}")
22 assert_equal("", stderr.chomp, "Expected no warnings about Ruby on stderr, but got output: #{stderr.chomp}")
23 end
24 end
25
26 step "--no-ruby option should disable custom facts" do
27 step "Agent #{agent}: create custom fact directory and custom fact" do
28 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
29 agent.mkdir_p(custom_dir)
30 custom_fact = File.join(custom_dir, 'custom_fact.rb')
31 create_remote_file(agent, custom_fact, content)
32
33 teardown do
34 agent.rm_rf(custom_fact)
35 end
36
37 on(agent, facter('--no-ruby custom_fact', :environment => { 'FACTERLIB' => custom_dir })) do
38 assert_equal("", stdout.chomp, "Expected custom fact to be disabled while using --no-ruby option, but it resolved as #{stdout.chomp}")
39 end
40 end
41 end
42 end
43 end
0 # Verify that -p loads external and custom facts from puppet locations
1 test_name "C14783: facter -p loads facts from puppet" do
2 tag 'risk:high'
3
4 confine :to, :platform => /Skipped/
5
6 agents.each do |agent|
7 external_dir = agent.puppet['pluginfactdest']
8 external_file = File.join(external_dir, "external.txt")
9 custom_dir = File.join(agent.puppet['plugindest'], "facter")
10 custom_file = File.join(custom_dir, 'custom.rb')
11
12 teardown do
13 agent.rm_rf(external_file)
14 agent.rm_rf(custom_dir)
15 end
16
17 step "Agent #{agent}: create external fact" do
18 agent.mkdir_p(external_dir)
19 create_remote_file(agent, external_file, "external=external")
20 end
21
22 step "Agent #{agent}: create custom fact" do
23 agent.mkdir_p(custom_dir)
24 create_remote_file(agent, custom_file, "Facter.add(:custom) { setcode { 'custom' } }")
25 end
26
27 step "Agent #{agent}: verify facts" do
28 on(agent, facter("-p external")) do |facter_output|
29 assert_equal("external", facter_output.stdout.chomp)
30 end
31
32 on(agent, facter("-p custom")) do |facter_output|
33 assert_equal("custom", facter_output.stdout.chomp)
34 end
35 end
36 end
37 end
0 test_name "--sequential argument does not generate any errors" do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 step "--sequential should generate no errors" do
5 on(agent, facter("--sequential --debug"), :acceptable_exit_codes => 0) do |facter_output|
6 assert_match(/Resolving facts sequentially/, facter_output.stderr, "Resolving facts sequentially")
7 end
8 end
9 end
10 end
0 # This test is intended to demonstrate that the `--show-legacy` command line option
1 # causes hidden old facts to appear in the fact list.
2 test_name "C87580: --show-legacy command-line option results in output with legacy (hidden) facts" do
3
4 agents.each do |agent|
5 step "Agent #{agent}: retrieve legacy output using a hash" do
6 on(agent, facter("--show-legacy")) do
7 assert_match(/^rubyversion => [0-9]+\.[0-9]+\.[0-9]+$/, stdout.chomp, 'hash legacy output does not contain legacy fact rubyversion')
8 end
9 end
10
11 step "Agent #{agent}: retrieve legacy output using the --json option" do
12 on(agent, facter("--show-legacy --json")) do
13 assert_match(/^ "rubyversion": "[0-9]+\.[0-9]+\.[0-9]+",$/, stdout.chomp, 'json legacy output does not contain legacy fact rubyversion')
14 end
15 end
16 end
17 end
0 test_name 'C98098: --strict flag returns errors on non-existent facts' do
1 tag 'risk:high'
2
3 agents.each do |agent|
4 step 'facter should return exit code 1 for querying non-existing-fact with --strict flag' do
5 on(agent, facter('non-existing-fact --strict'), :acceptable_exit_codes => 1) do |facter_output|
6 assert_match(/ERROR\s+.* - .*fact "non-existing-fact" does not exist/, facter_output.stderr, 'Unexpected error was detected!')
7 end
8 end
9 end
10 end
0 # This test is intended to ensure that the --trace command-line option works
1 # properly. This option provides backtraces for erroring custom Ruby facts.
2 # To test, we try to resolve an erroneous custom fact and catch the backtrace.
3 test_name "C99982: --trace command-line option enables backtraces for custom facts" do
4
5 require 'facter/acceptance/user_fact_utils'
6 extend Facter::Acceptance::UserFactUtils
7
8 content = <<EOM
9 Facter.add('custom_fact_trace') do
10 setcode do
11 non_existent_value
12 end
13 end
14 EOM
15
16 agents.each do |agent|
17 step "Agent #{agent}: create custom fact directory and executable custom fact" do
18 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
19 agent.mkdir_p(custom_dir)
20 custom_fact = File.join(custom_dir, 'custom_fact.rb')
21 create_remote_file(agent, custom_fact, content)
22 agent.chmod('+x', custom_fact)
23
24 teardown do
25 agent.rm_rf(custom_fact)
26 end
27
28 step "--trace option should provide a backtrace for a custom fact with errors" do
29 on(agent, facter("--custom-dir \"#{custom_dir}\" --trace custom_fact_trace"), :acceptable_exit_codes => [1]) do
30 assert_match(/backtrace:\s+#{custom_fact}/, stderr, "Expected a backtrace for erroneous custom fact")
31 end
32 end
33 end
34 end
35 end
0 # This test is intended to ensure that the --verbose command-line option
1 # works properly. This option provides verbose (INFO) output to stderr.
2 test_name "C99986: --verbose command-line option prints verbose information to stderr" do
3
4 agents.each do |agent|
5 step "Agent #{agent}: retrieve verbose info from stderr using --verbose option" do
6 on(agent, facter('--verbose')) do
7 assert_match(/INFO .*executed with command line: --verbose/, stderr, "Expected stderr to contain verbose (INFO) statements")
8 end
9 end
10 end
11 end
0 # This test is intended to ensure that the --version command-line option works
1 # properly. This option outputs the current Facter version.
2 test_name "C99983: --version command-line option returns the version string" do
3
4 agents.each do |agent|
5 step "Agent #{agent}: retrieve version info using the --version option" do
6 on(agent, facter('--version')) do
7 assert_match(/\d+\.\d+\.\d+/, stdout, "Output #{stdout} is not a recognized version string")
8 end
9 end
10 end
11 end
0 # This test is intended to ensure that the --yaml command-line option works
1 # properly. This option causes Facter to output facts in YAML format.
2 # A custom fact is used to test for parity between Facter's output and
3 # the expected YAML output.
4 test_name "C99967: --yaml command-line option results in valid YAML output" do
5
6 require 'yaml'
7 require 'facter/acceptance/user_fact_utils'
8 extend Facter::Acceptance::UserFactUtils
9
10 content = <<EOM
11 Facter.add('structured_fact') do
12 setcode do
13 { "foo" => {"nested" => "value1"}, "bar" => "value2", "baz" => "value3" }
14 end
15 end
16 EOM
17
18 agents.each do |agent|
19 step "Agent #{agent}: create a structured custom fact" do
20 custom_dir = get_user_fact_dir(agent['platform'], on(agent, facter('kernelmajversion')).stdout.chomp.to_f)
21 custom_fact = File.join(custom_dir, 'custom_fact.rb')
22 agent.mkdir_p(custom_dir)
23 create_remote_file(agent, custom_fact, content)
24 agent.chmod('+x', custom_fact)
25
26 teardown do
27 agent.rm_rf(custom_fact)
28 end
29
30 step "Agent #{agent}: retrieve output using the --yaml option" do
31 on(agent, facter("--custom-dir \"#{custom_dir}\" --yaml structured_fact")) do
32 begin
33 expected = {"structured_fact" => {"foo" => {"nested" => "value1"}, "bar" => "value2", "baz" => "value3" }}
34 assert_equal(expected, YAML.load(stdout), "YAML output does not match expected output")
35 rescue
36 fail_test "Couldn't parse output as YAML"
37 end
38 end
39 end
40 end
41 end
42 end
0 test_name 'facter should not update it`s session cache in same session' do
1 tag 'risk:high'
2
3 fact_content = <<-EOM
4 require 'facter'
5
6 seconds_before = Facter.value('system_uptime.seconds')
7 sleep(3)
8 seconds_after = Facter.value('system_uptime.seconds')
9
10 puts seconds_before == seconds_after
11 EOM
12
13 agents.each do |agent|
14 fact_dir = agent.tmpdir('test_scripts')
15 script_path = File.join(fact_dir, 'session_test.rb')
16 create_remote_file(agent, script_path, fact_content)
17
18 teardown do
19 agent.rm_rf(script_path)
20 end
21
22 on(agent, "#{ruby_command(agent)} #{script_path}") do |ruby_result|
23 assert_equal('true', ruby_result.stdout.chomp, 'Expect the session cache is not reset in same session')
24 end
25 end
26 end
0 test_name 'C93827: facter fqdn should return the hostname when its a fully qualified domain name' do
1 tag 'risk:high'
2 require 'timeout'
3
4 confine :except, :platform => 'windows'
5
6 fqdn = 'foo.bar.example.org'
7 fqdn_long = 'a23456789.b23456789.c23456789.d23456789.e23456789.f23456789.wxyz'
8
9 agents.each do |agent|
10 orig_hostname = on(agent, 'hostname').stdout.chomp
11
12 teardown do
13 step 'restore original hostname' do
14 on(agent, "hostname #{orig_hostname}")
15 end
16 end
17
18 step "set hostname as #{fqdn}" do
19 on(agent, "hostname #{fqdn}")
20 begin
21 Timeout.timeout(20) do
22 until on(agent, 'hostname').stdout =~ /#{fqdn}/
23 sleep(0.25) # on Solaris 11 hostname returns before the hostname is updated
24 end
25 end
26 rescue Timeout::Error
27 raise "Failed to reset the hostname of the test machine to #{fqdn}"
28 end
29 end
30
31 step 'validate facter uses hostname as the fqdn if its a fully qualified domain name' do
32 on(agent, 'facter fqdn') do |facter_output|
33 assert_equal(fqdn, facter_output.stdout.chomp, 'facter did not return the hostname set by the test')
34 end
35 end
36 end
37
38 step "long hostname as #{fqdn_long}" do
39 on(agent, "hostname #{fqdn_long}")
40 begin
41 Timeout.timeout(20) do
42 until on(agent, 'hostname').stdout =~ /#{fqdn_long}/
43 sleep(0.25) # on Solaris 11 hostname returns before the hostname is updated
44 end
45 end
46 rescue Timeout::Error
47 raise "Failed to reset the hostname of the test machine to #{fqdn_long}"
48 end
49 end
50
51 step 'validate facter uses hostname as the LONG fqdn if its a fully qualified domain name' do
52 on(agent, 'facter fqdn') do |facter_output|
53 assert_equal(fqdn_long, facter_output.stdout.chomp, 'facter did not return the hostname set by the test')
54 end
55 end
56 end
0 # frozen_string_literal: true
1
2 lib = File.expand_path('../lib', __dir__)
3 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5 Gem::Specification.new do |spec|
6 spec.name = 'facter-ng'
7 spec.version = '4.2.13'
8 spec.authors = ['Puppet']
9 spec.email = ['team-nw@puppet.com']
10 spec.homepage = 'https://github.com/puppetlabs/facter'
11
12 spec.summary = 'Facter, a system inventory tool'
13 spec.description = 'You can prove anything with facts!'
14 spec.license = 'Apache-2.0'
15
16 root_dir = File.join(__dir__, '..')
17 dirs =
18 Dir[File.join(root_dir, 'bin/facter-ng')] +
19 Dir[File.join(root_dir, 'LICENSE')] +
20 Dir[File.join(root_dir, 'lib/**/*.rb')] +
21 Dir[File.join(root_dir, 'lib/**/*.json')] +
22 Dir[File.join(root_dir, 'lib/**/*.conf')] +
23 Dir[File.join(root_dir, 'agent/**/*')] +
24 Dir[File.join(root_dir, 'lib/**/*.erb')]
25 base = "#{root_dir}#{File::SEPARATOR}"
26
27 spec.files = dirs.map { |path| path.sub(base, '') }
28
29 spec.required_ruby_version = '>= 2.3', '< 4.0'
30
31 spec.bindir = 'bin'
32 spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
33 spec.require_paths = ['agent/lib', 'lib']
34
35 spec.add_development_dependency 'bundler', '~> 2.0'
36 spec.add_development_dependency 'coveralls', '~> 0.8.23'
37 spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
38 spec.add_development_dependency 'rspec', '~> 3.0'
39 spec.add_development_dependency 'rubocop', '~> 0.74.0'
40 spec.add_development_dependency 'rubycritic', '~> 4.1.0'
41
42 spec.add_runtime_dependency 'ffi', '~> 1.9'
43 spec.add_runtime_dependency 'hocon', '~> 1.3'
44 spec.add_runtime_dependency 'sys-filesystem', '~> 1.3' unless Gem.win_platform?
45 spec.add_runtime_dependency 'thor', ['>= 1.0.1', '< 2.0']
46 end
0 # frozen_string_literal: true
1
2 require 'pathname'
3
4 ROOT_DIR = Pathname.new(File.expand_path('../../', __dir__)) unless defined?(ROOT_DIR)
5
6 require "#{ROOT_DIR}/lib/facter"
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'pathname'
4 require 'facter/framework/cli/cli_launcher.rb'
5
6 Facter::OptionsValidator.validate(ARGV)
7 processed_arguments = CliLauncher.prepare_arguments(ARGV)
8
9 CliLauncher.start(processed_arguments)
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'facter/framework/cli/cli_launcher.rb'
4
5 Facter::OptionsValidator.validate(ARGV)
6 processed_arguments = CliLauncher.prepare_arguments(ARGV)
7
8 CliLauncher.start(processed_arguments)
+0
-6
cmake/FindBLKID.cmake less more
0 include(FindDependency)
1 find_dependency(BLKID DISPLAY "blkid" HEADERS "blkid/blkid.h" LIBRARIES "libblkid.so.1" "blkid")
2
3 include(FeatureSummary)
4 set_package_properties(BLKID PROPERTIES DESCRIPTION "The library for the Linux blkid utility" URL "http://en.wikipedia.org/wiki/Util-linux")
5 set_package_properties(BLKID PROPERTIES TYPE OPTIONAL PURPOSE "Enables the partitions fact on Linux.")
+0
-6
cmake/FindCPPHOCON.cmake less more
0 include(FindDependency)
1 find_dependency(CPPHOCON DISPLAY "cpp-hocon" HEADERS "hocon/config.hpp" LIBRARIES "libcpp-hocon.a")
2
3 include(FeatureSummary)
4 set_package_properties(CPPHOCON PROPERTIES DESCRIPTION "A C++ parser for the HOCON configuration language" URL "https://github.com/puppetlabs/cpp-hocon")
5 set_package_properties(CPPHOCON PROPERTIES TYPE REQUIRED PURPOSE "Allows parsing of the Facter config file.")
+0
-54
cmake/FindDependency.cmake less more
0 # A function for finding dependencies
1 function(find_dependency)
2 include(CMakeParseArguments)
3 cmake_parse_arguments(FIND_DEPENDENCY "" "DISPLAY" "HEADERS;LIBRARIES" ${ARGN})
4
5 set(FIND_DEPENDENCY_NAME ${ARGV0})
6
7 # Setup the include path hint
8 if (${FIND_DEPENDENCY_NAME}_INCLUDEDIR)
9 set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_INCLUDEDIR}")
10 elseif (${FIND_DEPENDENCY_NAME}_ROOT)
11 set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/include" "${${FIND_DEPENDENCY_NAME}_ROOT}")
12 endif()
13
14 # Setup the library path hint
15 if (${FIND_DEPENDENCY_NAME}_LIBRARYDIR)
16 set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_LIBRARYDIR}")
17 elseif (${FIND_DEPENDENCY_NAME}_ROOT)
18 set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/lib")
19 endif()
20
21 # Find headers and libraries
22 find_path(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR NAMES ${FIND_DEPENDENCY_HEADERS} HINTS ${INCLUDE_HINTS})
23 find_library(${FIND_DEPENDENCY_NAME}_LIBRARY NAMES ${FIND_DEPENDENCY_LIBRARIES} HINTS ${LIBRARY_HINTS})
24
25 # Handle the find_package arguments
26 include(FindPackageHandleStandardArgs)
27 find_package_handle_standard_args(${FIND_DEPENDENCY_NAME} "${FIND_DEPENDENCY_DISPLAY} was not found." ${FIND_DEPENDENCY_NAME}_LIBRARY ${FIND_DEPENDENCY_NAME}_INCLUDE_DIR)
28
29 # Set the output variables in the parent's scope
30 if (${FIND_DEPENDENCY_NAME}_FOUND)
31 set(${FIND_DEPENDENCY_NAME}_FOUND ${${FIND_DEPENDENCY_NAME}_FOUND} PARENT_SCOPE)
32
33 # Include dirs
34 set(${FIND_DEPENDENCY_NAME}_INCLUDE_DIRS ${${FIND_DEPENDENCY_NAME}_INCLUDE_DIR} PARENT_SCOPE)
35
36 # Libraries
37 if (${FIND_DEPENDENCY_NAME}_LIBRARY)
38 set(${FIND_DEPENDENCY_NAME}_LIBRARIES ${${FIND_DEPENDENCY_NAME}_LIBRARY} PARENT_SCOPE)
39 else()
40 set(${FIND_DEPENDENCY_NAME}_LIBRARIES "" PARENT_SCOPE)
41 endif()
42
43 # Get the library name
44 get_filename_component(${FIND_DEPENDENCY_NAME}_LIBRARY_DIRS ${${FIND_DEPENDENCY_NAME}_LIBRARY} PATH)
45 set(${FIND_DEPENDENCY_NAME_LIBRARY} ${${FIND_DEPENDENCY_NAME_LIBRARY}} PARENT_SCOPE)
46
47 # Add a define for the found package
48 add_definitions(-DUSE_${FIND_DEPENDENCY_NAME})
49 endif()
50
51 # Advanced options for not cluttering the cmake UIs
52 mark_as_advanced(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR ${FIND_DEPENDENCY_NAME}_LIBRARY)
53 endfunction()
+0
-10
cmake/FindOPENSSL.cmake less more
0 include(FindDependency)
1 if (WIN32)
2 find_dependency(OPENSSL DISPLAY "OpenSSL" HEADERS "openssl/ssl.h" LIBRARIES "crypto" "libeay32" REQUIRED)
3 else()
4 find_dependency(OPENSSL DISPLAY "OpenSSL" HEADERS "openssl/ssl.h" LIBRARIES "crypto" REQUIRED)
5 endif()
6
7 include(FeatureSummary)
8 set_package_properties(OPENSSL PROPERTIES DESCRIPTION "An open-source implementation of the SSL and TLS protocols" URL "https://www.openssl.org/")
9 set_package_properties(OPENSSL PROPERTIES TYPE OPTIONAL PURPOSE "Enables SSH fingerprinting facts on POSIX systems.")
+0
-286
cmake/FindRuby.cmake less more
0 #.rst:
1 # FindRuby
2 # --------
3 #
4 # Find Ruby
5 #
6 # This module finds if Ruby is installed and determines where the
7 # include files and libraries are. Ruby 1.8, 1.9, 2.0 and 2.1 are
8 # supported.
9 #
10 # The minimum required version of Ruby can be specified using the
11 # standard syntax, e.g. find_package(Ruby 1.8)
12 #
13 # It also determines what the name of the library is. This code sets
14 # the following variables:
15 #
16 # ``RUBY_EXECUTABLE``
17 # full path to the ruby binary
18 # ``RUBY_INCLUDE_DIRS``
19 # include dirs to be used when using the ruby library
20 # ``RUBY_LIBRARY``
21 # full path to the ruby library
22 # ``RUBY_VERSION``
23 # the version of ruby which was found, e.g. "1.8.7"
24 # ``RUBY_FOUND``
25 # set to true if ruby ws found successfully
26 #
27 # Also:
28 #
29 # ``RUBY_INCLUDE_PATH``
30 # same as RUBY_INCLUDE_DIRS, only provided for compatibility reasons, don't use it
31
32 #=============================================================================
33 # Copyright 2004-2009 Kitware, Inc.
34 # Copyright 2008-2009 Alexander Neundorf <neundorf@kde.org>
35 #
36 # Distributed under the OSI-approved BSD License (the "License");
37 # see accompanying file Copyright.txt for details.
38 #
39 # This software is distributed WITHOUT ANY WARRANTY; without even the
40 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
41 # See the License for more information.
42 #=============================================================================
43 # (To distribute this file outside of CMake, substitute the full
44 # License text for the above reference.)
45
46 # RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'`
47 # RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'`
48 # RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'`
49 # RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'`
50 # RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'`
51
52 # uncomment the following line to get debug output for this file
53 # set(_RUBY_DEBUG_OUTPUT TRUE)
54
55 # Determine the list of possible names of the ruby executable depending
56 # on which version of ruby is required
57 set(_RUBY_POSSIBLE_EXECUTABLE_NAMES ruby)
58
59 # if 1.9 is required, don't look for ruby18 and ruby1.8, default to version 1.8
60 if(DEFINED Ruby_FIND_VERSION_MAJOR AND DEFINED Ruby_FIND_VERSION_MINOR)
61 set(Ruby_FIND_VERSION_SHORT_NODOT "${Ruby_FIND_VERSION_MAJOR}${RUBY_FIND_VERSION_MINOR}")
62 # we can't construct that if only major version is given
63 set(_RUBY_POSSIBLE_EXECUTABLE_NAMES
64 ruby${Ruby_FIND_VERSION_MAJOR}.${Ruby_FIND_VERSION_MINOR}
65 ruby${Ruby_FIND_VERSION_MAJOR}${Ruby_FIND_VERSION_MINOR}
66 ${_RUBY_POSSIBLE_EXECUTABLE_NAMES})
67 else()
68 set(Ruby_FIND_VERSION_SHORT_NODOT "18")
69 endif()
70
71 if(NOT Ruby_FIND_VERSION_EXACT)
72 list(APPEND _RUBY_POSSIBLE_EXECUTABLE_NAMES ruby2.1 ruby21)
73 list(APPEND _RUBY_POSSIBLE_EXECUTABLE_NAMES ruby2.0 ruby20)
74 list(APPEND _RUBY_POSSIBLE_EXECUTABLE_NAMES ruby1.9 ruby19)
75
76 # if we want a version below 1.9, also look for ruby 1.8
77 if("${Ruby_FIND_VERSION_SHORT_NODOT}" VERSION_LESS "19")
78 list(APPEND _RUBY_POSSIBLE_EXECUTABLE_NAMES ruby1.8 ruby18)
79 endif()
80
81 list(REMOVE_DUPLICATES _RUBY_POSSIBLE_EXECUTABLE_NAMES)
82 endif()
83
84 find_program(RUBY_EXECUTABLE NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES})
85
86 if(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
87 function(_RUBY_CONFIG_VAR RBVAR OUTVAR)
88 execute_process(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['${RBVAR}']"
89 RESULT_VARIABLE _RUBY_SUCCESS
90 OUTPUT_VARIABLE _RUBY_OUTPUT
91 ERROR_QUIET)
92 if(_RUBY_SUCCESS OR _RUBY_OUTPUT STREQUAL "")
93 execute_process(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['${RBVAR}']"
94 RESULT_VARIABLE _RUBY_SUCCESS
95 OUTPUT_VARIABLE _RUBY_OUTPUT
96 ERROR_QUIET)
97 endif()
98 set(${OUTVAR} "${_RUBY_OUTPUT}" PARENT_SCOPE)
99 endfunction()
100
101
102 # query the ruby version
103 _RUBY_CONFIG_VAR("MAJOR" RUBY_VERSION_MAJOR)
104 _RUBY_CONFIG_VAR("MINOR" RUBY_VERSION_MINOR)
105 _RUBY_CONFIG_VAR("TEENY" RUBY_VERSION_PATCH)
106
107 # query the different directories
108 _RUBY_CONFIG_VAR("archdir" RUBY_ARCH_DIR)
109 _RUBY_CONFIG_VAR("arch" RUBY_ARCH)
110 _RUBY_CONFIG_VAR("rubyhdrdir" RUBY_HDR_DIR)
111 _RUBY_CONFIG_VAR("rubyarchhdrdir" RUBY_ARCHHDR_DIR)
112 _RUBY_CONFIG_VAR("libdir" RUBY_POSSIBLE_LIB_DIR)
113 _RUBY_CONFIG_VAR("rubylibdir" RUBY_RUBY_LIB_DIR)
114
115 # site_ruby
116 _RUBY_CONFIG_VAR("sitearchdir" RUBY_SITEARCH_DIR)
117 _RUBY_CONFIG_VAR("sitelibdir" RUBY_SITELIB_DIR)
118
119 # vendor_ruby available ?
120 execute_process(COMMAND ${RUBY_EXECUTABLE} -r vendor-specific -e "print 'true'"
121 OUTPUT_VARIABLE RUBY_HAS_VENDOR_RUBY ERROR_QUIET)
122
123 if(RUBY_HAS_VENDOR_RUBY)
124 _RUBY_CONFIG_VAR("vendorlibdir" RUBY_VENDORLIB_DIR)
125 _RUBY_CONFIG_VAR("vendorarchdir" RUBY_VENDORARCH_DIR)
126 endif()
127
128 # save the results in the cache so we don't have to run ruby the next time again
129 set(RUBY_VERSION_MAJOR ${RUBY_VERSION_MAJOR} CACHE PATH "The Ruby major version" FORCE)
130 set(RUBY_VERSION_MINOR ${RUBY_VERSION_MINOR} CACHE PATH "The Ruby minor version" FORCE)
131 set(RUBY_VERSION_PATCH ${RUBY_VERSION_PATCH} CACHE PATH "The Ruby patch version" FORCE)
132 set(RUBY_ARCH_DIR ${RUBY_ARCH_DIR} CACHE PATH "The Ruby arch dir" FORCE)
133 set(RUBY_HDR_DIR ${RUBY_HDR_DIR} CACHE PATH "The Ruby header dir (1.9+)" FORCE)
134 set(RUBY_ARCHHDR_DIR ${RUBY_ARCHHDR_DIR} CACHE PATH "The Ruby arch header dir (2.0+)" FORCE)
135 set(RUBY_POSSIBLE_LIB_DIR ${RUBY_POSSIBLE_LIB_DIR} CACHE PATH "The Ruby lib dir" FORCE)
136 set(RUBY_RUBY_LIB_DIR ${RUBY_RUBY_LIB_DIR} CACHE PATH "The Ruby ruby-lib dir" FORCE)
137 set(RUBY_SITEARCH_DIR ${RUBY_SITEARCH_DIR} CACHE PATH "The Ruby site arch dir" FORCE)
138 set(RUBY_SITELIB_DIR ${RUBY_SITELIB_DIR} CACHE PATH "The Ruby site lib dir" FORCE)
139 set(RUBY_HAS_VENDOR_RUBY ${RUBY_HAS_VENDOR_RUBY} CACHE BOOL "Vendor Ruby is available" FORCE)
140 set(RUBY_VENDORARCH_DIR ${RUBY_VENDORARCH_DIR} CACHE PATH "The Ruby vendor arch dir" FORCE)
141 set(RUBY_VENDORLIB_DIR ${RUBY_VENDORLIB_DIR} CACHE PATH "The Ruby vendor lib dir" FORCE)
142
143 mark_as_advanced(
144 RUBY_ARCH_DIR
145 RUBY_ARCH
146 RUBY_HDR_DIR
147 RUBY_ARCHHDR_DIR
148 RUBY_POSSIBLE_LIB_DIR
149 RUBY_RUBY_LIB_DIR
150 RUBY_SITEARCH_DIR
151 RUBY_SITELIB_DIR
152 RUBY_HAS_VENDOR_RUBY
153 RUBY_VENDORARCH_DIR
154 RUBY_VENDORLIB_DIR
155 RUBY_VERSION_MAJOR
156 RUBY_VERSION_MINOR
157 RUBY_VERSION_PATCH
158 )
159 endif()
160
161 # In case RUBY_EXECUTABLE could not be executed (e.g. cross compiling)
162 # try to detect which version we found. This is not too good.
163 if(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
164 # by default assume 1.8.0
165 set(RUBY_VERSION_MAJOR 1)
166 set(RUBY_VERSION_MINOR 8)
167 set(RUBY_VERSION_PATCH 0)
168 # check whether we found 1.9.x
169 if(${RUBY_EXECUTABLE} MATCHES "ruby1.?9")
170 set(RUBY_VERSION_MAJOR 1)
171 set(RUBY_VERSION_MINOR 9)
172 endif()
173 # check whether we found 2.0.x
174 if(${RUBY_EXECUTABLE} MATCHES "ruby2.?0")
175 set(RUBY_VERSION_MAJOR 2)
176 set(RUBY_VERSION_MINOR 0)
177 endif()
178 # check whether we found 2.1.x
179 if(${RUBY_EXECUTABLE} MATCHES "ruby2.?1")
180 set(RUBY_VERSION_MAJOR 2)
181 set(RUBY_VERSION_MINOR 1)
182 endif()
183 endif()
184
185 if(RUBY_VERSION_MAJOR)
186 set(RUBY_VERSION "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}")
187 set(_RUBY_VERSION_SHORT "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}")
188 set(_RUBY_VERSION_SHORT_NODOT "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}")
189 set(_RUBY_NODOT_VERSION "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}")
190 endif()
191
192 find_path(RUBY_INCLUDE_DIR
193 NAMES ruby.h
194 HINTS
195 ${RUBY_HDR_DIR}
196 ${RUBY_ARCH_DIR}
197 /usr/lib/ruby/${_RUBY_VERSION_SHORT}/i586-linux-gnu/ )
198
199 set(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIR} )
200
201 # if ruby > 1.8 is required or if ruby > 1.8 was found, search for the config.h dir
202 if( "${Ruby_FIND_VERSION_SHORT_NODOT}" GREATER 18 OR "${_RUBY_VERSION_SHORT_NODOT}" GREATER 18 OR RUBY_HDR_DIR)
203 find_path(RUBY_CONFIG_INCLUDE_DIR
204 NAMES ruby/config.h config.h
205 HINTS
206 ${RUBY_HDR_DIR}/${RUBY_ARCH}
207 ${RUBY_ARCH_DIR}
208 ${RUBY_ARCHHDR_DIR}
209 )
210
211 set(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIRS} ${RUBY_CONFIG_INCLUDE_DIR} )
212 endif()
213
214
215 # Determine the list of possible names for the ruby library
216 set(_RUBY_POSSIBLE_LIB_NAMES ruby ruby-static ruby${_RUBY_VERSION_SHORT} ruby${_RUBY_VERSION_SHORT_NODOT} ruby-${_RUBY_VERSION_SHORT} ruby-${RUBY_VERSION})
217
218 if(WIN32)
219 set( _RUBY_MSVC_RUNTIME "" )
220 if( MSVC60 )
221 set( _RUBY_MSVC_RUNTIME "60" )
222 endif()
223 if( MSVC70 )
224 set( _RUBY_MSVC_RUNTIME "70" )
225 endif()
226 if( MSVC71 )
227 set( _RUBY_MSVC_RUNTIME "71" )
228 endif()
229 if( MSVC80 )
230 set( _RUBY_MSVC_RUNTIME "80" )
231 endif()
232 if( MSVC90 )
233 set( _RUBY_MSVC_RUNTIME "90" )
234 endif()
235
236 set(_RUBY_ARCH_PREFIX "")
237 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
238 set(_RUBY_ARCH_PREFIX "x64-")
239 endif()
240
241 list(APPEND _RUBY_POSSIBLE_LIB_NAMES
242 "${_RUBY_ARCH_PREFIX}msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}"
243 "${_RUBY_ARCH_PREFIX}msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}-static"
244 "${_RUBY_ARCH_PREFIX}msvcrt-ruby${_RUBY_NODOT_VERSION}"
245 "${_RUBY_ARCH_PREFIX}msvcrt-ruby${_RUBY_NODOT_VERSION}-static" )
246 endif()
247
248 find_library(RUBY_LIBRARY NAMES ${_RUBY_POSSIBLE_LIB_NAMES} HINTS ${RUBY_POSSIBLE_LIB_DIR} )
249
250 include(FindPackageHandleStandardArgs)
251 set(_RUBY_REQUIRED_VARS RUBY_EXECUTABLE RUBY_INCLUDE_DIR RUBY_LIBRARY)
252 if(_RUBY_VERSION_SHORT_NODOT GREATER 18)
253 list(APPEND _RUBY_REQUIRED_VARS RUBY_CONFIG_INCLUDE_DIR)
254 endif()
255
256 if(_RUBY_DEBUG_OUTPUT)
257 message(STATUS "--------FindRuby.cmake debug------------")
258 message(STATUS "_RUBY_POSSIBLE_EXECUTABLE_NAMES: ${_RUBY_POSSIBLE_EXECUTABLE_NAMES}")
259 message(STATUS "_RUBY_POSSIBLE_LIB_NAMES: ${_RUBY_POSSIBLE_LIB_NAMES}")
260 message(STATUS "RUBY_ARCH_DIR: ${RUBY_ARCH_DIR}")
261 message(STATUS "RUBY_HDR_DIR: ${RUBY_HDR_DIR}")
262 message(STATUS "RUBY_POSSIBLE_LIB_DIR: ${RUBY_POSSIBLE_LIB_DIR}")
263 message(STATUS "Found RUBY_VERSION: \"${RUBY_VERSION}\" , short: \"${_RUBY_VERSION_SHORT}\", nodot: \"${_RUBY_VERSION_SHORT_NODOT}\"")
264 message(STATUS "_RUBY_REQUIRED_VARS: ${_RUBY_REQUIRED_VARS}")
265 message(STATUS "RUBY_EXECUTABLE: ${RUBY_EXECUTABLE}")
266 message(STATUS "RUBY_LIBRARY: ${RUBY_LIBRARY}")
267 message(STATUS "RUBY_INCLUDE_DIR: ${RUBY_INCLUDE_DIR}")
268 message(STATUS "RUBY_CONFIG_INCLUDE_DIR: ${RUBY_CONFIG_INCLUDE_DIR}")
269 message(STATUS "--------------------")
270 endif()
271
272 FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ruby REQUIRED_VARS ${_RUBY_REQUIRED_VARS}
273 VERSION_VAR RUBY_VERSION )
274
275 mark_as_advanced(
276 RUBY_EXECUTABLE
277 RUBY_LIBRARY
278 RUBY_INCLUDE_DIR
279 RUBY_CONFIG_INCLUDE_DIR
280 )
281
282 # Set some variables for compatibility with previous version of this file
283 set(RUBY_POSSIBLE_LIB_PATH ${RUBY_POSSIBLE_LIB_DIR})
284 set(RUBY_RUBY_LIB_PATH ${RUBY_RUBY_LIB_DIR})
285 set(RUBY_INCLUDE_PATH ${RUBY_INCLUDE_DIRS})
+0
-10
cmake/FindUDEV.cmake less more
0 # based on https://gist.github.com/GabrielNagy/09a25f06a431684bbfa07a736f2191cb
1 # `find_dependency()` is provided by the FindDependency.cmake file.
2 # Facter can build against libudev.h to gather disk facts:
3 # https://github.com/puppetlabs/facter/blob/d5507926aca43adcbcc39f8cc5eeddcd81bc241f/lib/src/facts/linux/disk_resolver.cc#L83-L94
4 include(FindDependency)
5 find_dependency(UDEV DISPLAY "udev" HEADERS "libudev.h" LIBRARIES "udev")
6
7 include(FeatureSummary)
8 set_package_properties(UDEV PROPERTIES DESCRIPTION "A device manager for the Linux kernel" URL "http://www.freedesktop.org/wiki/Software/systemd")
9 set_package_properties(UDEV PROPERTIES PURPOSE "Reports disks serial numbers.")
+0
-6
cmake/FindWHEREAMI.cmake less more
0 include(FindDependency)
1 find_dependency(WHEREAMI DISPLAY "whereami" HEADERS "whereami/whereami.hpp" LIBRARIES "libwhereami.a")
2
3 include(FeatureSummary)
4 set_package_properties(WHEREAMI PROPERTIES DESCRIPTION "A hypervisor detection library" URL "https://github.com/puppetlabs/libwhereami")
5 set_package_properties(WHEREAMI PROPERTIES PURPOSE "Reports hypervisors in use.")
+0
-17
cmake/FindYAMLCPP.cmake less more
0 include(FindDependency)
1
2 if(YAMLCPP_STATIC)
3 set(yaml_lib "libyaml-cpp.a")
4 else()
5 set(yaml_lib "yaml-cpp")
6 endif()
7
8 if (WIN32)
9 find_dependency(YAMLCPP DISPLAY "yaml-cpp" HEADERS "yaml-cpp/yaml.h" LIBRARIES "libyaml-cppmd" "yaml-cpp")
10 else()
11 find_dependency(YAMLCPP DISPLAY "yaml-cpp" HEADERS "yaml-cpp/yaml.h" LIBRARIES ${yaml_lib})
12 endif()
13
14 include(FeatureSummary)
15 set_package_properties(YAMLCPP PROPERTIES DESCRIPTION "A YAML emitter and parser written in C++" URL "https://code.google.com/p/yaml-cpp/")
16 set_package_properties(YAMLCPP PROPERTIES TYPE REQUIRED PURPOSE "Enables support for outputting facts as YAML.")
0 # frozen_string_literal: true
1
2 Facter.add(:my_custom_fact) do
3 has_weight(10_000)
4 setcode do
5 Facter.value('os')
6 end
7 end
0 # frozen_string_literal: true
1
2 Facter.add(:oss) do
3 has_weight(10_000)
4 setcode do
5 'my_custom_os'
6 end
7 end
+0
-39
exe/CMakeLists.txt less more
0 cmake_minimum_required(VERSION 3.2.2)
1
2 set(FACTER_SOURCES
3 facter.cc
4 )
5
6 # Set compiler-specific flags
7 set(CMAKE_CXX_FLAGS ${FACTER_CXX_FLAGS})
8 leatherman_logging_namespace("puppetlabs.facter")
9
10 include_directories(
11 ../lib/inc # the libfacter headers
12 ${Boost_INCLUDE_DIRS}
13 ${YAMLCPP_INCLUDE_DIRS}
14 ${LEATHERMAN_RUBY_INCLUDE}
15 ${LEATHERMAN_FILE_UTIL_INCLUDE}
16 ${LEATHERMAN_UTIL_INCLUDE}
17 ${LEATHERMAN_NOWIDE_INCLUDE}
18 ${CPPHOCON_INCLUDE_DIRS}
19 )
20
21 link_directories(
22 ${CPPHOCON_LIBRARY_DIRS}
23 )
24
25 add_executable(facter ${FACTER_SOURCES})
26
27 if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
28 target_link_libraries(facter rt)
29 endif()
30
31 target_link_libraries(facter libfacter
32 ${CPPHOCON_LIBRARIES}
33 ${LEATHERMAN_LIBRARIES}
34 ${Boost_LIBRARIES}
35 ${CMAKE_THREAD_LIBS_INIT} # fix until Leatherman switches to private dependencies
36 )
37
38 leatherman_install(facter)
+0
-387
exe/facter.cc less more
0 #include <facter/version.h>
1 #include <facter/logging/logging.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/ruby/ruby.hpp>
4 #include <facter/util/config.hpp>
5 #include <hocon/program_options.hpp>
6 #include <leatherman/util/environment.hpp>
7 #include <leatherman/util/scope_exit.hpp>
8 #include <boost/algorithm/string.hpp>
9 // Note the caveats in nowide::cout/cerr; they're not synchronized with stdio.
10 // Thus they can't be relied on to flush before program exit.
11 // Use endl/ends or flush to force synchronization when necessary.
12 #include <boost/nowide/iostream.hpp>
13 #include <boost/nowide/args.hpp>
14
15 // boost includes are not always warning-clean. Disable warnings that
16 // cause problems before including the headers, then re-enable the warnings.
17 #pragma GCC diagnostic push
18 #pragma GCC diagnostic ignored "-Wattributes"
19 #include <boost/program_options.hpp>
20 #pragma GCC diagnostic pop
21
22 #include <iostream>
23 #include <set>
24 #include <algorithm>
25 #include <iterator>
26
27 using namespace std;
28 using namespace hocon;
29 using namespace facter::facts;
30 using namespace facter::logging;
31 using namespace facter::util::config;
32 using leatherman::util::environment;
33 namespace po = boost::program_options;
34
35 // Mark string for translation (alias for facter::logging::format)
36 using facter::logging::_;
37
38 void help(po::options_description& desc)
39 {
40 boost::nowide::cout <<
41 _("Synopsis\n"
42 "========\n"
43 "\n"
44 "Collect and display facts about the system.\n"
45 "\n"
46 "Usage\n"
47 "=====\n"
48 "\n"
49 " facter [options] [query] [query] [...]\n"
50 "\n"
51 "Options\n"
52 "=======\n\n"
53 "%1%\nDescription\n"
54 "===========\n"
55 "\n"
56 "Collect and display facts about the current system. The library behind\n"
57 "facter is easy to extend, making facter an easy way to collect information\n"
58 "about a system.\n"
59 "\n"
60 "If no queries are given, then all facts will be returned.\n"
61 "\n"
62 "Example Queries\n"
63 "===============\n\n"
64 " facter kernel\n"
65 " facter networking.ip\n"
66 " facter processors.models.0"
67 "\n"
68 "\n"
69 "Config File\n"
70 "===========\n"
71 "\n"
72 "Contains settings for configuring external and custom fact directories,\n"
73 "setting command line options, and blocking and caching facts.\n"
74 "Loaded by default from %2%.\n"
75 "See man page, README, or docs for more details.",
76 desc, default_config_location()) << endl;
77 }
78
79 void log_command_line(int argc, char** argv)
80 {
81 if (!is_enabled(level::info)) {
82 return;
83 }
84 ostringstream command_line;
85 for (int i = 1; i < argc; ++i) {
86 if (command_line.tellp() != static_cast<streampos>(0)) {
87 command_line << ' ';
88 }
89 command_line << argv[i];
90 }
91 log(level::info, "executed with command line: %1%.", command_line.str());
92 }
93
94 void log_queries(set<string> const& queries)
95 {
96 if (!is_enabled(level::info)) {
97 return;
98 }
99
100 if (queries.empty()) {
101 log(level::info, "resolving all facts.");
102 return;
103 }
104
105 ostringstream output;
106 for (auto const& query : queries) {
107 if (query.empty()) {
108 continue;
109 }
110 if (output.tellp() != static_cast<streampos>(0)) {
111 output << ' ';
112 }
113 output << query;
114 }
115 log(level::info, "requested queries: %1%.", output.str());
116 }
117
118 void print_fact_groups(map<string, vector<string>> const& fact_groups) {
119 for (auto& group : fact_groups) {
120 boost::nowide::cout << group.first << endl;
121 for (auto& fact : group.second) {
122 boost::nowide::cout << " - " << fact << endl;
123 }
124 }
125 }
126
127 int main(int argc, char **argv)
128 {
129 try
130 {
131 // Fix args on Windows to be UTF-8
132 boost::nowide::args arg_utf8(argc, argv);
133
134 // Setup logging
135 setup_logging(boost::nowide::cerr);
136
137 vector<string> external_directories;
138 vector<string> custom_directories;
139 unordered_map<string, int64_t> ttls;
140
141 // Build a list of options visible on the command line
142 // Keep this list sorted alphabetically
143 // Many of these options also can be specified in the config file,
144 // see facter::util::config. Because of differences between the way
145 // options are specified in the config file and on the command line,
146 // these options need to be specified separately (e.g. on the command
147 // line, flag presence indicates `true`, while in the config file, the
148 // boolean must be specified explicitly).
149 po::options_description visible_options("");
150 visible_options.add_options()
151 ("color", _("Enable color output.").c_str())
152 ("config,c", po::value<string>(), _("The location of the config file.").c_str())
153 ("custom-dir", po::value<vector<string>>(), _("A directory to use for custom facts.").c_str())
154 ("debug,d", po::bool_switch()->default_value(false), _("Enable debug output.").c_str())
155 ("external-dir", po::value<vector<string>>(), _("A directory to use for external facts.").c_str())
156 ("help,h", _("Print this help message.").c_str())
157 ("json,j", _("Output in JSON format.").c_str())
158 ("list-block-groups", _("List the names of all blockable fact groups.").c_str())
159 ("list-cache-groups", _("List the names of all cacheable fact groups.").c_str())
160 ("log-level,l", po::value<level>()->default_value(level::warning, "warn"), _("Set logging level.\nSupported levels are: none, trace, debug, info, warn, error, and fatal.").c_str())
161 ("no-block", _("Disable fact blocking.").c_str())
162 ("no-cache", _("Disable loading and refreshing facts from the cache").c_str())
163 ("no-color", _("Disable color output.").c_str())
164 ("no-custom-facts", po::bool_switch()->default_value(false), _("Disable custom facts.").c_str())
165 ("no-external-facts", po::bool_switch()->default_value(false), _("Disable external facts.").c_str())
166 ("no-ruby", po::bool_switch()->default_value(false), _("Disable loading Ruby, facts requiring Ruby, and custom facts.").c_str())
167 ("puppet,p", _("Load the Puppet libraries, thus allowing Facter to load Puppet-specific facts.").c_str())
168 ("show-legacy", _("Show legacy facts when querying all facts.").c_str())
169 ("trace", po::bool_switch()->default_value(false), _("Enable backtraces for custom facts.").c_str())
170 ("verbose", po::bool_switch()->default_value(false), _("Enable verbose (info) output.").c_str())
171 ("version,v", _("Print the version and exit.").c_str())
172 ("yaml,y", _("Output in YAML format.").c_str())
173 ("strict", _("Enable more aggressive error reporting.").c_str());
174
175 // Build a list of "hidden" options that are not visible on the command line
176 po::options_description hidden_options("");
177 hidden_options.add_options()
178 ("query", po::value<vector<string>>());
179
180 // Create the supported command line options (visible + hidden)
181 po::options_description command_line_options;
182 command_line_options.add(visible_options).add(hidden_options);
183
184 // Build a list of positional options (in our case, just queries)
185 po::positional_options_description positional_options;
186 positional_options.add("query", -1);
187
188 po::variables_map vm;
189 try {
190 po::store(po::command_line_parser(argc, argv).
191 options(command_line_options).positional(positional_options).run(), vm);
192
193 // Check for non-default config file location
194 hocon::shared_config hocon_conf;
195 if (vm.count("config")) {
196 string conf_dir = vm["config"].as<string>();
197 hocon_conf = load_config_from(conf_dir);
198 } else {
199 hocon_conf = load_default_config_file();
200 }
201
202 if (hocon_conf) {
203 load_global_settings(hocon_conf, vm);
204 load_cli_settings(hocon_conf, vm);
205 load_fact_settings(hocon_conf, vm);
206 load_fact_groups_settings(hocon_conf, vm);
207 ttls = load_ttls(hocon_conf);
208 }
209
210 // Check for a help option first before notifying
211 if (vm.count("help")) {
212 help(visible_options);
213 return EXIT_SUCCESS;
214 }
215
216 po::notify(vm);
217
218 // Check for conflicting options
219 if (vm.count("color") && vm.count("no-color")) {
220 throw po::error(_("color and no-color options conflict: please specify only one."));
221 }
222 if (vm.count("json") && vm.count("yaml")) {
223 throw po::error(_("json and yaml options conflict: please specify only one."));
224 }
225 if (vm["no-external-facts"].as<bool>() && vm.count("external-dir")) {
226 throw po::error(_("no-external-facts and external-dir options conflict: please specify only one."));
227 }
228 if (vm["no-custom-facts"].as<bool>() && vm.count("custom-dir")) {
229 throw po::error(_("no-custom-facts and custom-dir options conflict: please specify only one."));
230 }
231 if ((vm["debug"].as<bool>() + vm["verbose"].as<bool>() + (vm["log-level"].defaulted() ? 0 : 1)) > 1) {
232 throw po::error(_("debug, verbose, and log-level options conflict: please specify only one."));
233 }
234 if (vm["no-ruby"].as<bool>() && vm.count("custom-dir")) {
235 throw po::error(_("no-ruby and custom-dir options conflict: please specify only one."));
236 }
237 if (vm.count("puppet") && vm["no-custom-facts"].as<bool>()) {
238 throw po::error(_("puppet and no-custom-facts options conflict: please specify only one."));
239 }
240 if (vm.count("puppet") && vm["no-ruby"].as<bool>()) {
241 throw po::error(_("puppet and no-ruby options conflict: please specify only one."));
242 }
243 }
244 catch (exception& ex) {
245 colorize(boost::nowide::cerr, level::error);
246 boost::nowide::cerr << _("error: %1%", ex.what()) << endl;
247 colorize(boost::nowide::cerr);
248 help(visible_options);
249 return EXIT_FAILURE;
250 }
251
252 // Check for listing fact groups
253 if (vm.count("list-cache-groups")) {
254 collection facts;
255 facts.add_default_facts(!vm.count("no-ruby"));
256 print_fact_groups(facts.get_fact_groups());
257
258 if (!vm["no-external-facts"].as<bool>()) {
259 vector<string> external_directories;
260 if (vm.count("external-dir")) {
261 external_directories = vm["external-dir"].as<vector<string>>();
262 }
263 print_fact_groups(facts.get_external_facts_groups(external_directories));
264 }
265 return EXIT_SUCCESS;
266 }
267
268 // Check for printing the version
269 if (vm.count("version")) {
270 boost::nowide::cout << LIBFACTER_VERSION_WITH_COMMIT << endl;
271 return EXIT_SUCCESS;
272 }
273
274 if (vm.count("list-block-groups")) {
275 collection facts;
276 facts.add_default_facts(!vm.count("no-ruby"));
277 print_fact_groups(facts.get_blockable_fact_groups());
278 return EXIT_SUCCESS;
279 }
280
281 // Set colorization; if no option was specified, use the default
282 if (vm.count("color")) {
283 set_colorization(true);
284 } else if (vm.count("no-color")) {
285 set_colorization(false);
286 }
287
288 // Get the logging level
289 auto lvl= vm["log-level"].as<level>();
290 if (vm["debug"].as<bool>()) {
291 lvl = level::debug;
292 } else if (vm["verbose"].as<bool>()) {
293 lvl = level::info;
294 }
295 set_level(lvl);
296
297 log_command_line(argc, argv);
298
299 // Initialize Ruby in main
300 bool ruby = (!vm["no-ruby"].as<bool>()) && facter::ruby::initialize(vm["trace"].as<bool>());
301 leatherman::util::scope_exit ruby_cleanup{[ruby]() {
302 if (ruby) {
303 facter::ruby::uninitialize();
304 }
305 }};
306
307 // Build a set of queries from the command line
308 set<string> queries;
309 if (vm.count("query")) {
310 for (auto const &q : vm["query"].as<vector<string>>()) {
311 // Strip whitespace and query delimiter
312 string query = boost::trim_copy_if(q, boost::is_any_of(".") || boost::is_space());
313
314 // Erase any duplicate consecutive delimiters
315 query.erase(unique(query.begin(), query.end(), [](char a, char b) {
316 return a == b && a == '.';
317 }), query.end());
318
319 // Don't insert empty queries
320 if (query.empty()) {
321 continue;
322 }
323
324 queries.emplace(move(query));
325 }
326 }
327
328 log_queries(queries);
329
330 set<string> blocklist;
331 if (vm.count("blocklist") && !vm.count("no-block")) {
332 auto facts_to_block = vm["blocklist"].as<vector<string>>();
333 blocklist.insert(facts_to_block.begin(), facts_to_block.end());
334 }
335 bool ignore_cache = vm.count("no-cache");
336 collection facts(blocklist, ttls, ignore_cache);
337 facts.add_default_facts(ruby);
338
339 if (ruby && !vm["no-custom-facts"].as<bool>()) {
340 if (vm.count("custom-dir")) {
341 custom_directories = vm["custom-dir"].as<vector<string>>();
342 }
343 bool redirect_ruby_stdout = vm.count("json") || vm.count("yaml");
344 facter::ruby::load_custom_facts(facts, vm.count("puppet"), redirect_ruby_stdout, custom_directories);
345 }
346
347 if (!vm["no-external-facts"].as<bool>()) {
348 string inside_facter;
349 environment::get("INSIDE_FACTER", inside_facter);
350
351 if (inside_facter == "true") {
352 log(level::debug, "Environment variable INSIDE_FACTER is set to 'true'");
353 log(level::warning, "Facter was called recursively, skipping external facts. Add '--no-external-facts' to silence this warning");
354 } else {
355 environment::set("INSIDE_FACTER", "true");
356 if (vm.count("external-dir")) {
357 external_directories = vm["external-dir"].as<vector<string>>();
358 }
359 facts.add_external_facts(external_directories);
360 }
361 }
362
363 // Add the environment facts
364 facts.add_environment_facts();
365
366 // Output the facts
367 facter::facts::format fmt = facter::facts::format::hash;
368 if (vm.count("json")) {
369 fmt = facter::facts::format::json;
370 } else if (vm.count("yaml")) {
371 fmt = facter::facts::format::yaml;
372 }
373
374 bool show_legacy = vm.count("show-legacy");
375 bool strict_errors = vm.count("strict");
376 facts.write(boost::nowide::cout, fmt, queries, show_legacy, strict_errors);
377 boost::nowide::cout << endl;
378 } catch (locale_error const& e) {
379 boost::nowide::cerr << _("failed to initialize logging system due to a locale error: %1%", e.what()) << endl;
380 return 2; // special error code to indicate we failed harder than normal
381 } catch (exception& ex) {
382 log(level::fatal, "unhandled exception: %1%", ex.what());
383 }
384
385 return error_logged() ? EXIT_FAILURE : EXIT_SUCCESS;
386 }
00 ---
1 deb_build_mirrors:
2 - deb http://pl-build-tools.delivery.puppetlabs.net/debian __DIST__ main
3 packager: 'puppetlabs'
4 gpg_key: '7F438280EF8D349F'
5
6 # These are the build targets used by the packaging repo. Uncomment to allow use.
7 #final_mocks: 'pl-el-5-x86_64 pl-el-5-i386 pl-el-6-x86_64 pl-el-6-i386 pl-el-7-x86_64'
8 #default_cow: 'base-trusty-i386.cow'
9 #cows: 'base-precise-amd64.cow base-precise-i386.cow base-trusty-amd64.cow base-trusty-i386.cow base-wheezy-amd64.cow base-wheezy-i386.cow'
10 #pbuild_conf: '/etc/pbuilderrc'
11
12 tar_host: 'downloads.puppetlabs.com'
13 sign_tar: FALSE
1 build_gem: TRUE
2 project: facter
3 nonfinal_gem_path: '/opt/repository-nightlies/downloads/gems/facter-nightly'
+0
-11
ext/debian/changelog less more
0 facter (3.14.12-1puppetlabs1) lucid precise squeeze wheezy trusty; urgency=low
1
2 * Update to version
3
4 -- Puppet Labs Release <info@puppetlabs.com> Fri, 10 Jul 2020 11:23:09 +0000
5
6 facter (2.4.2-1puppetlabs1) lucid precise squeeze wheezy trusty; urgency=low
7
8 * Move from Ruby to C++ implementation.
9
10 -- Michael Smith <michael.smith@puppetlabs.com> Fri, 20 Mar 2015 15:12:52 -0700
+0
-1
ext/debian/compat less more
0 7
+0
-14
ext/debian/control less more
0 Source: facter
1 Section: devel
2 Priority: optional
3 Maintainer: Puppet Labs <info@puppetlabs.com>
4 Build-Depends: debhelper (>> 7), pl-gcc (>= 4.8.2-1puppetlabs3), pl-cmake (>= 3.2.2-1puppetlabs6), pl-libboost-devel (>= 1.55.0-1puppetlabs3), pl-libboost-static (>= 1.55.0-1puppetlabs3), pl-libyaml-cpp-devel (>= 0.5.1-1puppetlabs4), pl-libyaml-cpp-static (>= 0.5.1-1puppetlabs4), libblkid-dev, libcurl4-openssl-dev, libssl-dev, ruby1.9.1 | ruby (>= 1:1.9.3), ruby1.9.1-dev | ruby-dev (>= 1:1.9.3)
5 Standards-Version: 3.9.1
6 Homepage: http://www.puppetlabs.com
7
8 Package: facter
9 Architecture: any
10 Depends: ${shlibs:Depends}, ${misc:Depends}, libblkid1, libcurl3, libssl1.0.0 | libssl0.9.8
11 Description: A command line tool and library for collecting simple facts about a host operating system
12 Some of the facts are preconfigured, such as the hostname and the operating
13 system. Additional facts can be added through simple Ruby scripts.
+0
-19
ext/debian/copyright less more
0 This package was debianized by Melissa Stone <melissa@puppetlabs.com> on
1 Thu, 05 Jun 2014 14:07:11 -0800
2
3 It was downloaded from https://github.com/puppetlabs/facter
4
5 Upstream Author:
6
7 Puppet Labs <info@puppetlabs.com>
8
9 Copyright:
10
11 Copyright 2010-2011 Puppet Labs <info@puppetlabs.com>
12
13 License: ASL-2
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 On a Debian system, the license can be found at
17 /usr/share/common-licenses/Apache-2.0 .
18
+0
-2
ext/debian/docs less more
0 README.md
1 LICENSE
+0
-4
ext/debian/facter.install less more
0 usr/include/facter
1 usr/lib/
2 usr/bin/
3 usr/share/man/man8
+0
-0
ext/debian/lintian-overrides less more
(Empty file)
+0
-39
ext/debian/rules less more
0 #!/usr/bin/make -f
1
2 export DH_VERBOSE=1
3 export VERBOSE=1
4
5 PREFIX :=/usr
6 BUILDPREFIX :=/opt/pl-build-tools
7 CPPFLAGS :=$(shell dpkg-buildflags --get CPPFLAGS)
8 CFLAGS :=$(shell dpkg-buildflags --get CFLAGS)
9 CXXFLAGS :=$(shell dpkg-buildflags --get CXXFLAGS)
10 LDFLAGS :=$(shell dpkg-buildflags --get LDFLAGS)
11
12 # Hard-coded to 2. Our deb builders have a hard-limit at 4GB of memory.
13 NUMPROC :=2
14
15 export CPPFLAGS CFLAGS CXXFLAGS LDFLAGS
16
17 clean:
18
19 override_dh_auto_configure:
20 $(BUILDPREFIX)/bin/cmake \
21 -DCMAKE_TOOLCHAIN_FILE=$(BUILDPREFIX)/pl-build-toolchain.cmake \
22 -DCMAKE_VERBOSE_MAKEFILE=ON \
23 -DCMAKE_INSTALL_PREFIX=$(PREFIX) \
24 -DBOOST_STATIC=ON \
25 -DYAMLCPP_STATIC=ON \
26 .
27
28 override_dh_auto_build:
29 $(MAKE) -j$(NUMPROC) DESTDIR=$(CURDIR)/debian/tmp
30
31 override_dh_auto_install:
32 $(MAKE) -j$(NUMPROC) install DESTDIR=$(CURDIR)/debian/tmp
33
34 override_dh_auto_test:
35 $(BUILDPREFIX)/bin/ctest -V
36
37 %:
38 dh $@ --parallel=$(NUMPROC)
+0
-1
ext/debian/source/format less more
0 3.0 (quilt)
00 ---
11 project: 'facter'
22 author: 'Puppet Labs'
3 email: 'info@puppetlabs.com'
3 email: 'team-nw@puppet.com'
44 homepage: 'https://github.com/puppetlabs/facter'
55 summary: 'Facter, a system inventory tool'
66 description: 'You can prove anything with facts!'
7 # files to be packaged into a tarball and released with deb/rpm
8 files:
9 - '[A-Z]*'
10 - exe
11 - ext
12 - lib
13 - locales
14 - man
15 - scripts
16 - vendor
17 - cmake
18 # space separated list of files to *exclude* from the tarball
19 # note that each listing in files, above, is recursively copied into the tarball, so
20 # 'tar\_excludes' only needs to include any undesired subdirectories/files of the 'files'
21 # list to exclude
22 tar_excludes:
23 - ext/packaging
24 # Array of templates or globs of templates to evaluate. Note that without this key, the packaging will
25 # default to searching for any files in `ext` with the extension '.erb' and evaluate them. When this
26 # key is supplied, its values override the defaults, and all desired erb files must be specified with a path or glob.
27 templates:
28 - ext/redhat/facter.spec.erb
29 - ext/debian/changelog.erb
7 version_file: 'lib/facter/version.rb'
8 files: 'bin/facter lib/facter.rb lib/**/*.rb'
9 gem_files: 'bin/facter lib/facter.rb lib/**/*.rb'
10 gem_require_path: 'lib'
11 gem_executables: 'facter'
12 gem_license: 'Apache-2.0'
13 gem_default_executables: 'facter'
14 gem_required_ruby_version: ['>= 2.3', '< 4.0']
15 gem_runtime_dependencies:
16 hocon: ~> 1.3
17 thor: ['>= 1.0.1', '< 2.0']
+0
-113
ext/redhat/facter.spec less more
0 # Only >=el7 and fedora have a sufficient version of ruby available
1 %if 0%{?rhel} >= 7 || 0%{?fedora}
2 %{!?vendor_ruby: %global vendor_ruby %(ruby -rrbconfig -e "puts RbConfig::CONFIG['vendordir']")}
3 %endif
4
5 # Building debuginfo is pointless, as this has no symbols.
6 %global debug_package %{nil}
7
8 # VERSION is subbed out during rake srpm process
9 %global realversion 3.14.12
10 %global rpmversion 3.14.12
11
12 %global build_prefix /opt/pl-build-tools
13 %global _prefix /usr
14 %global _libdir %{_prefix}/lib
15
16 Name: facter
17 Summary: A command line tool and library for collecting simple facts about a host operating system
18 Version: %{rpmversion}
19 Release: 1%{?dist}
20 Epoch: 1
21 Vendor: %{?_host_vendor}
22 License: ASL 2.0
23 Group: System Environment/Base
24 URL: http://www.puppetlabs.com/puppet/related-projects/%{name}
25 # Note this URL will only be valid at official tags from Puppet Labs
26 Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{realversion}.tar.gz
27
28 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
29
30 # Only >=el7 and fedora have a sufficient version of ruby available
31 %if 0%{?rhel} >= 7 || 0%{?fedora}
32 BuildRequires: ruby
33 BuildRequires: ruby-devel
34 %endif
35 BuildRequires: pl-gcc >= 4.8.2-4
36 BuildRequires: pl-cmake >= 3.2.2-1
37 BuildRequires: pl-libboost-devel >= 1.55.0-4
38 BuildRequires: pl-libboost-static >= 1.55.0-4
39 BuildRequires: pl-libyaml-cpp-devel >= 0.5.1-5
40 BuildRequires: pl-libyaml-cpp-static >= 0.5.1-5
41
42 %if 0%{?rhel} > 5
43 BuildRequires: openssl-devel
44 BuildRequires: libblkid-devel
45 BuildRequires: libcurl-devel
46 %endif
47
48 Requires: glibc
49 %if 0%{?rhel} > 5
50 Requires: libblkid
51 Requires: libcurl
52 %endif
53 # If we're linking against openssl, we need to ensure those libraries are available
54 # during runtime. On EL7, those dependency libraries are available via the openssl-libs
55 # package, but we don't have that package on earlier versions. We want to install as
56 # few extra packages as possible, which is why we're installing openssl-libs when available.
57 %if 0%{?rhel} >= 7
58 Requires: openssl-libs
59 %endif
60 %if 0%{?rhel} == 6
61 Requires: openssl
62 %endif
63
64 AutoReq: 0
65 AutoProv: 0
66
67 %description
68 A command line tool and library for collecting simple facts about a host Operating
69 system. Some of the facts are preconfigured, such as the hostname and the
70 operating system. Additional facts can be added through simple Ruby scripts
71
72 %prep
73 %setup -q -n %{name}-%{realversion}
74
75 %build
76 rm -rf %{buildroot}
77 %{build_prefix}/bin/cmake \
78 -DCMAKE_TOOLCHAIN_FILE=%{build_prefix}/pl-build-toolchain.cmake \
79 -DCMAKE_VERBOSE_MAKEFILE=ON \
80 -DCMAKE_INSTALL_PREFIX=%{_prefix} \
81 -DBOOST_STATIC=ON \
82 -DYAMLCPP_STATIC=ON \
83 .
84 make %{?_smp_mflags} DESTDIR=%{buildroot}
85
86 %install
87 make %{?_smp_mflags} install DESTDIR=%{buildroot}
88
89 %clean
90 rm -rf %{buildroot}
91
92 %check
93 %{build_prefix}/bin/ctest -V
94
95 %files
96 %defattr(-,root,root,-)
97 %{_bindir}/facter
98 %{_libdir}/libfacter.so*
99 %{_includedir}/facter
100 %{_mandir}/man8/facter.8.gz
101 %if 0%{?rhel} >= 7 || 0%{?fedora}
102 %{vendor_ruby}/facter.rb
103 %endif
104 %doc LICENSE README.md
105
106
107 %changelog
108 * Fri Jul 10 2020 Puppet Labs Release <info@puppetlabs.com> - 1:3.14.12-1
109 - Build for 3.14.12
110
111 * Fri Mar 20 2015 Michael Smith <michael.smith@puppetlabs.com> - 2.4.2-1
112 - Move from Ruby to C++ implementation.
+0
-8
ext/windows/facter.bat less more
0 @echo off
1 SETLOCAL
2 if exist "%~dp0environment.bat" (
3 call "%~dp0environment.bat" %0 %*
4 ) else (
5 SET "PATH=%~dp0;%PATH%"
6 )
7 facter.exe %*
+0
-6
ext/windows/facter_interactive.bat less more
0 @echo off
1 SETLOCAL
2 echo Running Facter on demand ...
3 cd "%~dp0"
4 call .\facter.bat %*
5 PAUSE
+0
-9
ext/windows/run_facter_interactive.bat less more
0 @echo Running facter on demand ...
1 @echo off
2 SETLOCAL
3 if exist "%~dp0environment.bat" (
4 call "%~dp0environment.bat" %0 %*
5 ) else (
6 SET "PATH=%~dp0;%PATH%"
7 )
8 elevate.exe "%~dp0facter_interactive.bat"
0 my_external_fact=123
0 facts : {
1 blocklist : [ ],
2 ttls : [
3 { "uptime": 30 days }
4 ]
5 }
6
7 fact-groups : {
8 cached-custom-facts : ["os.name", "os.family"],
9 }
0 # frozen_string_literal: true
1
2 lib = File.expand_path('lib', __dir__)
3 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5 Gem::Specification.new do |spec|
6 spec.name = 'facter'
7 spec.version = '4.2.13'
8 spec.authors = ['Puppet']
9 spec.email = ['team-nw@puppet.com']
10 spec.homepage = 'https://github.com/puppetlabs/facter'
11
12 spec.summary = 'Facter, a system inventory tool'
13 spec.description = 'You can prove anything with facts!'
14 spec.license = 'Apache-2.0'
15
16 dirs =
17 Dir[File.join(__dir__, 'bin/facter')] +
18 Dir[File.join(__dir__, 'LICENSE')] +
19 Dir[File.join(__dir__, 'lib/**/*.rb')] +
20 Dir[File.join(__dir__, 'lib/**/*.json')] +
21 Dir[File.join(__dir__, 'lib/**/*.conf')] +
22 Dir[File.join(__dir__, 'lib/**/*.erb')]
23 base = "#{__dir__}#{File::SEPARATOR}"
24 spec.files = dirs.map { |path| path.sub(base, '') }
25
26 spec.required_ruby_version = '>= 2.3', '< 4.0'
27 spec.bindir = 'bin'
28 spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29 spec.require_paths = ['lib']
30
31 spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
32 spec.add_development_dependency 'rspec', '~> 3.0'
33 spec.add_development_dependency 'rubocop', '~> 0.81.0'
34 spec.add_development_dependency 'rubocop-performance', '~> 1.5.2'
35 spec.add_development_dependency 'rubocop-rspec', '~> 1.38'
36 spec.add_development_dependency 'simplecov', '~> 0.17.1'
37 spec.add_development_dependency 'sys-filesystem', '~> 1.3'
38 spec.add_development_dependency 'timecop', '~> 0.9'
39 spec.add_development_dependency 'webmock', '~> 3.12'
40 spec.add_development_dependency 'yard', '~> 0.9'
41
42 spec.add_runtime_dependency 'hocon', '~> 1.3'
43 spec.add_runtime_dependency 'thor', ['>= 1.0.1', '< 2.0']
44 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 #--
4 # Copyright 2004 Austin Ziegler <ruby-install@halostatue.ca>
5 # Install utility. Based on the original installation script for rdoc by the
6 # Pragmatic Programmers.
7 #
8 # This program is free software. It may be redistributed and/or modified under
9 # the terms of the GPL version 2 (or later) or the Ruby licence.
10 #
11 # Usage
12 # -----
13 # In most cases, if you have a typical project layout, you will need to do
14 # absolutely nothing to make this work for you. This layout is:
15 #
16 # bin/ # executable files -- "commands"
17 # lib/ # the source of the library
18 #
19 # The default behaviour:
20 # 1) Install commands from bin/ into the Ruby bin directory. On Windows, if a
21 # if a corresponding batch file (.bat or .cmd) exists in the bin directory,
22 # it will be copied over as well. Otherwise, a batch file (always .bat) will
23 # be created to run the specified command.
24 # 2) Install all library files ending in .rb from lib/ into Ruby's
25 # site_lib/version directory.
26 #
27 #++
28
29 require 'rbconfig'
30 require 'find'
31 require 'fileutils'
32 require 'tempfile'
33 require 'optparse'
34 require 'ostruct'
35
36 class Installer
37 include FileUtils
38 InstallOptions = OpenStruct.new
39
40 # Returns true if OS is windows (copied from facter/util/config.rb)
41 def windows?
42 (defined?(RbConfig) ? RbConfig : Config)::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i
43 end
44
45 def glob(list)
46 g = list.map { |i| Dir.glob(i) }
47 g.flatten!
48 g.compact!
49 g
50 end
51
52 def do_configs(configs, target, strip = 'ext/')
53 Dir.mkdir(target) unless File.directory? target
54 configs.each do |cf|
55 ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, ''))
56 install(cf, ocf, { mode: 0o644, preserve: true, verbose: true })
57 end
58 end
59
60 def do_bins(bins, target, strip = 's?bin/')
61 Dir.mkdir(target) unless File.directory? target
62 bins.each do |bf|
63 obf = bf.gsub(/#{strip}/, '')
64 install_binfile(bf, obf, target)
65 end
66 end
67
68 def do_libs(libs, strip = 'lib/')
69 libs.each do |lf|
70 olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, ''))
71 op = File.dirname(olf)
72 makedirs(op, { mode: 0o755, verbose: true })
73 chmod(0o755, op)
74 install(lf, olf, { mode: 0o644, preserve: true, verbose: true })
75 end
76 end
77
78 def do_man(man, strip = 'man/')
79 man.each do |mf|
80 omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, ''))
81 om = File.dirname(omf)
82 makedirs(om, { mode: 0o755, verbose: true })
83 chmod(0o755, om)
84 install(mf, omf, { mode: 0o644, preserve: true, verbose: true })
85
86 gzip = `which gzip`
87 gzip.chomp!
88 `#{gzip} -f #{omf}`
89 end
90 end
91
92 ##
93 # Prepare the file installation.
94 #
95 def prepare_installation
96 InstallOptions.configs = true
97 InstallOptions.batch_files = true
98
99 ARGV.options do |opts|
100 opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options]"
101 opts.separator ''
102 opts.on('--[no-]configs', 'Prevents the installation of config files', 'Default off.') do |onconfigs|
103 InstallOptions.configs = onconfigs
104 end
105 opts.on('--destdir[=OPTIONAL]',
106 'Installation prefix for all targets',
107 'Default essentially /') do |destdir|
108 InstallOptions.destdir = destdir
109 end
110 # opts.on('--configdir[=OPTIONAL]', 'Installation directory for config files', 'Default /etc') do |configdir|
111 # InstallOptions.configdir = configdir
112 # end
113 opts.on('--bindir[=OPTIONAL]',
114 'Installation directory for binaries',
115 'overrides RbConfig::CONFIG["bindir"]') do |bindir|
116 InstallOptions.bindir = bindir
117 end
118 opts.on('--ruby[=OPTIONAL]',
119 'Ruby interpreter to use with installation',
120 'overrides ruby used to call install.rb') do |ruby|
121 InstallOptions.ruby = ruby
122 end
123 opts.on('--sitelibdir[=OPTIONAL]',
124 'Installation directory for libraries',
125 'overrides RbConfig::CONFIG["sitelibdir"]') do |sitelibdir|
126 InstallOptions.sitelibdir = sitelibdir
127 end
128 opts.on('--mandir[=OPTIONAL]',
129 'Installation directory for man pages',
130 'overrides RbConfig::CONFIG["mandir"]') do |mandir|
131 InstallOptions.mandir = mandir
132 end
133 opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |_full|
134 InstallOptions.configs = true
135 end
136 opts.on('--no-batch-files', 'Prevents installation of batch files for windows', 'Default off') do |_batch_files|
137 InstallOptions.batch_files = false
138 end
139 opts.separator('')
140 opts.on_tail('--help', 'Shows this help text.') do
141 warn opts
142 exit
143 end
144
145 opts.parse!
146 end
147
148 version = [RbConfig::CONFIG['MAJOR'], RbConfig::CONFIG['MINOR']].join('.')
149 libdir = File.join(RbConfig::CONFIG['libdir'], 'ruby', version)
150
151 # Mac OS X 10.5 and higher declare bindir
152 # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin
153 # which is not generally where people expect executables to be installed
154 # These settings are appropriate defaults for all OS X versions.
155 RbConfig::CONFIG['bindir'] = '/usr/bin' if RUBY_PLATFORM =~ /^universal-darwin[\d\.]+$/
156
157 # if InstallOptions.configdir
158 # configdir = InstallOptions.configdir
159 # elsif windows?
160 # path = File.join(File.dirname(__FILE__), "lib", "custom_facts", "util", "config.rb")
161 # require_relative(path)
162
163 # configdir = File.join(LegacyFacter::Util::Config.windows_data_dir, "PuppetLabs", "facter", "etc")
164 # else
165 # configdir = File.join('/', 'etc', 'puppetlabs', 'facter')
166 # end
167
168 bindir = InstallOptions.bindir || RbConfig::CONFIG['bindir']
169
170 if InstallOptions.sitelibdir
171 sitelibdir = InstallOptions.sitelibdir
172 else
173 sitelibdir = RbConfig::CONFIG['sitelibdir']
174 if sitelibdir.nil?
175 sitelibdir = $LOAD_PATH.find { |x| x =~ /site_ruby/ }
176 if sitelibdir.nil?
177 sitelibdir = File.join(libdir, 'site_ruby')
178 elsif sitelibdir !~ Regexp.quote(version)
179 sitelibdir = File.join(sitelibdir, version)
180 end
181 end
182 end
183
184 mandir = InstallOptions.mandir || RbConfig::CONFIG['mandir']
185
186 # This is the new way forward
187 destdir = InstallOptions.destdir || ''
188
189 # configdir = join(destdir, configdir)
190 bindir = join(destdir, bindir)
191 mandir = join(destdir, mandir)
192 sitelibdir = join(destdir, sitelibdir)
193
194 # makedirs(configdir) if InstallOptions.configs
195 makedirs(bindir)
196 makedirs(mandir)
197 makedirs(sitelibdir)
198
199 InstallOptions.site_dir = sitelibdir
200 # InstallOptions.config_dir = configdir
201 InstallOptions.bin_dir = bindir
202 InstallOptions.lib_dir = libdir
203 InstallOptions.man_dir = mandir
204 end
205
206 ##
207 # Join two paths. On Windows, dir must be converted to a relative path,
208 # by stripping the drive letter, but only if the basedir is not empty.
209 #
210 def join(basedir, dir)
211 return "#{basedir}#{dir[2..-1]}" if windows? && !basedir.empty? && (dir.length > 2)
212
213 "#{basedir}#{dir}"
214 end
215
216 ##
217 # Install file(s) from ./bin to RbConfig::CONFIG['bindir']. Patch it on the way
218 # to insert a #! line; on a Unix install, the command is named as expected
219 # (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under
220 # windows, we add an '.rb' extension and let file associations do their stuff.
221 def install_binfile(from, op_file, target)
222 tmp_file = Tempfile.new('facter-binfile')
223
224 ruby = if !InstallOptions.ruby.nil?
225 InstallOptions.ruby
226 else
227 File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
228 end
229
230 File.open(from) do |ip|
231 File.open(tmp_file.path, 'w') do |op|
232 op.puts "#!#{ruby}"
233 contents = ip.readlines
234 contents.shift if contents[0] =~ /^#!/
235 op.write contents.join
236 end
237 end
238
239 if windows? && InstallOptions.batch_files
240 installed_wrapper = false
241
242 if File.exist?("#{from}.bat")
243 install("#{from}.bat", File.join(target, "#{op_file}.bat"), mode: 0o755, preserve: true, verbose: true)
244 installed_wrapper = true
245 end
246
247 if File.exist?("#{from}.cmd")
248 install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), mode: 0o755, preserve: true, verbose: true)
249 installed_wrapper = true
250 end
251
252 unless installed_wrapper
253 tmp_file2 = Tempfile.new('facter-wrapper')
254 cwv = <<-SCRIPT
255 @echo off
256 SETLOCAL
257 if exist "%~dp0environment.bat" (
258 call "%~dp0environment.bat" %0 %*
259 ) else (
260 SET "PATH=%~dp0;%PATH%"
261 )
262 ruby.exe -S -- facter %*
263 SCRIPT
264 File.open(tmp_file2.path, 'w') { |cw| cw.puts cwv }
265 install(tmp_file2.path, File.join(target, "#{op_file}.bat"), mode: 0o755, preserve: true, verbose: true)
266
267 tmp_file2.unlink
268 end
269 end
270 install(tmp_file.path, File.join(target, op_file), mode: 0o755, preserve: true, verbose: true)
271 tmp_file.unlink
272 end
273
274 # Change directory into the facter root so we don't get the wrong files for install.
275 def run
276 cd File.dirname(__FILE__) do
277 # Set these values to what you want installed.
278 bins = glob(%w[bin/facter])
279 libs = glob(%w[lib/**/*.rb lib/facter/fixtures/* lib/facter/os_hierarchy.json
280 lib/facter/fact_groups.conf lib/facter/templates/*])
281 man = glob(%w{man/man[0-9]/*})
282
283 prepare_installation
284
285 do_bins(bins, InstallOptions.bin_dir)
286 do_libs(libs)
287 do_man(man) unless windows?
288 end
289 end
290 end
291
292 Installer.new.run
+0
-448
lib/CMakeLists.txt less more
0 cmake_minimum_required(VERSION 3.2.2)
1
2 # Get the HEAD SHA1 commit message
3 get_commit_string(LIBFACTER_COMMIT)
4
5 # Generate a file containing the above version numbers
6 configure_file (
7 "version.h.in"
8 "${CMAKE_CURRENT_LIST_DIR}/inc/facter/version.h"
9 )
10
11 # Generate a .gemspec file based on the above version numbers
12 configure_file (
13 "gemspec.in"
14 "${CMAKE_CURRENT_LIST_DIR}/../.gemspec"
15 )
16
17 # Set compiler-specific flags
18 set(CMAKE_CXX_FLAGS ${FACTER_CXX_FLAGS})
19 if (WIN32)
20 # Disable unused-variable variable for boost ipv6 compilation on windows
21 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable")
22 endif()
23
24 leatherman_logging_namespace("puppetlabs.facter")
25
26 # Set the common (platform-independent) sources
27 set(LIBFACTER_COMMON_SOURCES
28 "src/cwrapper.cc"
29 "src/facts/array_value.cc"
30 "src/facts/collection.cc"
31 "src/facts/external_resolvers_factory.cc"
32 "src/facts/external/execution_resolver.cc"
33 "src/facts/external/json_resolver.cc"
34 "src/facts/external/resolver.cc"
35 "src/facts/external/text_resolver.cc"
36 "src/facts/external/yaml_resolver.cc"
37 "src/facts/map_value.cc"
38 "src/facts/resolver.cc"
39 "src/facts/resolvers/augeas_resolver.cc"
40 "src/facts/resolvers/disk_resolver.cc"
41 "src/facts/resolvers/dmi_resolver.cc"
42 "src/facts/resolvers/ec2_resolver.cc"
43 "src/facts/resolvers/filesystem_resolver.cc"
44 "src/facts/resolvers/fips_resolver.cc"
45 "src/facts/resolvers/gce_resolver.cc"
46 "src/facts/resolvers/hypervisors_resolver.cc"
47 "src/facts/resolvers/identity_resolver.cc"
48 "src/facts/resolvers/kernel_resolver.cc"
49 "src/facts/resolvers/ldom_resolver.cc"
50 "src/facts/resolvers/load_average_resolver.cc"
51 "src/facts/resolvers/memory_resolver.cc"
52 "src/facts/resolvers/networking_resolver.cc"
53 "src/facts/resolvers/operating_system_resolver.cc"
54 "src/facts/resolvers/path_resolver.cc"
55 "src/facts/resolvers/processor_resolver.cc"
56 "src/facts/resolvers/ruby_resolver.cc"
57 "src/facts/resolvers/ssh_resolver.cc"
58 "src/facts/resolvers/system_profiler_resolver.cc"
59 "src/facts/resolvers/timezone_resolver.cc"
60 "src/facts/resolvers/uptime_resolver.cc"
61 "src/facts/resolvers/virtualization_resolver.cc"
62 "src/facts/resolvers/xen_resolver.cc"
63 "src/facts/resolvers/zpool_resolver.cc"
64 "src/facts/resolvers/zone_resolver.cc"
65 "src/facts/resolvers/zfs_resolver.cc"
66 "src/facts/cache.cc"
67 "src/facts/scalar_value.cc"
68 "src/logging/logging.cc"
69 "src/ruby/aggregate_resolution.cc"
70 "src/ruby/chunk.cc"
71 "src/ruby/confine.cc"
72 "src/ruby/fact.cc"
73 "src/ruby/module.cc"
74 "src/ruby/resolution.cc"
75 "src/ruby/ruby.cc"
76 "src/ruby/ruby_value.cc"
77 "src/ruby/simple_resolution.cc"
78 "src/util/scoped_file.cc"
79 "src/util/string.cc"
80 "src/util/config/config.cc"
81 "src/util/yaml.cc"
82 )
83
84 # Set the POSIX sources if on a POSIX platform
85 if (UNIX)
86 set(LIBFACTER_STANDARD_SOURCES
87 "src/facts/posix/collection.cc"
88 "src/facts/posix/external_resolvers_factory.cc"
89 "src/facts/posix/identity_resolver.cc"
90 "src/facts/posix/networking_resolver.cc"
91 "src/facts/posix/operatingsystem_resolver.cc"
92 "src/facts/posix/processor_resolver.cc"
93 "src/facts/posix/ssh_resolver.cc"
94 "src/facts/posix/timezone_resolver.cc"
95 "src/facts/posix/uptime_resolver.cc"
96 "src/facts/posix/xen_resolver.cc"
97 "src/facts/posix/cache.cc"
98 "src/util/posix/scoped_addrinfo.cc"
99 "src/util/posix/scoped_descriptor.cc"
100 "src/util/config/posix/config.cc"
101 )
102
103 if (HAVE_UTMPX_H)
104 set(LIBFACTER_STANDARD_SOURCES ${LIBFACTER_STANDARD_SOURCES} "src/util/posix/utmpx_file.cc")
105 endif()
106
107 if (NOT AIX)
108 set(LIBFACTER_STANDARD_SOURCES
109 ${LIBFACTER_STANDARD_SOURCES}
110 "src/facts/posix/kernel_resolver.cc"
111 )
112 endif()
113 endif()
114
115 if (WIN32)
116 set(LIBFACTER_STANDARD_SOURCES
117 "src/facts/external/windows/powershell_resolver.cc"
118 "src/facts/windows/collection.cc"
119 "src/facts/windows/external_resolvers_factory.cc"
120 "src/util/windows/wsa.cc"
121 )
122 endif()
123
124 if (OPENSSL_FOUND)
125 set(LIBFACTER_STANDARD_SOURCES ${LIBFACTER_STANDARD_SOURCES} "src/util/scoped_bio.cc")
126 endif()
127
128 # Set the platform-specific sources
129 if (AIX)
130 set(LIBFACTER_PLATFORM_SOURCES
131 "src/facts/aix/collection.cc"
132 "src/facts/aix/disk_resolver.cc"
133 "src/facts/aix/filesystem_resolver.cc"
134 "src/facts/aix/kernel_resolver.cc"
135 "src/facts/aix/load_average_resolver.cc"
136 "src/facts/aix/memory_resolver.cc"
137 "src/facts/aix/networking_resolver.cc"
138 "src/facts/aix/nim_resolver.cc"
139 "src/facts/aix/operating_system_resolver.cc"
140 "src/facts/aix/processor_resolver.cc"
141 "src/facts/aix/serial_number_resolver.cc"
142 )
143
144 set(LIBFACTER_PLATFORM_LIBRARIES
145 odm
146 lvm
147 )
148 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
149 set(LIBFACTER_PLATFORM_SOURCES
150 "src/facts/bsd/filesystem_resolver.cc"
151 "src/facts/bsd/networking_resolver.cc"
152 "src/facts/bsd/uptime_resolver.cc"
153 "src/facts/glib/load_average_resolver.cc"
154 "src/facts/osx/dmi_resolver.cc"
155 "src/facts/osx/networking_resolver.cc"
156 "src/facts/osx/collection.cc"
157 "src/facts/osx/memory_resolver.cc"
158 "src/facts/osx/operating_system_resolver.cc"
159 "src/facts/osx/processor_resolver.cc"
160 "src/facts/osx/system_profiler_resolver.cc"
161 "src/facts/osx/virtualization_resolver.cc"
162 "src/util/bsd/scoped_ifaddrs.cc"
163 )
164 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "SunOS")
165 set(LIBFACTER_PLATFORM_SOURCES
166 "src/facts/glib/load_average_resolver.cc"
167 "src/facts/solaris/collection.cc"
168 "src/facts/solaris/disk_resolver.cc"
169 "src/facts/solaris/processor_resolver.cc"
170 "src/facts/solaris/operating_system_resolver.cc"
171 "src/facts/solaris/kernel_resolver.cc"
172 "src/facts/solaris/filesystem_resolver.cc"
173 "src/facts/solaris/dmi_resolver.cc"
174 "src/facts/solaris/virtualization_resolver.cc"
175 "src/facts/solaris/networking_resolver.cc"
176 "src/facts/solaris/memory_resolver.cc"
177 "src/facts/solaris/zfs_resolver.cc"
178 "src/facts/solaris/zone_resolver.cc"
179 "src/facts/solaris/zpool_resolver.cc"
180 "src/facts/solaris/ldom_resolver.cc"
181 "src/util/solaris/k_stat.cc"
182 "src/util/solaris/scoped_kstat.cc"
183 )
184 set(LIBFACTER_PLATFORM_LIBRARIES
185 kstat
186 socket
187 nsl
188 rt
189 )
190 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
191 set(LIBFACTER_PLATFORM_SOURCES
192 "src/facts/bsd/networking_resolver.cc"
193 "src/facts/glib/load_average_resolver.cc"
194 "src/facts/linux/disk_resolver.cc"
195 "src/facts/linux/dmi_resolver.cc"
196 "src/facts/linux/filesystem_resolver.cc"
197 "src/facts/linux/fips_resolver.cc"
198 "src/facts/linux/kernel_resolver.cc"
199 "src/facts/linux/memory_resolver.cc"
200 "src/facts/linux/networking_resolver.cc"
201 "src/facts/linux/operating_system_resolver.cc"
202 "src/facts/linux/os_linux.cc"
203 "src/facts/linux/uptime_resolver.cc"
204 "src/facts/linux/collection.cc"
205 "src/facts/linux/processor_resolver.cc"
206 "src/facts/linux/virtualization_resolver.cc"
207 "src/util/bsd/scoped_ifaddrs.cc"
208 )
209 set(LIBFACTER_PLATFORM_LIBRARIES
210 ${BLKID_LIBRARIES}
211 rt
212 ${UDEV_LIBRARIES}
213 )
214 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD")
215 set(LIBFACTER_PLATFORM_SOURCES
216 "src/facts/freebsd/collection.cc"
217 "src/facts/glib/load_average_resolver.cc"
218 "src/facts/bsd/filesystem_resolver.cc"
219 "src/facts/freebsd/filesystem_resolver.cc"
220 "src/facts/bsd/networking_resolver.cc"
221 "src/facts/bsd/uptime_resolver.cc"
222 "src/facts/freebsd/processor_resolver.cc"
223 "src/facts/freebsd/dmi_resolver.cc"
224 "src/facts/freebsd/networking_resolver.cc"
225 "src/util/bsd/scoped_ifaddrs.cc"
226 "src/facts/freebsd/zfs_resolver.cc"
227 "src/facts/freebsd/zpool_resolver.cc"
228 "src/facts/freebsd/virtualization_resolver.cc"
229 "src/facts/freebsd/memory_resolver.cc"
230 "src/facts/freebsd/operating_system_resolver.cc"
231 "src/util/freebsd/geom.cc"
232 "src/facts/freebsd/disk_resolver.cc"
233 )
234 set(LIBFACTER_PLATFORM_LIBRARIES
235 geom
236 )
237 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
238 set(LIBFACTER_PLATFORM_SOURCES
239 "src/facts/openbsd/collection.cc"
240 "src/facts/glib/load_average_resolver.cc"
241 "src/facts/bsd/filesystem_resolver.cc"
242 "src/facts/bsd/networking_resolver.cc"
243 "src/facts/bsd/uptime_resolver.cc"
244 "src/facts/openbsd/dmi_resolver.cc"
245 "src/facts/openbsd/networking_resolver.cc"
246 "src/facts/openbsd/processor_resolver.cc"
247 "src/util/bsd/scoped_ifaddrs.cc"
248 "src/facts/openbsd/memory_resolver.cc"
249 "src/facts/openbsd/virtualization_resolver.cc"
250 )
251 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
252 set(LIBFACTER_PLATFORM_SOURCES
253 "src/facts/windows/dmi_resolver.cc"
254 "src/facts/windows/identity_resolver.cc"
255 "src/facts/windows/kernel_resolver.cc"
256 "src/facts/windows/memory_resolver.cc"
257 "src/facts/windows/networking_resolver.cc"
258 "src/facts/windows/ssh_resolver.cc"
259 "src/facts/windows/operating_system_resolver.cc"
260 "src/facts/windows/processor_resolver.cc"
261 "src/facts/windows/timezone_resolver.cc"
262 "src/facts/windows/uptime_resolver.cc"
263 "src/facts/windows/virtualization_resolver.cc"
264 "src/facts/windows/cache.cc"
265 "src/util/config/windows/config.cc"
266 )
267
268 if (HAS_LTH_GET_DWORD)
269 set(LIBFACTER_PLATFORM_SOURCES
270 ${LIBFACTER_PLATFORM_SOURCES}
271 "src/facts/windows/fips_resolver.cc"
272 )
273 endif()
274 # The GetPerformanceInfo symbol has moved around a lot between Windows versions;
275 # these options tie it to the backwards-compatible version.
276 set(LIBFACTER_PLATFORM_LIBRARIES
277 Psapi.lib
278 Version.lib
279 Wbemuuid.lib
280 Secur32.lib
281 Ws2_32.lib
282 iphlpapi.lib
283 userenv.lib
284 )
285 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPSAPI_VERSION=1")
286 endif()
287
288 if (WIN32)
289 set(LIBFACTER_INSTALL_DESTINATION bin)
290 else()
291 set(LIBFACTER_INSTALL_DESTINATION lib${LIB_SUFFIX})
292 endif()
293
294 if (JRUBY_SUPPORT)
295 include_directories(${JNI_INCLUDE_DIRS})
296 set(LIBFACTER_COMMON_SOURCES ${LIBFACTER_COMMON_SOURCES} src/java/facter.cc)
297
298 configure_file(
299 Facter.java.in
300 "${CMAKE_BINARY_DIR}/lib/com/puppetlabs/Facter.java"
301 )
302
303 include(UseJava)
304 add_jar(facter-jruby-jar "${CMAKE_BINARY_DIR}/lib/com/puppetlabs/Facter.java" OUTPUT_NAME facter OUTPUT_DIR "${CMAKE_BINARY_DIR}/lib" ENTRY_POINT com/puppetlabs/Facter)
305
306 # javah does not atomically write the header file, so parallel builds can
307 # read it before it finishes writing if not careful.
308 # JDK versions after 9 don't provide javah. Use javac in these cases
309
310 if(Java_VERSION VERSION_LESS "10")
311 set(JAVAH_COMMAND ${Java_JAVAH_EXECUTABLE})
312 set(JAVAH_ARG -classpath facter.jar -d "${CMAKE_CURRENT_LIST_DIR}/src/java" com.puppetlabs.Facter)
313 else()
314 set(JAVAH_COMMAND ${Java_JAVAC_EXECUTABLE})
315 set(JAVAH_ARG -h "${CMAKE_CURRENT_LIST_DIR}/src/java" com/puppetlabs/Facter.java)
316 endif()
317
318 add_custom_command(OUTPUT "${CMAKE_CURRENT_LIST_DIR}/src/java/com_puppetlabs_Facter.h"
319 COMMAND ${JAVAH_COMMAND} ARGS ${JAVAH_ARG}
320 WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
321 DEPENDS facter-jruby-jar)
322 # Anything that depends on facter-jruby wants both the jar AND the completely written header.
323 add_custom_target(facter-jruby DEPENDS facter-jruby-jar "${CMAKE_CURRENT_LIST_DIR}/src/java/com_puppetlabs_Facter.h")
324 set(LIBFACTER_COMMON_SOURCES ${LIBFACTER_COMMON_SOURCES} src/java/com_puppetlabs_Facter.h)
325 endif()
326
327 # Set include directories
328 include_directories(
329 inc
330 ${Boost_INCLUDE_DIRS}
331 ${OPENSSL_INCLUDE_DIRS}
332 ${YAMLCPP_INCLUDE_DIRS}
333 ${CURL_INCLUDE_DIRS}
334 ${LEATHERMAN_INCLUDE_DIRS}
335 ${CPPHOCON_INCLUDE_DIRS}
336 )
337
338 link_directories(
339 ${Boost_LIBRARY_DIRS}
340 ${OPENSSL_LIBRARY_DIRS}
341 ${YAMLCPP_LIBRARY_DIRS}
342 ${CURL_LIBRARY_DIRS}
343 ${CPPHOCON_LIBRARY_DIRS}
344 ${WHEREAMI_LIBRARY_DIRS}
345 )
346
347 # Add the library target without a prefix (name already has the 'lib') and use '.so' for all platforms (Ruby extension file extension)
348 add_library(libfactersrc OBJECT ${LIBFACTER_COMMON_SOURCES} ${LIBFACTER_STANDARD_SOURCES} ${LIBFACTER_PLATFORM_SOURCES})
349 set_target_properties(libfactersrc PROPERTIES POSITION_INDEPENDENT_CODE true)
350 add_library(libfacter $<TARGET_OBJECTS:libfactersrc>)
351 if (BUILD_SHARED_LIBS)
352 set_target_properties(libfacter PROPERTIES PREFIX "" SUFFIX ".so" IMPORT_PREFIX "" IMPORT_SUFFIX ".so.a" VERSION ${PROJECT_VERSION})
353 endif()
354
355 if(AIX)
356 # On AIX we need to be built such that we are "dynamically
357 # loadable". This also means specifying an entry point, which for
358 # a ruby module should be our ruby init fn.
359 set_target_properties(libfacter PROPERTIES LINK_FLAGS "-Wl,-G -eInit_libfacter")
360 endif()
361
362 set(LIBS ${CPPHOCON_LIBRARIES} ${WHEREAMI_LIBRARIES})
363
364 # Static libraries should come before shared libraries, or you can end up
365 # with some really annoying link errors. However, we can configure whether
366 # Boost and Leatherman are linked statically or dynamically.
367 if (LEATHERMAN_SHARED)
368 # If Leatherman is shared, Boost should come first because
369 # it's static, or the order doesn't matter.
370 list(APPEND LIBS ${Boost_LIBRARIES} ${LEATHERMAN_LIBRARIES})
371 else()
372 # If Leatherman is static, it should come first as it depends
373 # on Boost.
374 list(APPEND LIBS ${LEATHERMAN_LIBRARIES} ${Boost_LIBRARIES})
375 endif()
376
377 list(APPEND LIBS
378 ${OPENSSL_LIBRARIES}
379 ${YAMLCPP_LIBRARIES}
380 ${CURL_LIBRARIES}
381 ${CMAKE_THREAD_LIBS_INIT}
382 )
383
384 # Link in additional libraries
385 target_link_libraries(libfacter PRIVATE ${LIBS} ${LIBFACTER_PLATFORM_LIBRARIES})
386
387 if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND BOOST_STATIC AND LEATHERMAN_USE_LOCALES)
388 target_link_libraries(libfacter PRIVATE iconv)
389 endif()
390
391 symbol_exports(libfacter "${CMAKE_CURRENT_LIST_DIR}/inc/facter/export.h")
392 target_compile_definitions(libfactersrc PRIVATE "-Dlibfacter_EXPORTS")
393
394 leatherman_install(libfacter)
395 install(DIRECTORY inc/facter DESTINATION include)
396
397 # Always generate spec_helper.rb, as they might be used in packaging
398 # and testing on another machine.
399 # Generate a file for ruby testing
400 configure_file (
401 "spec_helper.rb.in"
402 "${CMAKE_CURRENT_LIST_DIR}/spec/spec_helper.rb"
403 )
404
405 if (APPLE)
406 # Unfortunately the default DLEXT for most rubies on OSX is "bundle"
407 # Ruby calls dlopen for the extension, which doesn't care if the "bundle" really is a dylib
408 # Therefore, workaround this by symlinking "libfacter.bundle" to "libfacter.so"
409 add_custom_command(TARGET libfacter POST_BUILD COMMAND ln -sf libfacter.so libfacter.bundle WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
410 message(STATUS "\"make install\" will create a symlink from ${CMAKE_INSTALL_PREFIX}/${LIBFACTER_INSTALL_DESTINATION}/libfacter.bundle to ${CMAKE_INSTALL_PREFIX}/${LIBFACTER_INSTALL_DESTINATION}/libfacter.so")
411 install(CODE "EXECUTE_PROCESS(COMMAND ln -sf libfacter.so libfacter.bundle WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${LIBFACTER_INSTALL_DESTINATION})")
412 endif()
413
414 set(RUBY_LIB_INSTALL "" CACHE PATH "Specify the location to install facter.rb")
415 if(RUBY_LIB_INSTALL)
416 set(RUBY_VENDORDIR ${RUBY_LIB_INSTALL})
417 else()
418 # Disable actually installing facter.rb if Ruby is not present and RUBY_LIB_INSTALL not specified.
419 if (RUBY_FOUND)
420 message(STATUS "Ruby ${RUBY_VERSION} found.")
421
422 execute_process(COMMAND ${RUBY_EXECUTABLE} -rrbconfig -e "puts RbConfig::CONFIG['vendordir']" OUTPUT_VARIABLE RUBY_VENDORDIR_OUT)
423 string(STRIP ${RUBY_VENDORDIR_OUT} RUBY_VENDORDIR)
424 endif()
425 endif()
426
427 if(RUBY_VENDORDIR)
428 file(RELATIVE_PATH LIBFACTER_INSTALL_RELATIVE ${RUBY_VENDORDIR} ${CMAKE_INSTALL_PREFIX})
429 configure_file (
430 "facter.rb.in"
431 "${CMAKE_BINARY_DIR}/lib/facter.rb"
432 )
433
434 message(STATUS "\"make install\" will install facter.rb to ${RUBY_VENDORDIR}")
435 install(FILES ${CMAKE_BINARY_DIR}/lib/facter.rb DESTINATION ${RUBY_VENDORDIR})
436
437 if (JRUBY_SUPPORT)
438 message(STATUS "\"make install\" will install facter.jar to ${RUBY_VENDORDIR} to support JRuby")
439 install(FILES ${CMAKE_BINARY_DIR}/lib/facter.jar DESTINATION ${RUBY_VENDORDIR})
440 endif()
441 endif()
442
443 if (JRUBY_SUPPORT)
444 add_dependencies(libfacter facter-jruby)
445 endif()
446
447 add_subdirectory(tests)
+0
-2313
lib/Doxyfile less more
0 # Doxyfile 1.8.7
1
2 # This file describes the settings to be used by the documentation system
3 # doxygen (www.doxygen.org) for a project.
4 #
5 # All text after a double hash (##) is considered a comment and is placed in
6 # front of the TAG it is preceding.
7 #
8 # All text after a single hash (#) is considered a comment and will be ignored.
9 # The format is:
10 # TAG = value [value, ...]
11 # For lists, items can also be appended using:
12 # TAG += value [value, ...]
13 # Values that contain spaces should be placed between quotes (\" \").
14
15 #---------------------------------------------------------------------------
16 # Project related configuration options
17 #---------------------------------------------------------------------------
18
19 # This tag specifies the encoding used for all characters in the config file
20 # that follow. The default is UTF-8 which is also the encoding used for all text
21 # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
22 # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
23 # for the list of possible encodings.
24 # The default value is: UTF-8.
25
26 DOXYFILE_ENCODING = UTF-8
27
28 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
29 # double-quotes, unless you are using Doxywizard) that should identify the
30 # project for which the documentation is generated. This name is used in the
31 # title of most generated pages and in a few other places.
32 # The default value is: My Project.
33
34 PROJECT_NAME = facter
35
36 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
37 # could be handy for archiving the generated documentation or if some version
38 # control system is used.
39
40 PROJECT_NUMBER = 3.14.12
41
42 # Using the PROJECT_BRIEF tag one can provide an optional one line description
43 # for a project that appears at the top of each page and should give viewer a
44 # quick idea about the purpose of the project. Keep the description short.
45
46 PROJECT_BRIEF =
47
48 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
49 # the documentation. The maximum height of the logo should not exceed 55 pixels
50 # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
51 # to the output directory.
52
53 PROJECT_LOGO =
54
55 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
56 # into which the generated documentation will be written. If a relative path is
57 # entered, it will be relative to the location where doxygen was started. If
58 # left blank the current directory will be used.
59
60 OUTPUT_DIRECTORY =
61
62 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
63 # directories (in 2 levels) under the output directory of each output format and
64 # will distribute the generated files over these directories. Enabling this
65 # option can be useful when feeding doxygen a huge amount of source files, where
66 # putting all generated files in the same directory would otherwise causes
67 # performance problems for the file system.
68 # The default value is: NO.
69
70 CREATE_SUBDIRS = NO
71
72 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
73 # characters to appear in the names of generated files. If set to NO, non-ASCII
74 # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
75 # U+3044.
76 # The default value is: NO.
77
78 ALLOW_UNICODE_NAMES = NO
79
80 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
81 # documentation generated by doxygen is written. Doxygen will use this
82 # information to generate all constant output in the proper language.
83 # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
84 # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
85 # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
86 # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
87 # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
88 # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
89 # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
90 # Ukrainian and Vietnamese.
91 # The default value is: English.
92
93 OUTPUT_LANGUAGE = English
94
95 # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
96 # descriptions after the members that are listed in the file and class
97 # documentation (similar to Javadoc). Set to NO to disable this.
98 # The default value is: YES.
99
100 BRIEF_MEMBER_DESC = YES
101
102 # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
103 # description of a member or function before the detailed description
104 #
105 # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
106 # brief descriptions will be completely suppressed.
107 # The default value is: YES.
108
109 REPEAT_BRIEF = YES
110
111 # This tag implements a quasi-intelligent brief description abbreviator that is
112 # used to form the text in various listings. Each string in this list, if found
113 # as the leading text of the brief description, will be stripped from the text
114 # and the result, after processing the whole list, is used as the annotated
115 # text. Otherwise, the brief description is used as-is. If left blank, the
116 # following values are used ($name is automatically replaced with the name of
117 # the entity):The $name class, The $name widget, The $name file, is, provides,
118 # specifies, contains, represents, a, an and the.
119
120 ABBREVIATE_BRIEF =
121
122 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
123 # doxygen will generate a detailed section even if there is only a brief
124 # description.
125 # The default value is: NO.
126
127 ALWAYS_DETAILED_SEC = YES
128
129 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
130 # inherited members of a class in the documentation of that class as if those
131 # members were ordinary class members. Constructors, destructors and assignment
132 # operators of the base classes will not be shown.
133 # The default value is: NO.
134
135 INLINE_INHERITED_MEMB = YES
136
137 # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
138 # before files name in the file list and in the header files. If set to NO the
139 # shortest path that makes the file name unique will be used
140 # The default value is: YES.
141
142 FULL_PATH_NAMES = YES
143
144 # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
145 # Stripping is only done if one of the specified strings matches the left-hand
146 # part of the path. The tag can be used to show relative paths in the file list.
147 # If left blank the directory from which doxygen is run is used as the path to
148 # strip.
149 #
150 # Note that you can specify absolute paths here, but also relative paths, which
151 # will be relative from the directory where doxygen is started.
152 # This tag requires that the tag FULL_PATH_NAMES is set to YES.
153
154 STRIP_FROM_PATH = inc/
155
156 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
157 # path mentioned in the documentation of a class, which tells the reader which
158 # header file to include in order to use a class. If left blank only the name of
159 # the header file containing the class definition is used. Otherwise one should
160 # specify the list of include paths that are normally passed to the compiler
161 # using the -I flag.
162
163 STRIP_FROM_INC_PATH =
164
165 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
166 # less readable) file names. This can be useful is your file systems doesn't
167 # support long names like on DOS, Mac, or CD-ROM.
168 # The default value is: NO.
169
170 SHORT_NAMES = NO
171
172 # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
173 # first line (until the first dot) of a Javadoc-style comment as the brief
174 # description. If set to NO, the Javadoc-style will behave just like regular Qt-
175 # style comments (thus requiring an explicit @brief command for a brief
176 # description.)
177 # The default value is: NO.
178
179 JAVADOC_AUTOBRIEF = YES
180
181 # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
182 # line (until the first dot) of a Qt-style comment as the brief description. If
183 # set to NO, the Qt-style will behave just like regular Qt-style comments (thus
184 # requiring an explicit \brief command for a brief description.)
185 # The default value is: NO.
186
187 QT_AUTOBRIEF = NO
188
189 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
190 # multi-line C++ special comment block (i.e. a block of //! or /// comments) as
191 # a brief description. This used to be the default behavior. The new default is
192 # to treat a multi-line C++ comment block as a detailed description. Set this
193 # tag to YES if you prefer the old behavior instead.
194 #
195 # Note that setting this tag to YES also means that rational rose comments are
196 # not recognized any more.
197 # The default value is: NO.
198
199 MULTILINE_CPP_IS_BRIEF = NO
200
201 # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
202 # documentation from any documented member that it re-implements.
203 # The default value is: YES.
204
205 INHERIT_DOCS = YES
206
207 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
208 # new page for each member. If set to NO, the documentation of a member will be
209 # part of the file/class/namespace that contains it.
210 # The default value is: NO.
211
212 SEPARATE_MEMBER_PAGES = NO
213
214 # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
215 # uses this value to replace tabs by spaces in code fragments.
216 # Minimum value: 1, maximum value: 16, default value: 4.
217
218 TAB_SIZE = 4
219
220 # This tag can be used to specify a number of aliases that act as commands in
221 # the documentation. An alias has the form:
222 # name=value
223 # For example adding
224 # "sideeffect=@par Side Effects:\n"
225 # will allow you to put the command \sideeffect (or @sideeffect) in the
226 # documentation, which will result in a user-defined paragraph with heading
227 # "Side Effects:". You can put \n's in the value part of an alias to insert
228 # newlines.
229
230 ALIASES =
231
232 # This tag can be used to specify a number of word-keyword mappings (TCL only).
233 # A mapping has the form "name=value". For example adding "class=itcl::class"
234 # will allow you to use the command class in the itcl::class meaning.
235
236 TCL_SUBST =
237
238 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
239 # only. Doxygen will then generate output that is more tailored for C. For
240 # instance, some of the names that are used will be different. The list of all
241 # members will be omitted, etc.
242 # The default value is: NO.
243
244 OPTIMIZE_OUTPUT_FOR_C = NO
245
246 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
247 # Python sources only. Doxygen will then generate output that is more tailored
248 # for that language. For instance, namespaces will be presented as packages,
249 # qualified scopes will look different, etc.
250 # The default value is: NO.
251
252 OPTIMIZE_OUTPUT_JAVA = NO
253
254 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
255 # sources. Doxygen will then generate output that is tailored for Fortran.
256 # The default value is: NO.
257
258 OPTIMIZE_FOR_FORTRAN = NO
259
260 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
261 # sources. Doxygen will then generate output that is tailored for VHDL.
262 # The default value is: NO.
263
264 OPTIMIZE_OUTPUT_VHDL = NO
265
266 # Doxygen selects the parser to use depending on the extension of the files it
267 # parses. With this tag you can assign which parser to use for a given
268 # extension. Doxygen has a built-in mapping, but you can override or extend it
269 # using this tag. The format is ext=language, where ext is a file extension, and
270 # language is one of the parsers supported by doxygen: IDL, Java, Javascript,
271 # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
272 # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
273 # Fortran. In the later case the parser tries to guess whether the code is fixed
274 # or free formatted code, this is the default for Fortran type files), VHDL. For
275 # instance to make doxygen treat .inc files as Fortran files (default is PHP),
276 # and .f files as C (default is Fortran), use: inc=Fortran f=C.
277 #
278 # Note For files without extension you can use no_extension as a placeholder.
279 #
280 # Note that for custom extensions you also need to set FILE_PATTERNS otherwise
281 # the files are not read by doxygen.
282
283 EXTENSION_MAPPING =
284
285 # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
286 # according to the Markdown format, which allows for more readable
287 # documentation. See http://daringfireball.net/projects/markdown/ for details.
288 # The output of markdown processing is further processed by doxygen, so you can
289 # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
290 # case of backward compatibilities issues.
291 # The default value is: YES.
292
293 MARKDOWN_SUPPORT = YES
294
295 # When enabled doxygen tries to link words that correspond to documented
296 # classes, or namespaces to their corresponding documentation. Such a link can
297 # be prevented in individual cases by by putting a % sign in front of the word
298 # or globally by setting AUTOLINK_SUPPORT to NO.
299 # The default value is: YES.
300
301 AUTOLINK_SUPPORT = YES
302
303 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
304 # to include (a tag file for) the STL sources as input, then you should set this
305 # tag to YES in order to let doxygen match functions declarations and
306 # definitions whose arguments contain STL classes (e.g. func(std::string);
307 # versus func(std::string) {}). This also make the inheritance and collaboration
308 # diagrams that involve STL classes more complete and accurate.
309 # The default value is: NO.
310
311 BUILTIN_STL_SUPPORT = YES
312
313 # If you use Microsoft's C++/CLI language, you should set this option to YES to
314 # enable parsing support.
315 # The default value is: NO.
316
317 CPP_CLI_SUPPORT = NO
318
319 # Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
320 # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
321 # will parse them like normal C++ but will assume all classes use public instead
322 # of private inheritance when no explicit protection keyword is present.
323 # The default value is: NO.
324
325 SIP_SUPPORT = NO
326
327 # For Microsoft's IDL there are propget and propput attributes to indicate
328 # getter and setter methods for a property. Setting this option to YES will make
329 # doxygen to replace the get and set methods by a property in the documentation.
330 # This will only work if the methods are indeed getting or setting a simple
331 # type. If this is not the case, or you want to show the methods anyway, you
332 # should set this option to NO.
333 # The default value is: YES.
334
335 IDL_PROPERTY_SUPPORT = YES
336
337 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
338 # tag is set to YES, then doxygen will reuse the documentation of the first
339 # member in the group (if any) for the other members of the group. By default
340 # all members of a group must be documented explicitly.
341 # The default value is: NO.
342
343 DISTRIBUTE_GROUP_DOC = NO
344
345 # Set the SUBGROUPING tag to YES to allow class member groups of the same type
346 # (for instance a group of public functions) to be put as a subgroup of that
347 # type (e.g. under the Public Functions section). Set it to NO to prevent
348 # subgrouping. Alternatively, this can be done per class using the
349 # \nosubgrouping command.
350 # The default value is: YES.
351
352 SUBGROUPING = YES
353
354 # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
355 # are shown inside the group in which they are included (e.g. using \ingroup)
356 # instead of on a separate page (for HTML and Man pages) or section (for LaTeX
357 # and RTF).
358 #
359 # Note that this feature does not work in combination with
360 # SEPARATE_MEMBER_PAGES.
361 # The default value is: NO.
362
363 INLINE_GROUPED_CLASSES = NO
364
365 # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
366 # with only public data fields or simple typedef fields will be shown inline in
367 # the documentation of the scope in which they are defined (i.e. file,
368 # namespace, or group documentation), provided this scope is documented. If set
369 # to NO, structs, classes, and unions are shown on a separate page (for HTML and
370 # Man pages) or section (for LaTeX and RTF).
371 # The default value is: NO.
372
373 INLINE_SIMPLE_STRUCTS = NO
374
375 # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
376 # enum is documented as struct, union, or enum with the name of the typedef. So
377 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
378 # with name TypeT. When disabled the typedef will appear as a member of a file,
379 # namespace, or class. And the struct will be named TypeS. This can typically be
380 # useful for C code in case the coding convention dictates that all compound
381 # types are typedef'ed and only the typedef is referenced, never the tag name.
382 # The default value is: NO.
383
384 TYPEDEF_HIDES_STRUCT = YES
385
386 # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
387 # cache is used to resolve symbols given their name and scope. Since this can be
388 # an expensive process and often the same symbol appears multiple times in the
389 # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
390 # doxygen will become slower. If the cache is too large, memory is wasted. The
391 # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
392 # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
393 # symbols. At the end of a run doxygen will report the cache usage and suggest
394 # the optimal cache size from a speed point of view.
395 # Minimum value: 0, maximum value: 9, default value: 0.
396
397 LOOKUP_CACHE_SIZE = 0
398
399 #---------------------------------------------------------------------------
400 # Build related configuration options
401 #---------------------------------------------------------------------------
402
403 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
404 # documentation are documented, even if no documentation was available. Private
405 # class members and static file members will be hidden unless the
406 # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
407 # Note: This will also disable the warnings about undocumented members that are
408 # normally produced when WARNINGS is set to YES.
409 # The default value is: NO.
410
411 EXTRACT_ALL = NO
412
413 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
414 # be included in the documentation.
415 # The default value is: NO.
416
417 EXTRACT_PRIVATE = NO
418
419 # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
420 # scope will be included in the documentation.
421 # The default value is: NO.
422
423 EXTRACT_PACKAGE = YES
424
425 # If the EXTRACT_STATIC tag is set to YES all static members of a file will be
426 # included in the documentation.
427 # The default value is: NO.
428
429 EXTRACT_STATIC = YES
430
431 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
432 # locally in source files will be included in the documentation. If set to NO
433 # only classes defined in header files are included. Does not have any effect
434 # for Java sources.
435 # The default value is: YES.
436
437 EXTRACT_LOCAL_CLASSES = YES
438
439 # This flag is only useful for Objective-C code. When set to YES local methods,
440 # which are defined in the implementation section but not in the interface are
441 # included in the documentation. If set to NO only methods in the interface are
442 # included.
443 # The default value is: NO.
444
445 EXTRACT_LOCAL_METHODS = YES
446
447 # If this flag is set to YES, the members of anonymous namespaces will be
448 # extracted and appear in the documentation as a namespace called
449 # 'anonymous_namespace{file}', where file will be replaced with the base name of
450 # the file that contains the anonymous namespace. By default anonymous namespace
451 # are hidden.
452 # The default value is: NO.
453
454 EXTRACT_ANON_NSPACES = YES
455
456 # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
457 # undocumented members inside documented classes or files. If set to NO these
458 # members will be included in the various overviews, but no documentation
459 # section is generated. This option has no effect if EXTRACT_ALL is enabled.
460 # The default value is: NO.
461
462 HIDE_UNDOC_MEMBERS = NO
463
464 # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
465 # undocumented classes that are normally visible in the class hierarchy. If set
466 # to NO these classes will be included in the various overviews. This option has
467 # no effect if EXTRACT_ALL is enabled.
468 # The default value is: NO.
469
470 HIDE_UNDOC_CLASSES = NO
471
472 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
473 # (class|struct|union) declarations. If set to NO these declarations will be
474 # included in the documentation.
475 # The default value is: NO.
476
477 HIDE_FRIEND_COMPOUNDS = NO
478
479 # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
480 # documentation blocks found inside the body of a function. If set to NO these
481 # blocks will be appended to the function's detailed documentation block.
482 # The default value is: NO.
483
484 HIDE_IN_BODY_DOCS = NO
485
486 # The INTERNAL_DOCS tag determines if documentation that is typed after a
487 # \internal command is included. If the tag is set to NO then the documentation
488 # will be excluded. Set it to YES to include the internal documentation.
489 # The default value is: NO.
490
491 INTERNAL_DOCS = NO
492
493 # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
494 # names in lower-case letters. If set to YES upper-case letters are also
495 # allowed. This is useful if you have classes or files whose names only differ
496 # in case and if your file system supports case sensitive file names. Windows
497 # and Mac users are advised to set this option to NO.
498 # The default value is: system dependent.
499
500 CASE_SENSE_NAMES = NO
501
502 # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
503 # their full class and namespace scopes in the documentation. If set to YES the
504 # scope will be hidden.
505 # The default value is: NO.
506
507 HIDE_SCOPE_NAMES = NO
508
509 # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
510 # the files that are included by a file in the documentation of that file.
511 # The default value is: YES.
512
513 SHOW_INCLUDE_FILES = NO
514
515 # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
516 # grouped member an include statement to the documentation, telling the reader
517 # which file to include in order to use the member.
518 # The default value is: NO.
519
520 SHOW_GROUPED_MEMB_INC = YES
521
522 # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
523 # files with double quotes in the documentation rather than with sharp brackets.
524 # The default value is: NO.
525
526 FORCE_LOCAL_INCLUDES = NO
527
528 # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
529 # documentation for inline members.
530 # The default value is: YES.
531
532 INLINE_INFO = YES
533
534 # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
535 # (detailed) documentation of file and class members alphabetically by member
536 # name. If set to NO the members will appear in declaration order.
537 # The default value is: YES.
538
539 SORT_MEMBER_DOCS = YES
540
541 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
542 # descriptions of file, namespace and class members alphabetically by member
543 # name. If set to NO the members will appear in declaration order. Note that
544 # this will also influence the order of the classes in the class list.
545 # The default value is: NO.
546
547 SORT_BRIEF_DOCS = NO
548
549 # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
550 # (brief and detailed) documentation of class members so that constructors and
551 # destructors are listed first. If set to NO the constructors will appear in the
552 # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
553 # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
554 # member documentation.
555 # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
556 # detailed member documentation.
557 # The default value is: NO.
558
559 SORT_MEMBERS_CTORS_1ST = NO
560
561 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
562 # of group names into alphabetical order. If set to NO the group names will
563 # appear in their defined order.
564 # The default value is: NO.
565
566 SORT_GROUP_NAMES = YES
567
568 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
569 # fully-qualified names, including namespaces. If set to NO, the class list will
570 # be sorted only by class name, not including the namespace part.
571 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
572 # Note: This option applies only to the class list, not to the alphabetical
573 # list.
574 # The default value is: NO.
575
576 SORT_BY_SCOPE_NAME = NO
577
578 # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
579 # type resolution of all parameters of a function it will reject a match between
580 # the prototype and the implementation of a member function even if there is
581 # only one candidate or it is obvious which candidate to choose by doing a
582 # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
583 # accept a match between prototype and implementation in such cases.
584 # The default value is: NO.
585
586 STRICT_PROTO_MATCHING = NO
587
588 # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
589 # todo list. This list is created by putting \todo commands in the
590 # documentation.
591 # The default value is: YES.
592
593 GENERATE_TODOLIST = YES
594
595 # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
596 # test list. This list is created by putting \test commands in the
597 # documentation.
598 # The default value is: YES.
599
600 GENERATE_TESTLIST = YES
601
602 # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
603 # list. This list is created by putting \bug commands in the documentation.
604 # The default value is: YES.
605
606 GENERATE_BUGLIST = YES
607
608 # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
609 # the deprecated list. This list is created by putting \deprecated commands in
610 # the documentation.
611 # The default value is: YES.
612
613 GENERATE_DEPRECATEDLIST= YES
614
615 # The ENABLED_SECTIONS tag can be used to enable conditional documentation
616 # sections, marked by \if <section_label> ... \endif and \cond <section_label>
617 # ... \endcond blocks.
618
619 ENABLED_SECTIONS =
620
621 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
622 # initial value of a variable or macro / define can have for it to appear in the
623 # documentation. If the initializer consists of more lines than specified here
624 # it will be hidden. Use a value of 0 to hide initializers completely. The
625 # appearance of the value of individual variables and macros / defines can be
626 # controlled using \showinitializer or \hideinitializer command in the
627 # documentation regardless of this setting.
628 # Minimum value: 0, maximum value: 10000, default value: 30.
629
630 MAX_INITIALIZER_LINES = 30
631
632 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
633 # the bottom of the documentation of classes and structs. If set to YES the list
634 # will mention the files that were used to generate the documentation.
635 # The default value is: YES.
636
637 SHOW_USED_FILES = YES
638
639 # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
640 # will remove the Files entry from the Quick Index and from the Folder Tree View
641 # (if specified).
642 # The default value is: YES.
643
644 SHOW_FILES = YES
645
646 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
647 # page. This will remove the Namespaces entry from the Quick Index and from the
648 # Folder Tree View (if specified).
649 # The default value is: YES.
650
651 SHOW_NAMESPACES = YES
652
653 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
654 # doxygen should invoke to get the current version for each file (typically from
655 # the version control system). Doxygen will invoke the program by executing (via
656 # popen()) the command command input-file, where command is the value of the
657 # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
658 # by doxygen. Whatever the program writes to standard output is used as the file
659 # version. For an example see the documentation.
660
661 FILE_VERSION_FILTER =
662
663 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
664 # by doxygen. The layout file controls the global structure of the generated
665 # output files in an output format independent way. To create the layout file
666 # that represents doxygen's defaults, run doxygen with the -l option. You can
667 # optionally specify a file name after the option, if omitted DoxygenLayout.xml
668 # will be used as the name of the layout file.
669 #
670 # Note that if you run doxygen from a directory containing a file called
671 # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
672 # tag is left empty.
673
674 LAYOUT_FILE =
675
676 # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
677 # the reference definitions. This must be a list of .bib files. The .bib
678 # extension is automatically appended if omitted. This requires the bibtex tool
679 # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
680 # For LaTeX the style of the bibliography can be controlled using
681 # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
682 # search path. Do not use file names with spaces, bibtex cannot handle them. See
683 # also \cite for info how to create references.
684
685 CITE_BIB_FILES =
686
687 #---------------------------------------------------------------------------
688 # Configuration options related to warning and progress messages
689 #---------------------------------------------------------------------------
690
691 # The QUIET tag can be used to turn on/off the messages that are generated to
692 # standard output by doxygen. If QUIET is set to YES this implies that the
693 # messages are off.
694 # The default value is: NO.
695
696 QUIET = YES
697
698 # The WARNINGS tag can be used to turn on/off the warning messages that are
699 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
700 # this implies that the warnings are on.
701 #
702 # Tip: Turn warnings on while writing the documentation.
703 # The default value is: YES.
704
705 WARNINGS = YES
706
707 # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
708 # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
709 # will automatically be disabled.
710 # The default value is: YES.
711
712 WARN_IF_UNDOCUMENTED = YES
713
714 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
715 # potential errors in the documentation, such as not documenting some parameters
716 # in a documented function, or documenting parameters that don't exist or using
717 # markup commands wrongly.
718 # The default value is: YES.
719
720 WARN_IF_DOC_ERROR = YES
721
722 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
723 # are documented, but have no documentation for their parameters or return
724 # value. If set to NO doxygen will only warn about wrong or incomplete parameter
725 # documentation, but not about the absence of documentation.
726 # The default value is: NO.
727
728 WARN_NO_PARAMDOC = YES
729
730 # The WARN_FORMAT tag determines the format of the warning messages that doxygen
731 # can produce. The string should contain the $file, $line, and $text tags, which
732 # will be replaced by the file and line number from which the warning originated
733 # and the warning text. Optionally the format may contain $version, which will
734 # be replaced by the version of the file (if it could be obtained via
735 # FILE_VERSION_FILTER)
736 # The default value is: $file:$line: $text.
737
738 WARN_FORMAT = "$file:$line: $text"
739
740 # The WARN_LOGFILE tag can be used to specify a file to which warning and error
741 # messages should be written. If left blank the output is written to standard
742 # error (stderr).
743
744 WARN_LOGFILE = html/warnings.txt
745
746 #---------------------------------------------------------------------------
747 # Configuration options related to the input files
748 #---------------------------------------------------------------------------
749
750 # The INPUT tag is used to specify the files and/or directories that contain
751 # documented source files. You may enter file names like myfile.cpp or
752 # directories like /usr/src/myproject. Separate the files or directories with
753 # spaces.
754 # Note: If this tag is empty the current directory is searched.
755
756 INPUT = ../README.md \
757 ../Extensibility.md \
758 inc \
759 docs
760
761 # This tag can be used to specify the character encoding of the source files
762 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
763 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
764 # documentation (see: http://www.gnu.org/software/libiconv) for the list of
765 # possible encodings.
766 # The default value is: UTF-8.
767
768 INPUT_ENCODING = UTF-8
769
770 # If the value of the INPUT tag contains directories, you can use the
771 # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
772 # *.h) to filter out the source-files in the directories. If left blank the
773 # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
774 # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
775 # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
776 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
777 # *.qsf, *.as and *.js.
778
779 FILE_PATTERNS =
780
781 # The RECURSIVE tag can be used to specify whether or not subdirectories should
782 # be searched for input files as well.
783 # The default value is: NO.
784
785 RECURSIVE = YES
786
787 # The EXCLUDE tag can be used to specify files and/or directories that should be
788 # excluded from the INPUT source files. This way you can easily exclude a
789 # subdirectory from a directory tree whose root is specified with the INPUT tag.
790 #
791 # Note that relative paths are relative to the directory from which doxygen is
792 # run.
793
794 EXCLUDE =
795
796 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
797 # directories that are symbolic links (a Unix file system feature) are excluded
798 # from the input.
799 # The default value is: NO.
800
801 EXCLUDE_SYMLINKS = NO
802
803 # If the value of the INPUT tag contains directories, you can use the
804 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
805 # certain files from those directories.
806 #
807 # Note that the wildcards are matched against the file with absolute path, so to
808 # exclude all test directories for example use the pattern */test/*
809
810 EXCLUDE_PATTERNS =
811
812 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
813 # (namespaces, classes, functions, etc.) that should be excluded from the
814 # output. The symbol name can be a fully qualified name, a word, or if the
815 # wildcard * is used, a substring. Examples: ANamespace, AClass,
816 # AClass::ANamespace, ANamespace::*Test
817 #
818 # Note that the wildcards are matched against the file with absolute path, so to
819 # exclude all test directories use the pattern */test/*
820
821 EXCLUDE_SYMBOLS = rapidjson*
822
823 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
824 # that contain example code fragments that are included (see the \include
825 # command).
826
827 EXAMPLE_PATH =
828
829 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
830 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
831 # *.h) to filter out the source-files in the directories. If left blank all
832 # files are included.
833
834 EXAMPLE_PATTERNS =
835
836 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
837 # searched for input files to be used with the \include or \dontinclude commands
838 # irrespective of the value of the RECURSIVE tag.
839 # The default value is: NO.
840
841 EXAMPLE_RECURSIVE = NO
842
843 # The IMAGE_PATH tag can be used to specify one or more files or directories
844 # that contain images that are to be included in the documentation (see the
845 # \image command).
846
847 IMAGE_PATH =
848
849 # The INPUT_FILTER tag can be used to specify a program that doxygen should
850 # invoke to filter for each input file. Doxygen will invoke the filter program
851 # by executing (via popen()) the command:
852 #
853 # <filter> <input-file>
854 #
855 # where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
856 # name of an input file. Doxygen will then use the output that the filter
857 # program writes to standard output. If FILTER_PATTERNS is specified, this tag
858 # will be ignored.
859 #
860 # Note that the filter must not add or remove lines; it is applied before the
861 # code is scanned, but not when the output code is generated. If lines are added
862 # or removed, the anchors will not be placed correctly.
863
864 INPUT_FILTER =
865
866 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
867 # basis. Doxygen will compare the file name with each pattern and apply the
868 # filter if there is a match. The filters are a list of the form: pattern=filter
869 # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
870 # filters are used. If the FILTER_PATTERNS tag is empty or if none of the
871 # patterns match the file name, INPUT_FILTER is applied.
872
873 FILTER_PATTERNS =
874
875 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
876 # INPUT_FILTER ) will also be used to filter the input files that are used for
877 # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
878 # The default value is: NO.
879
880 FILTER_SOURCE_FILES = NO
881
882 # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
883 # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
884 # it is also possible to disable source filtering for a specific pattern using
885 # *.ext= (so without naming a filter).
886 # This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
887
888 FILTER_SOURCE_PATTERNS =
889
890 # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
891 # is part of the input, its contents will be placed on the main page
892 # (index.html). This can be useful if you have a project on for instance GitHub
893 # and want to reuse the introduction page also for the doxygen output.
894
895 USE_MDFILE_AS_MAINPAGE = README.md
896
897 #---------------------------------------------------------------------------
898 # Configuration options related to source browsing
899 #---------------------------------------------------------------------------
900
901 # If the SOURCE_BROWSER tag is set to YES then a list of source files will be
902 # generated. Documented entities will be cross-referenced with these sources.
903 #
904 # Note: To get rid of all source code in the generated output, make sure that
905 # also VERBATIM_HEADERS is set to NO.
906 # The default value is: NO.
907
908 SOURCE_BROWSER = YES
909
910 # Setting the INLINE_SOURCES tag to YES will include the body of functions,
911 # classes and enums directly into the documentation.
912 # The default value is: NO.
913
914 INLINE_SOURCES = NO
915
916 # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
917 # special comment blocks from generated source code fragments. Normal C, C++ and
918 # Fortran comments will always remain visible.
919 # The default value is: YES.
920
921 STRIP_CODE_COMMENTS = YES
922
923 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
924 # function all documented functions referencing it will be listed.
925 # The default value is: NO.
926
927 REFERENCED_BY_RELATION = NO
928
929 # If the REFERENCES_RELATION tag is set to YES then for each documented function
930 # all documented entities called/used by that function will be listed.
931 # The default value is: NO.
932
933 REFERENCES_RELATION = NO
934
935 # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
936 # to YES, then the hyperlinks from functions in REFERENCES_RELATION and
937 # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
938 # link to the documentation.
939 # The default value is: YES.
940
941 REFERENCES_LINK_SOURCE = YES
942
943 # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
944 # source code will show a tooltip with additional information such as prototype,
945 # brief description and links to the definition and documentation. Since this
946 # will make the HTML file larger and loading of large files a bit slower, you
947 # can opt to disable this feature.
948 # The default value is: YES.
949 # This tag requires that the tag SOURCE_BROWSER is set to YES.
950
951 SOURCE_TOOLTIPS = YES
952
953 # If the USE_HTAGS tag is set to YES then the references to source code will
954 # point to the HTML generated by the htags(1) tool instead of doxygen built-in
955 # source browser. The htags tool is part of GNU's global source tagging system
956 # (see http://www.gnu.org/software/global/global.html). You will need version
957 # 4.8.6 or higher.
958 #
959 # To use it do the following:
960 # - Install the latest version of global
961 # - Enable SOURCE_BROWSER and USE_HTAGS in the config file
962 # - Make sure the INPUT points to the root of the source tree
963 # - Run doxygen as normal
964 #
965 # Doxygen will invoke htags (and that will in turn invoke gtags), so these
966 # tools must be available from the command line (i.e. in the search path).
967 #
968 # The result: instead of the source browser generated by doxygen, the links to
969 # source code will now point to the output of htags.
970 # The default value is: NO.
971 # This tag requires that the tag SOURCE_BROWSER is set to YES.
972
973 USE_HTAGS = NO
974
975 # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
976 # verbatim copy of the header file for each class for which an include is
977 # specified. Set to NO to disable this.
978 # See also: Section \class.
979 # The default value is: YES.
980
981 VERBATIM_HEADERS = YES
982
983 #---------------------------------------------------------------------------
984 # Configuration options related to the alphabetical class index
985 #---------------------------------------------------------------------------
986
987 # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
988 # compounds will be generated. Enable this if the project contains a lot of
989 # classes, structs, unions or interfaces.
990 # The default value is: YES.
991
992 ALPHABETICAL_INDEX = YES
993
994 # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
995 # which the alphabetical index list will be split.
996 # Minimum value: 1, maximum value: 20, default value: 5.
997 # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
998
999 COLS_IN_ALPHA_INDEX = 5
1000
1001 # In case all classes in a project start with a common prefix, all classes will
1002 # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
1003 # can be used to specify a prefix (or a list of prefixes) that should be ignored
1004 # while generating the index headers.
1005 # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
1006
1007 IGNORE_PREFIX =
1008
1009 #---------------------------------------------------------------------------
1010 # Configuration options related to the HTML output
1011 #---------------------------------------------------------------------------
1012
1013 # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
1014 # The default value is: YES.
1015
1016 GENERATE_HTML = YES
1017
1018 # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
1019 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1020 # it.
1021 # The default directory is: html.
1022 # This tag requires that the tag GENERATE_HTML is set to YES.
1023
1024 HTML_OUTPUT = html
1025
1026 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
1027 # generated HTML page (for example: .htm, .php, .asp).
1028 # The default value is: .html.
1029 # This tag requires that the tag GENERATE_HTML is set to YES.
1030
1031 HTML_FILE_EXTENSION = .html
1032
1033 # The HTML_HEADER tag can be used to specify a user-defined HTML header file for
1034 # each generated HTML page. If the tag is left blank doxygen will generate a
1035 # standard header.
1036 #
1037 # To get valid HTML the header file that includes any scripts and style sheets
1038 # that doxygen needs, which is dependent on the configuration options used (e.g.
1039 # the setting GENERATE_TREEVIEW). It is highly recommended to start with a
1040 # default header using
1041 # doxygen -w html new_header.html new_footer.html new_stylesheet.css
1042 # YourConfigFile
1043 # and then modify the file new_header.html. See also section "Doxygen usage"
1044 # for information on how to generate the default header that doxygen normally
1045 # uses.
1046 # Note: The header is subject to change so you typically have to regenerate the
1047 # default header when upgrading to a newer version of doxygen. For a description
1048 # of the possible markers and block names see the documentation.
1049 # This tag requires that the tag GENERATE_HTML is set to YES.
1050
1051 HTML_HEADER =
1052
1053 # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
1054 # generated HTML page. If the tag is left blank doxygen will generate a standard
1055 # footer. See HTML_HEADER for more information on how to generate a default
1056 # footer and what special commands can be used inside the footer. See also
1057 # section "Doxygen usage" for information on how to generate the default footer
1058 # that doxygen normally uses.
1059 # This tag requires that the tag GENERATE_HTML is set to YES.
1060
1061 HTML_FOOTER =
1062
1063 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
1064 # sheet that is used by each HTML page. It can be used to fine-tune the look of
1065 # the HTML output. If left blank doxygen will generate a default style sheet.
1066 # See also section "Doxygen usage" for information on how to generate the style
1067 # sheet that doxygen normally uses.
1068 # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
1069 # it is more robust and this tag (HTML_STYLESHEET) will in the future become
1070 # obsolete.
1071 # This tag requires that the tag GENERATE_HTML is set to YES.
1072
1073 HTML_STYLESHEET =
1074
1075 # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
1076 # defined cascading style sheet that is included after the standard style sheets
1077 # created by doxygen. Using this option one can overrule certain style aspects.
1078 # This is preferred over using HTML_STYLESHEET since it does not replace the
1079 # standard style sheet and is therefor more robust against future updates.
1080 # Doxygen will copy the style sheet file to the output directory. For an example
1081 # see the documentation.
1082 # This tag requires that the tag GENERATE_HTML is set to YES.
1083
1084 HTML_EXTRA_STYLESHEET =
1085
1086 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
1087 # other source files which should be copied to the HTML output directory. Note
1088 # that these files will be copied to the base HTML output directory. Use the
1089 # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
1090 # files. In the HTML_STYLESHEET file, use the file name only. Also note that the
1091 # files will be copied as-is; there are no commands or markers available.
1092 # This tag requires that the tag GENERATE_HTML is set to YES.
1093
1094 HTML_EXTRA_FILES =
1095
1096 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
1097 # will adjust the colors in the stylesheet and background images according to
1098 # this color. Hue is specified as an angle on a colorwheel, see
1099 # http://en.wikipedia.org/wiki/Hue for more information. For instance the value
1100 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
1101 # purple, and 360 is red again.
1102 # Minimum value: 0, maximum value: 359, default value: 220.
1103 # This tag requires that the tag GENERATE_HTML is set to YES.
1104
1105 HTML_COLORSTYLE_HUE = 220
1106
1107 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
1108 # in the HTML output. For a value of 0 the output will use grayscales only. A
1109 # value of 255 will produce the most vivid colors.
1110 # Minimum value: 0, maximum value: 255, default value: 100.
1111 # This tag requires that the tag GENERATE_HTML is set to YES.
1112
1113 HTML_COLORSTYLE_SAT = 100
1114
1115 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
1116 # luminance component of the colors in the HTML output. Values below 100
1117 # gradually make the output lighter, whereas values above 100 make the output
1118 # darker. The value divided by 100 is the actual gamma applied, so 80 represents
1119 # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
1120 # change the gamma.
1121 # Minimum value: 40, maximum value: 240, default value: 80.
1122 # This tag requires that the tag GENERATE_HTML is set to YES.
1123
1124 HTML_COLORSTYLE_GAMMA = 80
1125
1126 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
1127 # page will contain the date and time when the page was generated. Setting this
1128 # to NO can help when comparing the output of multiple runs.
1129 # The default value is: YES.
1130 # This tag requires that the tag GENERATE_HTML is set to YES.
1131
1132 HTML_TIMESTAMP = YES
1133
1134 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
1135 # documentation will contain sections that can be hidden and shown after the
1136 # page has loaded.
1137 # The default value is: NO.
1138 # This tag requires that the tag GENERATE_HTML is set to YES.
1139
1140 HTML_DYNAMIC_SECTIONS = NO
1141
1142 # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
1143 # shown in the various tree structured indices initially; the user can expand
1144 # and collapse entries dynamically later on. Doxygen will expand the tree to
1145 # such a level that at most the specified number of entries are visible (unless
1146 # a fully collapsed tree already exceeds this amount). So setting the number of
1147 # entries 1 will produce a full collapsed tree by default. 0 is a special value
1148 # representing an infinite number of entries and will result in a full expanded
1149 # tree by default.
1150 # Minimum value: 0, maximum value: 9999, default value: 100.
1151 # This tag requires that the tag GENERATE_HTML is set to YES.
1152
1153 HTML_INDEX_NUM_ENTRIES = 100
1154
1155 # If the GENERATE_DOCSET tag is set to YES, additional index files will be
1156 # generated that can be used as input for Apple's Xcode 3 integrated development
1157 # environment (see: http://developer.apple.com/tools/xcode/), introduced with
1158 # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
1159 # Makefile in the HTML output directory. Running make will produce the docset in
1160 # that directory and running make install will install the docset in
1161 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
1162 # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
1163 # for more information.
1164 # The default value is: NO.
1165 # This tag requires that the tag GENERATE_HTML is set to YES.
1166
1167 GENERATE_DOCSET = NO
1168
1169 # This tag determines the name of the docset feed. A documentation feed provides
1170 # an umbrella under which multiple documentation sets from a single provider
1171 # (such as a company or product suite) can be grouped.
1172 # The default value is: Doxygen generated docs.
1173 # This tag requires that the tag GENERATE_DOCSET is set to YES.
1174
1175 DOCSET_FEEDNAME = "Doxygen generated docs"
1176
1177 # This tag specifies a string that should uniquely identify the documentation
1178 # set bundle. This should be a reverse domain-name style string, e.g.
1179 # com.mycompany.MyDocSet. Doxygen will append .docset to the name.
1180 # The default value is: org.doxygen.Project.
1181 # This tag requires that the tag GENERATE_DOCSET is set to YES.
1182
1183 DOCSET_BUNDLE_ID = org.doxygen.Project
1184
1185 # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
1186 # the documentation publisher. This should be a reverse domain-name style
1187 # string, e.g. com.mycompany.MyDocSet.documentation.
1188 # The default value is: org.doxygen.Publisher.
1189 # This tag requires that the tag GENERATE_DOCSET is set to YES.
1190
1191 DOCSET_PUBLISHER_ID = org.doxygen.Publisher
1192
1193 # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
1194 # The default value is: Publisher.
1195 # This tag requires that the tag GENERATE_DOCSET is set to YES.
1196
1197 DOCSET_PUBLISHER_NAME = Publisher
1198
1199 # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
1200 # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
1201 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
1202 # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
1203 # Windows.
1204 #
1205 # The HTML Help Workshop contains a compiler that can convert all HTML output
1206 # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
1207 # files are now used as the Windows 98 help format, and will replace the old
1208 # Windows help format (.hlp) on all Windows platforms in the future. Compressed
1209 # HTML files also contain an index, a table of contents, and you can search for
1210 # words in the documentation. The HTML workshop also contains a viewer for
1211 # compressed HTML files.
1212 # The default value is: NO.
1213 # This tag requires that the tag GENERATE_HTML is set to YES.
1214
1215 GENERATE_HTMLHELP = NO
1216
1217 # The CHM_FILE tag can be used to specify the file name of the resulting .chm
1218 # file. You can add a path in front of the file if the result should not be
1219 # written to the html output directory.
1220 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1221
1222 CHM_FILE =
1223
1224 # The HHC_LOCATION tag can be used to specify the location (absolute path
1225 # including file name) of the HTML help compiler ( hhc.exe). If non-empty
1226 # doxygen will try to run the HTML help compiler on the generated index.hhp.
1227 # The file has to be specified with full path.
1228 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1229
1230 HHC_LOCATION =
1231
1232 # The GENERATE_CHI flag controls if a separate .chi index file is generated (
1233 # YES) or that it should be included in the master .chm file ( NO).
1234 # The default value is: NO.
1235 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1236
1237 GENERATE_CHI = NO
1238
1239 # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
1240 # and project file content.
1241 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1242
1243 CHM_INDEX_ENCODING =
1244
1245 # The BINARY_TOC flag controls whether a binary table of contents is generated (
1246 # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
1247 # enables the Previous and Next buttons.
1248 # The default value is: NO.
1249 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1250
1251 BINARY_TOC = NO
1252
1253 # The TOC_EXPAND flag can be set to YES to add extra items for group members to
1254 # the table of contents of the HTML help documentation and to the tree view.
1255 # The default value is: NO.
1256 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1257
1258 TOC_EXPAND = NO
1259
1260 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
1261 # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
1262 # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
1263 # (.qch) of the generated HTML documentation.
1264 # The default value is: NO.
1265 # This tag requires that the tag GENERATE_HTML is set to YES.
1266
1267 GENERATE_QHP = NO
1268
1269 # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
1270 # the file name of the resulting .qch file. The path specified is relative to
1271 # the HTML output folder.
1272 # This tag requires that the tag GENERATE_QHP is set to YES.
1273
1274 QCH_FILE =
1275
1276 # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
1277 # Project output. For more information please see Qt Help Project / Namespace
1278 # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
1279 # The default value is: org.doxygen.Project.
1280 # This tag requires that the tag GENERATE_QHP is set to YES.
1281
1282 QHP_NAMESPACE =
1283
1284 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
1285 # Help Project output. For more information please see Qt Help Project / Virtual
1286 # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
1287 # folders).
1288 # The default value is: doc.
1289 # This tag requires that the tag GENERATE_QHP is set to YES.
1290
1291 QHP_VIRTUAL_FOLDER = doc
1292
1293 # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
1294 # filter to add. For more information please see Qt Help Project / Custom
1295 # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
1296 # filters).
1297 # This tag requires that the tag GENERATE_QHP is set to YES.
1298
1299 QHP_CUST_FILTER_NAME =
1300
1301 # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
1302 # custom filter to add. For more information please see Qt Help Project / Custom
1303 # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
1304 # filters).
1305 # This tag requires that the tag GENERATE_QHP is set to YES.
1306
1307 QHP_CUST_FILTER_ATTRS =
1308
1309 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
1310 # project's filter section matches. Qt Help Project / Filter Attributes (see:
1311 # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
1312 # This tag requires that the tag GENERATE_QHP is set to YES.
1313
1314 QHP_SECT_FILTER_ATTRS =
1315
1316 # The QHG_LOCATION tag can be used to specify the location of Qt's
1317 # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
1318 # generated .qhp file.
1319 # This tag requires that the tag GENERATE_QHP is set to YES.
1320
1321 QHG_LOCATION =
1322
1323 # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
1324 # generated, together with the HTML files, they form an Eclipse help plugin. To
1325 # install this plugin and make it available under the help contents menu in
1326 # Eclipse, the contents of the directory containing the HTML and XML files needs
1327 # to be copied into the plugins directory of eclipse. The name of the directory
1328 # within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
1329 # After copying Eclipse needs to be restarted before the help appears.
1330 # The default value is: NO.
1331 # This tag requires that the tag GENERATE_HTML is set to YES.
1332
1333 GENERATE_ECLIPSEHELP = NO
1334
1335 # A unique identifier for the Eclipse help plugin. When installing the plugin
1336 # the directory name containing the HTML and XML files should also have this
1337 # name. Each documentation set should have its own identifier.
1338 # The default value is: org.doxygen.Project.
1339 # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
1340
1341 ECLIPSE_DOC_ID = org.doxygen.Project
1342
1343 # If you want full control over the layout of the generated HTML pages it might
1344 # be necessary to disable the index and replace it with your own. The
1345 # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
1346 # of each HTML page. A value of NO enables the index and the value YES disables
1347 # it. Since the tabs in the index contain the same information as the navigation
1348 # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
1349 # The default value is: NO.
1350 # This tag requires that the tag GENERATE_HTML is set to YES.
1351
1352 DISABLE_INDEX = NO
1353
1354 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
1355 # structure should be generated to display hierarchical information. If the tag
1356 # value is set to YES, a side panel will be generated containing a tree-like
1357 # index structure (just like the one that is generated for HTML Help). For this
1358 # to work a browser that supports JavaScript, DHTML, CSS and frames is required
1359 # (i.e. any modern browser). Windows users are probably better off using the
1360 # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
1361 # further fine-tune the look of the index. As an example, the default style
1362 # sheet generated by doxygen has an example that shows how to put an image at
1363 # the root of the tree instead of the PROJECT_NAME. Since the tree basically has
1364 # the same information as the tab index, you could consider setting
1365 # DISABLE_INDEX to YES when enabling this option.
1366 # The default value is: NO.
1367 # This tag requires that the tag GENERATE_HTML is set to YES.
1368
1369 GENERATE_TREEVIEW = NO
1370
1371 # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
1372 # doxygen will group on one line in the generated HTML documentation.
1373 #
1374 # Note that a value of 0 will completely suppress the enum values from appearing
1375 # in the overview section.
1376 # Minimum value: 0, maximum value: 20, default value: 4.
1377 # This tag requires that the tag GENERATE_HTML is set to YES.
1378
1379 ENUM_VALUES_PER_LINE = 4
1380
1381 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
1382 # to set the initial width (in pixels) of the frame in which the tree is shown.
1383 # Minimum value: 0, maximum value: 1500, default value: 250.
1384 # This tag requires that the tag GENERATE_HTML is set to YES.
1385
1386 TREEVIEW_WIDTH = 250
1387
1388 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
1389 # external symbols imported via tag files in a separate window.
1390 # The default value is: NO.
1391 # This tag requires that the tag GENERATE_HTML is set to YES.
1392
1393 EXT_LINKS_IN_WINDOW = NO
1394
1395 # Use this tag to change the font size of LaTeX formulas included as images in
1396 # the HTML documentation. When you change the font size after a successful
1397 # doxygen run you need to manually remove any form_*.png images from the HTML
1398 # output directory to force them to be regenerated.
1399 # Minimum value: 8, maximum value: 50, default value: 10.
1400 # This tag requires that the tag GENERATE_HTML is set to YES.
1401
1402 FORMULA_FONTSIZE = 10
1403
1404 # Use the FORMULA_TRANPARENT tag to determine whether or not the images
1405 # generated for formulas are transparent PNGs. Transparent PNGs are not
1406 # supported properly for IE 6.0, but are supported on all modern browsers.
1407 #
1408 # Note that when changing this option you need to delete any form_*.png files in
1409 # the HTML output directory before the changes have effect.
1410 # The default value is: YES.
1411 # This tag requires that the tag GENERATE_HTML is set to YES.
1412
1413 FORMULA_TRANSPARENT = YES
1414
1415 # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
1416 # http://www.mathjax.org) which uses client side Javascript for the rendering
1417 # instead of using prerendered bitmaps. Use this if you do not have LaTeX
1418 # installed or if you want to formulas look prettier in the HTML output. When
1419 # enabled you may also need to install MathJax separately and configure the path
1420 # to it using the MATHJAX_RELPATH option.
1421 # The default value is: NO.
1422 # This tag requires that the tag GENERATE_HTML is set to YES.
1423
1424 USE_MATHJAX = NO
1425
1426 # When MathJax is enabled you can set the default output format to be used for
1427 # the MathJax output. See the MathJax site (see:
1428 # http://docs.mathjax.org/en/latest/output.html) for more details.
1429 # Possible values are: HTML-CSS (which is slower, but has the best
1430 # compatibility), NativeMML (i.e. MathML) and SVG.
1431 # The default value is: HTML-CSS.
1432 # This tag requires that the tag USE_MATHJAX is set to YES.
1433
1434 MATHJAX_FORMAT = HTML-CSS
1435
1436 # When MathJax is enabled you need to specify the location relative to the HTML
1437 # output directory using the MATHJAX_RELPATH option. The destination directory
1438 # should contain the MathJax.js script. For instance, if the mathjax directory
1439 # is located at the same level as the HTML output directory, then
1440 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
1441 # Content Delivery Network so you can quickly see the result without installing
1442 # MathJax. However, it is strongly recommended to install a local copy of
1443 # MathJax from http://www.mathjax.org before deployment.
1444 # The default value is: http://cdn.mathjax.org/mathjax/latest.
1445 # This tag requires that the tag USE_MATHJAX is set to YES.
1446
1447 MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
1448
1449 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
1450 # extension names that should be enabled during MathJax rendering. For example
1451 # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
1452 # This tag requires that the tag USE_MATHJAX is set to YES.
1453
1454 MATHJAX_EXTENSIONS =
1455
1456 # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
1457 # of code that will be used on startup of the MathJax code. See the MathJax site
1458 # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
1459 # example see the documentation.
1460 # This tag requires that the tag USE_MATHJAX is set to YES.
1461
1462 MATHJAX_CODEFILE =
1463
1464 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for
1465 # the HTML output. The underlying search engine uses javascript and DHTML and
1466 # should work on any modern browser. Note that when using HTML help
1467 # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
1468 # there is already a search function so this one should typically be disabled.
1469 # For large projects the javascript based search engine can be slow, then
1470 # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
1471 # search using the keyboard; to jump to the search box use <access key> + S
1472 # (what the <access key> is depends on the OS and browser, but it is typically
1473 # <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
1474 # key> to jump into the search results window, the results can be navigated
1475 # using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
1476 # the search. The filter options can be selected when the cursor is inside the
1477 # search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
1478 # to select a filter and <Enter> or <escape> to activate or cancel the filter
1479 # option.
1480 # The default value is: YES.
1481 # This tag requires that the tag GENERATE_HTML is set to YES.
1482
1483 SEARCHENGINE = YES
1484
1485 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
1486 # implemented using a web server instead of a web client using Javascript. There
1487 # are two flavors of web server based searching depending on the EXTERNAL_SEARCH
1488 # setting. When disabled, doxygen will generate a PHP script for searching and
1489 # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
1490 # and searching needs to be provided by external tools. See the section
1491 # "External Indexing and Searching" for details.
1492 # The default value is: NO.
1493 # This tag requires that the tag SEARCHENGINE is set to YES.
1494
1495 SERVER_BASED_SEARCH = NO
1496
1497 # When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
1498 # script for searching. Instead the search results are written to an XML file
1499 # which needs to be processed by an external indexer. Doxygen will invoke an
1500 # external search engine pointed to by the SEARCHENGINE_URL option to obtain the
1501 # search results.
1502 #
1503 # Doxygen ships with an example indexer ( doxyindexer) and search engine
1504 # (doxysearch.cgi) which are based on the open source search engine library
1505 # Xapian (see: http://xapian.org/).
1506 #
1507 # See the section "External Indexing and Searching" for details.
1508 # The default value is: NO.
1509 # This tag requires that the tag SEARCHENGINE is set to YES.
1510
1511 EXTERNAL_SEARCH = NO
1512
1513 # The SEARCHENGINE_URL should point to a search engine hosted by a web server
1514 # which will return the search results when EXTERNAL_SEARCH is enabled.
1515 #
1516 # Doxygen ships with an example indexer ( doxyindexer) and search engine
1517 # (doxysearch.cgi) which are based on the open source search engine library
1518 # Xapian (see: http://xapian.org/). See the section "External Indexing and
1519 # Searching" for details.
1520 # This tag requires that the tag SEARCHENGINE is set to YES.
1521
1522 SEARCHENGINE_URL =
1523
1524 # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
1525 # search data is written to a file for indexing by an external tool. With the
1526 # SEARCHDATA_FILE tag the name of this file can be specified.
1527 # The default file is: searchdata.xml.
1528 # This tag requires that the tag SEARCHENGINE is set to YES.
1529
1530 SEARCHDATA_FILE = searchdata.xml
1531
1532 # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
1533 # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
1534 # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
1535 # projects and redirect the results back to the right project.
1536 # This tag requires that the tag SEARCHENGINE is set to YES.
1537
1538 EXTERNAL_SEARCH_ID =
1539
1540 # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
1541 # projects other than the one defined by this configuration file, but that are
1542 # all added to the same external search index. Each project needs to have a
1543 # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
1544 # to a relative location where the documentation can be found. The format is:
1545 # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
1546 # This tag requires that the tag SEARCHENGINE is set to YES.
1547
1548 EXTRA_SEARCH_MAPPINGS =
1549
1550 #---------------------------------------------------------------------------
1551 # Configuration options related to the LaTeX output
1552 #---------------------------------------------------------------------------
1553
1554 # If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
1555 # The default value is: YES.
1556
1557 GENERATE_LATEX = NO
1558
1559 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
1560 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1561 # it.
1562 # The default directory is: latex.
1563 # This tag requires that the tag GENERATE_LATEX is set to YES.
1564
1565 LATEX_OUTPUT = latex
1566
1567 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
1568 # invoked.
1569 #
1570 # Note that when enabling USE_PDFLATEX this option is only used for generating
1571 # bitmaps for formulas in the HTML output, but not in the Makefile that is
1572 # written to the output directory.
1573 # The default file is: latex.
1574 # This tag requires that the tag GENERATE_LATEX is set to YES.
1575
1576 LATEX_CMD_NAME = latex
1577
1578 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
1579 # index for LaTeX.
1580 # The default file is: makeindex.
1581 # This tag requires that the tag GENERATE_LATEX is set to YES.
1582
1583 MAKEINDEX_CMD_NAME = makeindex
1584
1585 # If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
1586 # documents. This may be useful for small projects and may help to save some
1587 # trees in general.
1588 # The default value is: NO.
1589 # This tag requires that the tag GENERATE_LATEX is set to YES.
1590
1591 COMPACT_LATEX = NO
1592
1593 # The PAPER_TYPE tag can be used to set the paper type that is used by the
1594 # printer.
1595 # Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
1596 # 14 inches) and executive (7.25 x 10.5 inches).
1597 # The default value is: a4.
1598 # This tag requires that the tag GENERATE_LATEX is set to YES.
1599
1600 PAPER_TYPE = a4wide
1601
1602 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
1603 # that should be included in the LaTeX output. To get the times font for
1604 # instance you can specify
1605 # EXTRA_PACKAGES=times
1606 # If left blank no extra packages will be included.
1607 # This tag requires that the tag GENERATE_LATEX is set to YES.
1608
1609 EXTRA_PACKAGES =
1610
1611 # The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
1612 # generated LaTeX document. The header should contain everything until the first
1613 # chapter. If it is left blank doxygen will generate a standard header. See
1614 # section "Doxygen usage" for information on how to let doxygen write the
1615 # default header to a separate file.
1616 #
1617 # Note: Only use a user-defined header if you know what you are doing! The
1618 # following commands have a special meaning inside the header: $title,
1619 # $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
1620 # replace them by respectively the title of the page, the current date and time,
1621 # only the current date, the version number of doxygen, the project name (see
1622 # PROJECT_NAME), or the project number (see PROJECT_NUMBER).
1623 # This tag requires that the tag GENERATE_LATEX is set to YES.
1624
1625 LATEX_HEADER =
1626
1627 # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
1628 # generated LaTeX document. The footer should contain everything after the last
1629 # chapter. If it is left blank doxygen will generate a standard footer.
1630 #
1631 # Note: Only use a user-defined footer if you know what you are doing!
1632 # This tag requires that the tag GENERATE_LATEX is set to YES.
1633
1634 LATEX_FOOTER =
1635
1636 # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
1637 # other source files which should be copied to the LATEX_OUTPUT output
1638 # directory. Note that the files will be copied as-is; there are no commands or
1639 # markers available.
1640 # This tag requires that the tag GENERATE_LATEX is set to YES.
1641
1642 LATEX_EXTRA_FILES =
1643
1644 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
1645 # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
1646 # contain links (just like the HTML output) instead of page references. This
1647 # makes the output suitable for online browsing using a PDF viewer.
1648 # The default value is: YES.
1649 # This tag requires that the tag GENERATE_LATEX is set to YES.
1650
1651 PDF_HYPERLINKS = YES
1652
1653 # If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
1654 # the PDF file directly from the LaTeX files. Set this option to YES to get a
1655 # higher quality PDF documentation.
1656 # The default value is: YES.
1657 # This tag requires that the tag GENERATE_LATEX is set to YES.
1658
1659 USE_PDFLATEX = YES
1660
1661 # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
1662 # command to the generated LaTeX files. This will instruct LaTeX to keep running
1663 # if errors occur, instead of asking the user for help. This option is also used
1664 # when generating formulas in HTML.
1665 # The default value is: NO.
1666 # This tag requires that the tag GENERATE_LATEX is set to YES.
1667
1668 LATEX_BATCHMODE = NO
1669
1670 # If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
1671 # index chapters (such as File Index, Compound Index, etc.) in the output.
1672 # The default value is: NO.
1673 # This tag requires that the tag GENERATE_LATEX is set to YES.
1674
1675 LATEX_HIDE_INDICES = NO
1676
1677 # If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
1678 # code with syntax highlighting in the LaTeX output.
1679 #
1680 # Note that which sources are shown also depends on other settings such as
1681 # SOURCE_BROWSER.
1682 # The default value is: NO.
1683 # This tag requires that the tag GENERATE_LATEX is set to YES.
1684
1685 LATEX_SOURCE_CODE = NO
1686
1687 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
1688 # bibliography, e.g. plainnat, or ieeetr. See
1689 # http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
1690 # The default value is: plain.
1691 # This tag requires that the tag GENERATE_LATEX is set to YES.
1692
1693 LATEX_BIB_STYLE = plain
1694
1695 #---------------------------------------------------------------------------
1696 # Configuration options related to the RTF output
1697 #---------------------------------------------------------------------------
1698
1699 # If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
1700 # RTF output is optimized for Word 97 and may not look too pretty with other RTF
1701 # readers/editors.
1702 # The default value is: NO.
1703
1704 GENERATE_RTF = NO
1705
1706 # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
1707 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1708 # it.
1709 # The default directory is: rtf.
1710 # This tag requires that the tag GENERATE_RTF is set to YES.
1711
1712 RTF_OUTPUT = rtf
1713
1714 # If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
1715 # documents. This may be useful for small projects and may help to save some
1716 # trees in general.
1717 # The default value is: NO.
1718 # This tag requires that the tag GENERATE_RTF is set to YES.
1719
1720 COMPACT_RTF = NO
1721
1722 # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
1723 # contain hyperlink fields. The RTF file will contain links (just like the HTML
1724 # output) instead of page references. This makes the output suitable for online
1725 # browsing using Word or some other Word compatible readers that support those
1726 # fields.
1727 #
1728 # Note: WordPad (write) and others do not support links.
1729 # The default value is: NO.
1730 # This tag requires that the tag GENERATE_RTF is set to YES.
1731
1732 RTF_HYPERLINKS = NO
1733
1734 # Load stylesheet definitions from file. Syntax is similar to doxygen's config
1735 # file, i.e. a series of assignments. You only have to provide replacements,
1736 # missing definitions are set to their default value.
1737 #
1738 # See also section "Doxygen usage" for information on how to generate the
1739 # default style sheet that doxygen normally uses.
1740 # This tag requires that the tag GENERATE_RTF is set to YES.
1741
1742 RTF_STYLESHEET_FILE =
1743
1744 # Set optional variables used in the generation of an RTF document. Syntax is
1745 # similar to doxygen's config file. A template extensions file can be generated
1746 # using doxygen -e rtf extensionFile.
1747 # This tag requires that the tag GENERATE_RTF is set to YES.
1748
1749 RTF_EXTENSIONS_FILE =
1750
1751 #---------------------------------------------------------------------------
1752 # Configuration options related to the man page output
1753 #---------------------------------------------------------------------------
1754
1755 # If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
1756 # classes and files.
1757 # The default value is: NO.
1758
1759 GENERATE_MAN = NO
1760
1761 # The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
1762 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1763 # it. A directory man3 will be created inside the directory specified by
1764 # MAN_OUTPUT.
1765 # The default directory is: man.
1766 # This tag requires that the tag GENERATE_MAN is set to YES.
1767
1768 MAN_OUTPUT = man
1769
1770 # The MAN_EXTENSION tag determines the extension that is added to the generated
1771 # man pages. In case the manual section does not start with a number, the number
1772 # 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
1773 # optional.
1774 # The default value is: .3.
1775 # This tag requires that the tag GENERATE_MAN is set to YES.
1776
1777 MAN_EXTENSION = .3
1778
1779 # The MAN_SUBDIR tag determines the name of the directory created within
1780 # MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
1781 # MAN_EXTENSION with the initial . removed.
1782 # This tag requires that the tag GENERATE_MAN is set to YES.
1783
1784 MAN_SUBDIR =
1785
1786 # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
1787 # will generate one additional man file for each entity documented in the real
1788 # man page(s). These additional files only source the real man page, but without
1789 # them the man command would be unable to find the correct page.
1790 # The default value is: NO.
1791 # This tag requires that the tag GENERATE_MAN is set to YES.
1792
1793 MAN_LINKS = NO
1794
1795 #---------------------------------------------------------------------------
1796 # Configuration options related to the XML output
1797 #---------------------------------------------------------------------------
1798
1799 # If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
1800 # captures the structure of the code including all documentation.
1801 # The default value is: NO.
1802
1803 GENERATE_XML = NO
1804
1805 # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
1806 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1807 # it.
1808 # The default directory is: xml.
1809 # This tag requires that the tag GENERATE_XML is set to YES.
1810
1811 XML_OUTPUT = xml
1812
1813 # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
1814 # listings (including syntax highlighting and cross-referencing information) to
1815 # the XML output. Note that enabling this will significantly increase the size
1816 # of the XML output.
1817 # The default value is: YES.
1818 # This tag requires that the tag GENERATE_XML is set to YES.
1819
1820 XML_PROGRAMLISTING = YES
1821
1822 #---------------------------------------------------------------------------
1823 # Configuration options related to the DOCBOOK output
1824 #---------------------------------------------------------------------------
1825
1826 # If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
1827 # that can be used to generate PDF.
1828 # The default value is: NO.
1829
1830 GENERATE_DOCBOOK = NO
1831
1832 # The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
1833 # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
1834 # front of it.
1835 # The default directory is: docbook.
1836 # This tag requires that the tag GENERATE_DOCBOOK is set to YES.
1837
1838 DOCBOOK_OUTPUT = docbook
1839
1840 #---------------------------------------------------------------------------
1841 # Configuration options for the AutoGen Definitions output
1842 #---------------------------------------------------------------------------
1843
1844 # If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
1845 # Definitions (see http://autogen.sf.net) file that captures the structure of
1846 # the code including all documentation. Note that this feature is still
1847 # experimental and incomplete at the moment.
1848 # The default value is: NO.
1849
1850 GENERATE_AUTOGEN_DEF = NO
1851
1852 #---------------------------------------------------------------------------
1853 # Configuration options related to the Perl module output
1854 #---------------------------------------------------------------------------
1855
1856 # If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
1857 # file that captures the structure of the code including all documentation.
1858 #
1859 # Note that this feature is still experimental and incomplete at the moment.
1860 # The default value is: NO.
1861
1862 GENERATE_PERLMOD = NO
1863
1864 # If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
1865 # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
1866 # output from the Perl module output.
1867 # The default value is: NO.
1868 # This tag requires that the tag GENERATE_PERLMOD is set to YES.
1869
1870 PERLMOD_LATEX = NO
1871
1872 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
1873 # formatted so it can be parsed by a human reader. This is useful if you want to
1874 # understand what is going on. On the other hand, if this tag is set to NO the
1875 # size of the Perl module output will be much smaller and Perl will parse it
1876 # just the same.
1877 # The default value is: YES.
1878 # This tag requires that the tag GENERATE_PERLMOD is set to YES.
1879
1880 PERLMOD_PRETTY = YES
1881
1882 # The names of the make variables in the generated doxyrules.make file are
1883 # prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
1884 # so different doxyrules.make files included by the same Makefile don't
1885 # overwrite each other's variables.
1886 # This tag requires that the tag GENERATE_PERLMOD is set to YES.
1887
1888 PERLMOD_MAKEVAR_PREFIX =
1889
1890 #---------------------------------------------------------------------------
1891 # Configuration options related to the preprocessor
1892 #---------------------------------------------------------------------------
1893
1894 # If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
1895 # C-preprocessor directives found in the sources and include files.
1896 # The default value is: YES.
1897
1898 ENABLE_PREPROCESSING = YES
1899
1900 # If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
1901 # in the source code. If set to NO only conditional compilation will be
1902 # performed. Macro expansion can be done in a controlled way by setting
1903 # EXPAND_ONLY_PREDEF to YES.
1904 # The default value is: NO.
1905 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1906
1907 MACRO_EXPANSION = NO
1908
1909 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
1910 # the macro expansion is limited to the macros specified with the PREDEFINED and
1911 # EXPAND_AS_DEFINED tags.
1912 # The default value is: NO.
1913 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1914
1915 EXPAND_ONLY_PREDEF = NO
1916
1917 # If the SEARCH_INCLUDES tag is set to YES the includes files in the
1918 # INCLUDE_PATH will be searched if a #include is found.
1919 # The default value is: YES.
1920 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1921
1922 SEARCH_INCLUDES = YES
1923
1924 # The INCLUDE_PATH tag can be used to specify one or more directories that
1925 # contain include files that are not input files but should be processed by the
1926 # preprocessor.
1927 # This tag requires that the tag SEARCH_INCLUDES is set to YES.
1928
1929 INCLUDE_PATH =
1930
1931 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1932 # patterns (like *.h and *.hpp) to filter out the header-files in the
1933 # directories. If left blank, the patterns specified with FILE_PATTERNS will be
1934 # used.
1935 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1936
1937 INCLUDE_FILE_PATTERNS =
1938
1939 # The PREDEFINED tag can be used to specify one or more macro names that are
1940 # defined before the preprocessor is started (similar to the -D option of e.g.
1941 # gcc). The argument of the tag is a list of macros of the form: name or
1942 # name=definition (no spaces). If the definition and the "=" are omitted, "=1"
1943 # is assumed. To prevent a macro definition from being undefined via #undef or
1944 # recursively expanded use the := operator instead of the = operator.
1945 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1946
1947 PREDEFINED =
1948
1949 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
1950 # tag can be used to specify a list of macro names that should be expanded. The
1951 # macro definition that is found in the sources will be used. Use the PREDEFINED
1952 # tag if you want to use a different macro definition that overrules the
1953 # definition found in the source code.
1954 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1955
1956 EXPAND_AS_DEFINED =
1957
1958 # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
1959 # remove all references to function-like macros that are alone on a line, have
1960 # an all uppercase name, and do not end with a semicolon. Such function macros
1961 # are typically used for boiler-plate code, and will confuse the parser if not
1962 # removed.
1963 # The default value is: YES.
1964 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1965
1966 SKIP_FUNCTION_MACROS = YES
1967
1968 #---------------------------------------------------------------------------
1969 # Configuration options related to external references
1970 #---------------------------------------------------------------------------
1971
1972 # The TAGFILES tag can be used to specify one or more tag files. For each tag
1973 # file the location of the external documentation should be added. The format of
1974 # a tag file without this location is as follows:
1975 # TAGFILES = file1 file2 ...
1976 # Adding location for the tag files is done as follows:
1977 # TAGFILES = file1=loc1 "file2 = loc2" ...
1978 # where loc1 and loc2 can be relative or absolute paths or URLs. See the
1979 # section "Linking to external documentation" for more information about the use
1980 # of tag files.
1981 # Note: Each tag file must have a unique name (where the name does NOT include
1982 # the path). If a tag file is not located in the directory in which doxygen is
1983 # run, you must also specify the path to the tagfile here.
1984
1985 TAGFILES =
1986
1987 # When a file name is specified after GENERATE_TAGFILE, doxygen will create a
1988 # tag file that is based on the input files it reads. See section "Linking to
1989 # external documentation" for more information about the usage of tag files.
1990
1991 GENERATE_TAGFILE =
1992
1993 # If the ALLEXTERNALS tag is set to YES all external class will be listed in the
1994 # class index. If set to NO only the inherited external classes will be listed.
1995 # The default value is: NO.
1996
1997 ALLEXTERNALS = NO
1998
1999 # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
2000 # the modules index. If set to NO, only the current project's groups will be
2001 # listed.
2002 # The default value is: YES.
2003
2004 EXTERNAL_GROUPS = YES
2005
2006 # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
2007 # the related pages index. If set to NO, only the current project's pages will
2008 # be listed.
2009 # The default value is: YES.
2010
2011 EXTERNAL_PAGES = YES
2012
2013 # The PERL_PATH should be the absolute path and name of the perl script
2014 # interpreter (i.e. the result of 'which perl').
2015 # The default file (with absolute path) is: /usr/bin/perl.
2016
2017 PERL_PATH = /usr/bin/perl
2018
2019 #---------------------------------------------------------------------------
2020 # Configuration options related to the dot tool
2021 #---------------------------------------------------------------------------
2022
2023 # If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
2024 # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
2025 # NO turns the diagrams off. Note that this option also works with HAVE_DOT
2026 # disabled, but it is recommended to install and use dot, since it yields more
2027 # powerful graphs.
2028 # The default value is: YES.
2029
2030 CLASS_DIAGRAMS = YES
2031
2032 # You can define message sequence charts within doxygen comments using the \msc
2033 # command. Doxygen will then run the mscgen tool (see:
2034 # http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
2035 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
2036 # the mscgen tool resides. If left empty the tool is assumed to be found in the
2037 # default search path.
2038
2039 MSCGEN_PATH =
2040
2041 # You can include diagrams made with dia in doxygen documentation. Doxygen will
2042 # then run dia to produce the diagram and insert it in the documentation. The
2043 # DIA_PATH tag allows you to specify the directory where the dia binary resides.
2044 # If left empty dia is assumed to be found in the default search path.
2045
2046 DIA_PATH =
2047
2048 # If set to YES, the inheritance and collaboration graphs will hide inheritance
2049 # and usage relations if the target is undocumented or is not a class.
2050 # The default value is: YES.
2051
2052 HIDE_UNDOC_RELATIONS = YES
2053
2054 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
2055 # available from the path. This tool is part of Graphviz (see:
2056 # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
2057 # Bell Labs. The other options in this section have no effect if this option is
2058 # set to NO
2059 # The default value is: NO.
2060
2061 HAVE_DOT = NO
2062
2063 # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
2064 # to run in parallel. When set to 0 doxygen will base this on the number of
2065 # processors available in the system. You can set it explicitly to a value
2066 # larger than 0 to get control over the balance between CPU load and processing
2067 # speed.
2068 # Minimum value: 0, maximum value: 32, default value: 0.
2069 # This tag requires that the tag HAVE_DOT is set to YES.
2070
2071 DOT_NUM_THREADS = 0
2072
2073 # When you want a differently looking font n the dot files that doxygen
2074 # generates you can specify the font name using DOT_FONTNAME. You need to make
2075 # sure dot is able to find the font, which can be done by putting it in a
2076 # standard location or by setting the DOTFONTPATH environment variable or by
2077 # setting DOT_FONTPATH to the directory containing the font.
2078 # The default value is: Helvetica.
2079 # This tag requires that the tag HAVE_DOT is set to YES.
2080
2081 #DOT_FONTNAME = FreeSans
2082
2083 # The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
2084 # dot graphs.
2085 # Minimum value: 4, maximum value: 24, default value: 10.
2086 # This tag requires that the tag HAVE_DOT is set to YES.
2087
2088 DOT_FONTSIZE = 10
2089
2090 # By default doxygen will tell dot to use the default font as specified with
2091 # DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
2092 # the path where dot can find it using this tag.
2093 # This tag requires that the tag HAVE_DOT is set to YES.
2094
2095 DOT_FONTPATH =
2096
2097 # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
2098 # each documented class showing the direct and indirect inheritance relations.
2099 # Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
2100 # The default value is: YES.
2101 # This tag requires that the tag HAVE_DOT is set to YES.
2102
2103 CLASS_GRAPH = YES
2104
2105 # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
2106 # graph for each documented class showing the direct and indirect implementation
2107 # dependencies (inheritance, containment, and class references variables) of the
2108 # class with other documented classes.
2109 # The default value is: YES.
2110 # This tag requires that the tag HAVE_DOT is set to YES.
2111
2112 COLLABORATION_GRAPH = YES
2113
2114 # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
2115 # groups, showing the direct groups dependencies.
2116 # The default value is: YES.
2117 # This tag requires that the tag HAVE_DOT is set to YES.
2118
2119 GROUP_GRAPHS = YES
2120
2121 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
2122 # collaboration diagrams in a style similar to the OMG's Unified Modeling
2123 # Language.
2124 # The default value is: NO.
2125 # This tag requires that the tag HAVE_DOT is set to YES.
2126
2127 UML_LOOK = NO
2128
2129 # If the UML_LOOK tag is enabled, the fields and methods are shown inside the
2130 # class node. If there are many fields or methods and many nodes the graph may
2131 # become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
2132 # number of items for each type to make the size more manageable. Set this to 0
2133 # for no limit. Note that the threshold may be exceeded by 50% before the limit
2134 # is enforced. So when you set the threshold to 10, up to 15 fields may appear,
2135 # but if the number exceeds 15, the total amount of fields shown is limited to
2136 # 10.
2137 # Minimum value: 0, maximum value: 100, default value: 10.
2138 # This tag requires that the tag HAVE_DOT is set to YES.
2139
2140 UML_LIMIT_NUM_FIELDS = 10
2141
2142 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
2143 # collaboration graphs will show the relations between templates and their
2144 # instances.
2145 # The default value is: NO.
2146 # This tag requires that the tag HAVE_DOT is set to YES.
2147
2148 TEMPLATE_RELATIONS = NO
2149
2150 # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
2151 # YES then doxygen will generate a graph for each documented file showing the
2152 # direct and indirect include dependencies of the file with other documented
2153 # files.
2154 # The default value is: YES.
2155 # This tag requires that the tag HAVE_DOT is set to YES.
2156
2157 INCLUDE_GRAPH = YES
2158
2159 # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
2160 # set to YES then doxygen will generate a graph for each documented file showing
2161 # the direct and indirect include dependencies of the file with other documented
2162 # files.
2163 # The default value is: YES.
2164 # This tag requires that the tag HAVE_DOT is set to YES.
2165
2166 INCLUDED_BY_GRAPH = YES
2167
2168 # If the CALL_GRAPH tag is set to YES then doxygen will generate a call
2169 # dependency graph for every global function or class method.
2170 #
2171 # Note that enabling this option will significantly increase the time of a run.
2172 # So in most cases it will be better to enable call graphs for selected
2173 # functions only using the \callgraph command.
2174 # The default value is: NO.
2175 # This tag requires that the tag HAVE_DOT is set to YES.
2176
2177 CALL_GRAPH = NO
2178
2179 # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
2180 # dependency graph for every global function or class method.
2181 #
2182 # Note that enabling this option will significantly increase the time of a run.
2183 # So in most cases it will be better to enable caller graphs for selected
2184 # functions only using the \callergraph command.
2185 # The default value is: NO.
2186 # This tag requires that the tag HAVE_DOT is set to YES.
2187
2188 CALLER_GRAPH = NO
2189
2190 # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
2191 # hierarchy of all classes instead of a textual one.
2192 # The default value is: YES.
2193 # This tag requires that the tag HAVE_DOT is set to YES.
2194
2195 GRAPHICAL_HIERARCHY = YES
2196
2197 # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
2198 # dependencies a directory has on other directories in a graphical way. The
2199 # dependency relations are determined by the #include relations between the
2200 # files in the directories.
2201 # The default value is: YES.
2202 # This tag requires that the tag HAVE_DOT is set to YES.
2203
2204 DIRECTORY_GRAPH = YES
2205
2206 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
2207 # generated by dot.
2208 # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
2209 # to make the SVG files visible in IE 9+ (other browsers do not have this
2210 # requirement).
2211 # Possible values are: png, jpg, gif and svg.
2212 # The default value is: png.
2213 # This tag requires that the tag HAVE_DOT is set to YES.
2214
2215 DOT_IMAGE_FORMAT = png
2216
2217 # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
2218 # enable generation of interactive SVG images that allow zooming and panning.
2219 #
2220 # Note that this requires a modern browser other than Internet Explorer. Tested
2221 # and working are Firefox, Chrome, Safari, and Opera.
2222 # Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
2223 # the SVG files visible. Older versions of IE do not have SVG support.
2224 # The default value is: NO.
2225 # This tag requires that the tag HAVE_DOT is set to YES.
2226
2227 INTERACTIVE_SVG = NO
2228
2229 # The DOT_PATH tag can be used to specify the path where the dot tool can be
2230 # found. If left blank, it is assumed the dot tool can be found in the path.
2231 # This tag requires that the tag HAVE_DOT is set to YES.
2232
2233 DOT_PATH =
2234
2235 # The DOTFILE_DIRS tag can be used to specify one or more directories that
2236 # contain dot files that are included in the documentation (see the \dotfile
2237 # command).
2238 # This tag requires that the tag HAVE_DOT is set to YES.
2239
2240 DOTFILE_DIRS =
2241
2242 # The MSCFILE_DIRS tag can be used to specify one or more directories that
2243 # contain msc files that are included in the documentation (see the \mscfile
2244 # command).
2245
2246 MSCFILE_DIRS =
2247
2248 # The DIAFILE_DIRS tag can be used to specify one or more directories that
2249 # contain dia files that are included in the documentation (see the \diafile
2250 # command).
2251
2252 DIAFILE_DIRS =
2253
2254 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
2255 # that will be shown in the graph. If the number of nodes in a graph becomes
2256 # larger than this value, doxygen will truncate the graph, which is visualized
2257 # by representing a node as a red box. Note that doxygen if the number of direct
2258 # children of the root node in a graph is already larger than
2259 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
2260 # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
2261 # Minimum value: 0, maximum value: 10000, default value: 50.
2262 # This tag requires that the tag HAVE_DOT is set to YES.
2263
2264 DOT_GRAPH_MAX_NODES = 50
2265
2266 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
2267 # generated by dot. A depth value of 3 means that only nodes reachable from the
2268 # root by following a path via at most 3 edges will be shown. Nodes that lay
2269 # further from the root node will be omitted. Note that setting this option to 1
2270 # or 2 may greatly reduce the computation time needed for large code bases. Also
2271 # note that the size of a graph can be further restricted by
2272 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
2273 # Minimum value: 0, maximum value: 1000, default value: 0.
2274 # This tag requires that the tag HAVE_DOT is set to YES.
2275
2276 MAX_DOT_GRAPH_DEPTH = 0
2277
2278 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
2279 # background. This is disabled by default, because dot on Windows does not seem
2280 # to support this out of the box.
2281 #
2282 # Warning: Depending on the platform used, enabling this option may lead to
2283 # badly anti-aliased labels on the edges of a graph (i.e. they become hard to
2284 # read).
2285 # The default value is: NO.
2286 # This tag requires that the tag HAVE_DOT is set to YES.
2287
2288 DOT_TRANSPARENT = NO
2289
2290 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
2291 # files in one run (i.e. multiple -o and -T options on the command line). This
2292 # makes dot run faster, but since only newer versions of dot (>1.8.10) support
2293 # this, this feature is disabled by default.
2294 # The default value is: NO.
2295 # This tag requires that the tag HAVE_DOT is set to YES.
2296
2297 DOT_MULTI_TARGETS = NO
2298
2299 # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
2300 # explaining the meaning of the various boxes and arrows in the dot generated
2301 # graphs.
2302 # The default value is: YES.
2303 # This tag requires that the tag HAVE_DOT is set to YES.
2304
2305 GENERATE_LEGEND = YES
2306
2307 # If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
2308 # files that are used to generate the various graphs.
2309 # The default value is: YES.
2310 # This tag requires that the tag HAVE_DOT is set to YES.
2311
2312 DOT_CLEANUP = YES
+0
-110
lib/Facter.java.in less more
0 package com.puppetlabs;
1
2 import java.util.HashMap;
3 import java.util.TreeSet;
4
5 /**
6 * The Java shim for libfacter.
7 */
8 public class Facter {
9 static {
10 // Look for libfacter in the same place as facter.rb does
11 String facterDir = System.getenv("FACTERDIR");
12 if (facterDir == null) {
13 facterDir = "${CMAKE_INSTALL_PREFIX}";
14 }
15
16 // Load the library
17 System.load(facterDir.replaceAll("/$", "") + "/${LIBFACTER_INSTALL_DESTINATION}/libfacter.so");
18 }
19
20 /**
21 * Lookup a fact value by name.
22 * @param name The fact name.
23 * @return Returns the fact's value or null if not found.
24 */
25 public static native Object lookup(String name);
26
27 /**
28 * Entry point for testing.
29 * Expects one argument which is the fact to lookup.
30 * @param args The program arguments.
31 */
32 public static void main(String[] args) {
33 if (args.length != 1) {
34 System.out.println("usage: Facter <fact>");
35 return;
36 }
37 print(lookup(args[0]), 1);
38 System.out.println();
39 }
40
41 private static void indent(int level) {
42 if (level < 1) {
43 return;
44 }
45 for (int i = 0; i < (level * 2); ++i) {
46 System.out.print(" ");
47 }
48 }
49
50 @SuppressWarnings("unchecked")
51 private static void print(Object value, int level) {
52 if (value == null) {
53 return;
54 }
55
56 // Check for arrays
57 if (value instanceof Object[]) {
58 Object[] array = (Object[])value;
59 System.out.print("[\n");
60 boolean first = true;
61 for (Object element : array) {
62 if (first) {
63 first = false;
64 } else {
65 System.out.print(",\n");
66 }
67 indent(level);
68 print(element, 0);
69 }
70 System.out.print("\n");
71 indent(level - 1);
72 System.out.print("]");
73 return;
74 }
75
76 // Check for maps
77 if (value instanceof HashMap) {
78 HashMap map = (HashMap)value;
79 System.out.print("{\n");
80
81 // Print out the keys in lexicographical order (like a std::map)
82 TreeSet<String> keys = new TreeSet<String>(map.keySet());
83 boolean first = true;
84 for (String key : keys) {
85 if (first) {
86 first = false;
87 } else {
88 System.out.print(",\n");
89 }
90 indent(level);
91 System.out.format("%s => ", key);
92 print(map.get(key), level + 1);
93 }
94 System.out.print("\n");
95 indent(level - 1);
96 System.out.print("}");
97 return;
98 }
99
100 // Check for strings (print with quotes)
101 if (value instanceof String) {
102 System.out.format("\"%s\"", value);
103 return;
104 }
105
106 // Output the value
107 System.out.print(value);
108 }
109 }
+0
-11
lib/Gemfile less more
0 source ENV['GEM_SOURCE'] || "https://rubygems.org"
1
2 group :development, :test do
3 gem 'rake'
4 gem 'rspec'
5 gem 'mocha'
6 end
7
8 if File.exists? "#{__FILE__}.local"
9 eval(File.read("#{__FILE__}.local"), binding)
10 end
+0
-8
lib/Rakefile less more
0 # Rakefile for facter
1
2 require 'rake'
3 Dir['tasks/**/*.rake'].each { |t| load t }
4
5 task :default do
6 exec 'rake -T'
7 end
00 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
13 # Generates a markdown file containing fact documentation.
24 # usage: ruby generate.rb > facts.md
35
1214
1315 def format_facts(fact_hash)
1416 scope = OpenStruct.new({
15 :facts => fact_hash
16 })
17 facts: fact_hash
18 })
1719
1820 erb = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
1921 ERB.new(File.read(PATH_TO_TEMPLATE), trim_mode: '-')
2022 else
2123 ERB.new(File.read(PATH_TO_TEMPLATE), nil, '-')
2224 end
23 erb.result(scope.instance_eval {binding})
25 erb.result(scope.instance_eval { binding })
2426 end
2527
2628 print "## Modern Facts\n\n"
27 print format_facts(schema.reject {|name, info| info['hidden'] == true})
29 print format_facts(schema.reject { |_name, info| info['hidden'] == true })
2830 print "## Legacy Facts\n\n"
29 print format_facts(schema.reject {|name, info| info['hidden'].nil? || info['hidden'] == false})
31 print format_facts(schema.reject { |_name, info| info['hidden'].nil? || info['hidden'] == false })
0 # frozen_string_literal: true
1
2 require 'facter/framework/cli/cli_launcher'
3
4 cli = Facter::Cli.new([])
5
6 puts cli.man
2525 <%= schema['elements'].map{|name, info| format_fact_element(name, info)}.join('') %>
2626 <% end -%>
2727
28 <% if schema['resolution'] -%>
29 **Resolution:**
28 <% if schema['details'] -%>
29 **Details:**
3030
31 <%= schema['resolution'].split("\n").map { |line| "* " + line }.join("\n") %>
31 <%= schema['details'] %>
3232 <% end -%>
33
34 <% if schema['caveats'] -%>
35 **Caveats:**
36
37 <%= schema['caveats'].split("\n").map { |line| "* " + line }.join("\n") %>
3833 <% end -%>
39
40 ([↑ Back to top](#page-nav))
41
42 <% end -%>
0 # frozen_string_literal: true
1
2 module Facter
3 module Config
4 unless defined?(OS_HIERARCHY)
5 OS_HIERARCHY = [
6 {
7 'Linux' => [
8 {
9 'Debian' => [
10 'Elementary',
11 { 'Ubuntu' => [
12 'Linuxmint'
13 ] },
14 'Raspbian',
15 'Devuan'
16 ]
17 },
18 {
19 'Rhel' => %w[
20 Fedora
21 Amzn
22 Centos
23 Ol
24 Scientific
25 Meego
26 Oel
27 Ovs
28 ]
29 },
30 {
31 'Sles' => %w[
32 Opensuse
33 Sled
34 ]
35 },
36 'Gentoo',
37 'Alpine',
38 'Photon',
39 'Slackware',
40 'Mageia',
41 'Openwrt',
42 'Mariner'
43 ]
44 },
45 {
46 'Bsd' => [
47 'Freebsd'
48 ]
49 },
50 'Solaris',
51 'Macosx',
52 'Windows',
53 'Aix'
54 ].freeze
55 end
56 unless defined? FACT_GROUPS
57 FACT_GROUPS = {
58 'AIX NIM type' => [
59 'nim_type'
60 ],
61 'EC2' => %w[
62 ec2_metadata
63 ec2_userdata
64 ],
65 'GCE' => [
66 'gce'
67 ],
68 'Xen' => %w[
69 xen
70 xendomains
71 ],
72 'augeas' => %w[
73 augeas
74 augeasversion
75 ],
76 'desktop management interface' => %w[
77 dmi
78 bios_vendor
79 bios_version
80 bios_release_date
81 boardassettag
82 boardmanufacturer
83 boardproductname
84 boardserialnumber
85 chassisassettag
86 manufacturer
87 productname
88 serialnumber
89 uuid
90 chassistype
91 ],
92 'disks' => %w[
93 blockdevices
94 disks
95 ],
96 'file system' => %w[
97 mountpoints
98 filesystems
99 partitions
100 ],
101 'fips' => [
102 'fips_enabled'
103 ],
104 'hypervisors' => [
105 'hypervisors'
106 ],
107 'id' => %w[
108 id
109 gid
110 identity
111 ],
112 'kernel' => %w[
113 kernel
114 kernelversion
115 kernelrelease
116 kernelmajversion
117 ],
118 'load_average' => [
119 'load_averages'
120 ],
121 'memory' => %w[
122 memory
123 memoryfree
124 memoryfree_mb
125 memorysize
126 memorysize_mb
127 swapfree
128 swapfree_mb
129 swapsize
130 swapsize_mb
131 swapencrypted
132 ],
133 'networking' => %w[
134 networking
135 hostname
136 ipaddress
137 ipaddress6
138 netmask
139 netmask6
140 network
141 network6
142 scope6
143 macaddress
144 interfaces
145 domain
146 fqdn
147 dhcp_servers
148 ],
149 'operating system' => %w[
150 os
151 operatingsystem
152 osfamily
153 operatingsystemrelease
154 operatingsystemmajrelease
155 hardwaremodel
156 architecture
157 lsbdistid
158 lsbdistrelease
159 lsbdistcodename
160 lsbdistdescription
161 lsbmajdistrelease
162 lsbminordistrelease
163 lsbrelease
164 macosx_buildversion
165 macosx_productname
166 macosx_productversion
167 macosx_productversion_major
168 macosx_productversion_minor
169 macosx_productversion_patch
170 windows_display_version
171 windows_edition_id
172 windows_installation_type
173 windows_product_name
174 windows_release_id
175 system32
176 selinux
177 selinux_enforced
178 selinux_policyversion
179 selinux_current_mode
180 selinux_config_mode
181 selinux_config_policy
182 ],
183 'path' => [
184 'path'
185 ],
186 'processor' => %w[
187 processors
188 processorcount
189 physicalprocessorcount
190 hardwareisa
191 ],
192 'ssh' => %w[
193 ssh
194 sshdsakey
195 sshrsakey
196 sshecdsakey
197 sshed25519key
198 sshfp_dsa
199 sshfp_rsa
200 sshfp_ecdsa
201 sshfp_ed25519
202 ],
203 'system profiler' => %w[
204 system_profiler
205 sp_boot_mode
206 sp_boot_rom_version
207 sp_boot_volume
208 sp_cpu_type
209 sp_current_processor_speed
210 sp_kernel_version
211 sp_l2_cache_core
212 sp_l3_cache
213 sp_local_host_name
214 sp_machine_model
215 sp_machine_name
216 sp_number_processors
217 sp_os_version
218 sp_packages
219 sp_physical_memory
220 sp_platform_uuid
221 sp_secure_vm
222 sp_serial_number
223 sp_smc_version_system
224 sp_uptime
225 sp_user_name
226 ],
227 'timezone' => [
228 'timezone'
229 ],
230 'uptime' => %w[
231 system_uptime
232 uptime
233 uptime_days
234 uptime_hours
235 uptime_seconds
236 ],
237 'virtualization' => %w[
238 virtual
239 is_virtual
240 cloud
241 ],
242 'ldom' => [
243 'ldom'
244 ],
245 'Solaris zone' => %w[
246 zones
247 zonename
248 solaris_zones
249 ],
250 'ZFS' => %w[
251 zfs_version
252 zfs_featurenumbers
253 ],
254 'ZFS storage pool' => %w[
255 zpool_version
256 zpool_featureflags
257 zpool_featurenumbers
258 ],
259 'legacy' => [
260 'architecture',
261 'augeasversion',
262 'bios_release_date',
263 'bios_vendor',
264 'bios_version',
265 'blockdevice_*_model',
266 'blockdevice_*_size',
267 'blockdevice_*_vendor',
268 'blockdevices',
269 'boardassettag',
270 'boardmanufacturer',
271 'boardproductname',
272 'boardserialnumber',
273 'chassisassettag',
274 'chassistype',
275 'dhcp_servers',
276 'domain',
277 'fqdn',
278 'gid',
279 'hardwareisa',
280 'hardwaremodel',
281 'hostname',
282 'id',
283 'interfaces',
284 'ipaddress',
285 'ipaddress_.*',
286 'ipaddress_*',
287 'ipaddress6',
288 'ipaddress6_.*',
289 'ipaddress6_*',
290 'ldom_*',
291 'lsbdistcodename',
292 'lsbdistdescription',
293 'lsbdistid',
294 'lsbdistrelease',
295 'lsbmajdistrelease',
296 'lsbminordistrelease',
297 'lsbrelease',
298 'macaddress',
299 'macaddress_.*',
300 'macaddress_*',
301 'macosx_buildversion',
302 'macosx_productname',
303 'macosx_productversion',
304 'macosx_productversion_major',
305 'macosx_productversion_minor',
306 'macosx_productversion_patch',
307 'manufacturer',
308 'memoryfree',
309 'memoryfree_mb',
310 'memorysize',
311 'memorysize_mb',
312 'mtu_.*',
313 'mtu_*',
314 'netmask',
315 'netmask_.*',
316 'netmask_*',
317 'netmask6',
318 'netmask6_.*',
319 'netmask6_*',
320 'network',
321 'network_.*',
322 'network_*',
323 'network6',
324 'network6_.*',
325 'network6_*',
326 'operatingsystem',
327 'operatingsystemmajrelease',
328 'operatingsystemrelease',
329 'osfamily',
330 'physicalprocessorcount',
331 'processor[0-9]+.*',
332 'processorcount',
333 'productname',
334 'rubyplatform',
335 'rubysitedir',
336 'rubyversion',
337 'scope6',
338 'scope6_.*',
339 'selinux',
340 'selinux_config_mode',
341 'selinux_config_policy',
342 'selinux_current_mode',
343 'selinux_enforced',
344 'selinux_policyversion',
345 'serialnumber',
346 'sp_*',
347 'sp_boot_mode',
348 'sp_boot_rom_version',
349 'sp_boot_volume',
350 'sp_cpu_type',
351 'sp_current_processor_speed',
352 'sp_kernel_version',
353 'sp_l2_cache_core',
354 'sp_l3_cache',
355 'sp_local_host_name',
356 'sp_machine_model',
357 'sp_machine_name',
358 'sp_number_processors',
359 'sp_os_version',
360 'sp_packages',
361 'sp_physical_memory',
362 'sp_platform_uuid',
363 'sp_secure_vm',
364 'sp_serial_number',
365 'sp_smc_version_system',
366 'sp_uptime',
367 'sp_user_name',
368 'ssh.*key',
369 'ssh*key',
370 'sshfp_.*',
371 'sshfp_*',
372 'swapencrypted',
373 'swapfree',
374 'swapfree_mb',
375 'swapsize',
376 'swapsize_mb',
377 'system32',
378 'uptime',
379 'uptime_days',
380 'uptime_hours',
381 'uptime_seconds',
382 'uuid',
383 'windows_edition_id',
384 'windows_installation_type',
385 'windows_product_name',
386 'windows_release_id',
387 'xendomains',
388 'zone_*_brand',
389 'zone_*_id',
390 'zone_*_iptype',
391 'zone_*_name',
392 'zone_*_path',
393 'zone_*_status',
394 'zone_*_uuid',
395 'zonename',
396 'zones'
397 ]
398 }.freeze
399 end
400 end
401 end
0 # frozen_string_literal: true
1
2 # Aggregates provide a mechanism for facts to be resolved in multiple steps.
3 #
4 # Aggregates are evaluated in two parts: generating individual chunks and then
5 # aggregating all chunks together. Each chunk is a block of code that generates
6 # a value, and may depend on other chunks when it runs. After all chunks have
7 # been evaluated they are passed to the aggregate block as Hash<name, result>.
8 # The aggregate block converts the individual chunks into a single value that is
9 # returned as the final value of the aggregate.
10 #
11 # @api public
12 # @since 2.0.0
13 module Facter
14 module Core
15 class Aggregate
16 include LegacyFacter::Core::Suitable
17 include LegacyFacter::Core::Resolvable
18
19 # @!attribute [r] name
20 #
21 # @return [Symbol] The name of the aggregate resolution
22 #
23 # @api public
24 attr_reader :name
25
26 # @!attribute [r] fact_type
27 #
28 # @return [Symbol] The fact type of the aggregate resolution
29 #
30 # @api private
31 attr_reader :fact_type
32
33 # @!attribute [r] deps
34 #
35 # @return [LegacyFacter::Core::DirectedGraph]
36 #
37 # @api private
38 attr_reader :deps
39
40 # @!attribute [r] confines
41 #
42 # @return [Array<LegacyFacter::Core::Confine>] An array of confines restricting
43 # this to a specific platform
44 #
45 # @api private
46 attr_reader :confines
47
48 # @!attribute [r] fact
49 #
50 # @return [Facter::Util::Fact]
51 #
52 # @api private
53 attr_reader :fact
54
55 # Create a new aggregated resolution mechanism.
56 #
57 # @param name [String] The name of the resolution.
58 # @param fact [Facter::Fact] The fact to which this
59 # resolution will be added.
60 #
61 # @return [Facter::Util::Resolution] The created resolution
62 #
63 # @api private
64 def initialize(name, fact)
65 @name = name
66 @fact = fact
67
68 @confines = []
69 @chunks = {}
70
71 @aggregate = nil
72 @deps = LegacyFacter::Core::DirectedGraph.new
73 end
74
75 # Compares the weight of two aggregate facts
76 #
77 # @return [bool] Weight comparison result
78 #
79 # @api private
80 def <=>(other)
81 weight <=> other.weight
82 end
83
84 # Sets options for the aggregate fact
85 #
86 # @return [nil]
87 #
88 # @api private
89 def options(options)
90 accepted_options = %i[name timeout weight fact_type]
91 accepted_options.each do |option_name|
92 instance_variable_set("@#{option_name}", options.delete(option_name)) if options.key?(option_name)
93 end
94 raise ArgumentError, "Invalid aggregate options #{options.keys.inspect}" unless options.keys.empty?
95 end
96
97 # Evaluates the given block
98 #
99 # @return [String] Result of the block's evaluation
100 #
101 # @api private
102 def evaluate(&block)
103 instance_eval(&block)
104 end
105
106 # Define a new chunk for the given aggregate
107 #
108 # @example Defining a chunk with no dependencies
109 # aggregate.chunk(:mountpoints) do
110 # # generate mountpoint information
111 # end
112 #
113 # @example Defining an chunk to add mount options
114 # aggregate.chunk(:mount_options, :require => [:mountpoints]) do |mountpoints|
115 # # `mountpoints` is the result of the previous chunk
116 # # generate mount option information based on the mountpoints
117 # end
118 #
119 # @param name [Symbol] A name unique to this aggregate describing the chunk
120 #
121 # @param opts [Hash] Hash with options for the aggregate fact
122 #
123 # @return [Facter::Core::Aggregate] The aggregate object
124 #
125 # @api public
126 def chunk(name, opts = {}, &block)
127 evaluate_params(name, &block)
128
129 deps = Array(opts.delete(:require))
130
131 unless opts.empty?
132 raise ArgumentError, "Unexpected options passed to #{self.class.name}#chunk: #{opts.keys.inspect}"
133 end
134
135 @deps[name] = deps
136 @chunks[name] = block
137 self
138 end
139
140 # Define how all chunks should be combined
141 #
142 # @example Merge all chunks
143 # aggregate.aggregate do |chunks|
144 # final_result = {}
145 # chunks.each_value do |chunk|
146 # final_result.deep_merge(chunk)
147 # end
148 # final_result
149 # end
150 #
151 # @example Sum all chunks
152 # aggregate.aggregate do |chunks|
153 # total = 0
154 # chunks.each_value do |chunk|
155 # total += chunk
156 # end
157 # total
158 # end
159 #
160 # @yield [Hash<Symbol, Object>] A hash containing chunk names and
161 # chunk values
162 #
163 # @return [Facter::Core::Aggregate] The aggregate fact
164 #
165 # @api public
166 def aggregate(&block)
167 raise ArgumentError, "#{self.class.name}#aggregate requires a block" unless block_given?
168
169 @aggregate = block
170 self
171 end
172
173 # Returns the fact's resolution type
174 #
175 # @return [Symbol] The fact's type
176 #
177 # @api private
178 def resolution_type
179 :aggregate
180 end
181
182 private
183
184 def evaluate_params(name)
185 raise ArgumentError, "#{self.class.name}#chunk requires a block" unless block_given?
186 raise ArgumentError, "#{self.class.name}#expected chunk name to be a Symbol" unless name.is_a? Symbol
187 end
188
189 # Evaluate the results of this aggregate.
190 #
191 # @see Facter::Core::Resolvable#value
192 # @return [Object]
193 def resolve_value
194 chunk_results = run_chunks
195 aggregate_results(chunk_results)
196 end
197
198 # Order all chunks based on their dependencies and evaluate each one, passing
199 # dependent chunks as needed.
200 #
201 # @return [Hash<Symbol, Object>] A hash containing the chunk that
202 # generated value and the related value.
203 def run_chunks
204 results = {}
205 order_chunks.each do |(name, block)|
206 input = @deps[name].map { |dep_name| results[dep_name] }
207
208 output = block.call(*input)
209 results[name] = LegacyFacter::Util::Values.deep_freeze(output)
210 end
211
212 results
213 end
214
215 # Process the results of all chunks with the aggregate block and return the
216 # results. If no aggregate block has been specified, fall back to deep
217 # merging the given data structure
218 #
219 # @param results [Hash<Symbol, Object>] A hash of chunk names and the output
220 # of that chunk.
221 # @return [Object]
222 def aggregate_results(results)
223 if @aggregate
224 @aggregate.call(results)
225 else
226 default_aggregate(results)
227 end
228 end
229
230 def default_aggregate(results)
231 results.values.inject do |result, current|
232 LegacyFacter::Util::Values.deep_merge(result, current)
233 end
234 rescue LegacyFacter::Util::Values::DeepMergeError => e
235 raise ArgumentError, 'Could not deep merge all chunks (Original error: ' \
236 "#{e.message}), ensure that chunks return either an Array or Hash or " \
237 'override the aggregate block', e.backtrace
238 end
239
240 # Order chunks based on their dependencies
241 #
242 # @return [Array<Symbol, Proc>] A list of chunk names and blocks in evaluation order.
243 def order_chunks
244 unless @deps.acyclic?
245 raise DependencyError,
246 "Could not order chunks; found the following dependency cycles: #{@deps.cycles.inspect}"
247 end
248
249 sorted_names = @deps.tsort
250
251 sorted_names.map do |name|
252 [name, @chunks[name]]
253 end
254 end
255
256 class DependencyError < StandardError; end
257 end
258 end
259 end
0 # frozen_string_literal: true
1
2 require 'set'
3 require 'tsort'
4
5 module LegacyFacter
6 module Core
7 class DirectedGraph < Hash
8 include TSort
9
10 def acyclic?
11 cycles.empty?
12 end
13
14 def cycles
15 cycles = []
16 each_strongly_connected_component do |component|
17 cycles << component if component.size > 1
18 end
19 cycles
20 end
21
22 alias tsort_each_node each_key
23
24 def tsort_each_child(node)
25 fetch(node, []).each do |child|
26 yield child
27 end
28 end
29
30 def tsort
31 missing = Set.new(values.flatten) - Set.new(keys)
32
33 unless missing.empty?
34 raise MissingVertex, "Cannot sort elements; cannot depend on missing elements #{missing.to_a}"
35 end
36
37 super
38 rescue TSort::Cyclic
39 raise CycleError, "Cannot sort elements; found the following cycles: #{cycles.inspect}"
40 end
41
42 class CycleError < StandardError; end
43 class MissingVertex < StandardError; end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 require_relative 'popen3'
3
4 module Facter
5 module Core
6 module Execution
7 class Base
8 STDERR_MESSAGE = 'Command %s completed with the following stderr message: %s'
9 VALID_OPTIONS = %i[on_fail expand logger timeout].freeze
10
11 def initialize
12 @log = Log.new(self)
13 end
14
15 # This is part of the public API. No race condition can happen
16 # here because custom facts are executed sequentially
17 def with_env(values)
18 old = {}
19 values.each do |var, value|
20 # save the old value if it exists
21 if (old_val = ENV[var])
22 old[var] = old_val
23 end
24 # set the new (temporary) value for the environment variable
25 ENV[var] = value
26 end
27 # execute the caller's block, capture the return value
28 rv = yield
29 # use an ensure block to make absolutely sure we restore the variables
30 ensure
31 # restore the old values
32 values.each do |var, _value|
33 if old.include?(var)
34 ENV[var] = old[var]
35 else
36 # if there was no old value, delete the key from the current environment variables hash
37 ENV.delete(var)
38 end
39 end
40 # return the captured return value
41 rv
42 end
43
44 def execute(command, options = {})
45 on_fail, expand, logger, timeout = extract_options(options)
46
47 expanded_command = if !expand && builtin_command?(command) || logger
48 command
49 else
50 expand_command(command)
51 end
52
53 if expanded_command.nil?
54 if on_fail == :raise
55 raise Facter::Core::Execution::ExecutionFailure.new,
56 "Could not execute '#{command}': command not found"
57 end
58
59 return on_fail
60 end
61
62 out, = execute_command(expanded_command, on_fail, logger, timeout)
63 out
64 end
65
66 def execute_command(command, on_fail = nil, logger = nil, timeout = nil)
67 timeout ||= 300
68 begin
69 # Set LC_ALL and LANG to force i18n to C for the duration of this exec;
70 # this ensures that any code that parses the
71 # output of the command can expect it to be in a consistent / predictable format / locale
72 opts = { 'LC_ALL' => 'C', 'LANG' => 'C' }
73 require 'timeout'
74 @log.debug("Executing command: #{command}")
75 out, stderr = Popen3.popen3e(opts, command.to_s) do |_, stdout, stderr, pid|
76 stdout_messages = +''
77 stderr_messages = +''
78 out_reader = Thread.new { stdout.read }
79 err_reader = Thread.new { stderr.read }
80 begin
81 Timeout.timeout(timeout) do
82 stdout_messages << out_reader.value
83 stderr_messages << err_reader.value
84 end
85 rescue Timeout::Error
86 message = "Timeout encounter after #{timeout}s, killing process with pid: #{pid}"
87 Process.kill('KILL', pid)
88 on_fail == :raise ? (raise StandardError, message) : @log.debug(message)
89 ensure
90 out_reader.kill
91 err_reader.kill
92 end
93 [stdout_messages, stderr_messages]
94 end
95 log_stderr(stderr, command, logger)
96 rescue StandardError => e
97 message = "Failed while executing '#{command}': #{e.message}"
98 if logger
99 @log.debug(message)
100 return ''
101 end
102
103 return on_fail unless on_fail == :raise
104
105 raise Facter::Core::Execution::ExecutionFailure.new, message
106 end
107
108 [out.strip, stderr]
109 end
110
111 private
112
113 def extract_options(options)
114 on_fail = options.fetch(:on_fail, :raise)
115 expand = options.fetch(:expand, true)
116 logger = options[:logger]
117 timeout = (options[:timeout] || options[:time_limit] || options[:limit]).to_i
118 timeout = timeout.positive? ? timeout : nil
119
120 extra_keys = options.keys - VALID_OPTIONS
121 unless extra_keys.empty?
122 @log.warn("Unexpected key passed to Facter::Core::Execution.execute option: #{extra_keys.join(',')}" \
123 " - valid keys: #{VALID_OPTIONS.join(',')}")
124 end
125
126 [on_fail, expand, logger, timeout]
127 end
128
129 def log_stderr(msg, command, logger)
130 return if !msg || msg.empty?
131
132 unless logger
133 file_name = command.split('/').last
134 logger = Facter::Log.new(file_name)
135 end
136
137 logger.debug(format(STDERR_MESSAGE, command, msg.strip))
138 end
139
140 def builtin_command?(command)
141 output, _status = Open3.capture2("type #{command}")
142 output.chomp =~ /builtin/ ? true : false
143 end
144 end
145 end
146 end
147 end
0 # frozen_string_literal: true
1
2 #
3 # Because Open3 uses Process.detach the env $? is not set so
4 # this class reimplements Open3.popen3 with Process.wait instead.
5 # [FACT-2934] When calling Facter::Core::Execution, $? and $CHILD_STATUS
6 # ruby env variables should be set.
7 #
8
9 module Facter
10 module Core
11 module Execution
12 class Popen3
13 @log ||= Log.new(self)
14
15 def self.popen_rune(cmd, opts, child_io, parent_io) # :nodoc:
16 pid = spawn(*cmd, opts)
17 child_io.each(&:close)
18 result = [*parent_io, pid]
19 if defined? yield
20 begin
21 return yield(*result)
22 ensure
23 parent_io.each(&:close)
24 begin
25 Process.wait(pid)
26 rescue Errno::ENOENT
27 # For some reason, the first Process.wait executed in JRuby
28 # always fails with ENOENT. However, the command output is
29 # filled in so we just need to silently continue.
30 # https://github.com/jruby/jruby/issues/5971
31 raise unless RUBY_PLATFORM == 'java'
32
33 @log.debug('Caught ENOENT during Process.wait on JRuby, continuing...')
34 end
35 end
36 end
37 result
38 end
39
40 def self.popen3e(*cmd, &block)
41 opts = if cmd.last.is_a? Hash
42 cmd.pop.dup
43 else
44 {}
45 end
46 in_r, in_w = IO.pipe
47 opts[:in] = in_r
48 in_w.sync = true
49 out_r, out_w = IO.pipe
50 opts[:out] = out_w
51 err_r, err_w = IO.pipe
52 opts[:err] = err_w
53 popen_rune(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
54 end
55 end
56 end
57 end
58 end
0 module Facter
1 module Core
2 module Execution
3 class Posix < Facter::Core::Execution::Base
4 DEFAULT_SEARCH_PATHS = ['/sbin', '/usr/sbin'].freeze
5
6 def search_paths
7 # Make sure custom_facts is usable even for non-root users. Most commands
8 # in /sbin (like ifconfig) can be run as non privileged users as
9 # long as they do not modify anything - which we do not do with custom_facts
10 ENV['PATH'].split(File::PATH_SEPARATOR) + DEFAULT_SEARCH_PATHS
11 end
12
13 def which(bin)
14 if absolute_path?(bin)
15 return bin if File.executable?(bin) && FileTest.file?(bin)
16 else
17 search_paths.each do |dir|
18 dest = File.join(dir, bin)
19 return dest if File.executable?(dest) && FileTest.file?(dest)
20 end
21 end
22 nil
23 end
24
25 ABSOLUTE_PATH_REGEX = %r{^/}.freeze
26
27 def absolute_path?(path)
28 !!(path =~ ABSOLUTE_PATH_REGEX)
29 end
30
31 DOUBLE_QUOTED_COMMAND = /\A"(.+?)"(?:\s+(.*))?/.freeze
32 SINGLE_QUOTED_COMMAND = /\A'(.+?)'(?:\s+(.*))?/.freeze
33
34 def expand_command(command)
35 exe = nil
36 args = nil
37
38 if (match = (command.match(DOUBLE_QUOTED_COMMAND) || command.match(SINGLE_QUOTED_COMMAND)))
39 exe, args = match.captures
40 else
41 exe, args = command.split(/ /, 2)
42 end
43
44 return unless exe && (expanded = which(exe))
45
46 expanded = "'#{expanded}'" if expanded =~ /\s/
47 expanded << " #{args}" if args
48
49 expanded
50 end
51 end
52 end
53 end
54 end
0 module Facter
1 module Core
2 module Execution
3 class Windows < Facter::Core::Execution::Base
4 def search_paths
5 ENV['PATH'].split(File::PATH_SEPARATOR)
6 end
7
8 DEFAULT_COMMAND_EXTENSIONS = %w[.COM .EXE .BAT .CMD].freeze
9
10 def which(bin)
11 # `echo` is allowed for facter 3.x compatibility, otherwise
12 # all commands much be found on the PATH or absolute.
13 return bin if /^echo$/i =~ bin
14
15 if absolute_path?(bin)
16 return bin if File.executable?(bin)
17 else
18 search_paths.each do |dir|
19 dest = File.join(dir, bin)
20 dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
21 if File.extname(dest).empty?
22 exts = ENV['PATHEXT']
23 exts = exts ? exts.split(File::PATH_SEPARATOR) : DEFAULT_COMMAND_EXTENSIONS
24 exts.each do |ext|
25 destext = dest + ext
26 return destext if File.executable?(destext)
27 end
28 end
29 return dest if File.executable?(dest)
30 end
31 end
32 nil
33 end
34
35 slash = '[\\\\/]'
36 name = '[^\\\\/]+'
37 ABSOLUTE_PATH_REGEX =
38 /^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))/i.freeze
39
40 def absolute_path?(path)
41 !!(path =~ ABSOLUTE_PATH_REGEX)
42 end
43
44 DOUBLE_QUOTED_COMMAND = /\A"(.+?)"(?:\s+(.*))?/.freeze
45
46 def expand_command(command)
47 exe = nil
48 args = nil
49
50 if (match = command.match(DOUBLE_QUOTED_COMMAND))
51 exe, args = match.captures
52 else
53 exe, args = command.split(/ /, 2)
54 end
55
56 return unless exe && (expanded = which(exe))
57
58 expanded = "\"#{expanded}\"" if expanded =~ /\s+/
59 expanded << " #{args}" if args
60
61 expanded
62 end
63
64 def execute(command, options = {})
65 expand = options.fetch(:expand, true)
66 raise ArgumentError.new, 'Unsupported argument on Windows expand with value false' unless expand
67
68 super(command, options)
69 end
70 end
71 end
72 end
73 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Core
4 module Execution
5 @@impl = if LegacyFacter::Util::Config.windows?
6 Facter::Core::Execution::Windows.new
7 else
8 Facter::Core::Execution::Posix.new
9 end
10
11 def self.impl
12 @@impl
13 end
14
15 module_function
16
17 # Returns the locations to be searched when looking for a binary. This is
18 # currently determined by the +PATH+ environment variable plus `/sbin`
19 # and `/usr/sbin` when run on unix
20 #
21 # @return [Array<String>] The paths to be searched for binaries
22 #
23 # @api private
24 def search_paths
25 @@impl.search_paths
26 end
27
28 # Determines the full path to a binary. If the supplied filename does not
29 # already describe an absolute path then different locations (determined
30 # by {search_paths}) will be searched for a match.
31 #
32 # @param bin [String] The executable to locate
33 #
34 # @return [String/nil] The full path to the executable or nil if not
35 # found
36 #
37 # @api public
38 def which(bin)
39 @@impl.which(bin)
40 end
41
42 # Determine in a platform-specific way whether a path is absolute. This
43 # defaults to the local platform if none is specified.
44 #
45 # @param path [String] The path to check
46
47 # @param platform [:posix/:windows/nil] The platform logic to use
48 #
49 # @api private
50 def absolute_path?(path, platform = nil)
51 case platform
52 when :posix
53 Facter::Core::Execution::Posix.new.absolute_path?(path)
54 when :windows
55 Facter::Core::Execution::Windows.new.absolute_path?(path)
56 else
57 @@impl.absolute_path?(path)
58 end
59 end
60
61 # Given a command line, this returns the command line with the
62 # executable written as an absolute path. If the executable contains
63 # spaces, it has to be put in double quotes to be properly recognized.
64 #
65 # @param command [String] the command line
66 #
67 # @return [String/nil] The command line with the executable's path
68 # expanded, or nil if the executable cannot be found.
69 #
70 # @api private
71 def expand_command(command)
72 @@impl.expand_command(command)
73 end
74
75 # Overrides environment variables within a block of code. The
76 # specified values will be set for the duration of the block, after
77 # which the original values (if any) will be restored.
78 #
79 # @param values [Hash<String=>String>] A hash of the environment
80 # variables to override
81 #
82 # @return [String] The block's return string
83 #
84 # @api private
85 def with_env(values, &block)
86 @@impl.with_env(values, &block)
87 end
88
89 # Try to execute a command and return the output.
90 #
91 # @param command [String] Command to run
92 #
93 # @return [String/nil] Output of the program, or nil if the command does
94 # not exist or could not be executed.
95 #
96 # @deprecated Use #{execute} instead
97 # @api public
98 def exec(command)
99 @@impl.execute(command, on_fail: nil)
100 end
101
102 # Execute a command and return the output of that program.
103 #
104 # @param command [String] Command to run
105 #
106 # @param options [Hash] Hash with options for the command
107 #
108 # @option options [Object] :on_fail How to behave when the command could
109 # not be run. Specifying :raise will raise an error, anything else will
110 # return that object on failure. Default is :raise.
111 # @option options [Object] :logger Optional logger used to log the
112 # command's stderr.
113 # @option options :timeout Optional time out for the specified
114 # command. If no timeout is passed, a default of 300 seconds is used.
115 #
116 # @raise [Facter::Core::Execution::ExecutionFailure] If the command does
117 # not exist or could not be executed and :on_fail is set to :raise
118 #
119 # @return [String] the output of the program, or the value of :on_fail (if it's different than :raise) if
120 # command execution failed and :on_fail was specified.
121 #
122 # @api public
123 def execute(command, options = {})
124 @@impl.execute(command, options)
125 end
126
127 # Execute a command and return the stdout and stderr of that program.
128 #
129 # @param command [String] Command to run
130 #
131 # @param on_fail[Object] How to behave when the command could
132 # not be run. Specifying :raise will raise an error, anything else will
133 # return that object on failure. Default is :raise.
134 # @param logger Optional logger used to log the command's stderr.
135 # @param timeout Optional time out for the specified command. If no timeout is passed,
136 # a default of 300 seconds is used.
137 #
138 # @raise [Facter::Core::Execution::ExecutionFailure] If the command does
139 # not exist or could not be executed and :on_fail is set to :raise
140 #
141 # @return [String, String] the stdout and stderr of the program, or the value of
142 # :on_fail if command execution failed and :on_fail was specified.
143 #
144 # @api private
145 def execute_command(command, on_fail = nil, logger = nil, timeout = nil)
146 @@impl.execute_command(command, on_fail, logger, timeout)
147 end
148
149 class ExecutionFailure < StandardError; end
150 end
151 end
152 end
0 # frozen_string_literal: true
1
2 require 'facter/custom_facts/core/legacy_facter'
3 require 'facter/custom_facts/util/fact'
4 require 'facter/custom_facts/util/collection'
5 require 'facter/custom_facts/util/fact'
6 require 'facter/custom_facts/util/loader'
7 require 'facter/custom_facts/core/execution/base'
8 require 'facter/custom_facts/core/execution/windows'
9 require 'facter/custom_facts/core/execution/posix'
10 require 'facter/custom_facts/util/values'
11 require 'facter/custom_facts/util/confine'
12
13 require 'facter/custom_facts/util/config'
14
15 require 'facter/custom_facts/util/normalization'
16 require 'facter/custom_facts/core/execution'
17 require 'facter/custom_facts/core/resolvable'
18 require 'facter/custom_facts/core/suitable'
19 require 'facter/custom_facts/util/resolution'
20 require 'facter/custom_facts/core/directed_graph'
21 require 'facter/custom_facts/core/resolvable'
22 require 'facter/custom_facts/core/aggregate'
23 require 'facter/custom_facts/util/composite_loader'
24 require 'facter/custom_facts/util/parser'
25 require 'facter/custom_facts/util/directory_loader'
26 require 'facter/custom_facts/util/nothing_loader'
27 require 'facter/custom_facts/util/nothing_loader'
0 # frozen_string_literal: true
1
2 # Facter - Host Fact Detection and Reporting
3 #
4 # Copyright 2011 Puppet Labs Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17
18 require 'pathname'
19
20 require 'facter/custom_facts/core/file_loader'
21
22 # Functions as a hash of 'facts' about your system system, such as MAC
23 # address, IP address, architecture, etc.
24 #
25 # @example Retrieve a fact
26 # puts Facter['operatingsystem'].value
27 #
28 # @example Retrieve all facts
29 # Facter.to_hash
30 # => { "kernel"=>"Linux", "uptime_days"=>0, "ipaddress"=>"10.0.0.1" }
31 #
32 # @api public
33 module LegacyFacter
34 # Most core functionality of custom_facts is implemented in `Facter::Util`.
35 # @api public
36
37 include Comparable
38 include Enumerable
39
40 # module methods
41
42 # Accessor for the collection object which holds all the facts
43 # @return [LegacyFacter::Util::Collection] the collection of facts
44 #
45 # @api private
46 def self.collection
47 unless defined?(@collection) && @collection
48 @collection = LegacyFacter::Util::Collection.new(
49 LegacyFacter::Util::Loader.new,
50 LegacyFacter::Util::Config.ext_fact_loader
51 )
52 end
53 @collection
54 end
55
56 # Returns whether the JSON "feature" is available.
57 #
58 # @api private
59 def self.json?
60 require 'json'
61 true
62 rescue LoadError
63 false
64 end
65
66 # Returns a fact object by name. If you use this, you still have to
67 # call {Facter::Util::Fact#value `value`} on it to retrieve the actual
68 # value.
69 #
70 # @param name [String] the name of the fact
71 #
72 # @return [Facter::Util::Fact, nil] The fact object, or nil if no fact
73 # is found.
74 #
75 # @api public
76 def self.[](name)
77 collection.fact(name)
78 end
79
80 # (see [])
81 def self.fact(name)
82 collection.fact(name)
83 end
84
85 # Flushes cached values for all facts. This does not cause code to be
86 # reloaded; it only clears the cached results.
87 #
88 # @return [void]
89 #
90 # @api public
91 def self.flush
92 collection.flush
93 end
94
95 # Lists all fact names
96 #
97 # @return [Array<String>] array of fact names
98 #
99 # @api public
100 def self.list
101 collection.list
102 end
103
104 # Gets the value for a fact. Returns `nil` if no such fact exists.
105 #
106 # @param name [String] the fact name
107 # @return [Object, nil] the value of the fact, or nil if no fact is
108 # found
109 #
110 # @api public
111 def self.value(name)
112 collection.value(name)
113 end
114
115 # Gets a hash mapping fact names to their values
116 #
117 # @return [Hash{String => Object}] the hash of fact names and values
118 #
119 # @api public
120 def self.to_hash
121 collection.load_all
122 collection.to_hash
123 end
124
125 # Define a new fact or extend an existing fact.
126 #
127 # @param name [Symbol] The name of the fact to define
128 # @param options [Hash] A hash of options to set on the fact
129 #
130 # @return [Facter::Util::Fact] The fact that was defined
131 #
132 # @api public
133 # @see {Facter::Util::Collection#define_fact}
134 def self.define_fact(name, options = {}, &block)
135 collection.define_fact(name, options, &block)
136 end
137
138 # Adds a {Facter::Util::Resolution resolution} mechanism for a named
139 # fact. This does not distinguish between adding a new fact and adding
140 # a new way to resolve a fact.
141 #
142 # @overload add(name, options = {}, { || ... })
143 # @param name [String] the fact name
144 # @param options [Hash] optional parameters for the fact - attributes
145 # of {Facter::Util::Fact} and {Facter::Util::Resolution} can be
146 # supplied here
147 # @option options [Integer] :timeout set the
148 # {Facter::Util::Resolution#timeout timeout} for this resolution
149 # @param block [Proc] a block defining a fact resolution
150 #
151 # @return [Facter::Util::Fact] the fact object, which includes any previously
152 # defined resolutions
153 #
154 # @api public
155 def self.add(name, options = {}, &block)
156 collection.add(name, options, &block)
157 end
158
159 # Iterates over fact names and values
160 #
161 # @yieldparam [String] name the fact name
162 # @yieldparam [String] value the current value of the fact
163 #
164 # @return [void]
165 #
166 # @api public
167 def self.each
168 # Make sure all facts are loaded.
169 collection.load_all
170
171 collection.each do |*args|
172 yield(*args)
173 end
174 end
175
176 # Clears all cached values and removes all facts from memory.
177 #
178 # @return [void]
179 #
180 # @api public
181 def self.clear
182 LegacyFacter.flush
183 LegacyFacter.reset
184 end
185
186 # Removes all facts from memory. Use this when the fact code has
187 # changed on disk and needs to be reloaded.
188 #
189 # @return [void]
190 #
191 # @api public
192 def self.reset
193 @collection = nil
194 end
195
196 # Loads all facts.
197 #
198 # @return [void]
199 #
200 # @api public
201 def self.loadfacts
202 collection.load_all
203 end
204 end
0 # frozen_string_literal: true
1
2 require 'timeout'
3
4 # The resolvable mixin defines behavior for evaluating and returning fact
5 # resolutions.
6 #
7 # Classes including this mixin should implement a #name method describing
8 # the value being resolved and a #resolve_value that actually executes the code
9 # to resolve the value.
10 module LegacyFacter
11 module Core
12 module Resolvable
13 # The timeout, in seconds, for evaluating this resolution.
14 # @return [Integer]
15 # @api public
16 attr_accessor :timeout
17 attr_reader :logger
18
19 # Return the timeout period for resolving a value.
20 # (see #timeout)
21 # @return [Numeric]
22 # @comment requiring 'timeout' stdlib class causes Object#timeout to be
23 # defined which delegates to Timeout.timeout. This method may potentially
24 # overwrite the #timeout attr_reader on this class, so we define #limit to
25 # avoid conflicts.
26 def limit
27 @timeout || 0
28 end
29
30 ##
31 # on_flush accepts a block and executes the block when the resolution's value
32 # is flushed. This makes it possible to model a single, expensive system
33 # call inside of a Ruby object and then define multiple dynamic facts which
34 # resolve by sending messages to the model instance. If one of the dynamic
35 # facts is flushed then it can, in turn, flush the data stored in the model
36 # instance to keep all of the dynamic facts in sync without making multiple,
37 # expensive, system calls.
38 #
39 # Please see the Solaris zones fact for an example of how this feature may be
40 # used.
41 #
42 # @see Facter::Util::Fact#flush
43 # @see Facter::Util::Resolution#flush
44 #
45 # @api public
46 def on_flush(&block)
47 @on_flush_block = block
48 end
49
50 ##
51 # flush executes the block, if any, stored by the {on_flush} method
52 #
53 # @see Facter::Util::Fact#flush
54 # @see Facter::Util::Resolution#on_flush
55 #
56 # @api private
57 def flush
58 @on_flush_block&.call
59 end
60
61 # Resolves the fact's value or loggs an error if the value
62 # couldn't be resolved.
63 #
64 # @raise Facter::ResolveCustomFactError if an error was raised
65 # (except Timeout::Error or Narmalization::NarmalizationError
66 # in which case an error message is logged and the execution
67 # isn't suspended).
68 #
69 # @return value (hash, string, int, array, etc) or nil
70 #
71 # @api private
72 def value
73 result = nil
74
75 with_timing do
76 Timeout.timeout(limit) do
77 result = resolve_value
78 end
79 end
80
81 LegacyFacter::Util::Normalization.normalize(result)
82 rescue Timeout::Error => e
83 Facter.log_exception(e, "Timed out after #{limit} seconds while resolving #{qualified_name}")
84
85 nil
86 rescue LegacyFacter::Util::Normalization::NormalizationError => e
87 Facter.log_exception(e, "Fact resolution #{qualified_name} resolved to an invalid value: #{e.message}")
88
89 nil
90 rescue StandardError => e
91 Facter.log_exception(e, "Error while resolving custom fact #{qualified_name}: #{e.message}")
92
93 raise Facter::ResolveCustomFactError
94 end
95
96 private
97
98 def with_timing
99 starttime = Time.now.to_f
100
101 yield
102
103 finishtime = Time.now.to_f
104 ms = (finishtime - starttime) * 1000
105 Facter::Log.show_time format('%<qn>s: %<ms>.2fms', qn: qualified_name, ms: ms)
106 end
107
108 def qualified_name
109 "fact='#{@fact.name}', resolution='#{@name || '<anonymous>'}'"
110 end
111 end
112 end
113 end
0 # frozen_string_literal: true
1
2 # The Suitable mixin provides mechanisms for confining objects to run on
3 # certain platforms and determining the run precedence of these objects.
4 #
5 # Classes that include the Suitable mixin should define a `#confines` method
6 # that returns an Array of zero or more Facter::Util::Confine objects.
7 module LegacyFacter
8 module Core
9 module Suitable
10 # Sets the weight of this resolution. If multiple suitable resolutions
11 # are found, the one with the highest weight will be used. If weight
12 # is not given, the number of confines set on a resolution will be
13 # used as its weight (so that the most specific resolution is used).
14 #
15 # @param weight [Integer] the weight of this resolution
16 #
17 # @return [void]
18 #
19 # @api public
20 def has_weight(weight)
21 @weight = weight
22 self
23 end
24
25 # Sets the conditions for this resolution to be used. This method accepts
26 # multiple forms of arguments to determine suitability.
27 #
28 # @return [void]
29 #
30 # @api public
31 #
32 # @overload confine(confines)
33 # Confine a fact to a specific fact value or values. This form takes a
34 # hash of fact names and values. Every fact must match the values given for
35 # that fact, otherwise this resolution will not be considered suitable. The
36 # values given for a fact can be an array, in which case the value of the
37 # fact must be in the array for it to match.
38 # @param [Hash{String,Symbol=>String,Array<String>}] confines set of facts identified by the hash keys whose
39 # fact value must match the argument value.
40 # @example Confining to Linux
41 # Facter.add(:powerstates) do
42 # # This resolution only makes sense on linux systems
43 # confine :kernel => "Linux"
44 # setcode do
45 # File.read('/sys/power/states')
46 # end
47 # end
48 #
49 # @overload confine(confines, &block)
50 # Confine a fact to a block with the value of a specified fact yielded to
51 # the block.
52 # @param [String,Symbol] confines the fact name whose value should be
53 # yielded to the block
54 # @param [Proc] block determines the suitability of the fact. If the block
55 # evaluates to `false` or `nil` then the confined fact will not be
56 # evaluated.
57 # @yield [value] the value of the fact identified by {confines}
58 # @example Confine the fact to a host with an ipaddress in a specific
59 # subnet
60 # confine :ipaddress do |addr|
61 # require 'ipaddr'
62 # IPAddr.new('192.168.0.0/16').include? addr
63 # end
64 #
65 # @overload confine(&block)
66 # Confine a fact to a block. The fact will be evaluated only if the block
67 # evaluates to something other than `false` or `nil`.
68 # @param [Proc] block determines the suitability of the fact. If the block
69 # evaluates to `false` or `nil` then the confined fact will not be
70 # evaluated.
71 # @example Confine the fact to systems with a specific file.
72 # confine { File.readable? '/bin/foo' }
73 def confine(confines = nil, &block)
74 case confines
75 when Hash
76 confines.each do |fact, values|
77 @confines.push LegacyFacter::Util::Confine.new(fact, *values)
78 end
79 else
80 if block
81 if confines
82 @confines.push LegacyFacter::Util::Confine.new(confines, &block)
83 else
84 @confines.push LegacyFacter::Util::Confine.new(&block)
85 end
86 end
87 end
88 end
89
90 # Returns the importance of this resolution. If the weight was not
91 # given, the number of confines is used instead (so that a more
92 # specific resolution wins over a less specific one).
93 #
94 # @return [Integer] the weight of this resolution
95 #
96 # @api private
97 def weight
98 @weight || @confines.length
99 end
100
101 # Is this resolution mechanism suitable on the system in question?
102 #
103 # @api private
104 def suitable?
105 @confines.all?(&:true?)
106 end
107 end
108 end
109 end
0 # frozen_string_literal: true
1
2 # Manage which facts exist and how we access them. Largely just a wrapper
3 # around a hash of facts.
4 #
5 # @api private
6 module LegacyFacter
7 module Util
8 class Collection
9 def initialize(internal_loader, external_loader)
10 @facts = {}
11 @internal_loader = internal_loader
12 @external_loader = external_loader
13 @loaded = false
14 end
15
16 # Return a fact object by name.
17 def [](name)
18 value(name)
19 end
20
21 # Define a new fact or extend an existing fact.
22 #
23 # @param name [Symbol] The name of the fact to define
24 # @param options [Hash] A hash of options to set on the fact
25 #
26 # @return [Facter::Util::Fact] The fact that was defined
27 def define_fact(name, options = {}, &block)
28 fact = create_or_return_fact(name, options)
29
30 fact.instance_eval(&block) if block_given?
31
32 fact
33 rescue StandardError => e
34 log.log_exception("Unable to add fact #{name}: #{e}")
35 end
36
37 # Add a resolution mechanism for a named fact. This does not distinguish
38 # between adding a new fact and adding a new way to resolve a fact.
39 #
40 # @param name [Symbol] The name of the fact to define
41 # @param options [Hash] A hash of options to set on the fact and resolution
42 #
43 # @return [Facter::Util::Fact] The fact that was defined
44 def add(name, options = {}, &block)
45 fact = create_or_return_fact(name, options)
46
47 fact.add(options, &block)
48
49 fact
50 end
51
52 include Enumerable
53
54 # Iterate across all of the facts.
55 def each
56 load_all
57 @facts.each do |name, fact|
58 value = fact.value
59 yield name.to_s, value unless value.nil?
60 end
61 end
62
63 # Return a fact by name.
64 def fact(name)
65 name = canonicalize(name)
66
67 # Try to load the fact if necessary
68 load(name) unless @facts[name]
69
70 # Try HARDER
71 internal_loader.load_all unless @facts[name]
72
73 log.warnonce("No facts loaded from #{internal_loader.search_path.join(File::PATH_SEPARATOR)}") if @facts.empty?
74
75 @facts[name]
76 end
77
78 # Flush all cached values.
79 def flush
80 @facts.each { |_name, fact| fact.flush }
81 @external_facts_loaded = nil
82 end
83
84 # Return a list of all of the facts.
85 def list
86 load_all
87 @facts.keys
88 end
89
90 # Build a hash of external facts
91 def external_facts
92 return @external_facts unless @external_facts.nil?
93
94 load_external_facts
95 @external_facts = @facts.select { |_k, v| v.options[:fact_type] == :external }
96 end
97
98 def invalidate_custom_facts
99 @valid_custom_facts = false
100 end
101
102 def reload_custom_facts
103 @loaded = false
104 end
105
106 def custom_fact(fact_name)
107 internal_loader.load(fact_name)
108 @custom_facts = @facts.select { |_k, v| v.options[:fact_type] == :custom }
109 end
110
111 # Builds a hash of custom facts
112 def custom_facts
113 return @custom_facts if @valid_custom_facts
114
115 @valid_custom_facts = true
116
117 internal_loader.load_all unless @loaded
118 @loaded = true
119
120 @custom_facts = @facts.select { |_k, v| v.options[:fact_type] == :custom }
121 end
122
123 def load(name)
124 internal_loader.load(name)
125 load_external_facts
126 end
127
128 # Load all known facts.
129 def load_all
130 internal_loader.load_all
131 load_external_facts
132 end
133
134 attr_reader :internal_loader
135
136 attr_reader :external_loader
137
138 # Return a hash of all of our facts.
139 def to_hash
140 @facts.each_with_object({}) do |ary, h|
141 value = ary[1].value
142 unless value.nil?
143 # For backwards compatibility, convert the fact name to a string.
144 h[ary[0].to_s] = value
145 end
146 end
147 end
148
149 def value(name)
150 fact = fact(name)
151 fact&.value
152 end
153
154 private
155
156 def create_or_return_fact(name, options)
157 name = canonicalize(name)
158
159 fact = @facts[name]
160
161 if fact.nil?
162 fact = Facter::Util::Fact.new(name, options)
163 @facts[name] = fact
164 else
165 fact.extract_ldapname_option!(options)
166 end
167
168 fact
169 end
170
171 def canonicalize(name)
172 name.to_s.downcase.to_sym
173 end
174
175 def load_external_facts
176 return if @external_facts_loaded
177
178 @external_facts_loaded = true
179 external_loader.load(self)
180 end
181
182 def log
183 @log ||= Facter::Log.new(self)
184 end
185 end
186 end
187 end
0 # frozen_string_literal: true
1
2 # A composite loader that allows for more than one
3 # default directory loader
4
5 module LegacyFacter
6 module Util
7 class CompositeLoader
8 def initialize(loaders)
9 @loaders = loaders
10 end
11
12 def load(collection)
13 @loaders.each { |loader| loader.load(collection) }
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 require 'rbconfig'
3
4 # A module to return config related data
5 #
6 module LegacyFacter
7 module Util
8 module Config
9 def self.ext_fact_loader
10 @ext_fact_loader || LegacyFacter::Util::DirectoryLoader.new
11 end
12
13 def self.ext_fact_loader=(loader)
14 @ext_fact_loader = loader
15 end
16
17 def self.mac?
18 RbConfig::CONFIG['host_os'] =~ /darwin/i
19 end
20
21 # Returns true if OS is windows
22 def self.windows?
23 RbConfig::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i
24 end
25
26 def self.windows_data_dir
27 ENV['ProgramData'] || ENV['APPDATA'] if LegacyFacter::Util::Config.windows?
28 end
29
30 def self.external_facts_dirs
31 Facter::Options.external_dir
32 end
33
34 def self.facts_cache_dir
35 @facts_cache_dir ||= setup_default_cache_dir
36 end
37
38 def self.setup_default_ext_facts_dirs
39 if LegacyFacter::Util::Root.root?
40 windows_dir = windows_data_dir
41 Facter::Options[:default_external_dir] = if windows_dir
42 [File.join(windows_dir, 'PuppetLabs', 'facter', 'facts.d')]
43 else
44 [
45 '/etc/puppetlabs/facter/facts.d',
46 '/etc/facter/facts.d/',
47 '/opt/puppetlabs/facter/facts.d'
48 ]
49 end
50 elsif ENV['HOME']
51 Facter::Options[:default_external_dir] =
52 [File.join(ENV['HOME'], '.facter', 'facts.d'),
53 File.join(ENV['HOME'], '.puppetlabs', 'opt', 'facter', 'facts.d')]
54 else
55 Facter::Options[:default_external_dir] = []
56 end
57 end
58
59 if LegacyFacter::Util::Config.windows?
60 require_relative 'windows_root'
61 else
62 require_relative 'unix_root'
63 end
64
65 setup_default_ext_facts_dirs
66
67 def self.override_binary_dir=(dir)
68 @override_binary_dir = dir
69 end
70
71 def self.override_binary_dir
72 @override_binary_dir
73 end
74
75 def self.setup_default_cache_dir
76 windows_dir = windows_data_dir
77 @facts_cache_dir = if windows_dir
78 File.join(windows_dir, 'PuppetLabs', 'facter', 'cache', 'cached_facts')
79 else
80 '/opt/puppetlabs/facter/cache/cached_facts'
81 end
82 end
83
84 def self.setup_default_override_binary_dir
85 @override_binary_dir = if LegacyFacter::Util::Config.windows?
86 nil
87 else
88 '/opt/puppetlabs/puppet/bin'
89 end
90 end
91
92 setup_default_override_binary_dir
93 end
94 end
95 end
0 # frozen_string_literal: true
1
2 # A restricting tag for fact resolution mechanisms. The tag must be true
3 # for the resolution mechanism to be suitable.
4
5 module LegacyFacter
6 module Util
7 class Confine
8 attr_accessor :fact, :values
9
10 include LegacyFacter::Util::Values
11
12 # Add the restriction. Requires the fact name, an operator, and the value
13 # we're comparing to.
14 #
15 # @param fact [Symbol] Name of the fact
16 # @param values [Array] One or more values to match against.
17 # They can be any type that provides a === method.
18 # @param block [Proc] Alternatively a block can be supplied as a check. The fact
19 # value will be passed as the argument to the block. If the block returns
20 # true then the fact will be enabled, otherwise it will be disabled.
21 def initialize(fact = nil, *values, &block)
22 raise ArgumentError, 'The fact name must be provided' unless fact || block_given?
23 raise ArgumentError, 'One or more values or a block must be provided' if values.empty? && !block_given?
24
25 @fact = fact
26 @values = values
27 @block = block
28 end
29
30 def to_s
31 return @block.to_s if @block
32
33 format("'%<fact>s' '%<values>s'", fact: @fact, values: @values.join(','))
34 end
35
36 # Evaluate the fact, returning true or false.
37 # if we have a block paramter then we only evaluate that instead
38 def true?
39 if @block && !@fact
40 begin
41 return !!@block.call
42 rescue StandardError => e
43 log.debug "Confine raised #{e.class} #{e}"
44 return false
45 end
46 end
47
48 unless (fact = Facter[@fact])
49 log.debug format('No fact for %<fact>s', fact: @fact)
50 return false
51 end
52 value = convert(fact.value)
53
54 return false if value.nil?
55
56 if @block
57 begin
58 return !!@block.call(value)
59 rescue StandardError => e
60 log.debug "Confine raised #{e.class} #{e}"
61 return false
62 end
63 end
64
65 @values.any? { |v| convert(v) === value }
66 end
67
68 private
69
70 def log
71 @log ||= Facter::Log.new(self)
72 end
73 end
74 end
75 end
0 # frozen_string_literal: true
1
2 # A Facter plugin that loads external facts.
3 #
4 # Default Unix Directories:
5 # /opt/puppetlabs/custom_facts/facts.d, /etc/custom_facts/facts.d, /etc/puppetlabs/custom_facts/facts.d
6 #
7 # Beginning with Facter 3, only /opt/puppetlabs/custom_facts/facts.d will be a default external fact
8 # directory in Unix.
9 #
10 # Default Windows Direcotires:
11 # C:\ProgramData\Puppetlabs\custom_facts\facts.d (2008)
12 # C:\Documents and Settings\All Users\Application Data\Puppetlabs\custom_facts\facts.d (2003)
13 #
14 # Can also load from command-line specified directory
15 #
16 # Facts can be in the form of JSON, YAML or Text files
17 # and any executable that returns key=value pairs.
18
19 require 'yaml'
20
21 module LegacyFacter
22 module Util
23 class DirectoryLoader
24 class NoSuchDirectoryError < RuntimeError
25 end
26
27 # This value makes it highly likely that external facts will take
28 # precedence over all other facts
29 EXTERNAL_FACT_WEIGHT = 10_000
30
31 # Directory for fact loading
32 attr_reader :directories
33
34 def initialize(dir = LegacyFacter::Util::Config.external_facts_dirs, weight = EXTERNAL_FACT_WEIGHT)
35 @directories = [dir].flatten
36 @weight = weight
37 @log ||= Facter::Log.new(self)
38 end
39
40 # Load facts from files in fact directory using the relevant parser classes to
41 # parse them.
42 def load(collection)
43 weight = @weight
44
45 searched_facts, cached_facts = load_directory_entries(collection)
46
47 load_cached_facts(collection, cached_facts, weight)
48
49 load_searched_facts(collection, searched_facts, weight)
50 end
51
52 private
53
54 def log
55 @log ||= Facter::Log.new(self)
56 end
57
58 def load_directory_entries(_collection)
59 cm = Facter::CacheManager.new
60 facts = []
61 entries.each do |file|
62 basename = File.basename(file)
63 next if file_blocked?(basename)
64
65 if facts.find { |f| f.name == basename } && cm.fact_cache_enabled?(basename)
66 Facter.log_exception(Exception.new("Caching is enabled for group \"#{basename}\" while "\
67 'there are at least two external facts files with the same filename'))
68 else
69 searched_fact = Facter::SearchedFact.new(basename, nil, nil, :file)
70 searched_fact.file = file
71 facts << searched_fact
72 end
73 end
74
75 cm.resolve_facts(facts)
76 end
77
78 def load_cached_facts(collection, cached_facts, weight)
79 cached_facts.each do |cached_fact|
80 collection.add(cached_fact.name, value: cached_fact.value, fact_type: :external,
81 file: cached_fact.file) { has_weight(weight) }
82 end
83 end
84
85 def load_searched_facts(collection, searched_facts, weight)
86 searched_facts.each do |fact|
87 parser = LegacyFacter::Util::Parser.parser_for(fact.file)
88 next if parser.nil?
89
90 data = resolve_fact(fact, parser)
91
92 if data == false
93 log.warn "Could not interpret fact file #{fact.file}"
94 elsif (data == {}) || data.nil?
95 log.debug("Fact file #{fact.file} was parsed but no key=>value data was returned")
96 else
97 add_data(data, collection, fact, weight)
98 end
99 end
100 end
101
102 def add_data(data, collection, fact, weight)
103 data.each do |key, value|
104 collection.add(
105 key,
106 value: value,
107 fact_type: :external,
108 file: fact.file
109 ) { has_weight(weight) }
110 end
111 end
112
113 def resolve_fact(fact, parser)
114 data = nil
115 fact_name = File.basename(fact.file)
116 Facter::Framework::Benchmarking::Timer.measure(fact_name) { data = parser.results }
117
118 data
119 end
120
121 def entries
122 dirs = @directories.select { |directory| File.directory?(directory) }.map do |directory|
123 Dir.entries(directory).map { |directory_entry| File.join(directory, directory_entry) }.sort.reverse!
124 end
125 dirs.flatten.select { |f| should_parse?(f) }
126 rescue Errno::ENOENT
127 []
128 end
129
130 def should_parse?(file)
131 File.basename(file) !~ /^\./
132 end
133
134 def file_blocked?(file)
135 if Facter::Options[:blocked_facts].include? file
136 Facter.debug("External fact file #{file} blocked.")
137 return true
138 end
139 false
140 end
141 end
142 end
143 end
0 # frozen_string_literal: true
1
2 # This class represents a fact. Each fact has a name and multiple
3 # {Facter::Util::Resolution resolutions}.
4 #
5 # Create facts using {Facter.add}
6 #
7 # @api public
8 module Facter
9 module Util
10 class Fact
11 # The location from where fact is resolved
12 # @return [String]
13 attr_reader :location
14
15 # The name of the fact
16 # @return [String]
17 attr_reader :name
18
19 # @return [String]
20 # @deprecated
21 attr_accessor :ldapname
22
23 # Fact options e.g. fact_type
24 attr_accessor :options
25
26 # Weight of the resolution that was used to obtain the fact value
27 attr_accessor :used_resolution_weight
28
29 # Creates a new fact, with no resolution mechanisms. See {Facter.add}
30 # for the public API for creating facts.
31 # @param name [String] the fact name
32 # @param options [Hash] optional parameters
33 # @option options [String] :ldapname set the ldapname property on the fact
34 #
35 # @api private
36 def initialize(name, options = {})
37 @name = name.to_s.downcase.intern
38
39 @options = options.dup
40 extract_ldapname_option!(options)
41
42 @ldapname ||= @name.to_s
43
44 @resolves = []
45 @searching = false
46 @used_resolution_weight = 0
47
48 @value = nil
49 end
50
51 # Adds a new {Facter::Util::Resolution resolution}. This requires a
52 # block, which will then be evaluated in the context of the new
53 # resolution.
54 #
55 # @param options [Hash] A hash of options to set on the resolution
56 #
57 # @return [Facter::Util::Resolution]
58 #
59 # @api private
60 def add(options = {}, &block)
61 @options = @options.merge(options)
62
63 @location = options[:file]
64 @location ||= block.source_location if block_given?
65
66 define_resolution(nil, options, &block)
67 end
68
69 # Define a new named resolution or return an existing resolution with
70 # the given name.
71 #
72 # @param resolution_name [String] The name of the resolve to define or look up
73 # @param options [Hash] A hash of options to set on the resolution
74 # @return [Facter::Util::Resolution]
75 #
76 # @api public
77 def define_resolution(resolution_name, options = {}, &block)
78 resolution_type = options.delete(:type) || :simple
79
80 resolve = create_or_return_resolution(resolution_name, resolution_type)
81
82 resolve.options(options) unless options.empty?
83 resolve.evaluate(&block) if block
84
85 resolve
86 rescue StandardError => e
87 log.log_exception("Unable to add resolve #{resolution_name.inspect} for fact #{@name}: #{e.message}")
88 nil
89 end
90
91 # Retrieve an existing resolution by name
92 #
93 # @param name [String]
94 #
95 # @return [Facter::Util::Resolution, nil] The resolution if exists, nil if
96 # it doesn't exist or name is nil
97 def resolution(name)
98 return nil if name.nil?
99
100 @resolves.find { |resolve| resolve.name == name }
101 end
102
103 # Flushes any cached values.
104 #
105 # @return [void]
106 #
107 # @api private
108 def flush
109 @resolves.each(&:flush)
110 @value = nil
111 end
112
113 # Returns the value for this fact. This searches all resolutions by
114 # suitability and weight (see {Facter::Util::Resolution}). If no
115 # suitable resolution is found, it returns nil.
116 #
117 # @api public
118 def value
119 return @value unless @value.nil?
120
121 if @resolves.empty?
122 log.debug format('No resolves for %<name>s', name: @name)
123 return nil
124 end
125
126 searching do
127 suitable_resolutions = sort_by_weight(find_suitable_resolutions(@resolves))
128
129 Facter::Framework::Benchmarking::Timer.measure(@name) do
130 @value = find_first_real_value(suitable_resolutions)
131 end
132
133 announce_when_no_suitable_resolution(suitable_resolutions)
134 announce_when_no_value_found(@value)
135
136 @value = resolve_value
137 end
138
139 @value
140 end
141
142 # @api private
143 # @deprecated
144 def extract_ldapname_option!(options)
145 return unless options[:ldapname]
146
147 log.warnonce('ldapname is deprecated and will be removed in a future version')
148 self.ldapname = options.delete(:ldapname)
149 end
150
151 private
152
153 def log
154 @log ||= Facter::Log.new(self)
155 end
156
157 def resolve_value
158 return Facter.core_value(name) if @value.nil?
159
160 core_value = Facter.core_value(name) if @used_resolution_weight <= 0
161 core_value.nil? ? @value : core_value
162 end
163
164 # Are we in the midst of a search?
165 def searching?
166 @searching
167 end
168
169 # Lock our searching process, so we never get stuck in recursion.
170 def searching
171 raise "Caught recursion on #{@name}" if searching?
172
173 # If we've gotten this far, we're not already searching, so go ahead and do so.
174 @searching = true
175 begin
176 yield
177 ensure
178 @searching = false
179 end
180 end
181
182 def find_suitable_resolutions(resolutions)
183 resolutions.find_all(&:suitable?)
184 end
185
186 def sort_by_weight(resolutions)
187 # sort resolutions:
188 # - descending by weight
189 # - multiple facts have the same weight but different types, the :external fact take precedence
190 # - multiple facts with the same weight and type, the order is preserved.
191 # note: sort_by with index is slower than .sort
192 # we cannot use .sort because it is not stable: https://bugs.ruby-lang.org/issues/1089
193 # solution from: https://bugs.ruby-lang.org/issues/1089#note-10
194
195 # rubocop:disable Style/NestedTernaryOperator
196 idx = 0
197 resolutions.sort_by do |x|
198 [
199 -x.weight,
200 x.respond_to?(:fact_type) ? x.fact_type == :external ? 0 : 1 : 1,
201 idx += 1
202 ]
203 end
204 # rubocop:enable Style/NestedTernaryOperator
205 end
206
207 def find_first_real_value(resolutions)
208 resolutions.each do |resolve|
209 begin
210 value = resolve.value
211 rescue Facter::ResolveCustomFactError
212 break
213 end
214 @used_resolution_weight = resolve.weight
215 next if value.nil?
216
217 log_fact_path(resolve)
218
219 return value
220 end
221 nil
222 end
223
224 def log_fact_path(resolve)
225 fact = resolve.fact
226 log.debug("#{resolve.fact_type} fact #{fact.name} got resolved from: #{fact.location}")
227 end
228
229 def announce_when_no_suitable_resolution(resolutions)
230 return unless resolutions.empty?
231
232 log.debug format('Found no suitable resolves of %<resolver_length> for %<name>s',
233 resolver_length: @resolves.length, name: @name)
234 end
235
236 def announce_when_no_value_found(value)
237 log.debug(format('value for %<name>s is still nil', name: @name)) if value.nil?
238 end
239
240 def create_or_return_resolution(resolution_name, resolution_type)
241 resolve = resolution(resolution_name)
242
243 if resolve
244 if resolution_type != resolve.resolution_type
245 raise ArgumentError, "Cannot return resolution #{resolution_name} with type" \
246 " #{resolution_type}; already defined as #{resolve.resolution_type}"
247 end
248 else
249 case resolution_type
250 when :simple
251 resolve = Facter::Util::Resolution.new(resolution_name, self)
252 when :aggregate
253 resolve = Facter::Core::Aggregate.new(resolution_name, self)
254 else
255 raise ArgumentError,
256 "Expected resolution type to be one of (:simple, :aggregate) but was #{resolution_type}"
257 end
258
259 @resolves << resolve
260 end
261
262 resolve
263 end
264 end
265 end
266 end
0 # frozen_string_literal: true
1
2 # Load facts on demand.
3 module LegacyFacter
4 module Util
5 class Loader
6 def initialize(environment_vars = ENV)
7 @loaded = []
8 @environment_vars = environment_vars
9 end
10
11 # Load all resolutions for a single fact.
12 #
13 # @api public
14 # @param name [Symbol]
15 def load(fact)
16 # Now load from the search path
17 shortname = fact.to_s.downcase
18 load_env(shortname)
19
20 filename = shortname + '.rb'
21
22 paths = search_path
23 paths&.each do |dir|
24 # Load individual files
25 file = File.join(dir, filename)
26
27 load_file(file) if FileTest.file?(file)
28 end
29 end
30
31 # Load all facts from all directories.
32 #
33 # @api public
34 def load_all
35 return if defined?(@loaded_all)
36
37 load_env
38
39 paths = search_path
40 paths&.each do |dir|
41 # clean the search path of wrong slashes and backslashes
42 dir = dir.gsub(%r{[\/\\]+}, File::SEPARATOR)
43 # dir is already an absolute path
44 Dir.glob(File.join(dir, '*.rb')).each do |path|
45 # exclude dirs that end with .rb
46 load_file(path) if FileTest.file?(path)
47 end
48 end
49
50 @loaded_all = true
51 end
52
53 # List directories to search for fact files.
54 #
55 # Search paths are gathered from the following sources:
56 #
57 # 1. $LOAD_PATH entries are expanded to absolute paths
58 # 2. ENV['FACTERLIB'] is split and used verbatim
59 # 3. Entries from Facter.search_path are used verbatim
60 #
61 # @api public
62 # @return [Array<String>]
63 def search_path
64 search_paths = []
65 search_paths += $LOAD_PATH.map { |path| File.expand_path('facter', path) }
66
67 if @environment_vars.include?('FACTERLIB')
68 search_paths += @environment_vars['FACTERLIB'].split(File::PATH_SEPARATOR)
69 end
70
71 Facter::Options.custom_dir.each do |path|
72 search_paths << path
73 end
74
75 search_paths.delete_if { |path| !File.directory?(path) }
76
77 search_paths.uniq
78 end
79
80 private
81
82 def log
83 @log ||= Facter::Log.new(self)
84 end
85
86 # Load a file and record is paths to prevent duplicate loads.
87 #
88 # @api private
89 # @params file [String] The *absolute path* to the file to load
90 def load_file(file)
91 return if @loaded.include? file
92
93 # We have to specify Kernel.load, because we have a load method.
94 begin
95 # Store the file path so we don't try to reload it
96 @loaded << file
97 kernel_load(file)
98 rescue ScriptError => e
99 # Don't store the path if the file can't be loaded
100 # in case it's loadable later on.
101 @loaded.delete(file)
102 Facter.log_exception(e, "Error loading fact #{file}: #{e.message}")
103 rescue StandardError => e
104 Facter.log_exception(e, "error while resolving custom facts in #{file} #{e.message}")
105 end
106 end
107
108 # Load and execute the Ruby program specified in the file. This exists
109 # for testing purposes.
110 #
111 # @api private
112 # @return [Boolean]
113 def kernel_load(file)
114 Kernel.load(file)
115 end
116
117 # Load facts from the environment. If no name is provided,
118 # all will be loaded.
119 def load_env(fact = nil)
120 # Load from the environment, if possible
121 @environment_vars.each do |name, value|
122 # Skip anything that doesn't match our regex.
123 next unless name =~ /^facter_?(\w+)$/i
124
125 env_name = Regexp.last_match(1).downcase
126
127 # If a fact name was specified, skip anything that doesn't
128 # match it.
129 next if fact && (env_name != fact)
130
131 LegacyFacter.add(env_name, fact_type: :external, is_env: true) do
132 has_weight 1_000_000
133 setcode { value }
134 end
135
136 # Short-cut, if we are only looking for one value.
137 break if fact
138 end
139 end
140 end
141 end
142 end
0 # frozen_string_literal: true
1
2 module LegacyFacter
3 module Util
4 module Normalization
5 class NormalizationError < StandardError; end
6
7 # load Date and Time classes
8 require 'time'
9
10 VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, Symbol, String, Array, Hash, Date, Time].freeze
11
12 module_function
13
14 # Recursively normalize the given data structure
15 #
16 # @api public
17 # @raise [NormalizationError] If the data structure contained an invalid element.
18 # @return [void]
19 def normalize(value)
20 case value
21 when Integer, Float, TrueClass, FalseClass, NilClass, Symbol
22 value
23 when Date, Time
24 value.iso8601
25 when String
26 normalize_string(value)
27 when Array
28 normalize_array(value)
29 when Hash
30 normalize_hash(value)
31 else
32 raise NormalizationError, "Expected #{value} to be one of #{VALID_TYPES.inspect}, but was #{value.class}"
33 end
34 end
35
36 # @!method normalize_string(value)
37 #
38 # Attempt to normalize and validate the given string.
39 #
40 # On Ruby 1.8 the string is checked by stripping out all non UTF-8
41 # characters and comparing the converted string to the original. If they
42 # do not match then the string is considered invalid.
43 #
44 # On Ruby 1.9+, the string is validate by checking that the string encoding
45 # is UTF-8 and that the string content matches the encoding. If the string
46 # is not an expected encoding then it is converted to UTF-8.
47 #
48 # @api public
49 # @raise [NormalizationError] If the string used an unsupported encoding or did not match its encoding
50 # @param value [String]
51 # @return [void]
52
53 if RUBY_VERSION =~ /^1\.8/
54 require 'iconv'
55
56 def normalize_string(value)
57 converted = Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
58 raise NormalizationError, "String #{value.inspect} is not valid UTF-8" if converted != value
59
60 value
61 end
62 else
63 def normalize_string(value)
64 value = value.encode(Encoding::UTF_8)
65
66 unless value.valid_encoding?
67 raise NormalizationError, "String #{value.inspect} doesn't match the reported encoding #{value.encoding}"
68 end
69
70 value
71 rescue EncodingError
72 raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
73 end
74 end
75
76 # Validate all elements of the array.
77 #
78 # @api public
79 # @raise [NormalizationError] If one of the elements failed validation
80 # @param value [Array]
81 # @return [void]
82 def normalize_array(value)
83 value.collect do |elem|
84 normalize(elem)
85 end
86 end
87
88 # Validate all keys and values of the hash.
89 #
90 # @api public
91 # @raise [NormalizationError] If one of the keys or values failed normalization
92 # @param value [Hash]
93 # @return [void]
94 def normalize_hash(value)
95 Hash[value.collect { |k, v| [normalize(k), normalize(v)] }]
96 end
97 end
98 end
99 end
0 # frozen_string_literal: true
1
2 module LegacyFacter
3 module Util
4 # An external fact loader that doesn't load anything
5 # This makes it possible to disable loading
6 # of external facts
7 class NothingLoader
8 def load(collection); end
9 end
10 end
11 end
0 # frozen_string_literal: true
1
2 # This class acts as the factory and parent class for parsed
3 # facts such as scripts, text, json and yaml files.
4 #
5 # Parsers must subclass this class and provide their own #results method.
6
7 module LegacyFacter
8 module Util
9 module Parser
10 STDERR_MESSAGE = 'Command %s completed with the following stderr message: %s'
11
12 @parsers = []
13
14 # For support mutliple extensions you can pass an array of extensions as
15 # +ext+.
16 def self.extension_matches?(filename, ext)
17 extension = case ext
18 when String
19 ext.downcase
20 when Enumerable
21 ext.collect(&:downcase)
22 end
23 [extension].flatten.to_a.include?(file_extension(filename).downcase)
24 end
25
26 def self.file_extension(filename)
27 File.extname(filename).sub('.', '')
28 end
29
30 def self.register(klass, &suitable)
31 @parsers << [klass, suitable]
32 end
33
34 def self.parser_for(filename)
35 registration = @parsers.detect { |k| k[1].call(filename) }
36
37 if registration.nil?
38 NothingParser.new
39 else
40 registration[0].new(filename)
41 end
42 end
43
44 class Base
45 attr_reader :filename
46
47 def initialize(filename, content = nil)
48 @filename = filename
49 @content = content
50 end
51
52 def content
53 @content ||= Facter::Util::FileHelper.safe_read(filename, nil)
54 end
55
56 # results on the base class is really meant to be just an exception handler
57 # wrapper.
58 def results
59 parse_results
60 rescue StandardError => e
61 Facter.log_exception(e, "Failed to handle #{filename} as #{self.class} facts: #{e.message}")
62 nil
63 end
64
65 def parse_results
66 raise ArgumentError, 'Subclasses must respond to parse_results'
67 end
68
69 def parse_executable_output(output)
70 res = nil
71 begin
72 res = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') # Ruby 2.6+
73 YAML.safe_load(output, permitted_classes: [Symbol, Time])
74 else
75 YAML.safe_load(output, [Symbol, Time])
76 end
77 rescue StandardError => e
78 Facter.debug("Could not parse executable fact output as YAML or JSON (#{e.message})")
79 end
80 res = KeyValuePairOutputFormat.parse output unless res.is_a?(Hash)
81 res
82 end
83
84 def log_stderr(msg, command, file)
85 return if !msg || msg.empty?
86
87 file_name = file.split('/').last
88 logger = Facter::Log.new(file_name)
89
90 logger.warn(format(STDERR_MESSAGE, command, msg.strip))
91 end
92 end
93
94 module KeyValuePairOutputFormat
95 def self.parse(output)
96 return {} if output.nil?
97
98 result = {}
99 re = /^(.+?)=(.+)$/
100 output.each_line do |line|
101 if (match_data = re.match(line.chomp))
102 result[match_data[1]] = match_data[2]
103 end
104 end
105 result
106 end
107 end
108
109 # This regex was taken from Psych and adapted
110 # https://github.com/ruby/psych/blob/d2deaa9adfc88fc0b870df022a434d6431277d08/lib/psych/scalar_scanner.rb#L9
111 # It is used to detect Time in YAML, but we use it to wrap time objects in quotes to be treated as strings.
112 TIME =
113 /(\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d\d:\d\d(?:\.\d*)?(?:\s*(?:Z|[-+]\d{1,2}:?(?:\d\d)?))?\s*$)/.freeze
114
115 class YamlParser < Base
116 def parse_results
117 # Add quotes to Yaml time
118 cont = content.gsub(TIME, '"\1"')
119
120 if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') # Ruby 2.6+
121 YAML.safe_load(cont, permitted_classes: [Date], aliases: true)
122 else
123 YAML.safe_load(cont, [Date], [], [], true)
124 end
125 end
126 end
127
128 register(YamlParser) do |filename|
129 extension_matches?(filename, 'yaml')
130 end
131
132 class TextParser < Base
133 def parse_results
134 KeyValuePairOutputFormat.parse content
135 end
136 end
137
138 register(TextParser) do |filename|
139 extension_matches?(filename, 'txt')
140 end
141
142 class JsonParser < Base
143 def parse_results
144 if LegacyFacter.json?
145 JSON.parse(content)
146 else
147 log.warnonce "Cannot parse JSON data file #{filename} without the json library."
148 log.warnonce 'Suggested next step is `gem install json` to install the json library.'
149 nil
150 end
151 end
152
153 private
154
155 def log
156 @log ||= Facter::Log.new(self)
157 end
158 end
159
160 register(JsonParser) do |filename|
161 extension_matches?(filename, 'json')
162 end
163
164 class ScriptParser < Base
165 def parse_results
166 stdout, stderr = Facter::Core::Execution.execute_command(quote(filename))
167 log_stderr(stderr, filename, filename)
168 parse_executable_output(stdout)
169 end
170
171 private
172
173 def quote(filename)
174 filename.index(' ') ? "\"#{filename}\"" : filename
175 end
176 end
177
178 register(ScriptParser) do |filename|
179 if LegacyFacter::Util::Config.windows?
180 extension_matches?(filename, %w[bat cmd com exe]) && FileTest.file?(filename)
181 else
182 File.executable?(filename) && FileTest.file?(filename) && !extension_matches?(filename, %w[bat cmd com exe])
183 end
184 end
185
186 # Executes and parses the key value output of Powershell scripts
187 class PowershellParser < Base
188 # Returns a hash of facts from powershell output
189 def parse_results
190 powershell =
191 if File.readable?("#{ENV['SYSTEMROOT']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe")
192 "#{ENV['SYSTEMROOT']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe"
193 elsif File.readable?("#{ENV['SYSTEMROOT']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe")
194 "#{ENV['SYSTEMROOT']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe"
195 else
196 'powershell.exe'
197 end
198
199 shell_command =
200 "\"#{powershell}\" -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -File \"#{filename}\""
201 stdout, stderr = Facter::Core::Execution.execute_command(shell_command)
202 log_stderr(stderr, shell_command, filename)
203 parse_executable_output(stdout)
204 end
205 end
206
207 register(PowershellParser) do |filename|
208 LegacyFacter::Util::Config.windows? && extension_matches?(filename, 'ps1') && FileTest.file?(filename)
209 end
210
211 # A parser that is used when there is no other parser that can handle the file
212 # The return from results indicates to the caller the file was not parsed correctly.
213 class NothingParser
214 def results
215 nil
216 end
217 end
218 end
219 end
220 end
0 # This represents a fact resolution. A resolution is a concrete
1 # implementation of a fact. A single fact can have many resolutions and
2 # the correct resolution will be chosen at runtime. Each time
3 # {Facter.add} is called, a new resolution is created and added to the
4 # set of resolutions for the fact named in the call. Each resolution
5 # has a {#has_weight weight}, which defines its priority over other
6 # resolutions, and a set of {#confine _confinements_}, which defines the
7 # conditions under which it will be chosen. All confinements must be
8 # satisfied for a fact to be considered _suitable_.
9 #
10 # @api public
11 module Facter
12 module Util
13 class Resolution
14 # @api private
15 attr_accessor :code, :fact_type
16
17 # @api private
18 attr_writer :value
19
20 extend Facter::Core::Execution
21
22 class << self
23 # Expose command execution methods that were extracted into
24 # Facter::Core::Execution from Facter::Util::Resolution in Facter 2.0.0 for
25 # compatibility.
26 #
27 # @deprecated
28 #
29 # @api public
30 public :which, :exec
31
32 # @api private
33 public :with_env
34 end
35
36 include LegacyFacter::Core::Resolvable
37 include LegacyFacter::Core::Suitable
38
39 # @!attribute [rw] name
40 # The name of this resolution. The resolution name should be unique with
41 # respect to the given fact.
42 #
43 # @return [String]
44 #
45 # @api public
46 attr_accessor :name
47
48 # @!attribute [r] fact
49 #
50 # @return [Facter::Util::Fact] Associated fact with this resolution.
51 #
52 # @api private
53 attr_reader :fact, :last_evaluated, :file
54
55 # Create a new resolution mechanism.
56 #
57 # @param name [String] The name of the resolution.
58 # @param fact [Facter::Fact] The fact to which this
59 # resolution will be added.
60 #
61 # @return [Facter::Util::Resolution] The created resolution
62 #
63 # @api public
64 def initialize(name, fact)
65 @name = name
66 @fact = fact
67 @confines = []
68 @value = nil
69 @timeout = 0
70 @weight = nil
71 end
72
73 # Returns the fact's resolution type
74 #
75 # @return [Symbol] The fact's type
76 #
77 # @api private
78 def resolution_type
79 :simple
80 end
81
82 # Evaluate the given block in the context of this resolution. If a block has
83 # already been evaluated emit a warning to that effect.
84 #
85 # @return [String] Result of the block's evaluation
86 #
87 # @api private
88 def evaluate(&block)
89 if @last_evaluated
90 msg = "Already evaluated #{@name}"
91 msg << " at #{@last_evaluated}" if msg.is_a? String
92 msg << ', reevaluating anyways'
93 log.warn msg
94 end
95
96 instance_eval(&block)
97
98 # Ruby 1.9+ provides the source location of procs which can provide useful
99 # debugging information if a resolution is being evaluated twice. Since 1.8
100 # doesn't support this we opportunistically provide this information.
101 @last_evaluated = if block.respond_to? :source_location
102 block.source_location.join(':')
103 else
104 true
105 end
106 end
107
108 # Sets options for the aggregate fact
109 #
110 # @return [nil]
111 #
112 # @api private
113 def options(options)
114 accepted_options = %i[name value timeout weight fact_type file is_env]
115
116 accepted_options.each do |option_name|
117 instance_variable_set("@#{option_name}", options.delete(option_name)) if options.key?(option_name)
118 end
119
120 raise ArgumentError, "Invalid resolution options #{options.keys.inspect}" unless options.keys.empty?
121 end
122
123 # Sets the code block or external program that will be evaluated to
124 # get the value of the fact.
125 #
126 # @overload setcode(string)
127 # Sets an external program to call to get the value of the resolution
128 # @param [String] string the external program to run to get the
129 # value
130 #
131 # @overload setcode(&block)
132 # Sets the resolution's value by evaluating a block at runtime
133 # @param [Proc] block The block to determine the resolution's value.
134 # This block is run when the fact is evaluated. Errors raised from
135 # inside the block are rescued and printed to stderr.
136 #
137 # @return [Facter::Util::Resolution] Returns itself
138 #
139 # @api public
140 def setcode(string = nil, &block)
141 if string
142 @code = proc do
143 output = Facter::Core::Execution.execute(string, on_fail: nil)
144 if output.nil? || output.empty?
145 nil
146 else
147 output
148 end
149 end
150 elsif block_given?
151 @code = block
152 else
153 raise ArgumentError, 'You must pass either code or a block'
154 end
155 self
156 end
157
158 # Comparison is done based on weight and fact type.
159 # The greater the weight, the higher the priority.
160 # If weights are equal, we consider external facts greater than custom facts.
161 #
162 # @return [bool] Weight comparison result
163 #
164 # @api private
165 def <=>(other)
166 return compare_equal_weights(other) if weight == other.weight
167 return 1 if weight > other.weight
168 return -1 if weight < other.weight
169 end
170
171 private
172
173 def log
174 @log ||= Facter::Log.new(self)
175 end
176
177 # If the weights are equal, we consider external facts greater tan custom facts
178 def compare_equal_weights(other)
179 # Other is considered greater because self is custom fact and other is external
180 return -1 if fact_type == :custom && other.fact_type == :external
181
182 # Self is considered greater, because it is external fact and other is custom
183 return 1 if fact_type == :external && other.fact_type == :custom
184
185 # They are considered equal
186 0
187 end
188
189 def resolve_value
190 if !@value.nil?
191 @value
192 elsif @code.nil?
193 nil
194 elsif @code
195 @code.call
196 end
197 end
198 end
199 end
200 end
0 # frozen_string_literal: true
1
2 module LegacyFacter
3 module Util
4 module Root
5 def self.root?
6 Process.uid.zero?
7 end
8 end
9 end
10 end
0 # frozen_string_literal: true
1
2 module LegacyFacter
3 module Util
4 # A util module for custom_facts containing helper methods
5 module Values
6 module_function
7
8 class DeepFreezeError < StandardError; end
9
10 # Duplicate and deeply freeze a given data structure
11 #
12 # @param value [Object] The structure to freeze
13 # @return [void]
14 def deep_freeze(value)
15 case value
16 when Numeric, Symbol, TrueClass, FalseClass, NilClass
17 # These are immutable values, we can safely ignore them
18 value
19 when String
20 value.dup.freeze
21 when Array
22 value.map do |entry|
23 deep_freeze(entry)
24 end.freeze
25 when Hash
26 value.each_with_object({}) do |(key, val), hash|
27 hash[deep_freeze(key)] = deep_freeze(val)
28 end.freeze
29 else
30 raise DeepFreezeError, "Cannot deep freeze #{value}:#{value.class}"
31 end
32 end
33
34 class DeepMergeError < StandardError; end
35
36 # Perform a deep merge of two nested data structures.
37 #
38 # @param left [Object]
39 # @param right [Object]
40 # @param path [Array<String>] The traversal path followed when merging nested hashes
41 #
42 # @return [Object] The merged data structure.
43 def deep_merge(left, right, path = [])
44 ret = nil
45
46 if left.is_a?(Hash) && right.is_a?(Hash)
47 ret = left.merge(right) do |key, left_val, right_val|
48 path.push(key)
49 merged = deep_merge(left_val, right_val, path)
50 path.pop
51 merged
52 end
53 elsif left.is_a?(Array) && right.is_a?(Array)
54 ret = left.dup.concat(right)
55 elsif right.nil?
56 ret = left
57 elsif left.nil?
58 ret = right
59 elsif left.nil? && right.nil?
60 ret = nil
61 else
62 msg = "Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}"
63 unless path.empty?
64 msg << ' at root'
65 msg << path.map { |part| "[#{part.inspect}]" }.join
66 end
67 raise DeepMergeError, msg
68 end
69
70 ret
71 end
72
73 def convert(value)
74 value = value.to_s if value.is_a?(Symbol)
75 value = value.downcase if value.is_a?(String)
76 value
77 end
78
79 # Flatten the given data structure to something that's suitable to return
80 # as flat facts.
81 #
82 # @param path [String] The fact path to be prefixed to the given value.
83 # @param structure [Object] The data structure to flatten. Nested hashes
84 # will be recursively flattened, everything else will be returned as-is.
85 #
86 # @return [Hash] The given data structure prefixed with the given path
87 def flatten_structure(path, structure)
88 results = {}
89
90 if structure.is_a? Hash
91 structure.each_pair do |name, value|
92 new_path = "#{path}_#{name}".gsub(%r{\-|/}, '_')
93 results.merge! flatten_structure(new_path, value)
94 end
95 elsif structure.is_a? Array
96 structure.each_with_index do |value, index|
97 new_path = "#{path}_#{index}"
98 results.merge! flatten_structure(new_path, value)
99 end
100 else
101 results[path] = structure
102 end
103
104 results
105 end
106 end
107 end
108 end
0 # frozen_string_literal: true
1
2 module LegacyFacter
3 module Util
4 module Root
5 def self.root?
6 require 'facter/resolvers/windows/ffi/identity_ffi'
7 IdentityFFI.privileged?
8 end
9 end
10 end
11 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class AioAgentVersion
5 FACT_NAME = 'aio_agent_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Disks
5 FACT_NAME = 'disks'
6 ALIASES = %w[blockdevices blockdevice_.*_size].freeze
7
8 def call_the_resolver
9 facts = []
10 disks = Facter::Resolvers::Aix::Disks.resolve(:disks)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) if disks.nil? || disks.empty?
13
14 blockdevices = disks.keys.join(',')
15 facts.push(Facter::ResolvedFact.new(FACT_NAME, disks))
16 facts.push(Facter::ResolvedFact.new('blockdevices', blockdevices, :legacy))
17 add_legacy_facts(disks, facts)
18
19 facts
20 end
21
22 private
23
24 def add_legacy_facts(disks, facts)
25 disks.each do |disk_name, disk_info|
26 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes], :legacy))
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Filesystems
5 FACT_NAME = 'filesystems'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::Filesystem.resolve(:file_systems)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Hypervisors
5 class Lpar
6 FACT_NAME = 'hypervisors.lpar'
7
8 def call_the_resolver
9 lpar_partition_number = Facter::Resolvers::Lpar.resolve(:lpar_partition_number)
10 return Facter::ResolvedFact.new(FACT_NAME, nil) unless lpar_partition_number&.positive?
11
12 Facter::ResolvedFact.new(FACT_NAME,
13 partition_number: lpar_partition_number,
14 partition_name: Facter::Resolvers::Lpar.resolve(:lpar_partition_name))
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Hypervisors
5 class Wpar
6 FACT_NAME = 'hypervisors.wpar'
7
8 def call_the_resolver
9 wpar_key = Facter::Resolvers::Wpar.resolve(:wpar_key)
10 return Facter::ResolvedFact.new(FACT_NAME, nil) unless wpar_key&.positive?
11
12 Facter::ResolvedFact.new(FACT_NAME, key: wpar_key,
13 configured_id: Facter::Resolvers::Wpar.resolve(:wpar_configured_id))
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Identity
5 class Gid
6 FACT_NAME = 'identity.gid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:gid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Identity
5 class Group
6 FACT_NAME = 'identity.group'
7 ALIASES = 'gid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:group)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:privileged)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Identity
5 class Uid
6 FACT_NAME = 'identity.uid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:uid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:user)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Interfaces
5 FACT_NAME = 'interfaces'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
10 fact_value = fact_value&.any? ? fact_value.keys.sort.join(',') : nil
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:kernel)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
9 kernelmajversion = fact_value.split('-')[0]
10
11 Facter::ResolvedFact.new(FACT_NAME, kernelmajversion)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build).strip
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
9 kernelversion = fact_value.split('-')[0]
10
11 Facter::ResolvedFact.new(FACT_NAME, kernelversion)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class LoadAverages
5 FACT_NAME = 'load_averages'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::LoadAverages.resolve(:load_averages)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class MacaddressInterfaces
5 FACT_NAME = 'macaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("macaddress_#{interface_name}", info[:mac], :legacy) if info[:mac]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class Available
7 FACT_NAME = 'memory.swap.available'
8 ALIASES = 'swapfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:available_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class AvailableBytes
7 FACT_NAME = 'memory.swap.available_bytes'
8 ALIASES = 'swapfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
12 fact_value = fact_value[:available_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class Capacity
7 FACT_NAME = 'memory.swap.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
11 fact_value = fact_value[:capacity] if fact_value
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class Total
7 FACT_NAME = 'memory.swap.total'
8 ALIASES = 'swapsize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:total_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class TotalBytes
7 FACT_NAME = 'memory.swap.total_bytes'
8 ALIASES = 'swapsize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
12 fact_value = fact_value[:total_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class Used
7 FACT_NAME = 'memory.swap.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
11 if fact_value
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:used_bytes])
13 end
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module Swap
6 class UsedBytes
7 FACT_NAME = 'memory.swap.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:swap)
11 fact_value = fact_value[:used_bytes] if fact_value
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:available_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
12 fact_value = fact_value[:available_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
11 fact_value = fact_value[:capacity] if fact_value
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:total_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
12 fact_value = fact_value[:total_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
11 if fact_value
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:used_bytes])
13 end
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Memory.resolve(:system)
11 fact_value = fact_value[:used_bytes] if fact_value
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Mountpoints
5 FACT_NAME = 'mountpoints'
6
7 def call_the_resolver
8 mountpoints = Facter::Resolvers::Aix::Mountpoints.resolve(:mountpoints)
9
10 Facter::ResolvedFact.new(FACT_NAME, mountpoints)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class MtuInterfaces
5 FACT_NAME = 'mtu_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("mtu_#{interface_name}", info[:mtu], :legacy) if info[:mtu]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:domain)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:fqdn)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:hostname)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
10 fact_value = fact_value&.any? ? fact_value : nil
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Networking
5 class Scope6
6 FACT_NAME = 'networking.scope6'
7 ALIASES = 'scope6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Networking.resolve(:scope6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new('scope6', fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class NimType
5 FACT_NAME = 'nim_type'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Aix::Nim.resolve(:type)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Architecture.resolve(:architecture)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hardware.resolve(:hardware)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::OsLevel.resolve(:build)
11 major = fact_value.split('-')[0]
12
13 [Facter::ResolvedFact.new(FACT_NAME, full: fact_value.strip, major: major),
14 Facter::ResolvedFact.new(ALIASES.first, major, :legacy),
15 Facter::ResolvedFact.new(ALIASES.last, fact_value.strip, :legacy)]
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Partitions
5 FACT_NAME = 'partitions'
6
7 def call_the_resolver
8 partitions = Facter::Resolvers::Aix::Partitions.resolve(:partitions)
9
10 Facter::ResolvedFact.new(FACT_NAME, partitions.empty? ? nil : partitions)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Processor
5 FACT_NAME = 'processor.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 processors = Facter::Resolvers::Aix::Processors.resolve(:models)
11
12 processors.count.times do |iterator|
13 arr << Facter::ResolvedFact.new("processor#{iterator}", processors[iterator], :legacy)
14 end
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Cores
6 FACT_NAME = 'processors.cores'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Processors.resolve(:cores_per_socket)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Aix::Processors.resolve(:logical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:processor)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Processors.resolve(:models)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Processors
5 class Threads
6 FACT_NAME = 'processors.threads'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Aix::Processors.resolve(:threads_per_core)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Scope6Interfaces
5 FACT_NAME = 'scope6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 resolved_facts = []
10 interfaces = Facter::Resolvers::Aix::Networking.resolve(:interfaces)
11
12 interfaces&.each do |interface_name, info|
13 if info[:scope6]
14 resolved_facts << Facter::ResolvedFact.new("scope6_#{interface_name}", info[:scope6], :legacy)
15 end
16 end
17
18 resolved_facts
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Serialnumber
5 FACT_NAME = 'serialnumber'
6 TYPE = :legacy
7
8 def call_the_resolver
9 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
10 end
11
12 private
13
14 def fact_value
15 Facter::Resolvers::Aix::Serialnumber.resolve(:serialnumber)
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, fact_value)
9 end
10
11 private
12
13 def fact_value
14 resolver_data.map { |el| create_ssh_fact(el) }.inject(:merge)
15 end
16
17 def resolver_data
18 Facter::Resolvers::Ssh.resolve(:ssh)
19 end
20
21 def create_ssh_fact(ssh)
22 return {} unless ssh
23
24 { ssh.name.to_sym => {
25 fingerprints: {
26 sha1: ssh.fingerprint.sha1,
27 sha256: ssh.fingerprint.sha256
28 },
29 key: ssh.key,
30 type: ssh.type
31 } }
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
12 facts
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each do |ssh|
12 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
13 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
14 end
15 facts
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:days)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:hours)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:seconds)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:uptime)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Aix
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Alpine
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release, release_file: '/etc/alpine-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 class Lsbdistcodename
5 FACT_NAME = 'lsbdistcodename'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:codename)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 class Lsbdistdescription
5 FACT_NAME = 'lsbdistdescription'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:description)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 class Lsbdistid
5 FACT_NAME = 'lsbdistid'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 module Os
5 module Distro
6 class Codename
7 FACT_NAME = 'os.distro.codename'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::SpecificReleaseFile.resolve(
11 :release, release_file: '/etc/system-release'
12 ).match(/.*release.*(\(.*\)).*/)
13
14 fact_value = fact_value[1].gsub(/\(|\)/, '') if fact_value
15 fact_value ||= 'n/a'
16
17 Facter::ResolvedFact.new(FACT_NAME, fact_value)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 module Os
5 module Distro
6 class Description
7 FACT_NAME = 'os.distro.description'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::SpecificReleaseFile.resolve(
11 :release, release_file: '/etc/system-release'
12 )
13
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 module Os
5 module Distro
6 class Id
7 FACT_NAME = 'os.distro.id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::SpecificReleaseFile.resolve(
11 :release, release_file: '/etc/system-release'
12 ).split('release')[0].split.reject { |el| el.casecmp('linux').zero? }.join
13
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 version = determine_release_version
12
13 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
14
15 [Facter::ResolvedFact.new(FACT_NAME, version),
16 Facter::ResolvedFact.new(ALIASES[0], version['full'], :legacy),
17 Facter::ResolvedFact.new(ALIASES[1], version['major'], :legacy),
18 Facter::ResolvedFact.new(ALIASES[2], version['minor'], :legacy)]
19 end
20
21 def determine_release_version
22 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/system-release')
23 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
24
25 Facter::Util::Facts.release_hash_from_string(version)
26 end
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Amzn
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/system-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 full_version = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 versions_split = full_version.split('.')
10 major_version = versions_split[0]
11 Facter::ResolvedFact.new(FACT_NAME, major_version)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease).sub(/\A(\d+(\.\d+)*).*/, '\1')
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 class LoadAverages
5 FACT_NAME = 'load_averages'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::LoadAverages.resolve(:load_averages)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Bsd::Processors.resolve(:logical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7 ALIASES = 'processor.*'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Bsd::Processors.resolve(:models)
11 return nil unless fact_value
12
13 facts = [Facter::ResolvedFact.new(FACT_NAME, fact_value)]
14 fact_value.each_with_index do |value, index|
15 facts.push(Facter::ResolvedFact.new("processor#{index}", value, :legacy))
16 end
17 facts
18 end
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Bsd
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Bsd::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11 fact_value = 'amd64' if fact_value == 'x86_64'
12 fact_value = 'i386' if fact_value =~ /i[3456]86|pentium/
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 class Lsbdistcodename
5 FACT_NAME = 'lsbdistcodename'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:codename)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 class Lsbdistdescription
5 FACT_NAME = 'lsbdistdescription'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:description)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 class Lsbdistid
5 FACT_NAME = 'lsbdistid'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 module Distro
6 class Codename
7 FACT_NAME = 'os.distro.codename'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:version_codename)
11 fact_value ||= retrieve_from_version
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15
16 def retrieve_from_version
17 version = Facter::Resolvers::OsRelease.resolve(:version)
18 return unless version
19
20 codename = /\(.*\)$/.match(version).to_s.gsub(/\(|\)/, '')
21 return codename unless codename.empty?
22
23 /[A-Za-z]+\s[A-Za-z]+/.match(version).to_s.split(' ').first.downcase
24 end
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 module Distro
6 class Description
7 FACT_NAME = 'os.distro.description'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:pretty_name)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 module Distro
6 class Id
7 FACT_NAME = 'os.distro.id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:id).capitalize
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 fact_value = determine_release_for_os
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES[0], fact_value['full'], :legacy),
16 Facter::ResolvedFact.new(ALIASES[1], fact_value['major'], :legacy),
17 Facter::ResolvedFact.new(ALIASES[2], fact_value['minor'], :legacy)]
18 end
19
20 private
21
22 def determine_release_for_os
23 release = Facter::Resolvers::DebianVersion.resolve(:version)
24 return unless release
25
26 versions = release.split('.')
27 fact_value = {}
28 fact_value['full'] = release
29 fact_value['major'] = versions[0]
30 fact_value['minor'] = versions[1].gsub(/^0([1-9])/, '\1') if versions[1]
31 fact_value
32 end
33 end
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Debian
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 fact_value = determine_release_for_os
11
12 return Facter::ResolvedFact.new(FACT_NAME, fact_value) unless fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES.first, fact_value['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, fact_value['full'], :legacy)]
17 end
18
19 private
20
21 def determine_release_for_os
22 release = Facter::Resolvers::DebianVersion.resolve(:version)
23 return unless release
24
25 versions = release.split('.')
26 fact_value = {}
27 fact_value['full'] = release
28 fact_value['major'] = versions[0]
29 fact_value['minor'] = versions[1].gsub(/^0([1-9])/, '\1') if versions[1]
30 fact_value
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Devuan
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:release)
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless fact_value
13
14 release = construct_release(fact_value)
15
16 [Facter::ResolvedFact.new(FACT_NAME, release),
17 Facter::ResolvedFact.new(ALIASES[0], fact_value, :legacy),
18 Facter::ResolvedFact.new(ALIASES[1], release['major'], :legacy),
19 Facter::ResolvedFact.new(ALIASES[2], release['minor'], :legacy)]
20 end
21
22 def construct_release(version)
23 versions = version.split('.')
24
25 {}.tap do |release|
26 release['full'] = version
27 release['major'] = versions[0]
28 release['minor'] = versions[1] if versions[1]
29 end
30 end
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Devuan
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release, release_file: '/etc/devuan_version')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Disks
5 FACT_NAME = 'disks'
6
7 def call_the_resolver
8 disks = Facter::Resolvers::Freebsd::Geom.resolve(:disks)
9
10 Facter::ResolvedFact.new(FACT_NAME, disks)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Bios
6 class ReleaseDate
7 FACT_NAME = 'dmi.bios.release_date'
8 ALIASES = 'bios_release_date'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:bios_date)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Bios
6 class Vendor
7 FACT_NAME = 'dmi.bios.vendor'
8 ALIASES = 'bios_vendor'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:bios_vendor)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Bios
6 class Version
7 FACT_NAME = 'dmi.bios.version'
8 ALIASES = 'bios_version'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:bios_version)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 class Manufacturer
6 FACT_NAME = 'dmi.manufacturer'
7 ALIASES = 'manufacturer'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:sys_vendor)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Product
6 class Name
7 FACT_NAME = 'dmi.product.name'
8 ALIASES = 'productname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:product_name)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Product
6 class SerialNumber
7 FACT_NAME = 'dmi.product.serial_number'
8 ALIASES = 'serialnumber'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:product_serial)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Dmi
5 module Product
6 class Uuid
7 FACT_NAME = 'dmi.product.uuid'
8 ALIASES = 'uuid'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::DmiBios.resolve(:product_uuid)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Ec2Metadata
5 FACT_NAME = 'ec2_metadata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:metadata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 Facter::Util::Facts::Posix::VirtualDetector.platform =~ /kvm|xen|aws/
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Ec2Userdata
5 FACT_NAME = 'ec2_userdata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:userdata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 Facter::Util::Facts::Posix::VirtualDetector.platform =~ /kvm|xen|aws/
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Identity
5 class Gid
6 FACT_NAME = 'identity.gid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:gid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Identity
5 class Group
6 FACT_NAME = 'identity.group'
7 ALIASES = 'gid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:group)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:privileged)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Identity
5 class Uid
6 FACT_NAME = 'identity.uid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:uid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:user)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class IsVirtual
5 FACT_NAME = 'is_virtual'
6
7 def call_the_resolver
8 fact_value = Facter::Util::Facts::Posix::VirtualDetector.platform
9
10 Facter::ResolvedFact.new(FACT_NAME, check_if_virtual(fact_value))
11 end
12
13 private
14
15 def check_if_virtual(found_vm)
16 Facter::Util::Facts::PHYSICAL_HYPERVISORS.count(found_vm).zero?
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class Available
7 FACT_NAME = 'memory.swap.available'
8 ALIASES = 'swapfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:available_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class AvailableBytes
7 FACT_NAME = 'memory.swap.available_bytes'
8 ALIASES = 'swapfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:available_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class Capacity
7 FACT_NAME = 'memory.swap.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class Encrypted
7 FACT_NAME = 'memory.swap.encrypted'
8 ALIASES = 'swapencrypted'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:encrypted)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class Total
7 FACT_NAME = 'memory.swap.total'
8 ALIASES = 'swapsize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:total_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class TotalBytes
7 FACT_NAME = 'memory.swap.total_bytes'
8 ALIASES = 'swapsize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:total_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class Used
7 FACT_NAME = 'memory.swap.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module Swap
6 class UsedBytes
7 FACT_NAME = 'memory.swap.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SwapMemory.resolve(:used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:available_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:available_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:total_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:total_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::SystemMemory.resolve(:used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Mountpoints
5 FACT_NAME = 'mountpoints'
6
7 def call_the_resolver
8 mountpoints = Facter::Resolvers::Mountpoints.resolve(FACT_NAME.to_sym)
9 return Facter::ResolvedFact.new(FACT_NAME, nil) unless mountpoints
10
11 fact = {}
12 mountpoints.each do |mnt|
13 fact[mnt[:path].to_sym] = mnt.reject { |k| k == :path }
14 end
15
16 Facter::ResolvedFact.new(FACT_NAME, fact)
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Dhcp
6 FACT_NAME = 'networking.dhcp'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:dhcp)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:domain)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:fqdn)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:hostname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Networking
5 class Scope6
6 FACT_NAME = 'networking.scope6'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:scope6)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 installed_userland = Facter::Resolvers::Freebsd::FreebsdVersion.resolve(:installed_userland)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) if !installed_userland || installed_userland.empty?
13
14 value = build_release_hash_from_version(installed_userland)
15
16 [Facter::ResolvedFact.new(FACT_NAME, value),
17 Facter::ResolvedFact.new(ALIASES.first, value[:major], :legacy),
18 Facter::ResolvedFact.new(ALIASES.last, installed_userland, :legacy)]
19 end
20
21 private
22
23 def build_release_hash_from_version(version_string)
24 version, branch_value = version_string.split('-', 2)
25 major_value, minor_value = version.split('.')
26 patchlevel_value = branch_value.split('-p')[1]
27
28 value = {
29 full: version_string,
30 major: major_value,
31 branch: branch_value
32 }
33
34 value[:minor] = minor_value if minor_value
35 value[:patchlevel] = patchlevel_value if patchlevel_value
36
37 value
38 end
39 end
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Partitions
5 FACT_NAME = 'partitions'
6
7 def call_the_resolver
8 partitions = Facter::Resolvers::Freebsd::Geom.resolve(:partitions)
9
10 Facter::ResolvedFact.new(FACT_NAME, partitions)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::Processors.resolve(:logical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:processor)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7 ALIASES = 'processor.*'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Freebsd::Processors.resolve(:models)
11 return nil unless fact_value
12
13 facts = [Facter::ResolvedFact.new(FACT_NAME, fact_value)]
14 fact_value.each_with_index do |value, index|
15 facts.push(Facter::ResolvedFact.new("processor#{index}", value, :legacy))
16 end
17 facts
18 end
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Freebsd::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 result = Facter::Resolvers::Ssh.resolve(:ssh)
9 ssh_facts = {}
10 result.each { |ssh| ssh_facts.merge!(create_ssh_fact(ssh)) }
11 Facter::ResolvedFact.new(FACT_NAME, ssh_facts)
12 end
13
14 private
15
16 def create_ssh_fact(ssh)
17 { ssh.name.to_sym =>
18 { fingerprints: { sha1: ssh.fingerprint.sha1,
19 sha256: ssh.fingerprint.sha256 },
20 key: ssh.key,
21 type: ssh.type } }
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
12 facts
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each do |ssh|
12 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
13 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
14 end
15 facts
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:days)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:hours)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:seconds)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:uptime)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class Virtual
5 FACT_NAME = 'virtual'
6
7 def initialize
8 @log = Facter::Log.new(self)
9 end
10
11 def call_the_resolver
12 @log.debug('FreeBSD Virtual Resolver')
13 fact_value = Facter::Util::Facts::Posix::VirtualDetector.platform
14 @log.debug("Fact value is: #{fact_value}")
15
16 Facter::ResolvedFact.new(FACT_NAME, fact_value)
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class ZfsFeaturenumbers
5 FACT_NAME = 'zfs_featurenumbers'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::ZFS.resolve(:zfs_featurenumbers)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class ZfsVersion
5 FACT_NAME = 'zfs_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::ZFS.resolve(:zfs_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class ZpoolFeatureflags
5 FACT_NAME = 'zpool_featureflags'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_featureflags)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class ZpoolFeaturenumbers
5 FACT_NAME = 'zpool_featurenumbers'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_featurenumbers)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Freebsd
4 class ZpoolVersion
5 FACT_NAME = 'zpool_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Gentoo
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/gentoo-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class AioAgentVersion
5 FACT_NAME = 'aio_agent_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class AzMetadata
5 FACT_NAME = 'az_metadata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless azure_hypervisor?
9
10 fact_value = Facter::Resolvers::Az.resolve(:metadata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def azure_hypervisor?
18 Facter::Util::Facts::Posix::VirtualDetector.platform == 'hyperv'
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Cloud
5 class Provider
6 FACT_NAME = 'cloud.provider'
7
8 def call_the_resolver
9 provider = case Facter::Util::Facts::Posix::VirtualDetector.platform
10 when 'hyperv'
11 'azure' unless Facter::Resolvers::Az.resolve(:metadata).empty?
12 when 'kvm', 'xen'
13 'aws' unless Facter::Resolvers::Ec2.resolve(:metadata).empty?
14 when 'gce'
15 'gce' unless Facter::Resolvers::Gce.resolve(:metadata).empty?
16 end
17
18 Facter::ResolvedFact.new(FACT_NAME, provider)
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class DhcpServers
5 FACT_NAME = 'dhcp_servers'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = construct_addresses_hash
10 fact_value = !fact_value || fact_value.empty? ? nil : fact_value
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13
14 private
15
16 def construct_addresses_hash
17 primary_dhcp = Facter::Resolvers::Linux::Networking.resolve(:dhcp)
18 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
19 return unless interfaces
20
21 servers = { system: primary_dhcp }
22 interfaces&.each { |interface_name, info| servers[interface_name] = info[:dhcp] if info[:dhcp] }
23 servers
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Disks
5 FACT_NAME = 'disks'
6 ALIASES = %w[blockdevices blockdevice_.*_model blockdevice_.*_size blockdevice_.*_vendor].freeze
7
8 def call_the_resolver
9 facts = []
10 disks = Facter::Resolvers::Linux::Disks.resolve(:disks)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) if disks.nil? || disks.empty?
13
14 blockdevices = disks.keys.join(',')
15 facts.push(Facter::ResolvedFact.new(FACT_NAME, disks))
16 facts.push(Facter::ResolvedFact.new('blockdevices', blockdevices, :legacy))
17 add_legacy_facts(disks, facts)
18
19 facts
20 end
21
22 private
23
24 def add_legacy_facts(disks, facts)
25 disks.each do |disk_name, disk_info|
26 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_model", disk_info[:model], :legacy))
27 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes], :legacy))
28 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_vendor", disk_info[:vendor], :legacy))
29 end
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Bios
6 class ReleaseDate
7 FACT_NAME = 'dmi.bios.release_date'
8 ALIASES = 'bios_release_date'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:bios_date)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Bios
6 class Vendor
7 FACT_NAME = 'dmi.bios.vendor'
8 ALIASES = 'bios_vendor'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Bios
6 class Version
7 FACT_NAME = 'dmi.bios.version'
8 ALIASES = 'bios_version'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:bios_version)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Board
6 class AssetTag
7 FACT_NAME = 'dmi.board.asset_tag'
8 ALIASES = 'boardassettag'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:board_asset_tag)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Board
6 class Manufacturer
7 FACT_NAME = 'dmi.board.manufacturer'
8 ALIASES = 'boardmanufacturer'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:board_vendor)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Board
6 class Product
7 FACT_NAME = 'dmi.board.product'
8 ALIASES = 'boardproductname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:board_name)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Board
6 class SerialNumber
7 FACT_NAME = 'dmi.board.serial_number'
8 ALIASES = 'boardserialnumber'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:board_serial)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Chassis
6 class AssetTag
7 FACT_NAME = 'dmi.chassis.asset_tag'
8 ALIASES = 'chassisassettag'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:chassis_asset_tag)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Chassis
6 class Type
7 FACT_NAME = 'dmi.chassis.type'
8 ALIASES = 'chassistype'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:chassis_type)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 class Manufacturer
6 FACT_NAME = 'dmi.manufacturer'
7 ALIASES = 'manufacturer'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:sys_vendor)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Product
6 class Name
7 FACT_NAME = 'dmi.product.name'
8 ALIASES = 'productname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Product
6 class SerialNumber
7 FACT_NAME = 'dmi.product.serial_number'
8 ALIASES = 'serialnumber'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:product_serial)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Dmi
5 module Product
6 class Uuid
7 FACT_NAME = 'dmi.product.uuid'
8 ALIASES = 'uuid'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::DmiBios.resolve(:product_uuid)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Ec2Metadata
5 FACT_NAME = 'ec2_metadata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:metadata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 Facter::Util::Facts::Posix::VirtualDetector.platform =~ /kvm|xen|aws/
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Ec2Userdata
5 FACT_NAME = 'ec2_userdata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:userdata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 Facter::Util::Facts::Posix::VirtualDetector.platform =~ /kvm|xen|aws/
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Filesystems
5 FACT_NAME = 'filesystems'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Linux::Filesystems.resolve(:systems)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class FipsEnabled
5 FACT_NAME = 'fips_enabled'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Linux::FipsEnabled.resolve(:fips_enabled)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Gce
5 FACT_NAME = 'gce'
6
7 def call_the_resolver
8 bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
9
10 fact_value = bios_vendor&.include?('Google') ? Facter::Resolvers::Gce.resolve(:metadata) : nil
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Docker
6 FACT_NAME = 'hypervisors.docker'
7
8 def call_the_resolver
9 fact_value = check_docker
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_docker
14 info = Facter::Resolvers::Containers.resolve(:hypervisor)
15 info[:docker] if info
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class HyperV
6 FACT_NAME = 'hypervisors.hyperv'
7
8 def call_the_resolver
9 fact_value = check_hyper_v
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_hyper_v
14 manufacturer = Facter::Resolvers::Linux::DmiBios.resolve(:sys_vendor)
15 product_name = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
16
17 return {} if manufacturer =~ /Microsoft/ || product_name == 'Virtual Machine'
18
19 nil
20 end
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Kvm
6 FACT_NAME = 'hypervisors.kvm'
7
8 def initialize
9 @log = Facter::Log.new(self)
10 end
11
12 def call_the_resolver
13 hypervisor = discover_hypervisor
14 @log.debug("Detected hypervisor #{hypervisor}")
15
16 return Facter::ResolvedFact.new(FACT_NAME, nil) if %w[virtualbox parallels].include?(hypervisor)
17
18 fact_value = discover_provider if kvm?
19
20 Facter::ResolvedFact.new(FACT_NAME, fact_value)
21 end
22
23 private
24
25 def discover_hypervisor
26 product_name = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
27 @log.debug("Detected product name: #{product_name}")
28
29 return unless product_name
30
31 Facter::Util::Facts::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
32
33 product_name
34 end
35
36 def kvm?
37 product_name = discover_hypervisor
38 bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
39 @log.debug("Detected bios vendor: #{bios_vendor}")
40
41 Facter::Resolvers::VirtWhat.resolve(:vm) == 'kvm' ||
42 Facter::Resolvers::Lspci.resolve(:vm) == 'kvm' ||
43 bios_vendor&.include?('Amazon EC2') ||
44 bios_vendor&.include?('Google') ||
45 product_name&.include?('OpenStack')
46 end
47
48 def discover_provider
49 manufacturer = Facter::Resolvers::Linux::DmiBios.resolve(:sys_vendor)
50 @log.debug("Detected manufacturer: #{manufacturer}")
51
52 return { google: true } if manufacturer == 'Google'
53
54 return { openstack: true } if manufacturer =~ /^OpenStack/
55
56 return { amazon: true } if manufacturer =~ /^Amazon/
57
58 {}
59 end
60 end
61 end
62 end
63 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Lxc
6 FACT_NAME = 'hypervisors.lxc'
7
8 def call_the_resolver
9 fact_value = check_lxc
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_lxc
14 info = Facter::Resolvers::Containers.resolve(:hypervisor)
15 info[:lxc] if info
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Openvz
6 FACT_NAME = 'hypervisors.openvz'
7
8 def call_the_resolver
9 fact_value = check_openvz
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_openvz
14 openvz = Facter::Resolvers::OpenVz.resolve(:vm)
15 return unless openvz
16
17 id = Facter::Resolvers::OpenVz.resolve(:id)
18
19 { id: id.to_i, host: openvz == 'openvzhn' }
20 end
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class SystemdNspawn
6 FACT_NAME = 'hypervisors.systemd_nspawn'
7
8 def call_the_resolver
9 fact_value = check_nspawn
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_nspawn
14 info = Facter::Resolvers::Containers.resolve(:hypervisor)
15 info[:systemd_nspawn] if info
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class VirtualBox
6 FACT_NAME = 'hypervisors.virtualbox'
7
8 def call_the_resolver
9 fact_value = check_virtualbox
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 def check_virtualbox
14 virtualbox_details = nil
15
16 if Facter::Resolvers::Linux::DmiBios.resolve(:product_name) == 'VirtualBox' ||
17 Facter::Resolvers::VirtWhat.resolve(:vm) =~ /virtualbox/ ||
18 Facter::Resolvers::Lspci.resolve(:vm) == 'virtualbox'
19
20 virtualbox_details = {}
21
22 version = Facter::Resolvers::DmiDecode.resolve(:virtualbox_version)
23 revision = Facter::Resolvers::DmiDecode.resolve(:virtualbox_revision)
24
25 virtualbox_details[:version] = version if version
26 virtualbox_details[:revision] = revision if revision
27 end
28
29 virtualbox_details
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Vmware
6 FACT_NAME = 'hypervisors.vmware'
7
8 def initialize
9 @log = Facter::Log.new(self)
10 end
11
12 def call_the_resolver
13 if vmware?
14 @log.debug('Vmware hypervisor detected')
15 fact_value = {}
16
17 version = Facter::Resolvers::DmiDecode.resolve(:vmware_version)
18 fact_value[:version] = version if version && !version.empty?
19
20 return Facter::ResolvedFact.new(FACT_NAME, fact_value)
21 end
22
23 @log.debug('No Vmware hypervisor detected.')
24 []
25 end
26
27 private
28
29 def vmware?
30 Facter::Resolvers::VirtWhat.resolve(:vm) == 'vmware' ||
31 Facter::Resolvers::Linux::DmiBios.resolve(:product_name) == 'VMware' ||
32 Facter::Resolvers::Lspci.resolve(:vm) == 'vmware' ||
33 Facter::Resolvers::Linux::DmiBios.resolve(:sys_vendor) == 'VMware, Inc.'
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Hypervisors
5 class Xen
6 FACT_NAME = 'hypervisors.xen'
7
8 def initialize
9 @log = Facter::Log.new(self)
10 end
11
12 def call_the_resolver
13 if xen?
14 @log.debug('Xen hypervisor detected')
15 fact_value = {}
16
17 fact_value[:context] = hvm? ? 'hvm' : 'pv'
18 fact_value[:privileged] = Facter::Resolvers::Xen.resolve(:privileged)
19
20 return Facter::ResolvedFact.new(FACT_NAME, fact_value)
21 end
22
23 @log.debug('No Xen hypervisor detected.')
24 []
25 end
26
27 private
28
29 def xen?
30 Facter::Util::Facts::Posix::VirtualDetector.platform =~ /xen/
31 end
32
33 def hvm?
34 discover_hypervisor == 'xenhvm' || Facter::Resolvers::Lspci.resolve(:vm) == 'xenhvm'
35 end
36
37 def discover_hypervisor
38 product_name = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
39 return unless product_name
40
41 Facter::Util::Facts::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
42
43 product_name
44 end
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Identity
5 class Gid
6 FACT_NAME = 'identity.gid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:gid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Identity
5 class Group
6 FACT_NAME = 'identity.group'
7 ALIASES = 'gid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:group)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:privileged)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Identity
5 class Uid
6 FACT_NAME = 'identity.uid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:uid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:user)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Interfaces
5 FACT_NAME = 'interfaces'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value && !fact_value.empty? ? fact_value.keys.sort.join(',') : nil,
12 :legacy)
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class IsVirtual
5 FACT_NAME = 'is_virtual'
6
7 def call_the_resolver
8 fact_value = Facter::Util::Facts::Posix::VirtualDetector.platform
9
10 Facter::ResolvedFact.new(FACT_NAME, check_if_virtual(fact_value))
11 end
12
13 private
14
15 def check_if_virtual(found_vm)
16 Facter::Util::Facts::PHYSICAL_HYPERVISORS.count(found_vm).zero?
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 full_version = Facter::Resolvers::Uname.resolve(:kernelrelease)
9
10 Facter::ResolvedFact.new(FACT_NAME, major_version(full_version))
11 end
12
13 private
14
15 def major_version(full_version)
16 versions_split = full_version.split('.')
17 return versions_split[0] if versions_split.length <= 1
18
19 versions_split[0] + '.' + versions_split[1]
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 version_numbers = Facter::Resolvers::Uname.resolve(:kernelrelease).scan(/\d+/)
9 fact_value = version_numbers[0..2].join('.')
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class LoadAverages
5 FACT_NAME = 'load_averages'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Linux::LoadAverages.resolve(:load_averages)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Lsbdistrelease
5 FACT_NAME = 'lsbdistrelease'
6 ALIASES = %w[lsbmajdistrelease lsbminordistrelease].freeze
7 TYPE = :legacy
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::LsbRelease.resolve(:release)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil, :legacy) unless fact_value
13
14 version = fact_value.split('.')
15
16 [Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy),
17 Facter::ResolvedFact.new(ALIASES[0], version[0], :legacy),
18 Facter::ResolvedFact.new(ALIASES[1], version[1], :legacy)]
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class MacaddressInterfaces
5 FACT_NAME = 'macaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("macaddress_#{interface_name}", info[:mac], :legacy) if info[:mac]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class Available
7 FACT_NAME = 'memory.swap.available'
8 ALIASES = 'swapfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_free)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class AvailableBytes
7 FACT_NAME = 'memory.swap.available_bytes'
8 ALIASES = 'swapfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_free)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class Capacity
7 FACT_NAME = 'memory.swap.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class Total
7 FACT_NAME = 'memory.swap.total'
8 ALIASES = 'swapsize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_total)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class TotalBytes
7 FACT_NAME = 'memory.swap.total_bytes'
8 ALIASES = 'swapsize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_total)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class Used
7 FACT_NAME = 'memory.swap.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module Swap
6 class UsedBytes
7 FACT_NAME = 'memory.swap.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:swap_used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:memfree)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:memfree)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:total)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Linux::Memory.resolve(:total)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Memory.resolve(:used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Mountpoints
5 FACT_NAME = 'mountpoints'
6
7 def call_the_resolver
8 mountpoints = Facter::Resolvers::Mountpoints.resolve(FACT_NAME.to_sym)
9 return Facter::ResolvedFact.new(FACT_NAME, nil) unless mountpoints
10
11 fact = {}
12 mountpoints.each do |mnt|
13 fact[mnt[:path].to_sym] = mnt.reject { |k| k == :path }
14 end
15
16 Facter::ResolvedFact.new(FACT_NAME, fact)
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class MtuInterfaces
5 FACT_NAME = 'mtu_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("mtu_#{interface_name}", info[:mtu], :legacy) if info[:mtu]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Dhcp
6 FACT_NAME = 'networking.dhcp'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Networking.resolve(:dhcp)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Hostname.resolve(:domain)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Hostname.resolve(:fqdn)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Hostname.resolve(:hostname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Networking
5 class Scope6
6 FACT_NAME = 'networking.scope6'
7 ALIASES = 'scope6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Networking.resolve(:scope6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new('scope6', fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 fact_value = 'i386' if fact_value =~ /i[3456]86|pentium/
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Distro
6 class Codename
7 FACT_NAME = 'os.distro.codename'
8 ALIASES = 'lsbdistcodename'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:codename)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Distro
6 class Description
7 FACT_NAME = 'os.distro.description'
8 ALIASES = 'lsbdistdescription'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:description)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Distro
6 class Id
7 FACT_NAME = 'os.distro.id'
8 ALIASES = 'lsbdistid'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:release)
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless fact_value
13
14 versions = fact_value.split('.')
15 release = {
16 'full' => fact_value,
17 'major' => versions[0],
18 'minor' => versions[1]
19 }
20
21 [Facter::ResolvedFact.new(FACT_NAME, release),
22 Facter::ResolvedFact.new(ALIASES[0], fact_value, :legacy),
23 Facter::ResolvedFact.new(ALIASES[1], versions[0], :legacy),
24 Facter::ResolvedFact.new(ALIASES[2], versions[1], :legacy)]
25 end
26 end
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Distro
6 class Specification
7 FACT_NAME = 'os.distro.specification'
8 ALIASES = 'lsbrelease'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::LsbRelease.resolve(:lsb_version)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 id = Facter::Resolvers::OsRelease.resolve(:id_like)
11 id ||= Facter::Resolvers::OsRelease.resolve(:id)
12
13 fact_value = Facter::Util::Facts.discover_family(id)
14
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value.capitalize),
16 Facter::ResolvedFact.new(ALIASES, fact_value.capitalize, :legacy)]
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:name)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = Facter::Resolvers::OsRelease.resolve(:version_id)
11
12 [Facter::ResolvedFact.new(FACT_NAME, full: version, major: version),
13 Facter::ResolvedFact.new(ALIASES.first, version, :legacy),
14 Facter::ResolvedFact.new(ALIASES.last, version, :legacy)]
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class ConfigMode
7 FACT_NAME = 'os.selinux.config_mode'
8 ALIASES = 'selinux_config_mode'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SELinux.resolve(:config_mode)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class ConfigPolicy
7 FACT_NAME = 'os.selinux.config_policy'
8 ALIASES = 'selinux_config_policy'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SELinux.resolve(:config_policy)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class CurrentMode
7 FACT_NAME = 'os.selinux.current_mode'
8 ALIASES = 'selinux_current_mode'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SELinux.resolve(:current_mode)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class Enabled
7 FACT_NAME = 'os.selinux.enabled'
8 ALIASES = 'selinux'
9
10 def call_the_resolver
11 selinux = Facter::Resolvers::SELinux.resolve(:enabled)
12
13 [Facter::ResolvedFact.new(FACT_NAME, selinux),
14 Facter::ResolvedFact.new(ALIASES, selinux, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class Enforced
7 FACT_NAME = 'os.selinux.enforced'
8 ALIASES = 'selinux_enforced'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SELinux.resolve(:enforced)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Os
5 module Selinux
6 class PolicyVersion
7 FACT_NAME = 'os.selinux.policy_version'
8 ALIASES = 'selinux_policyversion'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SELinux.resolve(:policy_version)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Partitions
5 FACT_NAME = 'partitions'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, partitions)
9 end
10
11 def partitions
12 parts = Facter::Resolvers::Partitions.resolve(:partitions)
13 mountpoints = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
14 return parts unless mountpoints
15
16 mountpoints.reverse_each do |mnt|
17 next unless parts[mnt[:device]]
18
19 parts[mnt[:device]].merge!(mount: mnt[:path])
20 end
21 parts.empty? ? nil : parts
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Processor
5 FACT_NAME = 'processor.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 processors = Facter::Resolvers::Linux::Processors.resolve(:models)
11
12 (0...processors.count).each do |iterator|
13 arr << Facter::ResolvedFact.new("processor#{iterator}", processors[iterator], :legacy)
14 end
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Cores
6 FACT_NAME = 'processors.cores'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Lscpu.resolve(:cores_per_socket)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Processors.resolve(:processors)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:processor)
11 isa = get_isa(fact_value)
12
13 [Facter::ResolvedFact.new(FACT_NAME, isa), Facter::ResolvedFact.new(ALIASES, isa, :legacy)]
14 end
15
16 private
17
18 def get_isa(fact_value)
19 value_split = fact_value.split('.')
20 value_split.last
21 end
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Processors.resolve(:models)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Physicalcount
6 FACT_NAME = 'processors.physicalcount'
7 ALIASES = 'physicalprocessorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Processors
5 class Threads
6 FACT_NAME = 'processors.threads'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Linux::Lscpu.resolve(:threads_per_core)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Scope6Interfaces
5 FACT_NAME = 'scope6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 resolved_facts = []
10 interfaces = Facter::Resolvers::Linux::Networking.resolve(:interfaces)
11
12 interfaces&.each do |interface_name, info|
13 if info[:scope6]
14 resolved_facts << Facter::ResolvedFact.new("scope6_#{interface_name}", info[:scope6], :legacy)
15 end
16 end
17
18 resolved_facts
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, fact_value)
9 end
10
11 private
12
13 def fact_value
14 resolver_data.map { |el| create_ssh_fact(el) }.inject(:merge)
15 end
16
17 def resolver_data
18 Facter::Resolvers::Ssh.resolve(:ssh)
19 end
20
21 def create_ssh_fact(ssh)
22 return {} unless ssh
23
24 { ssh.name.to_sym => {
25 fingerprints: {
26 sha1: ssh.fingerprint.sha1,
27 sha256: ssh.fingerprint.sha256
28 },
29 key: ssh.key,
30 type: ssh.type
31 } }
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
12 facts
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each do |ssh|
12 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
13 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
14 end
15 facts
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 hypervisors = Facter::Resolvers::Containers.resolve(:hypervisor)
11
12 fact_value = if hypervisors && hypervisors[:docker]
13 Facter::Resolvers::Linux::DockerUptime.resolve(:days)
14 else
15 Facter::Resolvers::Uptime.resolve(:days)
16 end
17
18 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 hypervisors = Facter::Resolvers::Containers.resolve(:hypervisor)
11
12 fact_value = if hypervisors && hypervisors[:docker]
13 Facter::Resolvers::Linux::DockerUptime.resolve(:hours)
14 else
15 Facter::Resolvers::Uptime.resolve(:hours)
16 end
17
18 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 hypervisors = Facter::Resolvers::Containers.resolve(:hypervisor)
11
12 fact_value = if hypervisors && hypervisors[:docker]
13 Facter::Resolvers::Linux::DockerUptime.resolve(:seconds)
14 else
15 Facter::Resolvers::Uptime.resolve(:seconds)
16 end
17
18 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 hypervisors = Facter::Resolvers::Containers.resolve(:hypervisor)
11
12 fact_value = if hypervisors && hypervisors[:docker]
13 Facter::Resolvers::Linux::DockerUptime.resolve(:uptime)
14 else
15 Facter::Resolvers::Uptime.resolve(:uptime)
16 end
17
18 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Virtual
5 FACT_NAME = 'virtual'
6
7 def initialize
8 @log = Facter::Log.new(self)
9 end
10
11 def call_the_resolver
12 @log.debug('Linux Virtual Resolver')
13 fact_value = Facter::Util::Facts::Posix::VirtualDetector.platform
14 @log.debug("Fact value is: #{fact_value}")
15
16 Facter::ResolvedFact.new(FACT_NAME, fact_value)
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linux
4 class Xen
5 FACT_NAME = 'xen'
6 ALIASES = 'xendomains'
7
8 def call_the_resolver
9 xen_type = check_virt_what || check_xen
10 return Facter::ResolvedFact.new(FACT_NAME, nil) if !xen_type || xen_type != 'xen0'
11
12 domains = Facter::Resolvers::Xen.resolve(:domains) || []
13
14 [Facter::ResolvedFact.new(FACT_NAME, { domains: domains }),
15 Facter::ResolvedFact.new(ALIASES, domains.entries.join(','), :legacy)]
16 end
17
18 def check_virt_what
19 Facter::Resolvers::VirtWhat.resolve(:vm)
20 end
21
22 def check_xen
23 Facter::Resolvers::Xen.resolve(:vm)
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linuxmint
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:id)&.capitalize
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Linuxmint
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/linuxmint/info',
22 regex: /^RELEASE=(\d+)/ })
23 Facter::Util::Facts.release_hash_from_matchdata(version)
24 end
25
26 def from_os_release
27 version = Facter::Resolvers::OsRelease.resolve(:version_id)
28
29 Facter::Util::Facts.release_hash_from_string(version)
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class AioAgentVersion
5 FACT_NAME = 'aio_agent_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class DhcpServers
5 FACT_NAME = 'dhcp_servers'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = construct_addresses_hash
10 fact_value = !fact_value || fact_value.empty? ? nil : fact_value
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13
14 private
15
16 def construct_addresses_hash
17 primary_dhcp = Facter::Resolvers::Networking.resolve(:dhcp)
18 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
19 return unless interfaces
20
21 servers = { system: primary_dhcp }
22 interfaces&.each { |interface_name, info| servers[interface_name] = info[:dhcp] if info[:dhcp] }
23 servers
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Dmi
5 module Product
6 class Name
7 FACT_NAME = 'dmi.product.name'
8 ALIASES = 'productname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::DmiBios.resolve(:macosx_model)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Filesystems
5 FACT_NAME = 'filesystems'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Macosx::Filesystems.resolve(:macosx_filesystems)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Identity
5 class Gid
6 FACT_NAME = 'identity.gid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:gid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Identity
5 class Group
6 FACT_NAME = 'identity.group'
7 ALIASES = 'gid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:group)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:privileged)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Identity
5 class Uid
6 FACT_NAME = 'identity.uid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:uid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:user)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Interfaces
5 FACT_NAME = 'interfaces'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value ? fact_value.keys.sort.join(',') : nil, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class IsVirtual
5 FACT_NAME = 'is_virtual'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, virtual?)
9 end
10
11 private
12
13 def virtual?
14 hypervisor_name != nil
15 end
16
17 def hypervisor_name
18 model_identifier = Facter::Resolvers::Macosx::SystemProfiler.resolve(:model_identifier)
19 return 'vmware' if model_identifier&.start_with?('VMware')
20
21 boot_rom_version = Facter::Resolvers::Macosx::SystemProfiler.resolve(:boot_rom_version)
22 return 'virtualbox' if boot_rom_version&.start_with?('VirtualBox')
23
24 subsystem_vendor_id = Facter::Resolvers::Macosx::SystemProfiler.resolve(:subsystem_vendor_id)
25 return 'parallels' if subsystem_vendor_id&.start_with?('0x1ab8')
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 kernel_major_version = Facter::Resolvers::Uname.resolve(:kernelrelease).match(/[0-9]+\.[0-9]+/).to_s
9
10 Facter::ResolvedFact.new(FACT_NAME, kernel_major_version)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 kernel_version = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 Facter::ResolvedFact.new(FACT_NAME, kernel_version)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class LoadAverages
5 FACT_NAME = 'load_averages'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Macosx::LoadAverages.resolve(:load_averages)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class MacaddressInterfaces
5 FACT_NAME = 'macaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("macaddress_#{interface_name}", info[:mac], :legacy) if info[:mac]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class Available
7 FACT_NAME = 'memory.swap.available'
8 ALIASES = 'swapfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:available_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class AvailableBytes
7 FACT_NAME = 'memory.swap.available_bytes'
8 ALIASES = 'swapfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:available_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class Capacity
7 FACT_NAME = 'memory.swap.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class Encrypted
7 FACT_NAME = 'memory.swap.encrypted'
8 ALIASES = 'swapencrypted'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:encrypted)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class Total
7 FACT_NAME = 'memory.swap.total'
8 ALIASES = 'swapsize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:total_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class TotalBytes
7 FACT_NAME = 'memory.swap.total_bytes'
8 ALIASES = 'swapsize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:total_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class Used
7 FACT_NAME = 'memory.swap.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module Swap
6 class UsedBytes
7 FACT_NAME = 'memory.swap.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SwapMemory.resolve(:used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:available_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:available_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:capacity)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:total_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:total_bytes)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemMemory.resolve(:used_bytes)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Mountpoints
5 FACT_NAME = 'mountpoints'
6
7 def call_the_resolver
8 mountpoints = Facter::Resolvers::Macosx::Mountpoints.resolve(FACT_NAME.to_sym)
9 return Facter::ResolvedFact.new(FACT_NAME, nil) unless mountpoints
10
11 Facter::ResolvedFact.new(FACT_NAME, mountpoints)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class MtuInterfaces
5 FACT_NAME = 'mtu_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("mtu_#{interface_name}", info[:mtu], :legacy) if info[:mtu]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Dhcp
6 FACT_NAME = 'networking.dhcp'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:dhcp)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:domain)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:fqdn)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:hostname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, interfaces)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Networking
5 class Scope6
6 FACT_NAME = 'networking.scope6'
7 ALIASES = 'scope6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Networking.resolve(:scope6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new('scope6', fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 module Macosx
6 class Build
7 FACT_NAME = 'os.macosx.build'
8 ALIASES = 'macosx_buildversion'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SwVers.resolve(:buildversion)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 module Macosx
6 class Product
7 FACT_NAME = 'os.macosx.product'
8 ALIASES = 'macosx_productname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::SwVers.resolve(:productname)
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 module Macosx
6 class Version
7 FACT_NAME = 'os.macosx.version'
8 ALIASES = %w[macosx_productversion macosx_productversion_major macosx_productversion_minor
9 macosx_productversion_patch].freeze
10
11 def call_the_resolver
12 fact_value = Facter::Resolvers::SwVers.resolve(:productversion)
13 ver = version_hash(fact_value)
14
15 [Facter::ResolvedFact.new(FACT_NAME, ver),
16 Facter::ResolvedFact.new(ALIASES[0], fact_value, :legacy),
17 Facter::ResolvedFact.new(ALIASES[1], ver['major'], :legacy),
18 Facter::ResolvedFact.new(ALIASES[2], ver['minor'], :legacy),
19 Facter::ResolvedFact.new(ALIASES[3], ver['patch'], :legacy)]
20 end
21
22 def version_hash(fact_value)
23 versions = fact_value.split('.')
24 if versions[0] == '10'
25 { 'full' => fact_value, 'major' => "#{versions[0]}.#{versions[1]}", 'minor' => versions[-1] }
26 else
27 { 'full' => fact_value, 'major' => versions[0], 'minor' => versions.fetch(1, '0'),
28 'patch' => versions.fetch(2, '0') }
29 end
30 end
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease)
11 versions = fact_value.split('.')
12 release = {
13 'full' => fact_value,
14 'major' => versions[0],
15 'minor' => versions[1]
16 }
17
18 [Facter::ResolvedFact.new(FACT_NAME, release),
19 Facter::ResolvedFact.new(ALIASES.first, versions[0], :legacy),
20 Facter::ResolvedFact.new(ALIASES.last, fact_value, :legacy)]
21 end
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Cores
6 FACT_NAME = 'processors.cores'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:cores_per_socket)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:logicalcount)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:processor)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7 ALIASES = 'processor.*'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:models)
11 facts = [Facter::ResolvedFact.new(FACT_NAME, fact_value)]
12 fact_value.each_with_index do |value, index|
13 facts.push(Facter::ResolvedFact.new("processor#{index}", value, :legacy))
14 end
15 facts
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Physicalcount
6 FACT_NAME = 'processors.physicalcount'
7 ALIASES = 'physicalprocessorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:physicalcount)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Processors
5 class Threads
6 FACT_NAME = 'processors.threads'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Macosx::Processors.resolve(:threads_per_core)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Scope6Interfaces
5 FACT_NAME = 'scope6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 resolved_facts = []
10 interfaces = Facter::Resolvers::Networking.resolve(:interfaces)
11
12 interfaces&.each do |interface_name, info|
13 if info[:scope6]
14 resolved_facts << Facter::ResolvedFact.new("scope6_#{interface_name}", info[:scope6], :legacy)
15 end
16 end
17 resolved_facts
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, fact_value)
9 end
10
11 private
12
13 def fact_value
14 resolver_data.map { |el| create_ssh_fact(el) }.inject(:merge)
15 end
16
17 def resolver_data
18 Facter::Resolvers::Ssh.resolve(:ssh)
19 end
20
21 def create_ssh_fact(ssh)
22 return {} unless ssh
23
24 { ssh.name.to_sym => {
25 fingerprints: {
26 sha1: ssh.fingerprint.sha1,
27 sha256: ssh.fingerprint.sha256
28 },
29 key: ssh.key,
30 type: ssh.type
31 } }
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
12 facts
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each do |ssh|
12 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
13 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
14 end
15 facts
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class BootMode
6 FACT_NAME = 'system_profiler.boot_mode'
7 ALIASES = 'sp_boot_mode'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:boot_mode)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class BootRomVersion
6 FACT_NAME = 'system_profiler.boot_rom_version'
7 ALIASES = 'sp_boot_rom_version'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:boot_rom_version)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class BootVolume
6 FACT_NAME = 'system_profiler.boot_volume'
7 ALIASES = 'sp_boot_volume'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:boot_volume)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class ComputerName
6 FACT_NAME = 'system_profiler.computer_name'
7 ALIASES = 'sp_local_host_name'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:computer_name)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class Cores
6 FACT_NAME = 'system_profiler.cores'
7 ALIASES = 'sp_number_processors'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:total_number_of_cores)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class HardwareUuid
6 FACT_NAME = 'system_profiler.hardware_uuid'
7 ALIASES = 'sp_platform_uuid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:hardware_uuid)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class KernelVersion
6 FACT_NAME = 'system_profiler.kernel_version'
7 ALIASES = 'sp_kernel_version'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:kernel_version)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class L2CachePerCore
6 FACT_NAME = 'system_profiler.l2_cache_per_core'
7 ALIASES = 'sp_l2_cache_core'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:l2_cache_per_core)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class L3Cache
6 FACT_NAME = 'system_profiler.l3_cache'
7 ALIASES = 'sp_l3_cache'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:l3_cache)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class Memory
6 FACT_NAME = 'system_profiler.memory'
7 ALIASES = 'sp_physical_memory'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:memory)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class ModelIdentifier
6 FACT_NAME = 'system_profiler.model_identifier'
7 ALIASES = 'sp_machine_model'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:model_identifier)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class ModelName
6 FACT_NAME = 'system_profiler.model_name'
7 ALIASES = 'sp_machine_name'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:model_name)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class ProcessorName
6 FACT_NAME = 'system_profiler.processor_name'
7 ALIASES = 'sp_cpu_type'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:processor_name)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class ProcessorSpeed
6 FACT_NAME = 'system_profiler.processor_speed'
7 ALIASES = 'sp_current_processor_speed'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:processor_speed)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class Processors
6 FACT_NAME = 'system_profiler.processors'
7 ALIASES = 'sp_packages'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:number_of_processors)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class SecureVirtualMemory
6 FACT_NAME = 'system_profiler.secure_virtual_memory'
7 ALIASES = 'sp_secure_vm'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:secure_virtual_memory)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class SerialNumber
6 FACT_NAME = 'system_profiler.serial_number'
7 ALIASES = 'sp_serial_number'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:serial_number_system)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class SmcVersion
6 FACT_NAME = 'system_profiler.smc_version'
7 ALIASES = 'sp_smc_version_system'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:smc_version_system)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class SystemVersion
6 FACT_NAME = 'system_profiler.system_version'
7 ALIASES = 'sp_os_version'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:system_version)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class Uptime
6 FACT_NAME = 'system_profiler.uptime'
7 ALIASES = 'sp_uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:time_since_boot)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemProfiler
5 class Username
6 FACT_NAME = 'system_profiler.username'
7 ALIASES = 'sp_user_name'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Macosx::SystemProfiler.resolve(:user_name)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:days)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:hours)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:seconds)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:uptime)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Macosx
4 class Virtual
5 FACT_NAME = 'virtual'
6
7 def call_the_resolver
8 fact_value = check_vmware || check_virtualbox || check_parallels || 'physical'
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12
13 private
14
15 def check_vmware
16 model_identifier = Facter::Resolvers::Macosx::SystemProfiler.resolve(:model_identifier)
17 return 'vmware' if model_identifier&.start_with?('VMware')
18 end
19
20 def check_virtualbox
21 boot_rom_version = Facter::Resolvers::Macosx::SystemProfiler.resolve(:boot_rom_version)
22 return 'virtualbox' if boot_rom_version&.start_with?('VirtualBox')
23 end
24
25 def check_parallels
26 subsystem_vendor_id = Facter::Resolvers::Macosx::SystemProfiler.resolve(:subsystem_vendor_id)
27 return 'parallels' if subsystem_vendor_id&.start_with?('0x1ab8')
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Mageia
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/mageia-release',
22 regex: /Mageia release ([0-9.]+)/ })
23
24 Facter::Util::Facts.release_hash_from_matchdata(version)
25 end
26
27 def from_os_release
28 version = Facter::Resolvers::OsRelease.resolve(:version_id)
29
30 Facter::Util::Facts.release_hash_from_string(version)
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Mariner
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/mariner-release',
22 regex: /CBL\-Mariner ([0-9.]+)/ })
23 Facter::Util::Facts.release_hash_from_matchdata(version)
24 end
25
26 def from_os_release
27 version = Facter::Resolvers::OsRelease.resolve(:version_id)
28
29 Facter::Util::Facts.release_hash_from_string(version)
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Meego
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/meego-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Oel
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/enterprise-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Ol
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/oracle-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Openwrt
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/openwrt_version',
22 regex: /^(\d+.\d+.*)/ })
23 Facter::Util::Facts.release_hash_from_matchdata(version)
24 end
25
26 def from_os_release
27 version = Facter::Resolvers::OsRelease.resolve(:version_id)
28
29 Facter::Util::Facts.release_hash_from_string(version)
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Ovs
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/ovs-release')
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Photon
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/lsb-release',
22 regex: /DISTRIB_RELEASE="(\d+)\.(\d+)/ })
23 return if version.nil? || version[1].nil? || version[2].nil?
24
25 major = version[1].to_s
26 minor = version[2].to_s
27 version_data = major + '.' + minor
28
29 {
30 'full' => version_data,
31 'major' => major,
32 'minor' => minor
33 }
34 end
35
36 def from_os_release
37 version = Facter::Resolvers::OsRelease.resolve(:version_id)
38
39 Facter::Util::Facts.release_hash_from_string(version)
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 class Lsbdistcodename
5 FACT_NAME = 'lsbdistcodename'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:codename)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 class Lsbdistdescription
5 FACT_NAME = 'lsbdistdescription'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:description)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 class Lsbdistid
5 FACT_NAME = 'lsbdistid'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 module Distro
6 class Codename
7 FACT_NAME = 'os.distro.codename'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::RedHatRelease.resolve(:codename)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 module Distro
6 class Description
7 FACT_NAME = 'os.distro.description'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::RedHatRelease.resolve(:description)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 module Distro
6 class Id
7 FACT_NAME = 'os.distro.id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::RedHatRelease.resolve(:distributor_id)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 version = determine_release_version
12
13 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
14
15 [Facter::ResolvedFact.new(FACT_NAME, version),
16 Facter::ResolvedFact.new(ALIASES[0], version['full'], :legacy),
17 Facter::ResolvedFact.new(ALIASES[1], version['major'], :legacy),
18 Facter::ResolvedFact.new(ALIASES[2], version['minor'], :legacy)]
19 end
20
21 def determine_release_version
22 version = Facter::Resolvers::RedHatRelease.resolve(:version)
23 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
24
25 Facter::Util::Facts.release_hash_from_string(version)
26 end
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 [Facter::ResolvedFact.new(FACT_NAME, 'RedHat'), Facter::ResolvedFact.new(ALIASES, 'RedHat', :legacy)]
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:name)
11 fact_value ||= Facter::Resolvers::RedHatRelease.resolve(:name)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Rhel
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = determine_release_version
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def determine_release_version
20 version = Facter::Resolvers::RedHatRelease.resolve(:version)
21 version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
22
23 Facter::Util::Facts.release_hash_from_string(version)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Slackware
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = from_specific_file || from_os_release
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) unless version
13
14 [Facter::ResolvedFact.new(FACT_NAME, version),
15 Facter::ResolvedFact.new(ALIASES.first, version['major'], :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, version['full'], :legacy)]
17 end
18
19 def from_specific_file
20 version = Facter::Resolvers::SpecificReleaseFile.resolve(:release,
21 { release_file: '/etc/slackware-version',
22 regex: /Slackware ([0-9.]+)/ })
23 Facter::Util::Facts.release_hash_from_matchdata(version)
24 end
25
26 def from_os_release
27 version = Facter::Resolvers::OsRelease.resolve(:version_id)
28
29 Facter::Util::Facts.release_hash_from_string(version)
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 class Lsbdistcodename
5 FACT_NAME = 'lsbdistcodename'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:codename)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 class Lsbdistdescription
5 FACT_NAME = 'lsbdistdescription'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:description)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 class Lsbdistid
5 FACT_NAME = 'lsbdistid'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 module Distro
6 class Codename
7 FACT_NAME = 'os.distro.codename'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:version_codename)
11 fact_value = 'n/a' if !fact_value || fact_value.empty?
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 module Distro
6 class Description
7 FACT_NAME = 'os.distro.description'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::OsRelease.resolve(:pretty_name)
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 module Distro
6 class Id
7 FACT_NAME = 'os.distro.id'
8
9 def call_the_resolver
10 fact_value = if Facter::Resolvers::OsRelease.resolve(:version_id).start_with?('12')
11 'SUSE LINUX'
12 else
13 'SUSE'
14 end
15 Facter::ResolvedFact.new(FACT_NAME, fact_value)
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8 ALIASES = %w[lsbdistrelease lsbmajdistrelease lsbminordistrelease].freeze
9
10 def call_the_resolver
11 version = Facter::Resolvers::OsRelease.resolve(:version_id)
12 fact_value = build_fact_list(version)
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES[0], fact_value[:full], :legacy),
16 Facter::ResolvedFact.new(ALIASES[1], fact_value[:major], :legacy),
17 Facter::ResolvedFact.new(ALIASES[2], fact_value[:minor], :legacy)]
18 end
19
20 def build_fact_list(version)
21 if version.include?('.')
22 {
23 full: version,
24 major: version.split('.').first,
25 minor: version.split('.').last
26 }
27 else
28 { full: version, major: version, minor: nil }
29 end
30 end
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 [Facter::ResolvedFact.new(FACT_NAME, 'Suse'), Facter::ResolvedFact.new(ALIASES, 'Suse', :legacy)]
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Sles
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = Facter::Resolvers::OsRelease.resolve(:version_id)
11 fact_value = build_fact_list(version)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES.first, fact_value[:major], :legacy),
15 Facter::ResolvedFact.new(ALIASES.last, fact_value[:full], :legacy)]
16 end
17
18 def build_fact_list(version)
19 {
20 full: version,
21 major: version.split('.').first,
22 minor: version.split('.').last
23 }
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class AioAgentVersion
5 FACT_NAME = 'aio_agent_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class CurrentZone
5 FACT_NAME = 'solaris_zones.current'
6 ALIASES = 'zonename'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::ZoneName.resolve(:current_zone_name)
10
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class DhcpServers
5 FACT_NAME = 'dhcp_servers'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = construct_addresses_hash
10 fact_value = !fact_value || fact_value.empty? ? nil : fact_value
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13
14 private
15
16 def construct_addresses_hash
17 primary_dhcp = Facter::Resolvers::Solaris::Networking.resolve(:dhcp)
18 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
19 return unless interfaces
20
21 servers = { system: primary_dhcp }
22 interfaces&.each { |interface_name, info| servers[interface_name] = info[:dhcp] if info[:dhcp] }
23 servers
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Disks
5 FACT_NAME = 'disks'
6 ALIASES = %w[blockdevices blockdevice_.*_size blockdevice_.*_vendor].freeze
7
8 def call_the_resolver
9 facts = []
10 disks = Facter::Resolvers::Solaris::Disks.resolve(:disks)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil) if disks.nil? || disks.empty?
13
14 blockdevices = disks.keys.join(',')
15 facts.push(Facter::ResolvedFact.new(FACT_NAME, disks))
16 facts.push(Facter::ResolvedFact.new('blockdevices', blockdevices, :legacy))
17 add_legacy_facts(disks, facts)
18
19 facts
20 end
21
22 private
23
24 def add_legacy_facts(disks, facts)
25 disks.each do |disk_name, disk_info|
26 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_size", disk_info[:size_bytes], :legacy))
27 facts.push(Facter::ResolvedFact.new("blockdevice_#{disk_name}_vendor", disk_info[:vendor], :legacy))
28 end
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Bios
6 class ReleaseDate
7 FACT_NAME = 'dmi.bios.release_date'
8 ALIASES = 'bios_release_date'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:bios_release_date) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Bios
6 class Vendor
7 FACT_NAME = 'dmi.bios.vendor'
8 ALIASES = 'bios_vendor'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:bios_vendor) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Bios
6 class Version
7 FACT_NAME = 'dmi.bios.version'
8 ALIASES = 'bios_version'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:bios_version) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Chassis
6 class AssetTag
7 FACT_NAME = 'dmi.chassis.asset_tag'
8 ALIASES = 'chassisassettag'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:chassis_asset_tag) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Chassis
6 class Type
7 FACT_NAME = 'dmi.chassis.type'
8 ALIASES = 'chassistype'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:chassis_type) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 class Manufacturer
6 FACT_NAME = 'dmi.manufacturer'
7 ALIASES = 'manufacturer'
8
9 def call_the_resolver
10 fact_value = if isa == 'i386'
11 Facter::Resolvers::Solaris::Dmi.resolve(:manufacturer)
12 elsif isa == 'sparc'
13 Facter::Resolvers::Solaris::DmiSparc.resolve(:manufacturer)
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17
18 def isa
19 Facter::Resolvers::Uname.resolve(:processor)
20 end
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Product
6 class Name
7 FACT_NAME = 'dmi.product.name'
8 ALIASES = 'productname'
9
10 def call_the_resolver
11 fact_value = if isa == 'i386'
12 Facter::Resolvers::Solaris::Dmi.resolve(:product_name)
13 elsif isa == 'sparc'
14 Facter::Resolvers::Solaris::DmiSparc.resolve(:product_name)
15 end
16 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
17 end
18
19 def isa
20 Facter::Resolvers::Uname.resolve(:processor)
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Product
6 class SerialNumber
7 FACT_NAME = 'dmi.product.serial_number'
8 ALIASES = 'serialnumber'
9
10 def call_the_resolver
11 fact_value = if isa == 'i386'
12 Facter::Resolvers::Solaris::Dmi.resolve(:serial_number)
13 elsif isa == 'sparc'
14 Facter::Resolvers::Solaris::DmiSparc.resolve(:serial_number)
15 end
16 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
17 end
18
19 def isa
20 Facter::Resolvers::Uname.resolve(:processor)
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Dmi
5 module Product
6 class Uuid
7 FACT_NAME = 'dmi.product.uuid'
8 ALIASES = 'uuid'
9
10 def call_the_resolver
11 fact_value = nil
12 fact_value = Facter::Resolvers::Solaris::Dmi.resolve(:product_uuid) if isa == 'i386'
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15
16 def isa
17 Facter::Resolvers::Uname.resolve(:processor)
18 end
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Filesystems
5 FACT_NAME = 'filesystems'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Solaris::Filesystem.resolve(:file_systems)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Hypervisors
5 class Ldom
6 FACT_NAME = 'hypervisors.ldom'
7
8 def initialize
9 @log = Facter::Log.new(self)
10 end
11
12 def call_the_resolver
13 chassis_serial = Facter::Resolvers::Solaris::Ldom.resolve(:chassis_serial)
14 return Facter::ResolvedFact.new(FACT_NAME, nil) if !chassis_serial || chassis_serial.empty?
15
16 fact_value = %i[
17 chassis_serial control_domain domain_name
18 domain_uuid role_control role_io role_root role_service
19 ].map! { |key| [key, Facter::Utils.try_to_bool(Facter::Resolvers::Solaris::Ldom.resolve(key))] }.to_h
20
21 Facter::ResolvedFact.new(FACT_NAME, fact_value)
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Hypervisors
5 class Zone
6 FACT_NAME = 'hypervisors.zone'
7
8 def initialize
9 @log = Facter::Log.new(self)
10 end
11
12 def call_the_resolver
13 fact_value = current_zone
14
15 Facter::ResolvedFact.new(FACT_NAME, fact_value)
16 end
17
18 def current_zone
19 current_zone_name = Facter::Resolvers::Solaris::ZoneName.resolve(:current_zone_name)
20 return unless current_zone_name
21
22 zones = Facter::Resolvers::Solaris::Zone.resolve(:zone)
23 return nil unless zones
24
25 current_zone = zones.find { |r| r[:name] == current_zone_name }
26
27 {
28 brand: current_zone[:brand],
29 id: Facter::Utils.try_to_int(current_zone[:id]),
30 ip_type: current_zone[:iptype],
31 name: current_zone[:name],
32 uuid: current_zone[:uuid]
33 }
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Identity
5 class Gid
6 FACT_NAME = 'identity.gid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:gid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Identity
5 class Group
6 FACT_NAME = 'identity.group'
7 ALIASES = 'gid'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:group)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:privileged)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Identity
5 class Uid
6 FACT_NAME = 'identity.uid'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::PosxIdentity.resolve(:uid)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::PosxIdentity.resolve(:user)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Interfaces
5 FACT_NAME = 'interfaces'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value && !fact_value.empty? ? fact_value.keys.sort.join(',') : nil,
12 :legacy)
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class IsVirtual
5 FACT_NAME = 'is_virtual'
6
7 def initialize
8 @log = Facter::Log.new(self)
9 end
10
11 def call_the_resolver
12 @log.debug('Solaris Virtual Resolver')
13
14 fact_value = check_ldom || check_zone || check_xen || check_other_facts || 'physical'
15
16 @log.debug("Fact value is: #{fact_value}")
17
18 Facter::ResolvedFact.new(FACT_NAME, check_if_virtual(fact_value))
19 end
20
21 def check_ldom
22 @log.debug('Checking LDoms')
23 return unless Facter::Resolvers::Solaris::Ldom.resolve(:role_control) == 'false'
24
25 Facter::Resolvers::Solaris::Ldom.resolve(:role_impl)
26 end
27
28 def check_zone
29 @log.debug('Checking LDoms')
30 zone_name = Facter::Resolvers::Solaris::ZoneName.resolve(:current_zone_name)
31
32 return if zone_name == 'global'
33
34 'zone'
35 end
36
37 def check_xen
38 @log.debug('Checking XEN')
39 Facter::Resolvers::Xen.resolve(:vm)
40 end
41
42 def check_other_facts
43 isa = Facter::Resolvers::Uname.resolve(:processor)
44 klass = isa == 'sparc' ? 'DmiSparc' : 'Dmi'
45
46 product_name = Facter::Resolvers::Solaris.const_get(klass).resolve(:product_name)
47 bios_vendor = Facter::Resolvers::Solaris.const_get(klass).resolve(:bios_vendor)
48
49 return 'kvm' if bios_vendor&.include?('Amazon EC2')
50
51 return unless product_name
52
53 Facter::Util::Facts::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
54
55 nil
56 end
57
58 def check_if_virtual(found_vm)
59 Facter::Util::Facts::PHYSICAL_HYPERVISORS.count(found_vm).zero?
60 end
61 end
62 end
63 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelname)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 full_version = Facter::Resolvers::Uname.resolve(:kernelversion)
9 versions_split = full_version.split('.')
10 major_version = versions_split.length > 2 ? versions_split[0] + '.' + versions_split[1] : versions_split[0]
11 Facter::ResolvedFact.new(FACT_NAME, major_version)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelrelease)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Uname.resolve(:kernelversion)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Ldom
5 FACT_NAME = 'ldom'
6 ALIASES = %w[
7 ldom_domainchassis
8 ldom_domaincontrol
9 ldom_domainname
10 ldom_domainrole_control
11 ldom_domainrole_impl
12 ldom_domainrole_io
13 ldom_domainrole_root
14 ldom_domainrole_service
15 ldom_domainuuid
16 ].freeze
17
18 def initialize
19 @log = Facter::Log.new(self)
20 end
21
22 def call_the_resolver
23 @log.debug('Solving the ldom fact.')
24 fact_value = resolve_fact
25 return Facter::ResolvedFact.new(FACT_NAME, nil) if fact_value.nil?
26
27 create_resolved_facts_list(fact_value)
28 end
29
30 def resolve_fact
31 chassis_serial = resolve(:chassis_serial)
32 return nil if !chassis_serial || chassis_serial.empty?
33
34 {
35 domainchassis: chassis_serial,
36 domaincontrol: resolve(:control_domain),
37 domainname: resolve(:domain_name),
38 domainrole: {
39 control: resolve(:role_control),
40 impl: resolve(:role_impl),
41 io: resolve(:role_io),
42 root: resolve(:role_root),
43 service: resolve(:role_service)
44 },
45 domainuuid: resolve(:domain_uuid)
46 }
47 end
48
49 def resolve(key)
50 Facter::Resolvers::Solaris::Ldom.resolve(key)
51 end
52
53 def create_resolved_facts_list(fact_value)
54 resolved_facts = [Facter::ResolvedFact.new(FACT_NAME, fact_value)]
55 ALIASES.each do |fact_alias|
56 key = fact_alias.split('_')[1..-1].map!(&:to_sym)
57 resolved_facts << Facter::ResolvedFact.new(fact_alias, fact_value.dig(*key), :legacy)
58 end
59
60 resolved_facts
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class LoadAverages
5 FACT_NAME = 'load_averages'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::LoadAverages.resolve(:load_averages)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class MacaddressInterfaces
5 FACT_NAME = 'macaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("macaddress_#{interface_name}", info[:mac], :legacy) if info[:mac]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class Available
7 FACT_NAME = 'memory.swap.available'
8 ALIASES = 'swapfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:available_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class AvailableBytes
7 FACT_NAME = 'memory.swap.available_bytes'
8 ALIASES = 'swapfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
12 fact_value = fact_value[:available_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class Capacity
7 FACT_NAME = 'memory.swap.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
11 fact_value = fact_value[:capacity] if fact_value
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class Total
7 FACT_NAME = 'memory.swap.total'
8 ALIASES = 'swapsize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:total_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class TotalBytes
7 FACT_NAME = 'memory.swap.total_bytes'
8 ALIASES = 'swapsize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
12 fact_value = fact_value[:total_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class Used
7 FACT_NAME = 'memory.swap.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
11 if fact_value
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:used_bytes])
13 end
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module Swap
6 class UsedBytes
7 FACT_NAME = 'memory.swap.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:swap)
11 fact_value = fact_value[:used_bytes] if fact_value
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:available_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
12 fact_value = fact_value[:available_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
11 fact_value = fact_value[:capacity] if fact_value
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
12 if fact_value
13 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:total_bytes])
14 end
15 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
12 fact_value = fact_value[:total_bytes] if fact_value
13
14 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
15 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
16 end
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
11 if fact_value
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value[:used_bytes])
13 end
14 Facter::ResolvedFact.new(FACT_NAME, fact_value)
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Memory.resolve(:system)
11 fact_value = fact_value[:used_bytes] if fact_value
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Mountpoints
5 FACT_NAME = 'mountpoints'
6
7 def call_the_resolver
8 mountpoints = Facter::Resolvers::Solaris::Mountpoints.resolve(FACT_NAME.to_sym)
9 return Facter::ResolvedFact.new(FACT_NAME, nil) unless mountpoints
10
11 fact = {}
12 mountpoints.each do |mnt|
13 fact[mnt[:path].to_sym] = mnt.reject { |k| k == :path }
14 end
15
16 Facter::ResolvedFact.new(FACT_NAME, fact)
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class MtuInterfaces
5 FACT_NAME = 'mtu_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("mtu_#{interface_name}", info[:mtu], :legacy) if info[:mtu]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Dhcp
6 FACT_NAME = 'networking.dhcp'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:dhcp)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:domain)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:fqdn)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:hostname)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:interfaces)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Ipaddress.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 [Facter::ResolvedFact.new(FACT_NAME, 'Solaris'), Facter::ResolvedFact.new(ALIASES, 'Solaris', :legacy)]
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:machine)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 value = Facter::Resolvers::Uname.resolve(:kernelname)
11 fact_value = value == 'SunOS' ? 'Solaris' : value
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 full_value = Facter::Resolvers::Solaris::OsRelease.resolve(:full)
11 major_value = Facter::Resolvers::Solaris::OsRelease.resolve(:major)
12 minor_value = Facter::Resolvers::Solaris::OsRelease.resolve(:minor)
13
14 [Facter::ResolvedFact.new(FACT_NAME, full: full_value, major: major_value, minor: minor_value),
15 Facter::ResolvedFact.new(ALIASES.first, major_value, :legacy),
16 Facter::ResolvedFact.new(ALIASES.last, full_value, :legacy)]
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Cores
6 FACT_NAME = 'processors.cores'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:cores_per_socket)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:logical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uname.resolve(:processor)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7 ALIASES = 'processor.*'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:models)
11 facts = [Facter::ResolvedFact.new(FACT_NAME, fact_value)]
12 fact_value.each_with_index do |value, index|
13 facts.push(Facter::ResolvedFact.new("processor#{index}", value, :legacy))
14 end
15 facts
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Physicalcount
6 FACT_NAME = 'processors.physicalcount'
7 ALIASES = 'physicalprocessorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:physical_count)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Speed
6 FACT_NAME = 'processors.speed'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:speed)
10 speed = Facter::Util::Facts::UnitConverter.hertz_to_human_readable(fact_value)
11 Facter::ResolvedFact.new(FACT_NAME, speed)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Processors
5 class Threads
6 FACT_NAME = 'processors.threads'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Solaris::Processors.resolve(:threads_per_core)
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 Facter::ResolvedFact.new(FACT_NAME, fact_value)
9 end
10
11 private
12
13 def fact_value
14 resolver_data.map { |el| create_ssh_fact(el) }.inject(:merge)
15 end
16
17 def resolver_data
18 Facter::Resolvers::Ssh.resolve(:ssh)
19 end
20
21 def create_ssh_fact(ssh)
22 return {} unless ssh
23
24 { ssh.name.to_sym => {
25 fingerprints: {
26 sha1: ssh.fingerprint.sha1,
27 sha256: ssh.fingerprint.sha256
28 },
29 key: ssh.key,
30 type: ssh.type
31 } }
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
12 facts
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 result = Facter::Resolvers::Ssh.resolve(:ssh)
11 result.each do |ssh|
12 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
13 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
14 end
15 facts
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:days)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:hours)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:seconds)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Uptime.resolve(:uptime)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Virtual
5 FACT_NAME = 'virtual'
6
7 def initialize
8 @log = Facter::Log.new(self)
9 end
10
11 def call_the_resolver
12 @log.debug('Solaris Virtual Resolver')
13
14 fact_value = check_ldom || check_zone || check_xen || check_other_facts || 'physical'
15
16 @log.debug("Fact value is: #{fact_value}")
17
18 Facter::ResolvedFact.new(FACT_NAME, fact_value)
19 end
20
21 def check_ldom
22 @log.debug('Checking LDoms')
23 return unless Facter::Resolvers::Solaris::Ldom.resolve(:role_control) == 'false'
24
25 Facter::Resolvers::Solaris::Ldom.resolve(:role_impl)
26 end
27
28 def check_zone
29 @log.debug('Checking LDoms')
30 zone_name = Facter::Resolvers::Solaris::ZoneName.resolve(:current_zone_name)
31
32 return if zone_name == 'global'
33
34 'zone'
35 end
36
37 def check_xen
38 @log.debug('Checking XEN')
39 Facter::Resolvers::Xen.resolve(:vm)
40 end
41
42 def check_other_facts
43 isa = Facter::Resolvers::Uname.resolve(:processor)
44 klass = isa == 'sparc' ? 'DmiSparc' : 'Dmi'
45
46 product_name = Facter::Resolvers::Solaris.const_get(klass).resolve(:product_name)
47 bios_vendor = Facter::Resolvers::Solaris.const_get(klass).resolve(:bios_vendor)
48
49 return 'kvm' if bios_vendor&.include?('Amazon EC2')
50
51 return unless product_name
52
53 Facter::Util::Facts::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
54
55 nil
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class ZfsFeaturenumbers
5 FACT_NAME = 'zfs_featurenumbers'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::ZFS.resolve(:zfs_featurenumbers)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class ZfsVersion
5 FACT_NAME = 'zfs_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::ZFS.resolve(:zfs_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class Zones
5 FACT_NAME = 'solaris_zones.zones'
6 ALIASES = %w[
7 zone_.*_brand
8 zone_.*_iptype
9 zone_.*_name
10 zone_.*_uuid
11 zone_.*_id
12 zone_.*_path
13 zone_.*_status
14 zones
15 ].freeze
16
17 def call_the_resolver
18 resolved_facts = []
19 zones = {}
20
21 results = Facter::Resolvers::Solaris::Zone.resolve(:zone)
22 return Facter::ResolvedFact.new(FACT_NAME, nil) unless results
23
24 results&.each do |result|
25 zones.merge!(parse_result(result))
26 resolved_facts << create_legacy_zone_facts(result)
27 end
28
29 resolved_facts << Facter::ResolvedFact.new('solaris_zones.zones', zones)
30 resolved_facts << Facter::ResolvedFact.new('zones', results.count, :legacy)
31
32 resolved_facts.flatten
33 end
34
35 private
36
37 def parse_result(result)
38 {
39 result[:name].to_sym => {
40 brand: result[:brand],
41 id: result[:id],
42 ip_type: result[:iptype],
43 path: result[:path],
44 status: result[:status]
45 }
46 }
47 end
48
49 def create_legacy_zone_facts(zone)
50 legacy_facts = []
51 %w[brand iptype name uuid id path status].each do |key|
52 legacy_facts << Facter::ResolvedFact.new("zone_#{zone[:name]}_#{key}", zone[key.to_sym], :legacy)
53 end
54
55 legacy_facts
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class ZpoolFeatureflags
5 FACT_NAME = 'zpool_featureflags'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_featureflags)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class ZpoolFeaturenumbers
5 FACT_NAME = 'zpool_featurenumbers'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_featurenumbers)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Solaris
4 class ZpoolVersion
5 FACT_NAME = 'zpool_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Zpool.resolve(:zpool_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Ubuntu
4 class Lsbdistrelease
5 FACT_NAME = 'lsbdistrelease'
6 ALIASES = %w[lsbmajdistrelease lsbminordistrelease].freeze
7 TYPE = :legacy
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::LsbRelease.resolve(:release)
11
12 return Facter::ResolvedFact.new(FACT_NAME, nil, :legacy) unless fact_value
13
14 version = fact_value.split('.')
15
16 [Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy),
17 Facter::ResolvedFact.new(ALIASES[0], "#{version[0]}.#{version[1]}", :legacy),
18 Facter::ResolvedFact.new(ALIASES[1], version[2], :legacy)]
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Ubuntu
4 module Os
5 module Distro
6 class Release
7 FACT_NAME = 'os.distro.release'
8
9 def call_the_resolver
10 fact_value = determine_release_for_os
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14
15 private
16
17 def determine_release_for_os
18 release = Facter::Resolvers::OsRelease.resolve(:version_id)
19 return unless release
20
21 {
22 'full' => release,
23 'major' => release
24 }
25 end
26 end
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Ubuntu
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 version = Facter::Resolvers::OsRelease.resolve(:version_id)
11
12 [Facter::ResolvedFact.new(FACT_NAME, full: version, major: version),
13 Facter::ResolvedFact.new(ALIASES.first, version, :legacy),
14 Facter::ResolvedFact.new(ALIASES.last, version, :legacy)]
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class AioAgentVersion
5 FACT_NAME = 'aio_agent_version'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Windows::AioAgentVersion.resolve(:aio_agent_version)
9 Facter::ResolvedFact.new(FACT_NAME, fact_value)
10 end
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Augeas
5 class Version
6 FACT_NAME = 'augeas.version'
7 ALIASES = 'augeasversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Augeas.resolve(:augeas_version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class AzMetadata
5 FACT_NAME = 'az_metadata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless azure_hypervisor?
9
10 fact_value = Facter::Resolvers::Az.resolve(:metadata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def azure_hypervisor?
18 Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'hyperv'
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Cloud
5 class Provider
6 FACT_NAME = 'cloud.provider'
7
8 def call_the_resolver
9 virtual = Facter::Resolvers::Windows::Virtualization.resolve(:virtual)
10 provider = case virtual
11 when 'hyperv'
12 'azure' unless Facter::Resolvers::Az.resolve(:metadata).empty?
13 when 'kvm', 'xen'
14 'aws' unless Facter::Resolvers::Ec2.resolve(:metadata).empty?
15 when 'gce'
16 'gce' unless Facter::Resolvers::Gce.resolve(:metadata).empty?
17 end
18
19 Facter::ResolvedFact.new(FACT_NAME, provider)
20 end
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class DhcpServers
5 FACT_NAME = 'dhcp_servers'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = construct_addresses_hash
10 fact_value = !fact_value || fact_value.empty? ? nil : fact_value
11 Facter::ResolvedFact.new(FACT_NAME, fact_value, :legacy)
12 end
13
14 private
15
16 def construct_addresses_hash
17 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
18 return unless interfaces
19
20 servers = { system: Facter::Resolvers::Windows::Networking.resolve(:dhcp) }
21 interfaces&.each { |interface_name, info| servers[interface_name] = info[:dhcp] if info[:dhcp] }
22 servers
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Dmi
5 class Manufacturer
6 FACT_NAME = 'dmi.manufacturer'
7 ALIASES = 'manufacturer'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::DMIBios.resolve(:manufacturer)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Dmi
5 module Product
6 class Name
7 FACT_NAME = 'dmi.product.name'
8 ALIASES = 'productname'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::DMIComputerSystem.resolve(:name)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Dmi
5 module Product
6 class SerialNumber
7 FACT_NAME = 'dmi.product.serial_number'
8 ALIASES = 'serialnumber'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::DMIBios.resolve(:serial_number)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Dmi
5 module Product
6 class Uuid
7 FACT_NAME = 'dmi.product.uuid'
8 ALIASES = 'uuid'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::DMIComputerSystem.resolve(:uuid)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Ec2Metadata
5 FACT_NAME = 'ec2_metadata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:metadata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 virtual = Facter::Resolvers::Windows::Virtualization.resolve(:virtual)
19
20 virtual == 'kvm' || virtual =~ /xen/
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Ec2Userdata
5 FACT_NAME = 'ec2_userdata'
6
7 def call_the_resolver
8 return Facter::ResolvedFact.new(FACT_NAME, nil) unless aws_hypervisors?
9
10 fact_value = Facter::Resolvers::Ec2.resolve(:userdata)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value&.empty? ? nil : fact_value)
13 end
14
15 private
16
17 def aws_hypervisors?
18 virtual = Facter::Resolvers::Windows::Virtualization.resolve(:virtual)
19
20 virtual == 'kvm' || virtual =~ /xen/
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Facterversion
5 FACT_NAME = 'facterversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Facterversion.resolve(:facterversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class FipsEnabled
5 FACT_NAME = 'fips_enabled'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Windows::Fips.resolve(:fips_enabled)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Gce
5 FACT_NAME = 'gce'
6
7 def call_the_resolver
8 virtualization = Facter::Resolvers::Windows::Virtualization.resolve(:virtual)
9
10 fact_value = virtualization&.include?('gce') ? Facter::Resolvers::Gce.resolve(:metadata) : nil
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Hypervisors
5 class Hyperv
6 FACT_NAME = 'hypervisors.hyperv'
7
8 def call_the_resolver
9 fact_value = {} if hyperv?
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13
14 private
15
16 def hyperv?
17 Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'hyperv' ||
18 Facter::Resolvers::DMIBios.resolve(:manufacturer).include?('Microsoft')
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Hypervisors
5 class Kvm
6 FACT_NAME = 'hypervisors.kvm'
7
8 def call_the_resolver
9 fact_value = discover_provider || {} if kvm?
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13
14 private
15
16 def kvm?
17 product_name = Facter::Resolvers::DMIComputerSystem.resolve(:name)
18
19 (Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'kvm' ||
20 Facter::Resolvers::NetKVM.resolve(:kvm)) &&
21 product_name != 'VirtualBox' && !product_name.match(/^Parallels/)
22 end
23
24 def discover_provider
25 manufacturer = Facter::Resolvers::DMIBios.resolve(:manufacturer)
26
27 return { google: true } if manufacturer == 'Google'
28
29 return { openstack: true } if Facter::Resolvers::DMIComputerSystem.resolve(:name) =~ /^OpenStack/
30
31 return { amazon: true } if manufacturer =~ /^Amazon/
32 end
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Hypervisors
5 class Virtualbox
6 FACT_NAME = 'hypervisors.virtualbox'
7
8 def call_the_resolver
9 fact_value = populate_version_and_revision if virtualbox?
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13
14 private
15
16 def virtualbox?
17 Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'virtualbox' ||
18 Facter::Resolvers::DMIComputerSystem.resolve(:name) == 'VirtualBox'
19 end
20
21 def populate_version_and_revision
22 oem_strings = Facter::Resolvers::Windows::Virtualization.resolve(:oem_strings)
23 return unless oem_strings
24
25 version = revision = ''
26
27 oem_strings.each do |string|
28 version = string[8, string.size] if string.start_with?('vboxVer_') && version.empty?
29 revision = string[8, string.size] if string.start_with?('vboxRev_') && revision.empty?
30 end
31 { version: version, revision: revision }
32 end
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Hypervisors
5 class Vmware
6 FACT_NAME = 'hypervisors.vmware'
7
8 def call_the_resolver
9 fact_value = {} if vmware?
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13
14 private
15
16 def vmware?
17 Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'vmware' ||
18 Facter::Resolvers::DMIBios.resolve(:manufacturer) == 'VMware, Inc.'
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Hypervisors
5 class Xen
6 FACT_NAME = 'hypervisors.xen'
7
8 def call_the_resolver
9 if Facter::Resolvers::Windows::Virtualization.resolve(:virtual) == 'xen'
10 fact_value = { context: hvm? ? 'hvm' : 'pv' }
11 end
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15
16 private
17
18 def hvm?
19 Facter::Resolvers::DMIComputerSystem.resolve(:name) =~ /^HVM/
20 end
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Identity
5 class Privileged
6 FACT_NAME = 'identity.privileged'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Identity.resolve(:privileged)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Identity
5 class User
6 FACT_NAME = 'identity.user'
7 ALIASES = 'id'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Identity.resolve(:user)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Interfaces
5 FACT_NAME = 'interfaces'
6 TYPE = :legacy
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value ? fact_value.keys.join(',') : nil, :legacy)
12 end
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Ipaddress6Interfaces
5 FACT_NAME = 'ipaddress6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress6_#{interface_name}", info[:ip6], :legacy) if info[:ip6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class IpaddressInterfaces
5 FACT_NAME = 'ipaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("ipaddress_#{interface_name}", info[:ip], :legacy) if info[:ip]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class IsVirtual
5 FACT_NAME = 'is_virtual'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Kernel
5 FACT_NAME = 'kernel'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Kernel.resolve(:kernel)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Kernelmajversion
5 FACT_NAME = 'kernelmajversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Kernel.resolve(:kernelmajorversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Kernelrelease
5 FACT_NAME = 'kernelrelease'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Kernel.resolve(:kernelversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Kernelversion
5 FACT_NAME = 'kernelversion'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Kernel.resolve(:kernelversion)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class MacaddressInterfaces
5 FACT_NAME = 'macaddress_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("macaddress_#{interface_name}", info[:mac], :legacy) if info[:mac]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class Available
7 FACT_NAME = 'memory.system.available'
8 ALIASES = 'memoryfree'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Memory.resolve(:available_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class AvailableBytes
7 FACT_NAME = 'memory.system.available_bytes'
8 ALIASES = 'memoryfree_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Memory.resolve(:available_bytes)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class Capacity
7 FACT_NAME = 'memory.system.capacity'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Memory.resolve(:capacity)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class Total
7 FACT_NAME = 'memory.system.total'
8 ALIASES = 'memorysize'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Memory.resolve(:total_bytes)
12 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
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
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class TotalBytes
7 FACT_NAME = 'memory.system.total_bytes'
8 ALIASES = 'memorysize_mb'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::Memory.resolve(:total_bytes)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
14 Facter::ResolvedFact.new(ALIASES, Facter::Util::Facts::UnitConverter.bytes_to_mb(fact_value), :legacy)]
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class Used
7 FACT_NAME = 'memory.system.used'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Memory.resolve(:used_bytes)
11 fact_value = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(fact_value)
12
13 Facter::ResolvedFact.new(FACT_NAME, fact_value)
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Memory
5 module System
6 class UsedBytes
7 FACT_NAME = 'memory.system.used_bytes'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Memory.resolve(:used_bytes)
11
12 Facter::ResolvedFact.new(FACT_NAME, fact_value)
13 end
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class MtuInterfaces
5 FACT_NAME = 'mtu_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("mtu_#{interface_name}", info[:mtu], :legacy) if info[:mtu]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Netmask6Interfaces
5 FACT_NAME = 'netmask6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask6_#{interface_name}", info[:netmask6], :legacy) if info[:netmask6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class NetmaskInterfaces
5 FACT_NAME = 'netmask_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("netmask_#{interface_name}", info[:netmask], :legacy) if info[:netmask]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Network6Interfaces
5 FACT_NAME = 'network6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network6_#{interface_name}", info[:network6], :legacy) if info[:network6]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class NetworkInterfaces
5 FACT_NAME = 'network_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
11 interfaces&.each do |interface_name, info|
12 arr << Facter::ResolvedFact.new("network_#{interface_name}", info[:network], :legacy) if info[:network]
13 end
14
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Dhcp
6 FACT_NAME = 'networking.dhcp'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Windows::Networking.resolve(:dhcp)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Domain
6 FACT_NAME = 'networking.domain'
7 ALIASES = 'domain'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:domain)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Fqdn
6 FACT_NAME = 'networking.fqdn'
7 ALIASES = 'fqdn'
8
9 def call_the_resolver
10 domain = Facter::Resolvers::Windows::Networking.resolve(:domain)
11 hostname = Facter::Resolvers::Hostname.resolve(:hostname)
12 return Facter::ResolvedFact.new(FACT_NAME, nil) if !hostname || hostname.empty?
13
14 fact_value = domain && !domain.empty? ? [hostname, domain].compact.join('.') : hostname
15
16 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
17 end
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Hostname
6 FACT_NAME = 'networking.hostname'
7 ALIASES = 'hostname'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Hostname.resolve(:hostname)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Interfaces
6 FACT_NAME = 'networking.interfaces'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Ip
6 FACT_NAME = 'networking.ip'
7 ALIASES = 'ipaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:ip)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Ip6
6 FACT_NAME = 'networking.ip6'
7 ALIASES = 'ipaddress6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:ip6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Mac
6 FACT_NAME = 'networking.mac'
7 ALIASES = 'macaddress'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:mac)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Mtu
6 FACT_NAME = 'networking.mtu'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Windows::Networking.resolve(:mtu)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Netmask
6 FACT_NAME = 'networking.netmask'
7 ALIASES = 'netmask'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:netmask)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Netmask6
6 FACT_NAME = 'networking.netmask6'
7 ALIASES = 'netmask6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:netmask6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Network
6 FACT_NAME = 'networking.network'
7 ALIASES = 'network'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:network)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Network6
6 FACT_NAME = 'networking.network6'
7 ALIASES = 'network6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:network6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Primary
6 FACT_NAME = 'networking.primary'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Windows::Networking.resolve(:primary_interface)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Networking
5 class Scope6
6 FACT_NAME = 'networking.scope6'
7 ALIASES = 'scope6'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Networking.resolve(:scope6)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value),
13 Facter::ResolvedFact.new('scope6', fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 class Architecture
6 FACT_NAME = 'os.architecture'
7 ALIASES = 'architecture'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::HardwareArchitecture.resolve(:architecture)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 class Family
6 FACT_NAME = 'os.family'
7 ALIASES = 'osfamily'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Kernel.resolve(:kernel)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 class Hardware
6 FACT_NAME = 'os.hardware'
7 ALIASES = 'hardwaremodel'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::HardwareArchitecture.resolve(:hardware)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 class Name
6 FACT_NAME = 'os.name'
7 ALIASES = 'operatingsystem'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Kernel.resolve(:kernel)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 class Release
6 FACT_NAME = 'os.release'
7 ALIASES = %w[operatingsystemmajrelease operatingsystemrelease].freeze
8
9 def call_the_resolver
10 arr = []
11 input = {
12 consumerrel: description_resolver(:consumerrel),
13 description: description_resolver(:description),
14 version: kernel_resolver(:kernelmajorversion),
15 kernel_version: kernel_resolver(:kernelversion)
16 }
17
18 fact_value = Facter::Util::Facts::WindowsReleaseFinder.find_release(input)
19 arr << Facter::ResolvedFact.new(FACT_NAME, ({ full: fact_value, major: fact_value } if fact_value))
20 ALIASES.each { |aliass| arr << Facter::ResolvedFact.new(aliass, fact_value, :legacy) }
21 arr
22 end
23
24 def description_resolver(key)
25 Facter::Resolvers::WinOsDescription.resolve(key)
26 end
27
28 def kernel_resolver(key)
29 Facter::Resolvers::Kernel.resolve(key)
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class DisplayVersion
7 FACT_NAME = 'os.windows.display_version'
8 ALIASES = 'windows_display_version'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::ProductRelease.resolve(:display_version)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class EditionId
7 FACT_NAME = 'os.windows.edition_id'
8 ALIASES = 'windows_edition_id'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::ProductRelease.resolve(:edition_id)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class InstallationType
7 FACT_NAME = 'os.windows.installation_type'
8 ALIASES = 'windows_installation_type'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::ProductRelease.resolve(:installation_type)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class ProductName
7 FACT_NAME = 'os.windows.product_name'
8 ALIASES = 'windows_product_name'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::ProductRelease.resolve(:product_name)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class ReleaseId
7 FACT_NAME = 'os.windows.release_id'
8 ALIASES = 'windows_release_id'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::ProductRelease.resolve(:release_id)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Os
5 module Windows
6 class System32
7 FACT_NAME = 'os.windows.system32'
8 ALIASES = 'system32'
9
10 def call_the_resolver
11 fact_value = Facter::Resolvers::System32.resolve(:system32)
12
13 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
14 end
15 end
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Path
5 FACT_NAME = 'path'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Path.resolve(:path)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Processor
5 FACT_NAME = 'processor.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 processors = Facter::Resolvers::Processors.resolve(:models)
11
12 (0...processors.count).each do |iterator|
13 arr << Facter::ResolvedFact.new("processor#{iterator}", processors[iterator], :legacy)
14 end
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Cores
6 FACT_NAME = 'processors.cores'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Processors.resolve(:cores_per_socket)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Count
6 FACT_NAME = 'processors.count'
7 ALIASES = 'processorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Processors.resolve(:count)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Isa
6 FACT_NAME = 'processors.isa'
7 ALIASES = 'hardwareisa'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Processors.resolve(:isa)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Models
6 FACT_NAME = 'processors.models'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Processors.resolve(:models)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Physicalcount
6 FACT_NAME = 'processors.physicalcount'
7 ALIASES = 'physicalprocessorcount'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Processors.resolve(:physicalcount)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Processors
5 class Threads
6 FACT_NAME = 'processors.threads'
7
8 def call_the_resolver
9 fact_value = Facter::Resolvers::Processors.resolve(:threads_per_core)
10
11 Facter::ResolvedFact.new(FACT_NAME, fact_value)
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Ruby
5 class Platform
6 FACT_NAME = 'ruby.platform'
7 ALIASES = 'rubyplatform'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:platform)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Ruby
5 class Sitedir
6 FACT_NAME = 'ruby.sitedir'
7 ALIASES = 'rubysitedir'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:sitedir)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module Ruby
5 class Version
6 FACT_NAME = 'ruby.version'
7 ALIASES = 'rubyversion'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Ruby.resolve(:version)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Scope6Interfaces
5 FACT_NAME = 'scope6_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 arr = []
10 result = {}
11 interfaces = Facter::Resolvers::Windows::Networking.resolve(:interfaces)
12 interfaces&.each { |interface_name, info| result["scope6_#{interface_name}"] = info[:scope6] if info[:scope6] }
13
14 result.each { |fact, value| arr << Facter::ResolvedFact.new(fact, value, :legacy) }
15 arr
16 end
17 end
18 end
19 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Ssh
5 FACT_NAME = 'ssh'
6
7 def call_the_resolver
8 privileged = Facter::Resolvers::Identity.resolve(:privileged)
9 ssh_info = Facter::Resolvers::Windows::Ssh.resolve(:ssh) if privileged
10 ssh_facts = {}
11 ssh_info&.each { |ssh| ssh_facts.merge!(create_ssh_fact(ssh)) }
12 Facter::ResolvedFact.new(FACT_NAME, ssh_facts.empty? ? nil : ssh_facts)
13 end
14
15 private
16
17 def create_ssh_fact(ssh)
18 { ssh.name.to_sym =>
19 { fingerprints: { sha1: ssh.fingerprint.sha1,
20 sha256: ssh.fingerprint.sha256 },
21 key: ssh.key,
22 type: ssh.type } }
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Sshalgorithmkey
5 FACT_NAME = 'ssh.*key'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 privileged = Facter::Resolvers::Identity.resolve(:privileged)
11
12 return facts unless privileged
13
14 result = Facter::Resolvers::Windows::Ssh.resolve(:ssh)
15
16 result&.each { |ssh| facts << Facter::ResolvedFact.new("ssh#{ssh.name.to_sym}key", ssh.key, :legacy) }
17 facts
18 end
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class SshfpAlgorithm
5 FACT_NAME = 'sshfp_.*'
6 TYPE = :legacy
7
8 def call_the_resolver
9 facts = []
10 privileged = Facter::Resolvers::Identity.resolve(:privileged)
11
12 return facts unless privileged
13
14 result = Facter::Resolvers::Windows::Ssh.resolve(:ssh)
15
16 result&.each do |ssh|
17 facts << Facter::ResolvedFact.new("sshfp_#{ssh.name.to_sym}",
18 "#{ssh.fingerprint.sha1}\n#{ssh.fingerprint.sha256}", :legacy)
19 end
20 facts
21 end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module SystemUptime
5 class Days
6 FACT_NAME = 'system_uptime.days'
7 ALIASES = 'uptime_days'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Uptime.resolve(:days)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module SystemUptime
5 class Hours
6 FACT_NAME = 'system_uptime.hours'
7 ALIASES = 'uptime_hours'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Uptime.resolve(:hours)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module SystemUptime
5 class Seconds
6 FACT_NAME = 'system_uptime.seconds'
7 ALIASES = 'uptime_seconds'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Uptime.resolve(:seconds)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 module SystemUptime
5 class Uptime
6 FACT_NAME = 'system_uptime.uptime'
7 ALIASES = 'uptime'
8
9 def call_the_resolver
10 fact_value = Facter::Resolvers::Windows::Uptime.resolve(:uptime)
11
12 [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
13 end
14 end
15 end
16 end
17 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Timezone
5 FACT_NAME = 'timezone'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Windows::Timezone.resolve(:timezone)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facts
3 module Windows
4 class Virtual
5 FACT_NAME = 'virtual'
6
7 def call_the_resolver
8 fact_value = Facter::Resolvers::Windows::Virtualization.resolve(:virtual)
9
10 Facter::ResolvedFact.new(FACT_NAME, fact_value)
11 end
12 end
13 end
14 end
0 Usage
1 =====
2
3 facter [options] [query] [query] [...]
4
5 Options
6 =======
0 # frozen_string_literal: true
1
2 require 'benchmark'
3
4 module Facter
5 module Framework
6 module Benchmarking
7 class Timer
8 class << self
9 def measure(fact_name, prefix_message = '')
10 if Options[:timing]
11 time = Benchmark.measure { yield }
12
13 log = "fact '#{fact_name}', took: #{time.format('%r')} seconds"
14 prefix_message = "#{prefix_message} " unless prefix_message.empty?
15 puts "#{prefix_message}#{log}"
16 else
17 yield
18 end
19 end
20 end
21 end
22 end
23 end
24 end
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'thor'
4
5 module Facter
6 class Cli < Thor
7 class_option :color,
8 type: :boolean,
9 desc: 'Enable color output.'
10
11 class_option :no_color,
12 type: :boolean,
13 desc: 'Disable color output.'
14
15 class_option :config,
16 aliases: '-c',
17 type: :string,
18 desc: 'The location of the config file.'
19
20 class_option :custom_dir,
21 type: :string,
22 repeatable: true,
23 desc: 'A directory to use for custom facts.'
24
25 class_option :debug,
26 aliases: '-d',
27 type: :boolean,
28 desc: 'Enable debug output.'
29
30 class_option :external_dir,
31 type: :string,
32 repeatable: true,
33 desc: 'A directory to use for external facts.'
34
35 class_option :hocon,
36 type: :boolean,
37 desc: 'Output in Hocon format.'
38
39 class_option :json,
40 aliases: '-j',
41 type: :boolean,
42 desc: 'Output in JSON format.'
43
44 class_option :log_level,
45 aliases: '-l',
46 type: :string,
47 desc: 'Set logging level. Supported levels are: none, trace, debug, info, warn, error, and fatal.'
48
49 class_option :no_block,
50 type: :boolean,
51 desc: 'Disable fact blocking.'
52
53 class_option :no_cache,
54 type: :boolean,
55 desc: 'Disable loading and refreshing facts from the cache'
56
57 class_option :no_custom_facts,
58 type: :boolean,
59 desc: 'Disable custom facts.'
60
61 class_option :no_external_facts,
62 type: :boolean,
63 desc: 'Disable external facts.'
64
65 class_option :no_ruby,
66 type: :boolean,
67 desc: 'Disable loading Ruby, facts requiring Ruby, and custom facts.'
68
69 class_option :trace,
70 type: :boolean,
71 desc: 'Enable backtraces for custom facts.'
72
73 class_option :verbose,
74 type: :boolean,
75 desc: 'Enable verbose (info) output.'
76
77 class_option :show_legacy,
78 type: :boolean,
79 desc: 'Show legacy facts when querying all facts.'
80
81 class_option :yaml,
82 aliases: '-y',
83 type: :boolean,
84 desc: 'Output in YAML format.'
85
86 class_option :strict,
87 type: :boolean,
88 desc: 'Enable more aggressive error reporting.'
89
90 class_option :timing,
91 type: :boolean,
92 aliases: '-t',
93 desc: 'Show how much time it took to resolve each fact'
94
95 class_option :sequential,
96 type: :boolean,
97 desc: 'Resolve facts sequentially'
98
99 class_option :http_debug,
100 type: :boolean,
101 desc: 'Whether to write HTTP request and responses to stderr. This should never be used in production.'
102
103 class_option :puppet,
104 type: :boolean,
105 aliases: '-p',
106 desc: 'Load the Puppet libraries, thus allowing Facter to load Puppet-specific facts.'
107
108 desc '--man', 'Display manual.', hide: true
109 map ['--man'] => :man
110 def man(*args)
111 require 'erb'
112 negate_options = %w[block cache custom_facts external_facts]
113
114 template = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'man.erb')
115 erb = ERB.new(File.read(template), nil, '-')
116 erb.filename = template
117 puts erb.result(binding)
118 end
119
120 desc 'query', 'Default method', hide: true
121 def query(*args)
122 Facter.puppet_facts if options[:puppet]
123 output, status = Facter.to_user_output(@options, *args)
124 puts output
125
126 status = 1 if Facter::Log.errors?
127 exit status
128 end
129
130 desc 'arg_parser', 'Parse arguments', hide: true
131 def arg_parser(*args)
132 # ignore unknown options
133 args.reject! { |arg| arg.start_with?('-') }
134
135 Facter.values(@options, args)
136 end
137
138 desc '--version, -v', 'Print the version'
139 map ['--version', '-v'] => :version
140 def version(*_args)
141 puts Facter::VERSION
142 end
143
144 desc '--list-block-groups', 'List block groups'
145 map ['--list-block-groups'] => :list_block_groups
146 def list_block_groups
147 options = @options.map { |(k, v)| [k.to_sym, v] }.to_h
148 Facter::Options.init_from_cli(options)
149
150 block_groups = Facter::FactGroups.new.groups.to_yaml.lines[1..-1].join
151 block_groups.gsub!(/:\s*\n/, "\n")
152
153 puts block_groups
154 end
155
156 desc '--list-cache-groups', 'List cache groups'
157 map ['--list-cache-groups'] => :list_cache_groups
158 def list_cache_groups
159 options = @options.map { |(k, v)| [k.to_sym, v] }.to_h
160 Facter::Options.init_from_cli(options)
161
162 cache_groups = Facter::FactGroups.new.groups.to_yaml.lines[1..-1].join
163 cache_groups.gsub!(/:\s*\n/, "\n")
164
165 puts cache_groups
166 end
167
168 desc '--help, -h', 'Help for all arguments'
169 def help(*args)
170 help_string = +''
171 help_string << help_header(args)
172 help_string << add_class_options_to_help
173 help_string << add_commands_to_help
174
175 puts help_string
176 end
177
178 no_commands do
179 def help_header(_args)
180 path = File.join(File.dirname(__FILE__), '../../')
181
182 Facter::Util::FileHelper.safe_read("#{path}fixtures/facter_help_header")
183 end
184
185 IGNORE_OPTIONS = %w[log_level color no_color].freeze
186
187 def add_class_options_to_help
188 help_class_options = +''
189 class_options = Cli.class_options
190 class_options.each do |class_option|
191 option = class_option[1]
192 next if option.hide
193
194 help_class_options << build_option(option.name, option.aliases, option.description)
195 end
196
197 help_class_options
198 end
199
200 def add_commands_to_help
201 help_command_options = +''
202 Cli.commands
203 .select { |_k, command_class| command_class.instance_of?(Thor::Command) }
204 .each do |_k, command|
205 help_command_options << build_option(
206 command['name'],
207 [command['usage'].split(',')[1]],
208 command['description']
209 )
210 end
211
212 help_command_options
213 end
214
215 def build_option(name, aliases, description)
216 name = name.tr('_', '-')
217 help_option = +''
218 help_option << aliases.join(',').rjust(10)
219 help_option << ' '
220 help_option << "[--#{name}]".ljust(30)
221 help_option << " #{description}"
222 help_option << "\n"
223
224 help_option
225 end
226 end
227
228 def self.exit_on_failure?
229 true
230 end
231
232 default_task :query
233 end
234 end
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'facter/framework/logging/logger.rb'
4 Facter::Log.output(STDERR)
5 require 'facter'
6 require 'facter/framework/cli/cli'
7
8 class CliLauncher
9 class << self
10 def prepare_arguments(args, task = Facter::Cli.default_task)
11 args.unshift(task) unless
12 check_if_arguments_is_known(Facter::Cli.all_tasks, args) ||
13 check_if_arguments_is_known(Facter::Cli.instance_variable_get(:@map), args) ||
14 !task
15
16 reorder_program_arguments(args)
17 end
18
19 def start(args)
20 # stop parsing arguments if we don't recognize them
21 Thor.check_unknown_options!
22 Facter::Cli.start(args, debug: true)
23 rescue Thor::UnknownArgumentError => e
24 Facter::OptionsValidator.write_error_and_exit("unrecognised option '#{e.unknown.first}'")
25 end
26
27 private
28
29 def check_if_arguments_is_known(known_arguments, program_arguments)
30 program_arguments.each do |argument|
31 return true if known_arguments.key?(argument)
32 end
33
34 false
35 end
36
37 def reorder_program_arguments(program_arguments)
38 priority_arguments = Facter::Cli.instance_variable_get(:@map)
39
40 priority_args = []
41 normal_args = []
42
43 program_arguments.each do |argument|
44 if priority_arguments.include?(argument)
45 priority_args << argument
46 else
47 normal_args << argument
48 end
49 end
50
51 priority_args.concat(normal_args)
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 module Facter
3 class ConfigReader
4 class << self
5 attr_accessor :conf
6
7 def init(config_path = nil)
8 config_path ||= default_path
9 refresh_config(config_path)
10 self
11 end
12
13 def block_list
14 @conf['facts'] && @conf['facts']['blocklist']
15 end
16
17 def ttls
18 @conf['facts'] && @conf['facts']['ttls']
19 end
20
21 def global
22 @conf['global']
23 end
24
25 def cli
26 @conf['cli']
27 end
28
29 def fact_groups
30 @conf['fact-groups']
31 end
32
33 def refresh_config(config_path)
34 @conf = File.readable?(config_path) ? Hocon.load(config_path) : {}
35 rescue StandardError => e
36 log.warn("Facter failed to read config file #{config_path} with the following error: #{e.message}")
37 @conf = {}
38 end
39
40 private
41
42 def log
43 @log ||= Log.new(self)
44 end
45
46 def default_path
47 return '' if RUBY_PLATFORM == 'java'
48
49 os = OsDetector.instance.identifier
50
51 windows_path = File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'etc', 'facter.conf')
52 linux_path = File.join('/', 'etc', 'puppetlabs', 'facter', 'facter.conf')
53
54 os == :windows ? windows_path : linux_path
55 end
56 end
57 end
58 end
0 # frozen_string_literal: true
1
2 require 'facter/config'
3
4 module Facter
5 class FactGroups
6 attr_accessor :groups_ttls
7 attr_reader :groups, :block_list, :facts_ttls
8
9 STRING_TO_SECONDS = { 'ns' => 1.fdiv(1_000_000_000), 'nanos' => 1.fdiv(1_000_000_000),
10 'nanoseconds' => 1.fdiv(1_000_000_000),
11 'us' => 1.fdiv(1_000_000), 'micros' => 1.fdiv(1_000_000), 'microseconds' => 1.fdiv(1_000_000),
12 '' => 1.fdiv(1000), 'ms' => 1.fdiv(1000), 'milis' => 1.fdiv(1000),
13 'milliseconds' => 1.fdiv(1000),
14 's' => 1, 'seconds' => 1,
15 'm' => 60, 'minutes' => 60,
16 'h' => 3600, 'hours' => 3600,
17 'd' => 3600 * 24, 'days' => 3600 * 24 }.freeze
18
19 def initialize
20 @groups = Facter::Config::FACT_GROUPS.dup
21 load_groups
22 load_groups_from_options
23 load_facts_ttls
24
25 # Reverse sort facts so that children have precedence when caching. eg: os.macosx vs os
26 @facts_ttls = @facts_ttls.sort.reverse.to_h
27 end
28
29 # Breakes down blocked groups in blocked facts
30 def blocked_facts
31 fact_list = []
32
33 @block_list.each do |group_name|
34 # legacy is a special group and does not need to be broken into facts
35 next if group_name == 'legacy'
36
37 facts_for_block = @groups[group_name]
38
39 fact_list += facts_for_block || [group_name]
40 end
41
42 fact_list
43 end
44
45 # Get the group name a fact is part of
46 def get_fact_group(fact_name)
47 fact = get_fact(fact_name)
48 return fact[:group] if fact
49
50 # @groups.detect { |k, v| break k if Array(v).find { |f| fact_name =~ /^#{f}.*/ } }
51 @groups.detect do |k, v|
52 break k if Array(v).find { |f| fact_name.include?('.*') ? fact_name == f : fact_name =~ /^#{f}.*/ }
53 end
54 end
55
56 # Get config ttls for a given group
57 def get_group_ttls(group_name)
58 return unless (ttls = @groups_ttls.find { |g| g[group_name] })
59
60 ttls_to_seconds(ttls[group_name])
61 end
62
63 def get_fact(fact_name)
64 return @facts_ttls[fact_name] if @facts_ttls[fact_name]
65
66 result = @facts_ttls.select { |name, fact| break fact if fact_name =~ /^#{name}\..*/ }
67 return nil if result == {}
68
69 result
70 end
71
72 private
73
74 def load_groups_from_options
75 Options.external_dir.each do |dir|
76 next unless Dir.exist?(dir)
77
78 ext_facts = Dir.entries(dir)
79 ext_facts.reject! { |ef| ef =~ /^(\.|\.\.)$/ }
80 ext_facts.each do |ef|
81 @groups[ef] = nil
82 end
83 end
84 end
85
86 def load_facts_ttls
87 @facts_ttls ||= {}
88 return if @groups_ttls == []
89
90 @groups_ttls.reduce(:merge).each do |group, ttls|
91 ttls = ttls_to_seconds(ttls)
92 if @groups[group]
93 @groups[group].each do |fact|
94 if (@facts_ttls[fact] && @facts_ttls[fact][:ttls] < ttls) || @facts_ttls[fact].nil?
95 @facts_ttls[fact] = { ttls: ttls, group: group }
96 end
97 end
98 else
99 @facts_ttls[group] = { ttls: ttls, group: group }
100 end
101 end
102 end
103
104 def load_groups
105 config = ConfigReader.init(Options[:config])
106 @block_list = config.block_list || []
107 @groups_ttls = config.ttls || []
108 @groups.merge!(config.fact_groups) if config.fact_groups
109 end
110
111 def ttls_to_seconds(ttls)
112 duration, unit = ttls.split(' ', 2)
113 unit = '' if duration && !unit
114 unit = append_s(unit)
115 seconds = STRING_TO_SECONDS[unit]
116 if seconds
117 (duration.to_i * seconds).to_i
118 else
119 log = Log.new(self)
120 log.error("Could not parse time unit #{unit} (try #{STRING_TO_SECONDS.keys.reject(&:empty?).join(', ')})")
121 nil
122 end
123 end
124
125 def append_s(unit)
126 return unit + 's' if unit.length > 2 && unit[-1] != 's'
127
128 unit
129 end
130 end
131 end
0 # frozen_string_literal: true
1
2 module Facter
3 class CacheManager
4 def initialize
5 @groups = {}
6 @log = Log.new(self)
7 @fact_groups = Facter::FactGroups.new
8 @cache_dir = LegacyFacter::Util::Config.facts_cache_dir
9 end
10
11 def resolve_facts(searched_facts)
12 return searched_facts, [] if (!File.directory?(@cache_dir) || !Options[:cache]) && Options[:ttls].any?
13
14 facts = []
15 searched_facts.delete_if do |fact|
16 res = resolve_fact(fact)
17 if res
18 facts << res
19 true
20 else
21 false
22 end
23 end
24
25 [searched_facts, facts.flatten]
26 end
27
28 def cache_facts(resolved_facts)
29 return unless Options[:cache] && Options[:ttls].any?
30
31 @groups = {}
32 resolved_facts.each do |fact|
33 cache_fact(fact)
34 end
35
36 begin
37 write_cache unless @groups.empty?
38 rescue Errno::EACCES => e
39 @log.warn("Could not write cache: #{e.message}")
40 end
41 end
42
43 def fact_cache_enabled?(fact_name)
44 fact = @fact_groups.get_fact(fact_name)
45 cached = if fact
46 !fact[:ttls].nil?
47 else
48 false
49 end
50
51 # fact_group = @fact_groups.get_fact_group(fact_name)
52 # delete_cache(fact_group) if fact_group && !cached
53 cached
54 end
55
56 private
57
58 def resolve_fact(searched_fact)
59 fact_name = if searched_fact.file
60 File.basename(searched_fact.file)
61 else
62 searched_fact.name
63 end
64
65 return unless fact_cache_enabled?(fact_name)
66
67 fact = @fact_groups.get_fact(fact_name)
68
69 return if external_fact_in_custom_group?(searched_fact, fact_name, fact)
70
71 return unless fact
72
73 return unless check_ttls?(fact[:group], fact[:ttls])
74
75 read_fact(searched_fact, fact[:group])
76 end
77
78 def external_fact_in_custom_group?(searched_fact, fact_name, fact)
79 if searched_fact.type == :file && fact[:group] != fact_name
80 @log.error("Cannot cache '#{fact_name}' fact from '#{fact[:group]}' group. "\
81 'Caching custom group is not supported for external facts.')
82 return true
83 end
84
85 false
86 end
87
88 def read_fact(searched_fact, fact_group)
89 data = nil
90 Facter::Framework::Benchmarking::Timer.measure(searched_fact.name, 'cached') do
91 data = read_group_json(fact_group)
92 end
93 return unless data
94
95 unless searched_fact.file
96 return unless valid_format_version?(searched_fact, data, fact_group)
97
98 delete_cache(fact_group) unless data.keys.grep(/#{searched_fact.name}/).any?
99 # data.fetch(searched_fact.name) { delete_cache(fact_group) }
100 end
101
102 @log.debug("loading cached values for #{searched_fact.name} facts")
103
104 create_facts(searched_fact, data)
105 end
106
107 def valid_format_version?(searched_fact, data, fact_group)
108 unless data['cache_format_version'] == 1
109 @log.debug("The fact #{searched_fact.name} could not be read from the cache, \
110 cache_format_version is incorrect!")
111 delete_cache(fact_group)
112 return false
113 end
114
115 true
116 end
117
118 def create_facts(searched_fact, data)
119 if searched_fact.type == :file
120 resolve_external_fact(searched_fact, data)
121 else
122 return unless data[searched_fact.name]
123
124 [Facter::ResolvedFact.new(searched_fact.name, data[searched_fact.name], searched_fact.type,
125 searched_fact.user_query)]
126 end
127 end
128
129 def resolve_external_fact(searched_fact, data)
130 facts = []
131 data.each do |fact_name, fact_value|
132 next if fact_name == 'cache_format_version'
133
134 fact = Facter::ResolvedFact.new(fact_name, fact_value, searched_fact.type,
135 searched_fact.user_query)
136 fact.file = searched_fact.file
137 facts << fact
138 end
139 facts
140 end
141
142 def cache_fact(fact)
143 fact_name = if fact.file
144 File.basename(fact.file)
145 else
146 fact.name
147 end
148
149 group_name = @fact_groups.get_fact_group(fact_name)
150
151 return unless group_name
152
153 return unless fact_cache_enabled?(fact_name)
154
155 @groups[group_name] ||= {}
156 @groups[group_name][fact.name] = fact.value
157 end
158
159 def write_cache
160 unless File.directory?(@cache_dir)
161 require 'fileutils'
162 FileUtils.mkdir_p(@cache_dir)
163 end
164
165 @groups.each do |group_name, data|
166 next unless check_ttls?(group_name, @fact_groups.get_group_ttls(group_name))
167
168 cache_file_name = File.join(@cache_dir, group_name)
169
170 next if facts_already_cached?(cache_file_name, data)
171
172 @log.debug("caching values for #{group_name} facts")
173
174 data['cache_format_version'] = 1
175 File.write(cache_file_name, JSON.pretty_generate(data))
176 end
177 end
178
179 def facts_already_cached?(cache_file_name, data)
180 if File.readable?(cache_file_name)
181 file = Facter::Util::FileHelper.safe_read(cache_file_name)
182 begin
183 cached_data = JSON.parse(file) unless file.nil?
184 return true if (data.keys - cached_data.keys).empty?
185 rescue JSON::ParserError => e
186 @log.debug("Failed to read cache file #{cache_file_name}. Detail: #{e.message}")
187 rescue NoMethodError => e
188 @log.debug("No keys found in #{cache_file_name}. Detail: #{e.message}")
189 end
190 end
191 false
192 end
193
194 def read_group_json(group_name)
195 return @groups[group_name] if @groups.key?(group_name)
196
197 cache_file_name = File.join(@cache_dir, group_name)
198 data = nil
199 file = Facter::Util::FileHelper.safe_read(cache_file_name)
200 begin
201 data = JSON.parse(file) unless file.nil?
202 rescue JSON::ParserError
203 delete_cache(group_name)
204 end
205 @groups[group_name] = data
206 end
207
208 def check_ttls?(group_name, ttls)
209 return false unless ttls
210
211 cache_file_name = File.join(@cache_dir, group_name)
212 if File.readable?(cache_file_name)
213 file_time = File.mtime(cache_file_name)
214 expire_date = file_time + ttls
215 return true if expire_date > Time.now
216
217 File.delete(cache_file_name)
218 end
219
220 @log.debug("#{group_name} facts cache file expired, missing or is corrupt")
221 true
222 end
223
224 def delete_cache(group_name)
225 cache_file_name = File.join(@cache_dir, group_name)
226
227 begin
228 File.delete(cache_file_name) if File.readable?(cache_file_name)
229 rescue Errno::EACCES, Errno::EROFS => e
230 @log.warn("Could not delete cache: #{e.message}")
231 end
232 end
233 end
234 end
0 # frozen_string_literal: true
1
2 module Facter
3 class ExternalFactManager
4 def resolve_facts(searched_facts)
5 searched_facts = filter_external_facts(searched_facts)
6 external_facts(searched_facts)
7 end
8
9 private
10
11 def filter_external_facts(searched_facts)
12 searched_facts.select { |searched_fact| %i[custom external].include?(searched_fact.type) }
13 end
14
15 def external_facts(custom_facts)
16 resolved_custom_facts = []
17
18 custom_facts.each do |custom_fact|
19 fact = LegacyFacter[custom_fact.name]
20 resolved_fact = ResolvedFact.new(custom_fact.name, fact.value, :custom)
21 resolved_fact.user_query = custom_fact.user_query
22 resolved_fact.file = fact.options[:file]
23
24 resolved_custom_facts << resolved_fact
25 end
26
27 resolved_custom_facts
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facter
3 class CoreFact
4 def initialize(searched_fact)
5 @searched_fact = searched_fact
6 end
7
8 def create
9 fact_class = @searched_fact.fact_class
10
11 return unless fact_class
12
13 fact_value = nil
14 Facter::Framework::Benchmarking::Timer.measure(@searched_fact.name) do
15 fact_value = fact_class.new.call_the_resolver
16 end
17
18 fact_value
19 end
20 end
21 end
0 # frozen_string_literal: true
1
2 module Facter
3 class InternalFactManager
4 # resolves each SearchFact and filter out facts that do not match the given user query
5 # @param searched_facts [Array<Facter::SearchedFact>] array of searched facts
6 #
7 # @return [Array<Facter::ResolvedFact>]
8 #
9 # @api private
10 def resolve_facts(searched_facts)
11 internal_searched_facts = filter_internal_facts(searched_facts)
12 resolved_facts = if Options[:sequential]
13 resolve_sequentially(internal_searched_facts)
14 else
15 resolve_in_parallel(internal_searched_facts)
16 end
17
18 resolved_facts.flatten!
19 resolved_facts.compact!
20
21 nil_resolved_facts = resolve_nil_facts(searched_facts)
22
23 resolved_facts.concat(nil_resolved_facts)
24 end
25
26 private
27
28 def filter_internal_facts(searched_facts)
29 searched_facts.select { |searched_fact| %i[core legacy].include? searched_fact.type }
30 end
31
32 def valid_fact?(searched_fact, resolved_fact)
33 return if resolved_fact.value.nil?
34
35 searched_fact_name = searched_fact.name
36 if searched_fact_name.include?('.*')
37 resolved_fact.name.match(searched_fact_name)
38 else
39 resolved_fact.name == searched_fact_name
40 end
41 end
42
43 def resolve_nil_facts(searched_facts)
44 resolved_facts = []
45 searched_facts.select { |fact| fact.type == :nil }.each do |fact|
46 resolved_facts << ResolvedFact.new(fact.name, nil, :nil, fact.name)
47 end
48
49 resolved_facts
50 end
51
52 def resolve_sequentially(searched_facts)
53 searched_facts.map! { |searched_fact| resolve_fact(searched_fact) }
54 end
55
56 def resolve_in_parallel(searched_facts)
57 searched_facts.map! do |searched_fact|
58 Thread.new { resolve_fact(searched_fact) }
59 end.map!(&:value)
60 end
61
62 def resolve_fact(searched_fact)
63 fact_value = core_fact(searched_fact)
64 Array(fact_value).map! do |resolved_fact|
65 if valid_fact?(searched_fact, resolved_fact)
66 resolved_fact.user_query = searched_fact.user_query
67 resolved_fact
68 end
69 end
70 end
71
72 def core_fact(searched_fact)
73 fact = CoreFact.new(searched_fact)
74 fact.create
75 rescue StandardError => e
76 log.log_exception(e)
77 nil
78 end
79
80 def log
81 @log ||= Facter::Log.new(self)
82 end
83 end
84 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FactFilter
4 def filter_facts!(resolved_facts, user_query)
5 filter_legacy_facts!(resolved_facts) if user_query.empty?
6 filter_blocked_legacy_facts!(resolved_facts)
7 resolved_facts
8 end
9
10 private
11
12 # This will filter out the legacy facts that should be blocked. Because some legacy facts are just aliases
13 # to the core ones, even if they are blocked, facter will resolved them but they won't be displayed.
14
15 def filter_blocked_legacy_facts!(facts)
16 blocked_facts = Options[:blocked_facts] || []
17
18 facts.reject! do |fact|
19 blocked_facts.select { |blocked_fact| fact.name.match(/^#{blocked_fact}/) && fact.type == :legacy }.any?
20 end
21 end
22
23 def filter_legacy_facts!(resolved_facts)
24 return if Options[:show_legacy]
25
26 resolved_facts.reject!(&:legacy?)
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 class ClassDiscoverer
4 include Singleton
5
6 def initialize
7 @log = Log.new(self)
8 end
9
10 def discover_classes(operating_system)
11 os_module_name = Module.const_get("Facts::#{operating_system}")
12
13 # select only classes
14 find_nested_classes(os_module_name, discovered_classes = [])
15 discovered_classes
16 rescue NameError
17 @log.debug("There is no module named #{operating_system}")
18 []
19 end
20
21 def find_nested_classes(mod, discovered_classes)
22 mod.constants.each do |constant_name|
23 if mod.const_get(constant_name).instance_of? Class
24 discovered_classes << mod.const_get(constant_name)
25 elsif mod.const_get(constant_name).instance_of? Module
26 find_nested_classes(Module.const_get("#{mod.name}::#{constant_name}"), discovered_classes)
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 class ExternalFactLoader
4 def custom_facts
5 @custom_facts = load_custom_facts
6 end
7
8 def external_facts
9 @external_facts = load_external_facts
10 end
11
12 def load_fact(fact_name)
13 build_custom_facts(LegacyFacter.collection.custom_fact(fact_name)) || []
14 end
15
16 private
17
18 def load_custom_facts
19 custom_facts_to_load = LegacyFacter.collection.custom_facts
20 build_custom_facts(custom_facts_to_load) || []
21 end
22
23 def build_custom_facts(custom_facts_to_load)
24 custom_facts_to_load&.map do |k, v|
25 loaded_fact = LoadedFact.new(k.to_s, nil, :custom)
26 loaded_fact.is_env = v.options[:is_env] if v.options[:is_env]
27 loaded_fact
28 end
29 end
30
31 def load_external_facts
32 external_facts = []
33
34 external_facts_to_load = LegacyFacter.collection.external_facts
35
36 external_facts_to_load&.each do |k, v|
37 loaded_fact = LoadedFact.new(k.to_s, nil, :external)
38 loaded_fact.file = v.options[:file]
39 loaded_fact.is_env = v.options[:is_env]
40 external_facts << loaded_fact
41 end
42
43 external_facts
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FactLoader
4 include Singleton
5
6 attr_reader :internal_facts, :external_facts, :facts
7
8 def initialize
9 @log = Log.new(self)
10
11 @internal_facts = []
12 @external_facts = []
13 @custom_facts = []
14 @facts = []
15
16 @internal_loader ||= InternalFactLoader.new
17 @external_fact_loader ||= ExternalFactLoader.new
18 end
19
20 def load(user_query, options)
21 @internal_facts = load_internal_facts(user_query, options)
22 @custom_facts = load_custom_facts(options)
23 @external_facts = load_external_facts(options)
24
25 filter_env_facts
26
27 @facts = @internal_facts + @external_facts + @custom_facts
28 end
29
30 def load_internal_facts(user_query, options)
31 internal_facts = []
32 if user_query || options[:show_legacy]
33 # if we have a user query, then we must search in core facts and legacy facts
34 @log.debug('Loading all internal facts')
35 internal_facts = @internal_loader.facts
36 else
37 @log.debug('Load only core facts')
38 internal_facts = @internal_loader.core_facts
39 end
40
41 block_facts(internal_facts, options)
42 end
43
44 def load_custom_fact(options, fact_name)
45 return [] unless options[:custom_facts]
46
47 custom_facts = @external_fact_loader.load_fact(fact_name)
48 block_facts(custom_facts, options)
49 end
50
51 def load_custom_facts(options)
52 return [] unless options[:custom_facts]
53
54 @log.debug('Loading custom facts')
55 custom_facts = @external_fact_loader.custom_facts
56 block_facts(custom_facts, options)
57 end
58
59 def load_external_facts(options)
60 return [] unless options[:external_facts]
61
62 @log.debug('Loading external facts')
63 external_facts = @external_fact_loader.external_facts
64 block_facts(external_facts, options)
65 end
66
67 private
68
69 def filter_env_facts
70 env_fact_names = @external_facts.select { |fact| fact.is_env == true }.map(&:name)
71 return unless env_fact_names.any?
72
73 @internal_facts.delete_if do |fact|
74 if env_fact_names.include?(fact.name)
75 @log.debug("Reading #{fact.name} fact from environment variable")
76 true
77 else
78 false
79 end
80 end
81 end
82
83 def block_facts(facts, options)
84 blocked_facts = options[:blocked_facts] || []
85
86 facts.reject! { |fact| fact.type == :legacy } if options[:block_list]&.include?('legacy')
87
88 reject_list_core, reject_list_legacy = construct_reject_lists(blocked_facts, facts)
89
90 facts = facts.reject do |fact|
91 reject_list_core.include?(fact) || reject_list_core.find do |fact_to_block|
92 fact_to_block.klass == fact.klass
93 end || reject_list_legacy.include?(fact)
94 end
95
96 facts
97 end
98
99 def construct_reject_lists(blocked_facts, facts)
100 reject_list_core = []
101 reject_list_legacy = []
102
103 blocked_facts.each do |blocked|
104 facts.each do |fact|
105 next unless fact.name =~ /^#{blocked}\..*|^#{blocked}$/
106
107 if fact.type == :core
108 reject_list_core << fact
109 else
110 reject_list_legacy << fact
111 end
112 end
113 end
114
115 [reject_list_core, reject_list_legacy]
116 end
117 end
118 end
0 # frozen_string_literal: true
1
2 module Facter
3 class InternalFactLoader
4 attr_reader :facts
5
6 def core_facts
7 @facts.select { |fact| fact.type == :core }
8 end
9
10 def legacy_facts
11 @facts.select { |fact| fact.type == :legacy }
12 end
13
14 def initialize(os_descendents = nil)
15 @facts = []
16
17 os_descendents ||= OsDetector.instance.hierarchy
18 load_all_oses_in_descending_order(os_descendents)
19 end
20
21 private
22
23 def load_all_oses_in_descending_order(os_descendents)
24 os_descendents.reverse_each do |os|
25 load_for_os(os)
26 end
27 end
28
29 def load_for_os(operating_system)
30 # select only classes
31 classes = ClassDiscoverer.instance.discover_classes(operating_system)
32 classes.each do |class_name|
33 fact_name = class_name::FACT_NAME
34 # if fact is already loaded, skip it
35 unless @facts.any? { |fact| fact.name == fact_name }
36 type = class_name.const_defined?('TYPE') ? class_name::TYPE : :core
37 load_fact(fact_name, class_name, type)
38 end
39 next unless class_name.const_defined?('ALIASES')
40
41 [*class_name::ALIASES].each do |fact_alias|
42 load_fact(fact_alias, class_name, :legacy) unless @facts.any? { |fact| fact.name == fact_alias }
43 end
44 end
45 end
46
47 def load_fact(fact_name, klass, type)
48 loaded_fact = LoadedFact.new(fact_name, klass, type)
49 @facts << loaded_fact
50 end
51 end
52 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FactManager
4 include Singleton
5
6 def initialize
7 @internal_fact_mgr = InternalFactManager.new
8 @external_fact_mgr = ExternalFactManager.new
9 @fact_loader = FactLoader.instance
10 @options = Options.get
11 @log = Log.new(self)
12 end
13
14 def resolve_facts(user_query = [])
15 log_resolving_method
16 @options[:user_query] = user_query
17 cache_manager = Facter::CacheManager.new
18
19 searched_facts = QueryParser.parse(user_query, @fact_loader.load(user_query, @options))
20
21 searched_facts, cached_facts = cache_manager.resolve_facts(searched_facts)
22 internal_facts = @internal_fact_mgr.resolve_facts(searched_facts)
23 external_facts = @external_fact_mgr.resolve_facts(searched_facts)
24
25 resolved_facts = override_core_facts(internal_facts, external_facts)
26
27 resolved_facts = resolved_facts.concat(cached_facts)
28 cache_manager.cache_facts(resolved_facts)
29
30 FactFilter.new.filter_facts!(resolved_facts, user_query)
31
32 log_resolved_facts(resolved_facts)
33 resolved_facts
34 end
35
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 log_resolving_method
44 @options[:user_query] = user_query
45 @log.debug("resolving fact with user_query: #{user_query}")
46
47 @cache_manager = Facter::CacheManager.new
48
49 custom_facts = custom_fact_by_filename(user_query) || []
50 core_and_external_facts = core_or_external_fact(user_query) || []
51 resolved_facts = core_and_external_facts + custom_facts
52
53 if resolved_facts.empty? || resolved_facts.none? { |rf| rf.resolves?(user_query) }
54 resolved_facts.concat(all_custom_facts(user_query))
55 end
56
57 @cache_manager.cache_facts(resolved_facts)
58
59 log_resolved_facts(resolved_facts)
60 resolved_facts
61 end
62
63 def resolve_core(user_query = [], options = {})
64 log_resolving_method
65 @cache_manager = CacheManager.new
66 core_fact(user_query, options)
67 end
68
69 private
70
71 def log_resolving_method
72 if Options[:sequential]
73 @log.debugonce('Resolving facts sequentially')
74 else
75 @log.debugonce('Resolving fact in parallel')
76 end
77 end
78
79 def core_fact(user_query, options)
80 loaded_facts_hash = @fact_loader.load_internal_facts(user_query, options)
81
82 searched_facts = QueryParser.parse(user_query, loaded_facts_hash)
83 searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
84
85 resolved_facts = @internal_fact_mgr.resolve_facts(searched_facts)
86 resolved_facts = resolved_facts.concat(cached_facts)
87
88 FactFilter.new.filter_facts!(resolved_facts, user_query)
89
90 resolved_facts
91 end
92
93 def custom_fact_by_filename(user_query)
94 @log.debug("Searching fact: #{user_query} in file: #{user_query}.rb")
95
96 custom_fact = @fact_loader.load_custom_fact(@options, user_query)
97 return unless custom_fact.any?
98
99 searched_facts = parse_user_query(custom_fact, user_query)
100 searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
101
102 resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
103 resolved_facts = resolved_facts.concat(cached_facts)
104 resolved_facts if resolved_facts.any?
105 end
106
107 def core_or_external_fact(user_query)
108 @log.debug("Searching fact: #{user_query} in core facts and external facts")
109
110 core_facts = core_fact([user_query], @options)
111 external_facts = @fact_loader.load_external_facts(@options)
112 searched_facts = parse_user_query(external_facts, user_query)
113 searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
114
115 resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
116 resolved_facts = override_core_facts(core_facts, resolved_facts)
117 resolved_facts = resolved_facts.concat(cached_facts)
118
119 resolved_facts unless resolved_facts.map(&:value).compact.empty?
120 end
121
122 def all_custom_facts(user_query)
123 @log.debug("Searching fact: #{user_query} in all custom facts")
124
125 custom_facts = @fact_loader.load_custom_facts(@options)
126 searched_facts = parse_user_query(custom_facts, user_query)
127 searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
128
129 resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
130 resolved_facts.concat(cached_facts)
131 end
132
133 def parse_user_query(loaded_facts, user_query)
134 user_query = Array(user_query)
135 QueryParser.parse(user_query, loaded_facts)
136 end
137
138 def override_core_facts(core_facts, custom_facts)
139 return core_facts unless custom_facts
140
141 custom_facts.each do |custom_fact|
142 core_facts.delete_if { |core_fact| root_fact_name(core_fact) == custom_fact.name }
143 end
144
145 core_facts + custom_facts
146 end
147
148 def root_fact_name(fact)
149 fact.name.split('.').first
150 end
151
152 def log_resolved_facts(resolved_facts)
153 resolved_facts.each do |fact|
154 @log.debug("fact \"#{fact.name}\" has resolved to: #{fact.value}") unless fact.value.nil?
155 end
156 end
157 end
158 end
0 # frozen_string_literal: true
1
2 require 'open3'
3 require 'json'
4 require 'yaml'
5 require 'hocon'
6 require 'hocon/config_value_factory'
7 require 'singleton'
8 require 'logger'
9
10 @lib_path = File.join(File.dirname(__FILE__), '../../')
11
12 def load_dir(*dirs)
13 folder_path = File.join(@lib_path, dirs)
14 return unless Dir.exist?(folder_path.tr('*', ''))
15
16 files_to_require = Dir.glob(File.join(folder_path, '*.rb')).reject { |file| file =~ %r{/ffi/} }
17 files_to_require.each(&method(:require))
18 end
19
20 load_dir(%w[framework core options])
21 require 'facter/framework/core/options'
22 require 'facter/framework/logging/logger_helper'
23 require 'facter/framework/logging/logger'
24
25 require 'facter/util/file_helper'
26
27 require 'facter/resolvers/base_resolver'
28 require 'facter/framework/detector/os_hierarchy'
29 require 'facter/framework/detector/os_detector'
30
31 require 'facter/framework/config/config_reader'
32 require 'facter/framework/config/fact_groups'
33
34 load_dir(['config'])
35
36 load_dir(['util'])
37 load_dir(%w[util resolvers])
38 load_dir(%w[util facts])
39 load_dir(%w[util facts posix])
40 load_dir(%w[util resolvers networking])
41
42 load_dir(['resolvers'])
43 load_dir(['facts_utils'])
44 load_dir(%w[framework core])
45 load_dir(['models'])
46 load_dir(%w[framework benchmarking])
47
48 load_dir(%w[framework core fact_loaders])
49 load_dir(%w[framework core fact internal])
50 load_dir(%w[framework core fact external])
51 load_dir(%w[framework formatters])
52
53 os_hierarchy = OsDetector.instance.hierarchy
54 os_hierarchy.each { |operating_system| load_dir(['util', operating_system.downcase, '**']) }
55 os_hierarchy.each { |operating_system| load_dir(['facts', operating_system.downcase, '**']) }
56 os_hierarchy.each { |operating_system| load_dir(['resolvers', operating_system.downcase, '**']) }
57
58 require 'facter/custom_facts/core/legacy_facter'
59 load_dir(%w[framework utils])
60
61 require 'facter/framework/parsers/query_parser'
0 # frozen_string_literal: true
1
2 module Facter
3 class ConfigFileOptions
4 class << self
5 def init(config_path = nil)
6 @options = {}
7 Facter::ConfigReader.init(config_path)
8
9 augment_config_path(config_path)
10
11 augment_all
12 end
13
14 def get
15 @options || {}
16 end
17
18 private
19
20 def augment_structured_facts(global_conf)
21 return if !global_conf || global_conf['force-dot-resolution'].nil?
22
23 @options[:force_dot_resolution] = global_conf['force-dot-resolution']
24 end
25
26 def augment_all
27 augment_cli(Facter::ConfigReader.cli) if Options.cli?
28 augment_globals
29 augment_facts(Facter::ConfigReader.ttls, Facter::ConfigReader.fact_groups)
30 end
31
32 def augment_globals
33 augment_ruby(Facter::ConfigReader.global)
34
35 augment_structured_facts(Facter::ConfigReader.global)
36 augment_custom(Facter::ConfigReader.global)
37 augment_external(Facter::ConfigReader.global)
38 augment_show_legacy(Facter::ConfigReader.global)
39 augment_sequential(Facter::ConfigReader.global)
40 end
41
42 def augment_config_path(config_path)
43 @options[:config] = config_path
44 end
45
46 def augment_cli(file_cli_conf)
47 return unless file_cli_conf
48
49 @options[:debug] = file_cli_conf['debug'] unless file_cli_conf['debug'].nil?
50 @options[:trace] = file_cli_conf['trace'] unless file_cli_conf['trace'].nil?
51 @options[:verbose] = file_cli_conf['verbose'] unless file_cli_conf['verbose'].nil?
52 @options[:log_level] = file_cli_conf['log-level'].to_sym unless file_cli_conf['log-level'].nil?
53 end
54
55 def augment_custom(file_global_conf)
56 return unless file_global_conf
57
58 if Options.cli?
59 unless file_global_conf['no-custom-facts'].nil?
60 @options[:no_custom_facts] = file_global_conf['no-custom-facts']
61 end
62 end
63
64 @options[:custom_dir] = file_global_conf['custom-dir'] unless file_global_conf['custom-dir'].nil?
65 @options[:config_file_custom_dir] = @options[:custom_dir] || []
66 end
67
68 def augment_external(global_conf)
69 return unless global_conf
70
71 if Options.cli?
72 @options[:no_external_facts] = global_conf['no-external-facts'] unless global_conf['no-external-facts'].nil?
73 end
74
75 @options[:external_dir] = [global_conf['external-dir']].flatten unless global_conf['external-dir'].nil?
76 @options[:config_file_external_dir] = @options[:external_dir] || []
77 end
78
79 def augment_ruby(global_conf)
80 return unless global_conf
81 return unless Options.cli?
82
83 @options[:no_ruby] = global_conf['no-ruby'].nil? ? false : global_conf['no-ruby']
84 end
85
86 def augment_show_legacy(global_conf)
87 return unless global_conf
88
89 @options[:show_legacy] = global_conf['show-legacy'] unless global_conf['show-legacy'].nil?
90 end
91
92 def augment_sequential(global_conf)
93 return unless global_conf
94
95 @options[:sequential] = global_conf['sequential'] unless global_conf['sequential'].nil?
96 end
97
98 def augment_facts(ttls, groups)
99 fact_groups = Facter::FactGroups.new
100
101 @options[:blocked_facts] = fact_groups.blocked_facts unless fact_groups.blocked_facts.nil?
102 @options[:block_list] = fact_groups.block_list
103 @options[:ttls] = ttls unless ttls.nil?
104
105 f_groups = fact_groups.groups || {}
106 f_groups = groups.merge(f_groups) unless groups.nil?
107 @options[:fact_groups] = f_groups
108 end
109 end
110 end
111 end
0 # frozen_string_literal: true
1
2 module Facter
3 class OptionStore
4 # default options
5 @debug = false
6 @verbose = false
7 # TODO: constant is not yet available when running puppet facts
8 @log_level = :warn
9 @show_legacy = true
10 @custom_facts = true
11 @blocked_facts = []
12 @ruby = true
13 @external_facts = true
14 @config = nil
15 @strict = false
16 @json = false
17 @cache = true
18 @yaml = false
19 @puppet = false
20 @ttls = []
21 @block = true
22 @cli = nil
23 @config_file_custom_dir = []
24 @config_file_external_dir = []
25 @default_external_dir = []
26 @fact_groups = {}
27 @sequential = true
28 @ttls = []
29 @block_list = []
30 @color = true
31 @trace = false
32 @timing = false
33 @external_dir = []
34 @custom_dir = []
35 @hocon = false
36 @allow_external_loggers = true
37 @force_dot_resolution = false
38 @http_debug = false
39
40 class << self
41 attr_reader :debug, :verbose, :log_level, :show_legacy,
42 :custom_facts, :blocked_facts, :ruby, :external_facts
43
44 attr_accessor :config, :strict, :json,
45 :cache, :yaml, :puppet, :ttls, :block, :cli, :config_file_custom_dir,
46 :config_file_external_dir, :default_external_dir, :fact_groups, :force_dot_resolution,
47 :block_list, :color, :trace, :sequential, :timing, :hocon, :allow_external_loggers, :http_debug
48
49 attr_writer :external_dir
50
51 def all
52 options = {}
53 instance_variables.each do |iv|
54 variable_name = iv.to_s.delete('@')
55 options[variable_name.to_sym] = OptionStore.send(variable_name.to_sym)
56 end
57 options
58 end
59
60 def no_ruby=(bool)
61 if bool
62 @ruby = false
63 @custom_facts = false
64 @blocked_facts << 'ruby'
65 else
66 @ruby = true
67 end
68 end
69
70 def no_block=(bool)
71 @block = !bool
72 end
73
74 def no_cache=(bool)
75 @cache = !bool
76 end
77
78 def no_color=(bool)
79 @color = !bool
80 end
81
82 def external_dir
83 return fallback_external_dir if @external_dir.empty? && @external_facts
84
85 @external_dir
86 end
87
88 def blocked_facts=(*facts)
89 @blocked_facts += [*facts]
90
91 @blocked_facts.flatten!
92 end
93
94 def custom_dir
95 return @config_file_custom_dir unless @custom_dir.any?
96
97 @custom_dir
98 end
99
100 def custom_dir=(*dirs)
101 @ruby = true
102
103 @custom_dir = [*dirs]
104 @custom_dir.flatten!
105 end
106
107 def debug=(bool)
108 if bool == true
109 self.log_level = :debug
110 else
111 @debug = false
112 self.log_level = Facter::DEFAULT_LOG_LEVEL
113 end
114 end
115
116 def verbose=(bool)
117 if bool == true
118 @verbose = true
119 self.log_level = :info
120 else
121 @verbose = false
122 self.log_level = Facter::DEFAULT_LOG_LEVEL
123 end
124 end
125
126 def no_custom_facts=(bool)
127 if bool == false
128 @custom_facts = true
129 @ruby = true
130 else
131 @custom_facts = false
132 end
133 end
134
135 def no_external_facts=(bool)
136 @external_facts = !bool
137 end
138
139 def log_level=(level)
140 level = level.to_sym
141 case level
142 when :trace
143 @log_level = :debug
144 when :debug
145 @log_level = :debug
146 @debug = true
147 else
148 @log_level = level
149 end
150
151 Facter::Log.level = @log_level
152 end
153
154 def show_legacy=(bool)
155 if bool == true
156 @show_legacy = bool
157 @ruby = true
158 else
159 @show_legacy = false
160 end
161 end
162
163 def set(key, value)
164 send("#{key}=".to_sym, value)
165 end
166
167 def reset
168 @debug = false
169 @verbose = false
170 # TODO: constant is not yet available when running puppet facts
171 @log_level = :warn
172 @show_legacy = true
173 @ruby = true
174 @json = false
175 @hocon = false
176 @cache = true
177 @yaml = false
178 @puppet = false
179 @ttls = []
180 @block = true
181 @cli = nil
182 @http_debug = false
183 reset_config
184 end
185
186 def reset_config
187 @blocked_facts = []
188 @external_facts = true
189 @config = nil
190 @strict = false
191 @config_file_custom_dir = []
192 @config_file_external_dir = []
193 @default_external_dir = []
194 @fact_groups = {}
195 @block_list = {}
196 @color = true
197 @sequential = true
198 @ttls = []
199 @trace = false
200 @timing = false
201 @allow_external_loggers = true
202 reset_facts
203 end
204
205 def reset_facts
206 @custom_facts = true
207 @force_dot_resolution = false
208 @external_dir = []
209 @custom_dir = []
210 end
211
212 def fallback_external_dir
213 return @config_file_external_dir if @config_file_external_dir.any?
214
215 @default_external_dir
216 end
217 end
218 end
219 end
0 # frozen_string_literal: true
1
2 module Facter
3 module OptionsValidator
4 INVALID_PAIRS_RULES = { '--color' => '--no-color',
5 '--json' => ['--no-json', '-y', '--yaml', '--hocon'],
6 '--yaml' => ['--no-yaml', '-j', '--hocon'],
7 '--hocon' => '--no-hocon',
8 '-j' => ['--no-json', '--hocon'],
9 '-y' => ['--no-yaml', '-j', '--hocon'],
10 '--puppet' => ['--no-puppet', '--no-ruby', '--no-custom-facts'],
11 '-p' => ['--no-puppet', '--no-ruby', '--no-custom-facts'],
12 '--no-external-facts' => '--external-dir',
13 '--custom-dir' => ['--no-custom-facts', '--no-ruby'] }.freeze
14 DUPLICATED_OPTIONS_RULES = { '-j' => '--json', '-y' => '--yaml', '-p' => '--puppet', '-h' => '--help',
15 '-v' => '--version', '-l' => '--log-level', '-d' => '--debug',
16 '-c' => '--config' }.freeze
17 LOG_LEVEL = %i[none trace debug info warn error fatal].freeze
18
19 def self.validate(options)
20 DUPLICATED_OPTIONS_RULES.each do |key, value|
21 if options.include?(key) && options.include?(value)
22 write_error_and_exit("option #{value} cannot be specified more than once.")
23 end
24 end
25
26 INVALID_PAIRS_RULES.each do |key, value|
27 common_values = [value].flatten & options
28 if options.include?(key) && common_values.any?
29 write_error_and_exit("#{key} and #{common_values.first} options conflict: please specify only one.")
30 end
31 end
32 end
33
34 def self.write_error_and_exit(message)
35 log = Facter::Log.new(self)
36 log.error(message, true)
37 Facter::Cli.start(['--help'])
38
39 exit 1
40 end
41
42 def self.validate_configs(options)
43 conflicting_configs(options).each do |op|
44 next unless op.values[0] && op.values[1]
45
46 message = "#{op.keys[0]} and #{op.keys[1]} options conflict: please specify only one"
47 write_error_and_exit(message)
48 end
49 validate_log_options(options)
50 end
51
52 def self.conflicting_configs(options)
53 no_ruby = !options[:ruby]
54 no_custom_facts = !options[:custom_facts]
55 puppet = options[:puppet]
56 custom_dir = options[:custom_dir].nil? ? false : options[:custom_dir].any?
57 external_dir = options[:external_dir].nil? ? false : options[:external_dir].any?
58
59 [
60 { 'no-ruby' => no_ruby, 'custom-dir' => custom_dir },
61 { 'no-external-facts' => !options[:external_facts], 'external-dir' => external_dir },
62 { 'no-custom-facts' => no_custom_facts, 'custom-dir' => custom_dir },
63 { 'no-ruby' => no_ruby, 'puppet' => puppet },
64 { 'no-custom-facts' => no_custom_facts, 'puppet' => puppet }
65 ]
66 end
67
68 def self.validate_log_options(options)
69 # TODO: find a better way to do this
70 return if options[:debug] == true && options[:log_level] == :debug
71 return if options[:verbose] == true && options[:log_level] == :info
72
73 return unless [options[:debug],
74 options[:verbose],
75 options[:log_level] != Facter::DEFAULT_LOG_LEVEL]
76 .count(true) > 1
77
78 message = 'debug, verbose, and log-level options conflict: please specify only one.'
79 write_error_and_exit(message)
80 end
81 end
82 end
0 # frozen_string_literal: true
1
2 module Facter
3 class Options
4 class << self
5 def cli?
6 OptionStore.cli
7 end
8
9 def get
10 OptionStore.all
11 end
12
13 def [](key)
14 OptionStore.send(key.to_sym)
15 end
16
17 def []=(key, value)
18 OptionStore.send("#{key}=".to_sym, value)
19 end
20
21 def custom_dir?
22 OptionStore.custom_dir && OptionStore.custom_facts
23 end
24
25 def custom_dir
26 [OptionStore.custom_dir].flatten
27 end
28
29 def external_dir?
30 OptionStore.external_dir && OptionStore.external_facts
31 end
32
33 def external_dir
34 OptionStore.external_dir
35 end
36
37 def init
38 OptionStore.cli = false
39 ConfigFileOptions.init
40 store(ConfigFileOptions.get)
41 end
42
43 def init_from_cli(cli_options = {})
44 Facter::OptionStore.cli = true
45 Facter::OptionStore.show_legacy = false
46 Facter::OptionStore.trace = cli_options[:trace]
47 OptionStore.set(:config, cli_options[:config])
48 ConfigFileOptions.init(cli_options[:config])
49 store(ConfigFileOptions.get)
50 store(cli_options)
51
52 Facter::OptionsValidator.validate_configs(get)
53 end
54
55 def store(options)
56 options.each do |key, value|
57 value = munge_option(key, value)
58 OptionStore.set(key, value)
59 end
60 end
61
62 private
63
64 def munge_option(key, value)
65 return value unless key.to_sym == :log_level
66
67 case value.to_sym
68 when :log_level
69 ''
70 when :none
71 'unknown'
72 else
73 value
74 end
75 end
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 module Facter
3 class SessionCache
4 @resolvers = []
5
6 def self.subscribe(resolver)
7 @resolvers << resolver
8 end
9
10 def self.invalidate_all_caches
11 @resolvers.uniq.each(&:invalidate_cache)
12 @resolvers = []
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 require 'rbconfig'
3
4 class OsDetector
5 include Singleton
6
7 attr_reader :identifier, :hierarchy
8
9 def initialize(*_args)
10 @log = Facter::Log.new(self)
11 @os_hierarchy = Facter::OsHierarchy.new
12 @identifier = detect
13 end
14
15 private
16
17 def detect
18 host_os = RbConfig::CONFIG['host_os']
19 identifier = case host_os
20 when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
21 :windows
22 when /darwin|mac os/
23 :macosx
24 when /linux/
25 detect_distro
26 when /freebsd/
27 :freebsd
28 when /bsd/
29 :bsd
30 when /solaris/
31 :solaris
32 when /aix/
33 :aix
34 else
35 raise "unknown os: #{host_os.inspect}"
36 end
37
38 @hierarchy = detect_hierarchy(identifier)
39 @identifier = identifier
40 end
41
42 def detect_hierarchy(identifier)
43 hierarchy = @os_hierarchy.construct_hierarchy(identifier)
44 if hierarchy.empty?
45 @log.debug("Could not detect hierarchy using os identifier: #{identifier} , trying with family")
46
47 # https://www.commandlinux.com/man-page/man5/os-release.5.html
48 detect_family.to_s.split.each do |family|
49 hierarchy = @os_hierarchy.construct_hierarchy(family)
50 return hierarchy unless hierarchy.empty?
51 end
52 end
53
54 if hierarchy.empty?
55 @log.debug("Could not detect hierarchy using family #{detect_family}, falling back to Linux")
56 hierarchy = @os_hierarchy.construct_hierarchy(:linux)
57 end
58
59 hierarchy
60 end
61
62 def detect_family
63 Facter::Resolvers::OsRelease.resolve(:id_like)
64 end
65
66 def detect_based_on_release_file
67 @identifier = :devuan if File.readable?('/etc/devuan_version')
68 end
69
70 def detect_distro
71 detect_based_on_release_file
72 return @identifier if @identifier
73
74 [Facter::Resolvers::OsRelease,
75 Facter::Resolvers::RedHatRelease,
76 Facter::Resolvers::SuseRelease].each do |resolver|
77 @identifier = resolver.resolve(:id)
78 break if @identifier
79 end
80
81 @identifier&.downcase&.to_sym
82 end
83 end
0 # frozen_string_literal: true
1
2 require 'facter/config'
3
4 module Facter
5 class OsHierarchy
6 def initialize
7 @log = Log.new(self)
8 @os_hierarchy = Facter::Config::OS_HIERARCHY
9 end
10
11 def construct_hierarchy(searched_os)
12 return [] if searched_os.nil?
13
14 searched_os = searched_os.to_s.capitalize
15 if @os_hierarchy.nil?
16 @log.debug("There is no os_hierarchy, will fall back to: #{searched_os}")
17 return [searched_os]
18 end
19
20 @searched_path = []
21 search(@os_hierarchy, searched_os, [])
22
23 @searched_path.map { |os_name| os_name.to_s.capitalize }
24 end
25
26 private
27
28 def search(json_data, searched_element, path)
29 # we hit a dead end, the os was not found on this branch
30 # and we cannot go deeper
31 return unless json_data
32
33 json_data.each do |tree_node|
34 # we found the searched OS, so save the path from the tree
35 @searched_path = path.dup << tree_node if tree_node == searched_element
36
37 next unless tree_node.is_a?(Hash)
38
39 tree_node.each do |k, v|
40 return @searched_path = path.dup << k if k == searched_element
41
42 search(v, searched_element, path << k)
43 path.pop
44 end
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FormatterFactory
4 def self.build(options)
5 return JsonFactFormatter.new if options[:json]
6 return YamlFactFormatter.new if options[:yaml]
7 return HoconFactFormatter.new if options[:hocon]
8
9 LegacyFactFormatter.new
10 end
11 end
12 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FormatterHelper
4 class << self
5 def retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
6 facts_to_display = FactCollection.new
7 user_queries.each do |user_query|
8 fact_collection = build_fact_collection_for_user_query(user_query, resolved_facts)
9
10 printable_value = fact_collection.dig_fact(user_query)
11 facts_to_display.merge!(user_query => printable_value)
12 end
13
14 Facter::Utils.sort_hash_by_key(facts_to_display)
15 end
16
17 def retrieve_fact_collection(resolved_facts)
18 fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
19 Facter::Utils.sort_hash_by_key(fact_collection)
20 end
21
22 def retrieve_fact_value_for_single_query(user_query, resolved_facts)
23 fact_collection = build_fact_collection_for_user_query(user_query, resolved_facts)
24 fact_collection = Utils.sort_hash_by_key(fact_collection)
25 fact_collection.dig_fact(user_query)
26 end
27
28 private
29
30 def build_fact_collection_for_user_query(user_query, resolved_facts)
31 facts_for_query = resolved_facts.select { |resolved_fact| resolved_fact.user_query == user_query }
32 FactCollection.new.build_fact_collection!(facts_for_query)
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 module Facter
3 class HoconFactFormatter
4 def initialize
5 @log = Log.new(self)
6 end
7
8 def format(resolved_facts)
9 user_queries = resolved_facts.uniq(&:user_query).map(&:user_query)
10
11 return if user_queries.count < 1
12 return format_for_multiple_user_queries(user_queries, resolved_facts) if user_queries.count > 1
13
14 user_query = user_queries.first
15 return format_for_no_query(resolved_facts) if user_query.empty?
16 return format_for_single_user_query(user_queries.first, resolved_facts) unless user_query.empty?
17 end
18
19 private
20
21 def format_for_no_query(resolved_facts)
22 @log.debug('Formatting for no user query')
23 fact_collection = FormatterHelper.retrieve_fact_collection(resolved_facts)
24 hash_to_hocon(fact_collection)
25 end
26
27 def format_for_multiple_user_queries(user_queries, resolved_facts)
28 @log.debug('Formatting for multiple user queries')
29
30 facts_to_display = FormatterHelper.retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
31 hash_to_hocon(facts_to_display)
32 end
33
34 def format_for_single_user_query(user_query, resolved_facts)
35 @log.debug('Formatting for single user query')
36
37 fact_value = FormatterHelper.retrieve_fact_value_for_single_query(user_query, resolved_facts)
38
39 return '' unless fact_value
40
41 fact_value.class == Hash ? hash_to_hocon(fact_value) : fact_value
42 end
43
44 def hash_to_hocon(fact_collection)
45 render_opts = Hocon::ConfigRenderOptions.new(false, false, true, false)
46 Hocon::ConfigFactory.parse_string(fact_collection.to_json).root.render(render_opts)
47 end
48 end
49 end
0 # frozen_string_literal: true
1
2 module Facter
3 class JsonFactFormatter
4 def initialize
5 @log = Facter::Log.new(self)
6 end
7
8 def format(resolved_facts)
9 user_queries = resolved_facts.uniq(&:user_query).map(&:user_query)
10
11 if user_queries.count == 1 && user_queries.first.empty?
12 format_for_no_query(resolved_facts)
13 else
14 format_for_user_queries(user_queries, resolved_facts)
15 end
16 end
17
18 private
19
20 def format_for_no_query(resolved_facts)
21 @log.debug('No user query provided')
22
23 fact_collection = FormatterHelper.retrieve_fact_collection(resolved_facts)
24 JSON.pretty_generate(fact_collection)
25 end
26
27 def format_for_user_queries(user_queries, resolved_facts)
28 @log.debug('User provided a query')
29
30 facts_to_display = FormatterHelper.retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
31 JSON.pretty_generate(facts_to_display)
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facter
3 class LegacyFactFormatter
4 def initialize
5 @log = Log.new(self)
6 end
7
8 def format(resolved_facts)
9 user_queries = resolved_facts.uniq(&:user_query).map(&:user_query)
10
11 return if user_queries.count < 1
12 return format_for_multiple_user_queries(user_queries, resolved_facts) if user_queries.count > 1
13
14 user_query = user_queries.first
15 return format_for_no_query(resolved_facts) if user_query.empty?
16
17 format_for_single_user_query(user_queries.first, resolved_facts)
18 end
19
20 private
21
22 def format_for_no_query(resolved_facts)
23 @log.debug('Formatting for no user query')
24 fact_collection = Facter::FormatterHelper.retrieve_fact_collection(resolved_facts)
25 pretty_json = hash_to_facter_format(fact_collection)
26
27 pretty_json = remove_enclosing_accolades(pretty_json)
28 pretty_json = remove_comma_and_quotation(pretty_json)
29 handle_newlines(pretty_json)
30 end
31
32 def format_for_multiple_user_queries(user_queries, resolved_facts)
33 @log.debug('Formatting for multiple user queries')
34
35 facts_to_display = Facter::FormatterHelper.retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
36
37 facts_to_display.each { |k, v| facts_to_display[k] = v.nil? ? '' : v }
38 pretty_json = hash_to_facter_format(facts_to_display)
39 pretty_json = remove_enclosing_accolades(pretty_json)
40 pretty_json = remove_comma_and_quotation(pretty_json)
41 pretty_json = handle_newlines(pretty_json)
42
43 @log.debug('Remove quotes from value if value is a string')
44 pretty_json.gsub(/^(\S*) => \"(.*)\"/, '\1 => \2')
45 end
46
47 def format_for_single_user_query(user_query, resolved_facts)
48 @log.debug('Formatting for single user query')
49
50 fact_value = Facter::FormatterHelper.retrieve_fact_value_for_single_query(user_query, resolved_facts)
51
52 return fact_value if fact_value.is_a?(String)
53
54 pretty_json = fact_value.nil? ? '' : hash_to_facter_format(fact_value)
55
56 @log.debug('Remove quotes from value if it is a simple string')
57 pretty_json.gsub(/^"(.*)\"/, '\1')
58 end
59
60 def hash_to_facter_format(facts_hash)
61 @log.debug('Converting hash to pretty json')
62 pretty_json = JSON.pretty_generate(facts_hash)
63
64 @log.debug('Change key value delimiter from : to =>')
65 pretty_json.gsub!(/":\s/, '" => ')
66
67 @log.debug('Remove quotes from parent nodes')
68 pretty_json.gsub!(/\"(.*)\"\ =>/, '\1 =>')
69
70 @log.debug('Remove double backslashes from paths')
71 pretty_json.gsub(/\\\\/, '\\')
72 end
73
74 def remove_enclosing_accolades(pretty_fact_json)
75 @log.debug('Removing enclosing accolades')
76 pretty_fact_json = pretty_fact_json[1..-2]
77
78 @log.debug('Remove empty lines')
79 pretty_fact_json.gsub!(/^$\n/, '')
80
81 @log.debug('Fix indentation after removing enclosed accolades')
82 pretty_fact_json = pretty_fact_json.gsub(/^\s\s(.*)$/, '\1')
83
84 @log.debug('remove comas from query results')
85 pretty_fact_json.gsub(/^},/, '}')
86 end
87
88 def handle_newlines(pretty_fact_json)
89 @log.debug('Convert newline characters to actual newlines')
90 pretty_fact_json.gsub('\n', "\n")
91 end
92
93 def remove_comma_and_quotation(output)
94 # quotation marks that come after \ are not removed
95 @log.debug('Remove unnecessary comma and quotation marks on root facts')
96 output.split("\n")
97 .map! { |line| line =~ /^[\s]+/ ? line : line.gsub(/,$|(?<!\\)\"/, '').gsub('\\"', '"') }.join("\n")
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 module Facter
3 class YamlFactFormatter
4 def initialize
5 @log = Log.new(self)
6 end
7
8 def format(resolved_facts)
9 user_queries = resolved_facts.uniq(&:user_query).map(&:user_query)
10
11 facts_to_display = if user_queries.count == 1 && user_queries.first.empty?
12 FormatterHelper.retrieve_fact_collection(resolved_facts)
13 else
14 FormatterHelper.retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
15 end
16
17 facts_to_display = Psych.parse_stream(facts_to_display.to_yaml)
18 facts_to_display.children[0].tag_directives = []
19 yaml_pretty = quote_special_strings(facts_to_display)
20
21 @log.debug('Replace --- from yaml beginning, to keep it compatible with C facter')
22 yaml_pretty.gsub(/^---[\r\n]+/, '')
23 end
24
25 private
26
27 def quote_special_strings(fact_hash)
28 require 'psych'
29
30 fact_hash.grep(Psych::Nodes::Scalar).each do |node|
31 next unless needs_quote?(node.value)
32
33 node.plain = false
34 node.quoted = true
35 node.style = Psych::Nodes::Scalar::DOUBLE_QUOTED
36 end
37
38 fact_hash = unquote_keys(fact_hash)
39 fact_hash.yaml
40 end
41
42 def unquote_keys(fact_hash)
43 fact_hash.grep(Psych::Nodes::Mapping).each do |node|
44 node.children.each_slice(2) do |k, _|
45 k.plain = true
46 k.quoted = false
47 k.style = Psych::Nodes::Scalar::ANY
48 end
49 end
50 fact_hash
51 end
52
53 def needs_quote?(value)
54 return false if value =~ /true|false/
55 return false if value[/^[0-9]+$/]
56 return true if value =~ /y|Y|yes|Yes|YES|n|N|no|No|NO|True|TRUE|False|FALSE|on|On|ON|off|Off|OFF|:/
57 return false if value[/[a-zA-Z]/]
58 return false if value[/[0-9]+\.[0-9]+/]
59
60 true
61 end
62 end
63 end
0 # frozen_string_literal: true
1
2 require 'logger'
3
4 module Facter
5 RED = "\e[31m"
6 GREEN = "\e[32m"
7 YELLOW = "\e[33m"
8 CYAN = "\e[36m"
9 RESET = "\e[0m"
10
11 DEFAULT_LOG_LEVEL = :warn
12
13 class Log
14 @@logger = nil
15 @@message_callback = nil
16 @@has_errors = false
17 @@debug_messages = []
18 @@warn_messages = []
19 @@timing = false
20
21 class << self
22 def clear_messages
23 @@debug_messages.clear
24 @@warn_messages.clear
25 end
26
27 def on_message(&block)
28 @@message_callback = block
29 end
30
31 def level=(log_level)
32 @@logger.level = log_level
33 end
34
35 def level
36 @@logger.level
37 end
38
39 def errors?
40 @@has_errors
41 end
42
43 def output(output)
44 return if @@logger
45
46 @@logger = Logger.new(output)
47 set_logger_format
48 @@logger.level = DEFAULT_LOG_LEVEL
49 end
50
51 def set_logger_format
52 @@logger.formatter = proc do |severity, datetime, _progname, msg|
53 datetime = datetime.strftime(@datetime_format || '%Y-%m-%d %H:%M:%S.%6N ')
54 "[#{datetime}] #{severity} #{msg} \n"
55 end
56 end
57
58 # Print an exception message, and optionally a backtrace if trace is set
59
60 # Print timing information
61 #
62 # @param string [String] the time to print
63 # @return [void]
64 #
65 # @api private
66 def show_time(string)
67 return unless string && timing?
68
69 if @@message_callback
70 @@message_callback.call(:info, string)
71 else
72 warn("#{GREEN}#{string}#{RESET}")
73 end
74 end
75
76 # Enable or disable logging of timing information
77 #
78 # @param bool [true, false]
79 # @return [void]
80 #
81 # @api private
82 def timing(bool)
83 @@timing = bool
84 end
85
86 # Returns whether timing output is turned on
87 #
88 # @api private
89 def timing?
90 @@timing
91 end
92 end
93
94 def initialize(logged_class)
95 @class_name = LoggerHelper.determine_callers_name(logged_class)
96 return unless @@logger.nil?
97
98 @@logger = Logger.new(STDOUT)
99 @@logger.level = DEFAULT_LOG_LEVEL
100 end
101
102 def debug(msg)
103 return unless debugging_active?
104
105 if @@message_callback && Options[:allow_external_loggers]
106 @@message_callback.call(:debug, msg)
107 else
108 msg = colorize(msg, CYAN) if Options[:color]
109 @@logger.debug(@class_name + ' - ' + msg)
110 end
111 end
112
113 def debugonce(msg)
114 return unless debugging_active?
115
116 message_string = msg.to_s
117 return if @@debug_messages.include? message_string
118
119 @@debug_messages << message_string
120 debug(message_string)
121 end
122
123 def info(msg)
124 if msg.nil? || msg.empty?
125 empty_message_error(msg)
126 elsif @@message_callback && Options[:allow_external_loggers]
127 @@message_callback.call(:info, msg)
128 else
129 msg = colorize(msg, GREEN) if Options[:color]
130 @@logger.info(@class_name + ' - ' + msg)
131 end
132 end
133
134 def warn(msg)
135 if @@message_callback && Options[:allow_external_loggers]
136 @@message_callback.call(:warn, msg)
137 else
138 msg = colorize(msg, YELLOW) if Options[:color]
139 @@logger.warn(@class_name + ' - ' + msg)
140 end
141 end
142
143 def warnonce(message)
144 message_string = message.to_s
145 return if @@warn_messages.include? message_string
146
147 @@warn_messages << message_string
148 warn(message_string)
149 end
150
151 def error(msg, colorize = false)
152 @@has_errors = true
153
154 if @@message_callback && Options[:allow_external_loggers]
155 @@message_callback.call(:error, msg)
156 else
157 msg = colorize(msg, RED) if colorize || Options[:color]
158 @@logger.error(@class_name + ' - ' + msg)
159 end
160 end
161
162 def log_exception(exception)
163 msg = exception.message
164 msg += "\n" + exception.backtrace.join("\n") if Options[:trace]
165
166 error(msg, true)
167 end
168
169 private
170
171 def colorize(msg, color)
172 "#{color}#{msg}#{RESET}"
173 end
174
175 def debugging_active?
176 return true unless Facter.respond_to?(:debugging?)
177
178 Facter.debugging?
179 end
180
181 def empty_message_error(msg)
182 invoker = caller(1..1).first.slice(/.*:\d+/)
183 @@logger.warn "#{self.class}#debug invoked with invalid message #{msg.inspect}:#{msg.class} at #{invoker}"
184 end
185 end
186 end
0 # frozen_string_literal: true
1
2 class LoggerHelper
3 class << self
4 def determine_callers_name(sender_self)
5 class_name = case sender_self
6 when String
7 sender_self
8 when Class
9 sender_self.name
10 when Module
11 sender_self.name
12 else # when class is singleton
13 sender_self.class.name
14 end
15 class_name
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 module Facter
3 class QueryParser
4 @log = Log.new(self)
5 class << self
6 # Searches for facts that could resolve a user query.
7 # There are 4 types of facts:
8 # root facts
9 # e.g. networking
10 # child facts
11 # e.g. networking.dhcp
12 # composite facts
13 # e.g. networking.interfaces.en0.bindings.address
14 # regex facts (legacy)
15 # e.g. impaddress_end160
16 #
17 # Because a root fact will always be resolved by a collection of child facts,
18 # we can return one or more child facts for each parent.
19 #
20 # query - is the user input used to search for facts
21 # loaded_fact - is a list with all facts for the current operating system
22 #
23 # Returns a list of SearchedFact objects that resolve the users query.
24 def parse(query_list, loaded_fact)
25 matched_facts = []
26 @query_list = query_list
27
28 return no_user_query(loaded_fact) unless query_list.any?
29
30 query_list.each do |query|
31 found_facts = search_for_facts(query, loaded_fact)
32 matched_facts << found_facts
33 end
34
35 matched_facts.flatten(1)
36 end
37
38 def no_user_query(loaded_facts)
39 searched_facts = []
40 loaded_facts.each do |loaded_fact|
41 searched_facts << SearchedFact.new(loaded_fact.name, loaded_fact.klass, '', loaded_fact.type)
42 end
43 searched_facts
44 end
45
46 def search_for_facts(query, loaded_fact_hash)
47 resolvable_fact_list = []
48 query = query.to_s
49 query_tokens = query.end_with?('.*') ? [query] : query.split('.')
50 size = query_tokens.size
51
52 size.times do |i|
53 query_token_range = 0..size - i - 1
54 resolvable_fact_list = get_facts_matching_tokens(query_tokens, query_token_range, loaded_fact_hash)
55
56 return resolvable_fact_list if resolvable_fact_list.any?
57 end
58
59 resolvable_fact_list << SearchedFact.new(query, nil, query, :nil) if resolvable_fact_list.empty?
60
61 resolvable_fact_list
62 end
63
64 def get_facts_matching_tokens(query_tokens, query_token_range, loaded_fact_hash)
65 resolvable_fact_list = []
66
67 loaded_fact_hash.each do |loaded_fact|
68 query_fact = query_tokens[query_token_range].join('.')
69
70 next unless found_fact?(loaded_fact.name, query_fact)
71
72 searched_fact = construct_loaded_fact(query_tokens, loaded_fact)
73 resolvable_fact_list << searched_fact
74 end
75
76 @log.debug "List of resolvable facts: #{resolvable_fact_list.inspect}" if resolvable_fact_list.any?
77 resolvable_fact_list
78 end
79
80 def found_fact?(fact_name, query_fact)
81 fact_with_wildcard = fact_name.include?('.*') && !query_fact.include?('.')
82
83 processed_equery_fact = query_fact.gsub('\\', '\\\\\\\\')
84
85 return false if fact_with_wildcard && !query_fact.match("^#{fact_name}$")
86
87 return false unless fact_with_wildcard || fact_name.match("^#{processed_equery_fact}($|\\.)")
88
89 true
90 end
91
92 def construct_loaded_fact(query_tokens, loaded_fact)
93 user_query = @query_list.any? ? query_tokens.join('.') : ''
94 fact_name = loaded_fact.name.to_s
95 klass_name = loaded_fact.klass
96 type = loaded_fact.type
97 sf = SearchedFact.new(fact_name, klass_name, user_query, type)
98 sf.file = loaded_fact.file
99
100 sf
101 end
102 end
103 end
104 end
0 # frozen_string_literal: true
1
2 module Facter
3 class FactCollection < Hash
4 def initialize
5 super
6 @log = Log.new(self)
7 end
8
9 def to_yaml
10 deep_to_h.to_yaml
11 end
12
13 # Transorms a list of {Facter::ResolvedFact} into a nested collection.
14 # @param facts [Array<Facter::ResolvedFact>]
15 #
16 # @return [FactCollection]
17 #
18 # @api private
19 def build_fact_collection!(facts)
20 facts.each do |fact|
21 next if %i[core legacy].include?(fact.type) && fact.value.nil?
22
23 bury_fact(fact)
24 end
25
26 self
27 end
28
29 def dig_fact(user_query)
30 value(user_query)
31 rescue KeyError, TypeError
32 nil
33 end
34
35 # Collection#fetch implementation for nested collections.
36 # @param user_query [String] the search terms, separated by "."
37 #
38 # @return [String]
39 #
40 # @example for fact_collection = { "os": { "name": "Darwin" } }
41 # fact_collection.fetch("os.name") #=> "Darwin"
42 #
43 # @api private
44 def value(user_query)
45 fetch(user_query) do
46 split_user_query = Facter::Utils.split_user_query(user_query)
47 split_user_query.reduce(self) do |memo, key|
48 raise KeyError unless memo.respond_to?(:fetch)
49
50 memo.fetch(key) { memo.fetch(key.to_s) }
51 end
52 end
53 end
54
55 def bury(*args)
56 raise ArgumentError, '2 or more arguments required' if args.count < 2
57
58 if args.count == 2
59 self[args[0]] = args[1]
60 else
61 arg = args.shift
62 self[arg] = FactCollection.new unless self[arg]
63 self[arg].bury(*args) unless args.empty?
64 end
65
66 self
67 end
68
69 private
70
71 def deep_to_h(collection = self)
72 collection.each_pair.with_object({}) do |(key, value), hash|
73 hash[key] = value.is_a?(FactCollection) ? deep_to_h(value) : value
74 end
75 end
76
77 def bury_fact(fact)
78 split_fact_name = extract_fact_name(fact)
79 bury(*split_fact_name << fact.value)
80 rescue NoMethodError
81 @log.error("#{fact.type.to_s.capitalize} fact `#{fact.name}` cannot be added to collection."\
82 ' The format of this fact is incompatible with other'\
83 " facts that belong to `#{fact.name.split('.').first}` group")
84 end
85
86 def extract_fact_name(fact)
87 case fact.type
88 when :legacy
89 [fact.name]
90 when :custom, :external
91 Options[:force_dot_resolution] == true ? fact.name.split('.') : [fact.name]
92 else
93 fact.name.split('.')
94 end
95 end
96 end
97 end
0 # frozen_string_literal: true
1
2 module Facter
3 class LoadedFact
4 attr_reader :name, :klass, :type
5 attr_accessor :file, :is_env
6
7 def initialize(name, klass, type = nil, file = nil)
8 @name = name
9 @klass = klass
10 @type = type.nil? ? :core : type
11 @file = file
12 @is_env = false
13 end
14 end
15 end
0 # frozen_string_literal: true
1
2 module Facter
3 class ResolvedFact
4 attr_reader :name, :type
5 attr_accessor :user_query, :value, :file
6
7 def initialize(name, value = '', type = :core, user_query = nil)
8 @name = name
9 @value = Utils.deep_stringify_keys(value)
10 @type = type
11 @user_query = user_query
12 end
13
14 def legacy?
15 type == :legacy
16 end
17
18 def core?
19 type == :core
20 end
21
22 def to_s
23 @value.to_s
24 end
25
26 def resolves?(user_query)
27 @name == user_query
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facter
3 class SearchedFact
4 attr_reader :name, :fact_class, :user_query, :type
5 attr_accessor :file
6
7 def initialize(fact_name, fact_class, user_query, type)
8 @name = fact_name
9 @fact_class = fact_class
10 @user_query = user_query
11 @type = type
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 require 'sys/filesystem'
3
4 module Sys
5 class Filesystem
6 module Structs
7 class Statvfs < FFI::Struct
8 # We must remove the instance variable layout defined by sys-filesystem, because setting
9 # it the second time will make FFI log a warning message.
10 remove_instance_variable(:@layout) if @layout
11
12 if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i
13 layout(
14 :f_bsize, :ulong,
15 :f_frsize, :ulong,
16 :f_blocks, :uint,
17 :f_bfree, :uint,
18 :f_bavail, :uint,
19 :f_files, :uint,
20 :f_ffree, :uint,
21 :f_favail, :uint,
22 :f_fsid, :ulong,
23 :f_flag, :ulong,
24 :f_namemax, :ulong
25 )
26 elsif RbConfig::CONFIG['host'] =~ /bsd/i
27 layout(
28 :f_bavail, :uint64,
29 :f_bfree, :uint64,
30 :f_blocks, :uint64,
31 :f_favail, :uint64,
32 :f_ffree, :uint64,
33 :f_files, :uint64,
34 :f_bsize, :ulong,
35 :f_flag, :ulong,
36 :f_frsize, :ulong,
37 :f_fsid, :ulong,
38 :f_namemax, :ulong
39 )
40 elsif RbConfig::CONFIG['host'] =~ /sunos|solaris/i
41 layout(
42 :f_bsize, :ulong,
43 :f_frsize, :ulong,
44 :f_blocks, :uint64_t,
45 :f_bfree, :uint64_t,
46 :f_bavail, :uint64_t,
47 :f_files, :uint64_t,
48 :f_ffree, :uint64_t,
49 :f_favail, :uint64_t,
50 :f_fsid, :ulong,
51 :f_basetype, [:char, 16],
52 :f_flag, :ulong,
53 :f_namemax, :ulong,
54 :f_fstr, [:char, 32],
55 :f_filler, [:ulong, 16]
56 )
57 elsif RbConfig::CONFIG['host'] =~ /i686/i
58 layout(
59 :f_bsize, :ulong,
60 :f_frsize, :ulong,
61 :f_blocks, :uint,
62 :f_bfree, :uint,
63 :f_bavail, :uint,
64 :f_files, :uint,
65 :f_ffree, :uint,
66 :f_favail, :uint,
67 :f_fsid, :ulong,
68 :f_flag, :ulong,
69 :f_namemax, :ulong,
70 :f_spare, [:int, 6]
71 )
72 else
73 layout(
74 :f_bsize, :ulong,
75 :f_frsize, :ulong,
76 :f_blocks, :uint64,
77 :f_bfree, :uint64,
78 :f_bavail, :uint64,
79 :f_files, :uint64,
80 :f_ffree, :uint64,
81 :f_favail, :uint64,
82 :f_fsid, :ulong,
83 :f_flag, :ulong,
84 :f_namemax, :ulong,
85 :f_spare, [:int, 6]
86 )
87 end
88 end
89 end
90 end
91 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class AioAgentVersion < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_agent_version }
12 end
13
14 def read_agent_version
15 aio_agent_version = Facter::Util::FileHelper.safe_read('/opt/puppetlabs/puppet/VERSION', nil)&.chomp
16 aio_agent_version = aio_agent_version&.match(/^\d+\.\d+\.\d+(\.\d+){0,2}/)&.to_s
17 @fact_list[:aio_agent_version] = aio_agent_version
18 end
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Architecture < BaseResolver
5 # :architecture
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_architecture(fact_name) }
13 end
14
15 def read_architecture(fact_name)
16 require 'facter/util/aix/odm_query'
17
18 proc_number = read_proc
19 odmquery = Facter::Util::Aix::ODMQuery.new
20 odmquery
21 .equals('name', proc_number)
22 .equals('attribute', 'type')
23
24 result = odmquery.execute
25
26 return unless result
27
28 result.each_line do |line|
29 if line.include?('value')
30 @fact_list[:architecture] = line.split('=')[1].strip.delete('\"')
31 break
32 end
33 end
34
35 @fact_list[fact_name]
36 end
37
38 def read_proc
39 odmquery = Facter::Util::Aix::ODMQuery.new
40 odmquery
41 .equals('PdDvLn', 'processor/sys/proc_rspc')
42 .equals('status', '1')
43
44 result = odmquery.execute
45 result.each_line do |line|
46 return line.split('=')[1].strip.delete('\"') if line.include?('name')
47 end
48 end
49 end
50 end
51 end
52 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Disks < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { execute_lspv(fact_name) }
13 end
14
15 def execute_lspv(fact_name)
16 result = Facter::Core::Execution.execute('lspv', logger: log)
17
18 return if result.empty?
19
20 @fact_list[:disks] = {}
21
22 result.each_line do |line|
23 disk_name = line.split(' ')[0].strip
24 size = find_size(disk_name)
25 @fact_list[:disks][disk_name] = size if size
26 end
27
28 @fact_list[fact_name]
29 end
30
31 def find_size(name)
32 stdout = Facter::Core::Execution.execute("lspv #{name}", logger: log)
33
34 return if stdout.empty?
35
36 info_size = Facter::Util::Aix::InfoExtractor.extract(stdout, :lspv)
37
38 return unless info_size['PV STATE']
39
40 size_bytes = compute_size(info_size)
41
42 {
43 size_bytes: size_bytes,
44 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes)
45 }
46 end
47
48 def compute_size(size_hash)
49 physical_partitions = size_hash['TOTAL PPs'].to_i + size_hash['FREE PPs'].to_i
50 size_physical_partition = size_hash['PP SIZE']
51 exp = if size_physical_partition[/mega/]
52 Facter::Util::Aix::InfoExtractor::MEGABYTES_EXPONENT
53 else
54 Facter::Util::Aix::InfoExtractor::GIGABYTES_EXPONENT
55 end
56 size_physical_partition.to_i * physical_partitions * exp
57 end
58 end
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 module FFI
6 RTM_IFINFO = 0xe
7 RTM_NEWADDR = 0xc
8
9 RTAX_DST = 0
10 RTAX_GATEWAY = 1
11 RTAX_NETMASK = 2
12 RTAX_GENMASK = 3
13 RTAX_IFP = 4
14 RTAX_IFA = 5
15 RTAX_AUTHOR = 6
16 RTAX_BRD = 7
17 RTAX_MAX = 8
18
19 RTA_DST = 0x1
20 RTA_GATEWAY = 0x2
21 RTA_NETMASK = 0x4
22 RTA_GENMASK = 0x8
23 RTA_IFP = 0x10
24 RTA_IFA = 0x20
25 RTA_AUTHOR = 0x40
26 RTA_BRD = 0x80
27 RTA_DOWNSTREAM = 0x100
28
29 AF_UNSPEC = 0
30 AF_INET = 2
31 AF_INET6 = 24
32 AF_MAX = 30
33
34 INET_ADDRSTRLEN = 16
35 INET6_ADDRSTRLEN = 46
36
37 RTAX_LIST = [
38 RTAX_DST,
39 RTAX_GATEWAY,
40 RTAX_NETMASK,
41 RTAX_GENMASK,
42 RTAX_IFP,
43 RTAX_IFA,
44 RTAX_AUTHOR,
45 RTAX_BRD
46 ].freeze
47
48 RTA_LIST = [
49 RTA_DST,
50 RTA_GATEWAY,
51 RTA_NETMASK,
52 RTA_GENMASK,
53 RTA_IFP,
54 RTA_IFA,
55 RTA_AUTHOR,
56 RTA_BRD,
57 RTA_DOWNSTREAM
58 ].freeze
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3 require_relative 'structs'
4 require_relative 'ffi'
5 module Facter
6 module Resolvers
7 module Aix
8 module FfiHelper
9 KINFO_GET_AVENRUN = 1
10 KINFO_READ = 8 << 8
11 KINFO_RT = 1 << 8
12 KINFO_RT_IFLIST = KINFO_RT | 3
13
14 module Libc
15 extend ::FFI::Library
16
17 RTLD_LAZY = 0x00000004
18 RTLD_GLOBAL = 0x00010000
19 RTLD_MEMBER = 0x00040000
20
21 @ffi_lib_flags = RTLD_LAZY | RTLD_GLOBAL | RTLD_MEMBER
22 ffi_lib 'libc.a(shr.o)'
23
24 attach_function :getkerninfo, %i[int pointer pointer int], :int
25 attach_function :inet_ntop, %i[int pointer pointer uint], :string
26 end
27
28 def self.read_load_averages
29 averages = ::FFI::MemoryPointer.new(:long_long, 3)
30 averages_size = ::FFI::MemoryPointer.new(:int, 1)
31 averages_size.write_int(averages.size)
32
33 return if Libc.getkerninfo(KINFO_READ | KINFO_GET_AVENRUN, averages, averages_size, 0).negative?
34
35 averages.read_array_of_long_long(3).map { |x| (x / 65_536.0) }
36 end
37
38 def self.read_interfaces
39 ksize = Libc.getkerninfo(KINFO_RT_IFLIST, nil, nil, 0)
40
41 log.debug('getkerninfo call was unsuccessful') if ksize.zero?
42
43 ksize_ptr = ::FFI::MemoryPointer.new(:int, ksize.size)
44 ksize_ptr.write_int(ksize)
45
46 result_ptr = ::FFI::MemoryPointer.new(:char, ksize)
47
48 res = Libc.getkerninfo(KINFO_RT_IFLIST, result_ptr, ksize_ptr, 0)
49 log.debug('getkerninfo call was unsuccessful') if res == -1
50
51 cursor = 0
52
53 interfaces = {}
54 while cursor < ksize_ptr.read_int
55 hdr = FFI::IfMsghdr.new(result_ptr + cursor)
56
57 case hdr[:ifm_type]
58 when FFI::RTM_IFINFO
59 link_addr = FFI::SockaddrDl.new(hdr.to_ptr + hdr.size)
60
61 interface_name = link_addr[:sdl_data].to_s[0, link_addr[:sdl_nlen]]
62 interfaces[interface_name] ||= {}
63
64 when FFI::RTM_NEWADDR
65 addresses = {}
66 addr_cursor = cursor + hdr.size
67 FFI::RTAX_LIST.each do |key|
68 xand = hdr[:ifm_addrs] & FFI::RTA_LIST[key]
69 next unless xand != 0
70
71 sockaddr = FFI::Sockaddr.new(result_ptr + addr_cursor)
72 addresses[key] = sockaddr
73 roundup_nr = roundup(sockaddr)
74 addr_cursor += roundup_nr
75 end
76
77 family = FFI::AF_UNSPEC
78
79 addresses.each do |_k, addr|
80 if family != FFI::AF_UNSPEC &&
81 addr[:sa_family] != FFI::AF_UNSPEC &&
82 family != addr[:sa_family]
83 family = FFI::AF_MAX
84 break
85 end
86 family = addr[:sa_family]
87 end
88
89 if addresses[FFI::RTAX_NETMASK][:sa_len]
90 addresses[FFI::RTAX_NETMASK][:sa_family] = family
91 netmask = address_to_string(addresses[FFI::RTAX_NETMASK])
92 end
93
94 address = address_to_string(addresses[FFI::RTAX_IFA]) if addresses[FFI::RTAX_IFA][:sa_len]
95
96 if addresses[FFI::RTAX_NETMASK][:sa_len] && addresses[FFI::RTAX_IFA][:sa_len]
97 network = address_to_string(addresses[FFI::RTAX_IFA], addresses[FFI::RTAX_NETMASK])
98 end
99
100 bindings = family == FFI::AF_INET ? :bindings : :bindings6
101 interfaces[interface_name][bindings] ||= []
102 interfaces[interface_name][bindings] << {
103 netmask: netmask.read_string,
104 address: address.read_string,
105 network: network.read_string
106 }
107 else
108 log.debug("got an unknown RT_IFLIST message: #{hdr[:ifm_type]}")
109 end
110
111 cursor += hdr[:ifm_msglen]
112 end
113
114 interfaces
115 end
116
117 def self.roundup(sockaddr)
118 if sockaddr[:sa_len].positive?
119 1 + ((sockaddr[:sa_len] - 1) | (1.size - 1))
120 else
121 1.size
122 end
123 end
124
125 def self.address_to_string(sockaddr, mask = nil)
126 if sockaddr[:sa_family] == FFI::AF_INET
127 in_addr_ip = FFI::SockaddrIn.new(sockaddr.to_ptr)
128
129 if mask && sockaddr[:sa_family] == mask[:sa_family]
130 in_addr_mask = FFI::SockaddrIn.new(mask.to_ptr)
131 in_addr_ip[:sin_addr][:s_addr] &= in_addr_mask[:sin_addr][:s_addr]
132 end
133
134 buffer = ::FFI::MemoryPointer.new(:char, FFI::INET_ADDRSTRLEN)
135 Libc.inet_ntop(FFI::AF_INET, in_addr_ip[:sin_addr].to_ptr, buffer, 16)
136
137 buffer
138 elsif sockaddr[:sa_family] == FFI::AF_INET6
139 in_addr_ip = FFI::SockaddrIn6.new(sockaddr.to_ptr)
140 if mask && sockaddr[:sa_family] == mask[:sa_family]
141 in_addr_mask = FFI::SockaddrIn6.new(sockaddr.to_ptr)
142 16.times do |i|
143 in_addr_ip[:sin6_addr][:u6_addr8][i] &= in_addr_mask[:sin6_addr][:u6_addr8][i]
144 end
145 end
146
147 buffer = ::FFI::MemoryPointer.new(:char, FFI::INET6_ADDRSTRLEN)
148 Libc.inet_ntop(FFI::AF_INET6, in_addr_ip[:sin6_addr].to_ptr, buffer, 16)
149
150 buffer
151 end
152 end
153
154 def self.log
155 @log ||= Log.new(self)
156 end
157 end
158 end
159 end
160 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 module FFI
6 class SockaddrDl < ::FFI::Struct
7 layout :sdl_len, :uchar,
8 :sdl_family, :uchar,
9 :sdl_index, :ushort,
10 :sdl_type, :uchar,
11 :sdl_nlen, :uchar,
12 :sdl_alen, :uchar,
13 :sdl_slen, :uchar,
14 :sdl_data, [:char, 120]
15 end
16
17 class IfMsghdr < ::FFI::Struct
18 layout :ifm_msglen, :ushort,
19 :ifm_version, :uchar,
20 :ifm_type, :uchar,
21 :ifm_addrs, :int,
22 :ifm_flags, :int,
23 :ifm_index, :ushort,
24 :ifm_addrlen, :uchar
25 end
26
27 class Sockaddr < ::FFI::Struct
28 layout :sa_len, :uchar,
29 :sa_family, :uchar,
30 :sa_data, [:char, 14]
31 end
32
33 class InAddr < ::FFI::Struct
34 layout :s_addr, :uint
35 end
36
37 class In6Addr < ::FFI::Struct
38 layout :u6_addr8, [:uchar, 16]
39 end
40
41 class SockaddrIn < ::FFI::Struct
42 layout :sin_len, :uchar,
43 :sin_family, :uchar,
44 :sin_port, :ushort,
45 :sin_addr, InAddr,
46 :sin_zero, [:uchar, 8]
47 end
48
49 class SockaddrIn6 < ::FFI::Struct
50 layout :sin6_len, :uchar,
51 :sin6_family, :uchar,
52 :sin6_port, :ushort,
53 :sin6_flowinfo, :uint,
54 :sin6_addr, In6Addr,
55 :sin6_scope_id, :uint
56 end
57
58 class SockaddrStorage < ::FFI::Struct
59 layout :ss_len, :uchar,
60 :ss_family, :uchar,
61 :ss_pad, [:char, 6],
62 :ss_align, :long_long,
63 :ss_pad2, [:char, 1264]
64 end
65 end
66 end
67 end
68 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Filesystem < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_vtf_file(fact_name) }
13 end
14
15 def read_vtf_file(fact_name)
16 file_content = Facter::Util::FileHelper.safe_readlines('/etc/vfs')
17 return if file_content.empty?
18
19 file_content = file_content.map do |line|
20 next if line =~ /#|%/ # skip lines that are comments or defaultvfs line
21
22 line.split(' ').first
23 end
24
25 @fact_list[:file_systems] = file_content.compact.sort.join(',')
26 @fact_list[fact_name]
27 end
28 end
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Hardware < BaseResolver
5 # :hardware
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_hardware(fact_name) }
13 end
14
15 def read_hardware(fact_name)
16 require 'facter/util/aix/odm_query'
17 odmquery = Facter::Util::Aix::ODMQuery.new
18 odmquery
19 .equals('name', 'sys0')
20 .equals('attribute', 'modelname')
21
22 result = odmquery.execute
23
24 return unless result
25
26 result.each_line do |line|
27 if line.include?('value')
28 @fact_list[:hardware] = line.split('=')[1].strip.delete('\"')
29 break
30 end
31 end
32
33 @fact_list[fact_name]
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class LoadAverages < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_load_averages(fact_name) }
13 end
14
15 def read_load_averages(fact_name)
16 require_relative 'ffi/ffi_helper'
17 @fact_list[:load_averages] = {}.tap do |h|
18 h['1m'], h['5m'], h['15m'] = FfiHelper.read_load_averages
19 end
20
21 @fact_list[fact_name]
22 end
23 end
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Memory < BaseResolver
6 init_resolver
7 # :hardware
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { execute_svmon(fact_name) }
14 end
15
16 def execute_svmon(fact_name)
17 result = Facter::Core::Execution.execute('svmon', logger: log)
18 return if result.empty?
19
20 pagesize = call_pagesize.to_i
21 return if pagesize.zero?
22
23 @fact_list[:system] = @fact_list[:swap] = {}
24
25 result.each_line do |line|
26 @fact_list[:system] = populate_system(line, pagesize) if line.include?('memory')
27 @fact_list[:swap] = populate_swap(line, pagesize) if line =~ /pg\sspace/
28 end
29
30 @fact_list[fact_name]
31 end
32
33 def call_pagesize
34 Facter::Core::Execution.execute('pagesize', logger: log).strip
35 end
36
37 def populate_system(content, pagesize)
38 content = content.split(' ')
39
40 total = content[1].to_i * pagesize
41 used = content[2].to_i * pagesize
42
43 { available_bytes: content[3].to_i * pagesize,
44 total_bytes: total,
45 used_bytes: used,
46 capacity: Facter::Util::Resolvers::FilesystemHelper.compute_capacity(used, total) }
47 end
48
49 def populate_swap(content, pagesize)
50 content = content.split(' ')
51
52 total = content[2].to_i * pagesize
53 used = content[3].to_i * pagesize
54
55 { available_bytes: total - used,
56 total_bytes: total,
57 used_bytes: used,
58 capacity: Facter::Util::Resolvers::FilesystemHelper.compute_capacity(used, total) }
59 end
60 end
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Mountpoints < BaseResolver
6 init_resolver
7
8 BLOCK_SIZE = 512
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_mount(fact_name) }
15 end
16
17 def read_mount(fact_name)
18 @fact_list[:mountpoints] = {}
19 output = Facter::Core::Execution.execute('mount', logger: log)
20 output.split("\n").drop(2).map do |line|
21 next if line =~ /procfs|ahafs/
22
23 add_mount_points_fact(line)
24 end
25
26 retrieve_sizes_for_mounts
27 @fact_list[fact_name]
28 end
29
30 def add_mount_points_fact(line)
31 elem = line.split("\s")
32
33 elem.shift unless line[0] == ' '
34
35 @fact_list[:mountpoints][elem[1]] = { device: elem[0], filesystem: elem[2],
36 options: elem.last.include?(':') ? [] : elem.last.split(',') }
37 end
38
39 def retrieve_sizes_for_mounts
40 output = Facter::Core::Execution.execute('df -P', logger: log)
41 output.split("\n").drop(1).map do |line|
42 next if line =~ /-\s+-\s+-/
43
44 mount_info = line.split("\s")
45 mount_info[3] = translate_to_bytes(mount_info[3])
46 mount_info[2] = translate_to_bytes(mount_info[2])
47 mount_info[1] = translate_to_bytes(mount_info[1])
48 compute_sizes(mount_info)
49 end
50 end
51
52 def translate_to_bytes(strin_size)
53 strin_size.to_i * BLOCK_SIZE
54 end
55
56 def compute_sizes(info)
57 available_bytes = info[3]
58 used_bytes = info[2]
59 size_bytes = info[1]
60 @fact_list[:mountpoints][info.last].merge!(
61 capacity: Facter::Util::Resolvers::FilesystemHelper.compute_capacity(used_bytes, size_bytes),
62 available_bytes: available_bytes,
63 used_bytes: used_bytes,
64 size_bytes: size_bytes,
65 available: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(available_bytes),
66 used: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(used_bytes),
67 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes)
68 )
69 end
70 end
71 end
72 end
73 end
74 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Networking < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_netstat(fact_name) }
13 end
14
15 def read_netstat(fact_name)
16 @fact_list[:interfaces] = {}
17 output = Facter::Core::Execution.execute('netstat -rn', logger: log)
18 output = output.each_line.select { |line| (line =~ /\s\s[0-9]+.[0-9]+.[0-9]+.[0-9]+|\s\s.*:[0-9a-f]+/) }
19 @fact_list[:interfaces] = load_interfaces
20
21 populate_with_mtu_and_mac!(@fact_list[:interfaces])
22 get_primary_interface_info(output)
23 Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
24 @fact_list[fact_name]
25 end
26
27 def get_primary_interface_info(output)
28 primary_interface_info = output.find { |line| line =~ /default/ }&.split(' ')
29 @fact_list[:primary_interface] = primary_interface_info[5] if primary_interface_info
30 end
31
32 def build_bindings(name, ip, mask_length, is_ipv4)
33 bind_to_add = is_ipv4 ? :bindings : :bindings6
34 ip = ip.gsub(/%[0-9]$/, '') # remove mask information if it exists
35 mask_length = mask_length.to_i - 1 unless is_ipv4
36 @fact_list[:interfaces][name] ||= {}
37 @fact_list[:interfaces][name][bind_to_add] ||= []
38 @fact_list[:interfaces][name][bind_to_add] << Facter::Util::Resolvers::Networking
39 .build_binding(ip, mask_length)
40 end
41
42 def populate_with_mtu_and_mac!(interfaces)
43 output = Facter::Core::Execution.execute('netstat -in', logger: log)
44 output.each_line do |line|
45 next if line =~ /Name\s/
46
47 info = line.split("\s")
48 interface_name = info[0]
49 mac = info[3][/^([0-9a-f]{1,2}[\.:-]){5}([0-9a-f]{1,2})$/]
50 interfaces[interface_name][:mtu] = info[1].to_i
51 interfaces[interface_name][:mac] = format_mac_address(mac) if mac
52 end
53 end
54
55 def format_mac_address(address)
56 address.split('.').map { |e| format('%<mac_address>02s', mac_address: e) }.join(':').tr(' ', '0')
57 end
58
59 def load_interfaces
60 require_relative 'ffi/ffi_helper'
61
62 FfiHelper.read_interfaces
63 end
64 end
65 end
66 end
67 end
68 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Nim < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_niminfo(fact_name) }
13 end
14
15 def read_niminfo(fact_name)
16 output = Facter::Util::FileHelper.safe_read('/etc/niminfo', nil)
17
18 return unless output
19
20 type = /NIM_CONFIGURATION=(.*)/.match(output)
21 @fact_list[:type] = type[1] if type[1] && /master|standalone/.match?(type[1])
22
23 @fact_list[fact_name]
24 end
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class OsLevel < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_oslevel(fact_name) }
13 end
14
15 def read_oslevel(fact_name)
16 output = Facter::Core::Execution.execute('/usr/bin/oslevel -s', logger: log)
17 @fact_list[:build] = output unless output.empty?
18 @fact_list[:kernel] = 'AIX'
19
20 @fact_list[fact_name]
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Partitions < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { query_cudv(fact_name) }
13 end
14
15 def query_cudv(fact_name)
16 odmquery = Facter::Util::Aix::ODMQuery.new
17 odmquery.equals('PdDvLn', 'logical_volume/lvsubclass/lvtype')
18
19 result = odmquery.execute
20
21 return unless result
22
23 @fact_list[:partitions] = {}
24
25 result.each_line do |line|
26 next unless line.include?('name')
27
28 part_name = line.split('=')[1].strip.delete('"')
29 part = "/dev/#{part_name}"
30 info = populate_from_lslv(part_name)
31 @fact_list[:partitions][part] = info if info
32 end
33
34 @fact_list[fact_name]
35 end
36
37 def populate_from_lslv(name)
38 stdout = Facter::Core::Execution.execute("lslv -L #{name}", logger: log)
39
40 return if stdout.empty?
41
42 info_hash = Facter::Util::Aix::InfoExtractor.extract(stdout, :lslv)
43 size_bytes = compute_size(info_hash)
44
45 part_info = {
46 filesystem: info_hash['TYPE'],
47 size_bytes: size_bytes,
48 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes)
49 }
50 mount = info_hash['MOUNT POINT']
51 label = info_hash['LABEL']
52 part_info[:mount] = mount unless %r{N/A} =~ mount
53 part_info[:label] = label.strip unless /None/ =~ label
54 part_info
55 end
56
57 def compute_size(info_hash)
58 physical_partitions = info_hash['PPs'].to_i
59 size_physical_partition = info_hash['PP SIZE']
60 exp = if size_physical_partition[/mega/]
61 Facter::Util::Aix::InfoExtractor::MEGABYTES_EXPONENT
62 else
63 Facter::Util::Aix::InfoExtractor::GIGABYTES_EXPONENT
64 end
65 size_physical_partition.to_i * physical_partitions * exp
66 end
67 end
68 end
69 end
70 end
71 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Processors < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { query_pddv(fact_name) }
13 end
14
15 def query_pddv(fact_name)
16 @fact_list[:models] = []
17 @fact_list[:logical_count] = 0
18 @fact_list[:cores_per_socket] = 0
19 @fact_list[:threads_per_core] = 0
20
21 odmquery = Facter::Util::Aix::ODMQuery.new
22 odmquery.equals('class', 'processor')
23
24 result = odmquery.execute
25
26 return unless result
27
28 proc_names = retrieve_from_array(result.scan(/uniquetype\s=\s.*/), 1)
29
30 proc_names.each { |name| populate_from_cudv(name) }
31
32 @fact_list[fact_name]
33 end
34
35 def populate_from_cudv(name)
36 odmquery = Facter::Util::Aix::ODMQuery.new
37 odmquery.equals('PdDvLn', name)
38
39 result = odmquery.execute
40
41 return unless result
42
43 names = retrieve_from_array(result.scan(/name\s=\s.*/), 1)
44 status = retrieve_from_array(result.scan(/\s+status\s=\s.*/), 1)
45
46 names.each_with_index { |elem, idx| query_cuat(elem) if status[idx] == '1' }
47 end
48
49 def query_cuat(name)
50 odmquery = Facter::Util::Aix::ODMQuery.new
51 odmquery.equals('name', name)
52
53 result = odmquery.execute
54
55 return unless result
56
57 type, frequency, smt_threads, smt_enabled = process(result)
58
59 @fact_list[:speed] ||= frequency if frequency
60
61 threads = smt_enabled ? smt_threads : 1
62
63 @fact_list[:logical_count] += threads
64 @fact_list[:cores_per_socket] += 1
65 @fact_list[:threads_per_core] = threads
66 @fact_list[:models].concat([type] * threads)
67 end
68
69 def process(stdout)
70 type = retrieve_from_array(stdout.scan(/attribute\s=\s"type"\n\s+value\s=\s.*/), 2).first
71 frequency = retrieve_from_array(stdout.scan(/attribute\s=\s"frequency"\n\s+value\s=\s.*/), 2).first
72 smt_threads = retrieve_from_array(stdout.scan(/attribute\s=\s"smt_threads"\n\s+value\s=\s.*/), 2).first
73 smt_enabled = retrieve_from_array(stdout.scan(/attribute\s=\s"smt_enabled"\n\s+value\s=\s.*/), 2).first
74 [type, frequency.to_i, smt_threads.to_i, smt_enabled]
75 end
76
77 def retrieve_from_array(array, pos)
78 array.map { |elem| elem.split('=')[pos].strip.delete('"') }
79 end
80 end
81 end
82 end
83 end
84 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Aix
5 class Serialnumber < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_serialnumber(fact_name) }
13 end
14
15 def read_serialnumber(fact_name)
16 odmquery = Facter::Util::Aix::ODMQuery.new
17 odmquery
18 .equals('name', 'sys0')
19 .equals('attribute', 'systemid')
20 result = odmquery.execute
21
22 result.each_line do |line|
23 if line.include?('value')
24 @fact_list[:serialnumber] = line.split('=')[1].strip.delete('\"')[6..-1]
25 break
26 end
27 end
28
29 @fact_list[fact_name]
30 end
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Augeas < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_augeas_version(fact_name) }
12 end
13
14 def read_augeas_version(fact_name)
15 @fact_list[:augeas_version] = read_augeas_from_cli
16 @fact_list[:augeas_version] ||= read_augeas_from_gem
17
18 @fact_list[fact_name]
19 end
20
21 def read_augeas_from_cli
22 command = if File.readable?('/opt/puppetlabs/puppet/bin/augparse')
23 '/opt/puppetlabs/puppet/bin/augparse'
24 else
25 'augparse'
26 end
27
28 output = Facter::Core::Execution.execute("#{command} --version 2>&1", logger: log)
29 Regexp.last_match(1) if output =~ /^augparse (\d+\.\d+\.\d+)/
30 end
31
32 def read_augeas_from_gem
33 require 'augeas'
34
35 if Gem.loaded_specs['augeas']
36 ::Augeas.create { |aug| aug.get('/augeas/version') }
37 else
38 # it is used for legacy augeas <= 0.5.0 (ruby-augeas gem)
39 ::Augeas.open { |aug| aug.get('/augeas/version') }
40 end
41 end
42 end
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Az < BaseResolver
5 init_resolver
6
7 AZ_METADATA_URL = 'http://169.254.169.254/metadata/instance?api-version=2020-09-01'
8 AZ_SESSION_TIMEOUT = 5
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 log.debug('Querying Az metadata')
15 @fact_list.fetch(fact_name) { read_facts(fact_name) }
16 end
17
18 def read_facts(fact_name)
19 @fact_list[:metadata] = {}
20 data = get_data_from(AZ_METADATA_URL)
21 @fact_list[:metadata] = JSON.parse(data) unless data.empty?
22
23 @fact_list[fact_name]
24 end
25
26 def get_data_from(url)
27 headers = { Metadata: 'true' }
28 Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout })
29 end
30
31 def determine_session_timeout
32 session_env = ENV['AZ_SESSION_TIMEOUT']
33 session_env ? session_env.to_i : AZ_SESSION_TIMEOUT
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class BaseResolver
5 def self.log
6 @log ||= Log.new(self)
7 end
8
9 def self.invalidate_cache
10 @fact_list = {}
11 end
12
13 def self.init_resolver
14 @fact_list = {}
15 @semaphore = Mutex.new
16 end
17
18 def self.subscribe_to_manager
19 Facter::SessionCache.subscribe(self)
20 end
21
22 def self.resolve(fact_name, options = {})
23 @semaphore.synchronize do
24 subscribe_to_manager
25 post_resolve(fact_name, options)
26
27 cache_nil_for_unresolved_facts(fact_name)
28 end
29 rescue NoMethodError => e
30 log.debug("Could not resolve #{fact_name}, got #{e} at #{e.backtrace[0]}")
31 @fact_list[fact_name] = nil
32 rescue LoadError, NameError => e
33 log.debug("Resolving fact #{fact_name}, but got #{e} at #{e.backtrace[0]}")
34 @fact_list[fact_name] = nil
35 end
36
37 def self.cache_nil_for_unresolved_facts(fact_name)
38 @fact_list.fetch(fact_name) { @fact_list[fact_name] = nil }
39 @fact_list[fact_name]
40 end
41
42 def self.post_resolve(_fact_name, _options)
43 raise NotImplementedError, "You must implement post_resolve(fact_name, options) method in #{name}"
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3
4 module Facter
5 module Bsd
6 module FfiHelper
7 module Libc
8 extend FFI::Library
9
10 ffi_lib 'c'
11 attach_function :sysctl, %i[pointer uint pointer pointer pointer size_t], :int
12 end
13
14 def self.sysctl(type, oids)
15 name = FFI::MemoryPointer.new(:uint, oids.size)
16 name.write_array_of_uint(oids)
17 namelen = oids.size
18
19 oldp = FFI::Pointer::NULL
20 oldlenp = FFI::MemoryPointer.new(:size_t)
21
22 newp = FFI::Pointer::NULL
23 newlen = 0
24
25 if type == :string
26 res = Libc.sysctl(name, namelen, oldp, oldlenp, newp, newlen)
27 return nil if res.negative?
28 else
29 oldlenp.write(:size_t, FFI.type_size(type))
30 end
31
32 oldp = FFI::MemoryPointer.new(:uint8_t, oldlenp.read(:size_t))
33 res = Libc.sysctl(name, namelen, oldp, oldlenp, newp, newlen)
34 return nil if res.negative?
35
36 case type
37 when :string
38 oldp.read_string
39 else
40 oldp.read(type)
41 end
42 end
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Bsd
5 class Processors < BaseResolver
6 init_resolver
7 @log = Facter::Log.new(self)
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { collect_processors_info(fact_name) }
14 end
15
16 def collect_processors_info(fact_name)
17 require 'facter/resolvers/bsd/ffi/ffi_helper'
18
19 count = logical_count
20 model = processor_model
21 speed = processor_speed
22
23 @fact_list[:logical_count] = count
24 @fact_list[:models] = Array.new(count, model) if count && model
25 @fact_list[:speed] = speed * 1000 * 1000 if speed
26
27 @fact_list[fact_name]
28 end
29
30 CTL_HW = 6
31 HW_MODEL = 2
32 HW_NCPU = 3
33 HW_CPUSPEED = 12
34
35 def processor_model
36 Facter::Bsd::FfiHelper.sysctl(:string, [CTL_HW, HW_MODEL])
37 end
38
39 def logical_count
40 Facter::Bsd::FfiHelper.sysctl(:uint32_t, [CTL_HW, HW_NCPU])
41 end
42
43 def processor_speed
44 Facter::Bsd::FfiHelper.sysctl(:uint32_t, [CTL_HW, HW_CPUSPEED])
45 end
46 end
47 end
48 end
49 end
50 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Containers < BaseResolver
5 # :virtual
6 # :hypervisor
7
8 init_resolver
9
10 INFO = { 'docker' => 'id', 'lxc' => 'name' }.freeze
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { read_cgroup(fact_name) }
17 end
18
19 def read_cgroup(fact_name)
20 output_cgroup = Facter::Util::FileHelper.safe_read('/proc/1/cgroup', nil)
21 output_environ = Facter::Util::FileHelper.safe_read('/proc/1/environ', nil)
22 return unless output_cgroup && output_environ
23
24 output_docker = %r{docker/(.+)}.match(output_cgroup)
25 output_lxc = %r{^/lxc/([^/]+)}.match(output_cgroup)
26 lxc_from_environ = /container=lxc/ =~ output_environ
27
28 info, vm = extract_vm_and_info(output_docker, output_lxc, lxc_from_environ)
29 info, vm = extract_for_nspawn(output_environ) unless vm
30 @fact_list[:vm] = vm
31 @fact_list[:hypervisor] = { vm.to_sym => info } if vm
32 @fact_list[fact_name]
33 end
34
35 def extract_vm_and_info(output_docker, output_lxc, lxc_from_environ)
36 vm = nil
37 if output_docker
38 vm = 'docker'
39 info = output_docker[1]
40 end
41 vm = 'lxc' if output_lxc || lxc_from_environ
42 info = output_lxc[1] if output_lxc
43
44 [info ? { INFO[vm] => info } : {}, vm]
45 end
46
47 def extract_for_nspawn(output_environ)
48 nspawn = /container=systemd-nspawn/ =~ output_environ
49 if nspawn
50 vm = 'systemd_nspawn'
51 info = Facter::Util::FileHelper.safe_read('/etc/machine-id', nil)
52 end
53 [info ? { 'id' => info.strip } : {}, vm]
54 end
55 end
56 end
57 end
58 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class DebianVersion < BaseResolver
5 # :major
6 # :minor
7 # :full
8
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_debian_version(fact_name) }
16 end
17
18 def read_debian_version(fact_name)
19 file_content = Facter::Util::FileHelper.safe_read('/etc/debian_version')
20 @fact_list[:version] = file_content.strip unless file_content.empty?
21
22 @fact_list[fact_name]
23 end
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Disks < BaseResolver
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
10 DIR = '/sys/block'
11 FILE_PATHS = { model: 'device/model',
12 size: 'size',
13 vendor: 'device/vendor',
14 type: 'queue/rotational',
15 serial: 'false',
16 wwn: 'false' }.freeze
17
18 class << self
19 private
20
21 def post_resolve(fact_name, _options)
22 @fact_list.fetch(fact_name) do
23 return unless @fact_list.empty?
24
25 build_disks_hash
26
27 read_facts
28
29 @fact_list[:disks] = nil if @fact_list[:disks].empty?
30 @fact_list[fact_name]
31 end
32 end
33
34 def lsblk(option, disk)
35 result = Facter::Core::Execution.execute(
36 "lsblk -dn -o #{option} /dev/#{disk}", on_fail: '', timeout: 1
37 ).strip
38 result.empty? ? nil : result
39 end
40
41 def read_facts
42 FILE_PATHS.each do |key, file|
43 @fact_list[:disks].each do |disk, value|
44 file_path = File.join(DIR, disk, file)
45
46 result = if file == 'false'
47 lsblk(key, disk)
48 else
49 Facter::Util::FileHelper.safe_read(file_path, nil)&.strip
50 end
51
52 next unless result
53
54 value[key] = case key
55 when :size
56 # Linux always considers sectors to be 512 bytes long
57 # independently of the devices real block size.
58 construct_size(value, result)
59 when :type
60 result == '0' ? 'ssd' : 'hdd'
61 else
62 result
63 end
64 end
65 end
66 end
67
68 def build_disks_hash
69 valid_disks = Facter::Util::FileHelper.dir_children(DIR)
70 .select { |disk| File.readable?(File.join(DIR, disk, 'device')) }
71
72 @fact_list[:disks] = {}
73 valid_disks.each { |disk| @fact_list[:disks][disk] = {} }
74 end
75
76 def construct_size(facts, value)
77 value = value.to_i * 512
78 facts[:size_bytes] = value
79 facts[:size] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(value)
80 end
81 end
82 end
83 end
84 end
85 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class DmiBios < BaseResolver
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
10 class << self
11 # :bios_vendor
12 # :bios_date
13 # :bios_version
14 # :board_asset_tag
15 # :board_vendor
16 # :board_serial
17 # :board_name
18 # :chassis_asset_tag
19 # :chassis_type
20 # :sys_vendor
21 # :product_serial
22 # :product_name
23 # :product_uuid
24
25 private
26
27 def post_resolve(fact_name, _options)
28 @fact_list.fetch(fact_name) { read_facts(fact_name) }
29 end
30
31 def read_facts(fact_name)
32 files = %w[bios_date bios_vendor bios_version board_asset_tag board_vendor board_name
33 board_serial chassis_asset_tag chassis_type sys_vendor product_name
34 product_serial product_uuid]
35 return unless File.directory?('/sys/class/dmi')
36
37 file_content = Facter::Util::FileHelper.safe_read("/sys/class/dmi/id/#{fact_name}", nil)
38 .encode('UTF-8', invalid: :replace)
39 if files.include?(fact_name.to_s) && file_content
40 file_content = file_content.strip
41 @fact_list[fact_name] = file_content unless file_content.empty?
42 chassis_to_name(@fact_list[fact_name]) if fact_name == :chassis_type
43 end
44 @fact_list[fact_name]
45 end
46
47 def chassis_to_name(chassis_type)
48 types = ['Other', nil, 'Desktop', 'Low Profile Desktop', 'Pizza Box', 'Mini Tower', 'Tower',
49 'Portable', 'Laptop', 'Notebook', 'Hand Held', 'Docking Station', 'All in One', 'Sub Notebook',
50 'Space-Saving', 'Lunch Box', 'Main System Chassis', 'Expansion Chassis', 'SubChassis',
51 'Bus Expansion Chassis', 'Peripheral Chassis', 'Storage Chassis', 'Rack Mount Chassis',
52 'Sealed-Case PC', 'Multi-system', 'CompactPCI', 'AdvancedTCA', 'Blade', 'Blade Enclosure',
53 'Tablet', 'Convertible', 'Detachable']
54 @fact_list[:chassis_type] = types[chassis_type.to_i - 1]
55 end
56 end
57 end
58 end
59 end
60 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class DmiDecode < BaseResolver
5 init_resolver
6
7 ADDRESS_TO_VERSION = {
8 0xe8480 => 'ESXi 2.5',
9 0xe7c70 => 'ESXi 3.0',
10 0xe66c0 => 'ESXi 3.5',
11 0xe7910 => 'ESXi 3.5',
12 0xea550 => 'ESXi 4.0',
13 0xea6c0 => 'ESXi 4.0',
14 0xea2e0 => 'ESXi 4.1',
15 0xe72c0 => 'ESXi 5.0',
16 0xea0c0 => 'ESXi 5.1',
17 0xea050 => 'ESXi 5.5',
18 0xe99e0 => 'ESXi 6.0',
19 0xE9A40 => 'ESXi 6.0',
20 0xea580 => 'ESXi 6.5',
21 0xEA520 => 'ESXi 6.7',
22 0xEA490 => 'ESXi 6.7',
23 0xea5e0 => 'Fusion 8.5'
24 }.freeze
25
26 class << self
27 private
28
29 def post_resolve(fact_name, _options)
30 @fact_list.fetch(fact_name) { run_dmidecode(fact_name) }
31 end
32
33 def run_dmidecode(fact_name)
34 output = Facter::Core::Execution.execute('dmidecode', logger: log)
35
36 @fact_list[:virtualbox_version] = output.match(/vboxVer_(\S+)/)&.captures&.first
37 @fact_list[:virtualbox_revision] = output.match(/vboxRev_(\S+)/)&.captures&.first
38 @fact_list[:vmware_version] = extract_vmware_version(output)
39
40 @fact_list[fact_name]
41 end
42
43 def extract_vmware_version(output)
44 address_of_version = output.match(/Address:\s(0x[a-zA-Z0-9]*)/)&.captures&.first
45
46 ADDRESS_TO_VERSION[address_of_version&.hex]
47 end
48 end
49 end
50 end
51 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Ec2 < BaseResolver
5 init_resolver
6
7 EC2_METADATA_ROOT_URL = 'http://169.254.169.254/latest/meta-data/'
8 EC2_USERDATA_ROOT_URL = 'http://169.254.169.254/latest/user-data/'
9 EC2_SESSION_TIMEOUT = 5
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 log.debug('Querying Ec2 metadata')
16 @fact_list.fetch(fact_name) { read_facts(fact_name) }
17 end
18
19 def read_facts(fact_name)
20 @fact_list[:metadata] = {}
21 query_for_metadata(EC2_METADATA_ROOT_URL, @fact_list[:metadata])
22 @fact_list[:userdata] = get_data_from(EC2_USERDATA_ROOT_URL).strip.force_encoding('UTF-8')
23 @fact_list[fact_name]
24 end
25
26 def query_for_metadata(url, container)
27 metadata = get_data_from(url)
28 metadata.each_line do |line|
29 next if line.empty?
30
31 http_path_component = build_path_component(line)
32 next if http_path_component == 'security-credentials/'
33
34 if http_path_component.end_with?('/')
35 child = {}
36 child[http_path_component] = query_for_metadata("#{url}#{http_path_component}", child)
37 child.reject! { |key, _info| key == http_path_component }
38 name = http_path_component.chomp('/')
39 container[name] = child
40 else
41 container[http_path_component] = get_data_from("#{url}#{http_path_component}").strip
42 end
43 end
44 end
45
46 def build_path_component(line)
47 array_match = /^(\d+)=.*$/.match(line)
48 array_match ? "#{array_match[1]}/" : line.strip
49 end
50
51 def get_data_from(url)
52 headers = {}
53 headers['X-aws-ec2-metadata-token'] = v2_token if v2_token
54 Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout })
55 end
56
57 def determine_session_timeout
58 session_env = ENV['EC2_SESSION_TIMEOUT']
59 session_env ? session_env.to_i : EC2_SESSION_TIMEOUT
60 end
61
62 def v2_token
63 @v2_token ||= begin
64 token = Facter::Util::Resolvers::AwsToken.get
65 token == '' ? nil : token
66 end
67 end
68 end
69 end
70 end
71 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class EosRelease < BaseResolver
5 # :name
6 # :version
7 # :codename
8
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_eos_release(fact_name) }
16 end
17
18 def read_eos_release(fact_name)
19 output = Facter::Util::FileHelper.safe_read('/etc/Eos-release', nil)
20 return @fact_list[fact_name] = nil if output.nil?
21
22 output_strings = output.split(' ')
23
24 @fact_list[:name] = output_strings[0]
25 @fact_list[:version] = output_strings[-1]
26 @fact_list[fact_name]
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Facterversion < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_version_file }
12 end
13
14 def read_version_file
15 @fact_list[:facterversion] = Facter::VERSION
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Filesystems < BaseResolver
6 # :systems
7
8 init_resolver
9
10 @log = Facter::Log.new(self)
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { read_filesystems(fact_name) }
17 end
18
19 def read_filesystems(fact_name)
20 output = Facter::Util::FileHelper.safe_readlines('/proc/filesystems', nil)
21 return unless output
22
23 filesystems = []
24 output.each do |line|
25 tokens = line.split(' ')
26 filesystems << tokens if tokens.size == 1 && tokens.first != 'fuseblk'
27 end
28 @fact_list[:systems] = filesystems.sort.join(',')
29 @fact_list[fact_name]
30 end
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class FipsEnabled < BaseResolver
6 #:fips_enabled
7
8 init_resolver
9
10 @log = Facter::Log.new(self)
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { read_fips_file(fact_name) }
17 end
18
19 def read_fips_file(fact_name)
20 file_output = Facter::Util::FileHelper.safe_read('/proc/sys/crypto/fips_enabled')
21 @fact_list[:fips_enabled] = file_output.strip == '1'
22 @fact_list[fact_name]
23 end
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class DmiBios < BaseResolver
6 init_resolver
7
8 class << self
9 #:model
10
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_facts(fact_name) }
15 end
16
17 def read_facts(fact_name)
18 require_relative 'ffi/ffi_helper'
19
20 @fact_list[:bios_date] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.bios.reldate')
21 @fact_list[:bios_vendor] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.bios.vendor')
22 @fact_list[:bios_version] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.bios.version')
23
24 @fact_list[:product_name] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.system.product')
25 @fact_list[:product_serial] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.system.serial')
26 @fact_list[:product_uuid] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.system.uuid')
27
28 @fact_list[:sys_vendor] = Facter::Freebsd::FfiHelper.kenv(:get, 'smbios.system.maker')
29
30 @fact_list[fact_name]
31 end
32 end
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3
4 module Facter
5 module Freebsd
6 module FfiHelper
7 module Libc
8 extend FFI::Library
9
10 KENV_GET = 0
11
12 KENV_MVALLEN = 128
13
14 ffi_lib 'c'
15 attach_function :kenv, %i[int string pointer int], :int
16 attach_function :sysctlbyname, %i[string pointer pointer pointer size_t], :int
17 end
18
19 def self.kenv(action, name, value = nil)
20 case action
21 when :get
22 len = Libc::KENV_MVALLEN + 1
23 value = FFI::MemoryPointer.new(:char, len)
24 res = Libc.kenv(Libc::KENV_GET, name, value, len)
25 return nil if res.negative?
26
27 value.read_string(res).chomp("\0")
28 else
29 raise "Action #{action} not supported"
30 end
31 end
32
33 def self.sysctl_by_name(type, name)
34 oldp = FFI::Pointer::NULL
35 oldlenp = FFI::MemoryPointer.new(:size_t)
36
37 newp = FFI::Pointer::NULL
38 newlenp = 0
39
40 if type == :string
41 res = Libc.sysctlbyname(name, oldp, oldlenp, newp, newlenp)
42 return nil if res.negative?
43 else
44 oldlenp.write(:size_t, FFI.type_size(type))
45 end
46
47 oldp = FFI::MemoryPointer.new(:uint8_t, oldlenp.read(:size_t))
48 res = Libc.sysctlbyname(name, oldp, oldlenp, newp, newlenp)
49 return nil if res.negative?
50
51 case type
52 when :string
53 oldp.read_string
54 else
55 oldp.read(type)
56 end
57 end
58 end
59 end
60 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class FreebsdVersion < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { freebsd_version_system_call(fact_name) }
13 end
14
15 def freebsd_version_system_call(fact_name)
16 output = Facter::Core::Execution.execute('/bin/freebsd-version -k', logger: log)
17
18 @fact_list[:installed_kernel] = output.strip unless output.empty?
19
20 output = Facter::Core::Execution.execute('/bin/freebsd-version -ru', logger: log)
21
22 build_fact_list(output) unless output.empty?
23
24 @fact_list[fact_name]
25 end
26
27 def build_fact_list(output)
28 freebsd_version_results = output.split("\n")
29
30 @fact_list[:running_kernel] = freebsd_version_results[0].strip
31 @fact_list[:installed_userland] = freebsd_version_results[1].strip
32 end
33 end
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class Geom < BaseResolver
6 init_resolver
7
8 DISKS_ATTRIBUTES = %i[read_model read_serial_number read_size].freeze
9 PARTITIONS_ATTRIBUTES = %i[read_partlabel read_partuuid read_size].freeze
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_facts(fact_name) }
16 end
17
18 def read_facts(fact_name)
19 require_relative 'ffi/ffi_helper'
20 require 'rexml/document'
21
22 topology = geom_topology
23 read_data('DISK', topology, DISKS_ATTRIBUTES)
24 read_data('PART', topology, PARTITIONS_ATTRIBUTES)
25
26 @fact_list[fact_name]
27 end
28
29 def read_data(fact_name, geom_topology, data_to_read)
30 fact_list_key = fact_name == 'DISK' ? :disks : :partitions
31 @fact_list[fact_list_key] = {}
32
33 each_geom_class_provider(fact_name, geom_topology) do |provider|
34 name = provider.get_text('./name').value
35
36 @fact_list[fact_list_key][name] = data_to_read.map do |x|
37 send(x, provider)
38 end.inject(:merge)
39 end
40 end
41
42 def each_geom_class_provider(geom_class_name, geom_topology, &block)
43 REXML::XPath.each(geom_topology, "/mesh/class[name/text() = '#{geom_class_name}']/geom/provider", &block)
44 end
45
46 def read_size(provider)
47 res = {}
48 return res unless (mediasize = provider.get_text('mediasize'))
49
50 mediasize = Integer(mediasize.value)
51
52 res[:size_bytes] = mediasize
53 res[:size] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(mediasize)
54 res
55 end
56
57 def read_model(provider)
58 res = {}
59 return res unless (model = provider.get_text('./config/descr'))
60
61 res[:model] = model.value
62 res
63 end
64
65 def read_partlabel(provider)
66 res = {}
67 return res unless (rawuuid = provider.get_text('./config/label'))
68
69 res[:partlabel] = rawuuid.value
70 res
71 end
72
73 def read_partuuid(provider)
74 res = {}
75 return res unless (rawuuid = provider.get_text('./config/rawuuid'))
76
77 res[:partuuid] = rawuuid.value
78 res
79 end
80
81 def read_serial_number(provider)
82 res = {}
83 return res unless (serial_number = provider.get_text('./config/ident'))
84
85 res[:serial_number] = serial_number.value
86 res
87 end
88
89 def geom_topology
90 REXML::Document.new(geom_confxml)
91 end
92
93 def geom_confxml
94 Facter::Freebsd::FfiHelper.sysctl_by_name(:string, 'kern.geom.confxml')
95 end
96 end
97 end
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/bsd/processors'
3
4 module Facter
5 module Resolvers
6 module Freebsd
7 class Processors < BaseResolver
8 init_resolver
9 @log = Facter::Log.new(self)
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { collect_processors_info(fact_name) }
16 end
17
18 def collect_processors_info(fact_name)
19 require 'facter/resolvers/freebsd/ffi/ffi_helper'
20
21 count = logical_count
22 model = processors_model
23 speed = processors_speed
24
25 @fact_list[:logical_count] = count
26 @fact_list[:models] = Array.new(count, model) if logical_count && model
27 @fact_list[:speed] = speed * 1000 * 1000 if speed
28
29 @fact_list[fact_name]
30 end
31
32 def processors_model
33 Facter::Freebsd::FfiHelper.sysctl_by_name(:string, 'hw.model')
34 end
35
36 def logical_count
37 Facter::Freebsd::FfiHelper.sysctl_by_name(:uint32_t, 'hw.ncpu')
38 end
39
40 def processors_speed
41 Facter::Freebsd::FfiHelper.sysctl_by_name(:uint32_t, 'hw.clockrate')
42 end
43 end
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class SwapMemory < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_swap_memory(fact_name) }
13 end
14
15 def read_swap_memory(fact_name) # rubocop:disable Metrics/AbcSize
16 output = Facter::Core::Execution.execute('swapinfo -k', logger: log)
17 data = output.split("\n")[1..-1].map { |line| line.split(/\s+/) }
18
19 unless data.empty?
20 @fact_list[:total_bytes] = kilobytes_to_bytes(data.map { |line| line[1].to_i }.inject(:+))
21 @fact_list[:used_bytes] = kilobytes_to_bytes(data.map { |line| line[2].to_i }.inject(:+))
22 @fact_list[:available_bytes] = kilobytes_to_bytes(data.map { |line| line[3].to_i }.inject(:+))
23 @fact_list[:capacity] = Facter::Util::Resolvers::FilesystemHelper
24 .compute_capacity(@fact_list[:used_bytes],
25 @fact_list[:total_bytes])
26 @fact_list[:encrypted] = data.map { |line| line[0].end_with?('.eli') }.all?
27 end
28
29 @fact_list[fact_name]
30 end
31
32 def kilobytes_to_bytes(quantity)
33 (quantity.to_f * 1024).to_i
34 end
35 end
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class SystemMemory < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { calculate_system_memory(fact_name) }
13 end
14
15 def calculate_system_memory(fact_name)
16 read_total_memory_in_bytes
17 read_available_memory_in_bytes
18
19 @fact_list[:used_bytes] = @fact_list[:total_bytes] - @fact_list[:available_bytes]
20 @fact_list[:capacity] = Facter::Util::Resolvers::FilesystemHelper
21 .compute_capacity(@fact_list[:used_bytes], @fact_list[:total_bytes])
22
23 @fact_list[fact_name]
24 end
25
26 def read_available_memory_in_bytes
27 output = Facter::Core::Execution.execute('vmstat -H --libxo json', logger: log)
28 data = JSON.parse(output)
29 @fact_list[:available_bytes] = data['memory']['free-memory'] * 1024
30 end
31
32 def read_total_memory_in_bytes
33 require_relative 'ffi/ffi_helper'
34
35 @fact_list[:total_bytes] = Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'hw.physmem')
36 end
37 end
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Freebsd
5 class Virtual < BaseResolver
6 init_resolver
7
8 class << self
9 #:model
10
11 VM_GUEST_SYSCTL_NAMES = {
12 'hv' => 'hyperv',
13 'microsoft' => 'hyperv',
14 'oracle' => 'virtualbox',
15 'xen' => 'xenu',
16 'none' => nil
17 }.freeze
18
19 private
20
21 def post_resolve(fact_name, _options)
22 @fact_list.fetch(fact_name) { read_facts(fact_name) }
23 end
24
25 def read_facts(fact_name)
26 require_relative 'ffi/ffi_helper'
27
28 if Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'security.jail.jailed').zero?
29 vm = Facter::Freebsd::FfiHelper.sysctl_by_name(:string, 'kern.vm_guest')
30
31 vm = VM_GUEST_SYSCTL_NAMES[vm] if VM_GUEST_SYSCTL_NAMES.key?(vm)
32
33 @fact_list[:vm] = vm
34 else
35 @fact_list[:vm] = 'jail'
36 end
37
38 @fact_list[fact_name]
39 end
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Gce < BaseResolver
5 init_resolver
6
7 METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=json'
8 HEADERS = { "Metadata-Flavor": 'Google', "Accept": 'application/json' }.freeze
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 log.debug('reading Gce metadata')
15 @fact_list.fetch(fact_name) { read_facts(fact_name) }
16 end
17
18 def read_facts(fact_name)
19 @fact_list[:metadata] = query_for_metadata
20 @fact_list[fact_name]
21 end
22
23 def query_for_metadata
24 gce_data = extract_to_hash(Facter::Util::Resolvers::Http.get_request(METADATA_URL, HEADERS))
25 parse_instance(gce_data)
26
27 gce_data.empty? ? nil : gce_data
28 end
29
30 def extract_to_hash(metadata)
31 JSON.parse(metadata)
32 rescue JSON::ParserError => e
33 log.debug("Trying to parse result but got: #{e.message}")
34 {}
35 end
36
37 def parse_instance(gce_data)
38 instance_data = gce_data['instance']
39 return if instance_data.nil? || instance_data.empty?
40
41 %w[image machineType zone].each do |key|
42 instance_data[key] = instance_data[key].split('/').last if instance_data[key]
43 end
44
45 network = instance_data.dig('networkInterfaces', 0, 'network')
46 instance_data['networkInterfaces'][0]['network'] = network.split('/').last unless network.nil?
47
48 gce_data['instance'] = instance_data
49 end
50 end
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Hostname < BaseResolver
5 # :fqdn
6 # :domain
7 # :hostname
8
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { retrieve_info(fact_name) }
16 end
17
18 def retrieve_info(fact_name)
19 require 'socket'
20 output = Socket.gethostname
21 hostname, domain = retrieve_from_fqdn(output)
22
23 fqdn = retrieve_with_addrinfo(hostname) if hostname_and_no_domain?(hostname, domain)
24
25 _, domain = retrieve_from_fqdn(fqdn) if exists_and_valid_fqdn?(fqdn, hostname)
26
27 domain = read_domain unless exists_and_not_empty?(domain)
28
29 construct_fact_list(hostname, domain, fqdn)
30 @fact_list[fact_name]
31 end
32
33 def retrieve_from_fqdn(output)
34 if output =~ /(.*?)\.(.+$)/
35 log.debug("Managed to read hostname: #{Regexp.last_match(1)} and domain: #{Regexp.last_match(2)}")
36 [Regexp.last_match(1), Regexp.last_match(2)]
37 else
38 log.debug("Only managed to read hostname: #{output}, no domain was found.")
39 [output, '']
40 end
41 end
42
43 def retrieve_with_addrinfo(host)
44 begin
45 name = Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)[0]
46 rescue StandardError => e
47 @log.debug("Socket.getaddrinfo failed to retrieve fqdn for hostname #{host} with: #{e}")
48 return
49 end
50 return if name.nil? || name.empty? || host == name[2] || name[2] == name[3]
51
52 name[2]
53 end
54
55 def exists_and_valid_fqdn?(fqdn, hostname)
56 exists_and_not_empty?(fqdn) && fqdn.start_with?("#{hostname}.")
57 end
58
59 def hostname_and_no_domain?(hostname, domain)
60 domain.empty? && !hostname.empty?
61 end
62
63 def read_domain
64 file_content = Facter::Util::FileHelper.safe_read('/etc/resolv.conf')
65 if file_content =~ /^domain\s+(\S+)/
66 Regexp.last_match(1)
67 elsif file_content =~ /^search\s+(\S+)/
68 Regexp.last_match(1)
69 end
70 end
71
72 def construct_fqdn(host, domain, fqdn)
73 return fqdn if exists_and_not_empty?(fqdn)
74 return if host.nil? || host.empty?
75
76 exists_and_not_empty?(domain) ? "#{host}.#{domain}" : host
77 end
78
79 def construct_fact_list(hostname, domain, fqdn)
80 @fact_list[:hostname] = hostname
81 @fact_list[:domain] = domain
82 @fact_list[:fqdn] = construct_fqdn(@fact_list[:hostname], @fact_list[:domain], fqdn)
83 end
84
85 def exists_and_not_empty?(variable)
86 variable && !variable.empty?
87 end
88 end
89 end
90 end
91 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class PosxIdentity < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { retrieve_identity(fact_name) }
14 end
15
16 def retrieve_identity(fact_name)
17 require 'etc'
18
19 login_info = Etc.getpwuid
20 @fact_list[:gid] = login_info.gid
21 @fact_list[:group] = Etc.getgrgid(login_info.gid).name
22 @fact_list[:privileged] = login_info.uid.zero?
23 @fact_list[:uid] = login_info.uid
24 @fact_list[:user] = login_info.name
25 @fact_list[fact_name]
26 end
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class DockerUptime < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { detect_uptime(fact_name) }
13 end
14
15 def detect_uptime(fact_name)
16 days, hours, minutes, seconds = extract_uptime_from_docker
17 total_seconds = convert_to_seconds(days, hours, minutes, seconds)
18 @fact_list = Facter::Util::Resolvers::UptimeHelper.create_uptime_hash(total_seconds)
19
20 @fact_list[fact_name]
21 end
22
23 def extract_uptime_from_docker
24 # time format [dd-][hh:]mm:ss
25 time = Facter::Core::Execution.execute('ps -o etime= -p "1"', logger: log)
26 extracted_time = time.split(/[-:]/)
27
28 reversed_time = extracted_time.reverse
29 seconds = reversed_time[0].to_i
30 minutes = reversed_time[1].to_i
31 hours = reversed_time[2].to_i
32 days = reversed_time[3].to_i
33
34 [days, hours, minutes, seconds]
35 end
36
37 def convert_to_seconds(days, hours, minutes, seconds)
38 days * 24 * 3600 + hours * 3600 + minutes * 60 + seconds
39 end
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Hostname < BaseResolver
6 # :fqdn
7 # :domain
8 # :hostname
9
10 init_resolver
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { retrieve_info(fact_name) }
17 end
18
19 def retrieve_info(fact_name)
20 require 'socket'
21
22 output = retrieving_hostname
23 return nil unless output
24
25 # Check if the gethostname method retrieved fqdn
26 hostname, domain = parse_fqdn(output)
27
28 fqdn = retrieve_fqdn_for_host(hostname) if hostname_and_no_domain?(hostname, domain)
29
30 _, domain = parse_fqdn(fqdn) if exists_and_valid_fqdn?(fqdn, hostname)
31
32 domain = read_domain unless exists_and_not_empty?(domain)
33
34 construct_fact_list(hostname, domain, fqdn)
35 @fact_list[fact_name]
36 end
37
38 def retrieving_hostname
39 output = Socket.gethostname || ''
40 if output.empty? || output['0.0.0.0']
41 begin
42 require 'facter/util/resolvers/ffi/hostname'
43
44 output = Facter::Util::Resolvers::Ffi::Hostname.getffihostname
45 rescue LoadError => e
46 log.debug(e.message)
47 output = nil
48 end
49 end
50
51 log.debug("Tried to retrieve hostname and got: #{output}")
52 return output unless output&.empty?
53 end
54
55 def parse_fqdn(output)
56 if output =~ /(.*?)\.(.+$)/
57 log.debug("Managed to read hostname: #{Regexp.last_match(1)} and domain: #{Regexp.last_match(2)}")
58 [Regexp.last_match(1), Regexp.last_match(2)]
59 else
60 log.debug("Only managed to read hostname: #{output}, no domain was found.")
61 [output, '']
62 end
63 end
64
65 def retrieve_fqdn_for_host(host)
66 begin
67 name = Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)[0]
68 rescue StandardError => e
69 log.debug("Socket.getaddrinfo failed to retrieve fqdn for hostname #{host} with: #{e}")
70 end
71
72 return name[2] if !name.nil? && !name.empty? && host != name[2] && name[2] != name[3]
73
74 retrieve_fqdn_for_host_with_ffi(host)
75 end
76
77 def retrieve_fqdn_for_host_with_ffi(host)
78 require 'facter/util/resolvers/ffi/hostname'
79
80 fqdn = Facter::Util::Resolvers::Ffi::Hostname.getffiaddrinfo(host)
81 log.debug("FFI getaddrinfo was called and it retrieved: #{fqdn}")
82 fqdn
83 rescue LoadError => e
84 log.debug(e.message)
85 nil
86 end
87
88 def exists_and_valid_fqdn?(fqdn, hostname)
89 exists_and_not_empty?(fqdn) && fqdn.start_with?("#{hostname}.")
90 end
91
92 def hostname_and_no_domain?(hostname, domain)
93 domain.empty? && !hostname.empty?
94 end
95
96 def read_domain
97 file_content = Facter::Util::FileHelper.safe_read('/etc/resolv.conf')
98 if file_content =~ /^domain\s+(\S+)/
99 Regexp.last_match(1)
100 elsif file_content =~ /^search\s+(\S+)/
101 Regexp.last_match(1)
102 end
103 end
104
105 def construct_fqdn(host, domain, fqdn)
106 return fqdn if exists_and_not_empty?(fqdn)
107 return if host.nil? || host.empty?
108
109 exists_and_not_empty?(domain) ? "#{host}.#{domain}" : host
110 end
111
112 def construct_fact_list(hostname, domain, fqdn)
113 @fact_list[:hostname] = hostname
114 @fact_list[:domain] = domain
115 @fact_list[:fqdn] = construct_fqdn(@fact_list[:hostname], @fact_list[:domain], fqdn)
116 end
117
118 def exists_and_not_empty?(variable)
119 variable && !variable.empty?
120 end
121 end
122 end
123 end
124 end
125 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class LoadAverages < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_load_averages_file(fact_name) }
13 end
14
15 def read_load_averages_file(fact_name)
16 output = Facter::Util::FileHelper.safe_read('/proc/loadavg')
17 @fact_list[:load_averages] = {}.tap { |h| h['1m'], h['5m'], h['15m'], = output.split.map(&:to_f) }
18
19 @fact_list[fact_name]
20 end
21 end
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Networking < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { retrieve_network_info(fact_name) }
13
14 @fact_list[fact_name]
15 end
16
17 def retrieve_network_info(fact_name)
18 add_info_from_socket_reader
19 add_info_from_routing_table
20 retrieve_primary_interface
21 Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
22 add_flags
23 @fact_list[fact_name]
24 end
25
26 def add_info_from_socket_reader
27 @fact_list[:interfaces] = Facter::Util::Linux::SocketParser.retrieve_interfaces(log)
28 mtu_and_indexes = interfaces_mtu_and_index
29
30 @fact_list[:interfaces].keys.each do |interface_name|
31 mtu(interface_name, mtu_and_indexes)
32 dhcp(interface_name, mtu_and_indexes)
33
34 @log.debug("Found interface #{interface_name} with #{@fact_list[:interfaces][interface_name]}")
35 end
36 end
37
38 def interfaces_mtu_and_index
39 mtu_and_indexes = {}
40 output = Facter::Core::Execution.execute('ip link show', logger: log)
41 output.each_line do |line|
42 next unless line.include?('mtu')
43
44 parse_ip_command_line(line, mtu_and_indexes)
45 end
46 mtu_and_indexes
47 end
48
49 def parse_ip_command_line(line, mtu_and_indexes)
50 mtu = line.match(/mtu (\d+)/)&.captures&.first&.to_i
51 index_tokens = line.split(':')
52 index = index_tokens[0].strip
53 # vlans are displayed as <vlan_name>@<physical_interface>
54 name = index_tokens[1].split('@').first.strip
55 mtu_and_indexes[name] = { index: index, mtu: mtu }
56 end
57
58 def mtu(interface_name, mtu_and_indexes)
59 mtu = mtu_and_indexes.dig(interface_name, :mtu)
60 @fact_list[:interfaces][interface_name][:mtu] = mtu unless mtu.nil?
61 end
62
63 def dhcp(interface_name, mtu_and_indexes)
64 dhcp = Facter::Util::Linux::Dhcp.dhcp(interface_name, mtu_and_indexes.dig(interface_name, :index), log)
65 @fact_list[:interfaces][interface_name][:dhcp] = dhcp unless dhcp.nil?
66 end
67
68 def add_info_from_routing_table
69 routes4, routes6 = Facter::Util::Linux::RoutingTable.read_routing_table(log)
70 compare_ips(routes4, :bindings)
71 compare_ips(routes6, :bindings6)
72 end
73
74 def add_flags
75 flags = Facter::Util::Linux::IfInet6.read_flags
76 flags.each_pair do |iface, ips|
77 next unless @fact_list[:interfaces].key?(iface)
78
79 ips.each_pair do |ip, ip_flags|
80 next unless @fact_list[:interfaces][iface].key?(:bindings6)
81
82 @fact_list[:interfaces][iface][:bindings6].each do |binding|
83 next unless binding[:address] == ip
84
85 binding[:flags] = ip_flags
86 end
87 end
88 end
89 end
90
91 def compare_ips(routes, binding_key)
92 routes.each do |route|
93 next unless @fact_list[:interfaces].key?(route[:interface])
94
95 interface_data = @fact_list[:interfaces][route[:interface]]
96 add_binding_if_missing(interface_data, binding_key, route)
97 end
98 end
99
100 def add_binding_if_missing(interface_data, binding_key, route)
101 interface_binding = interface_data[binding_key]
102
103 if interface_binding.nil?
104 interface_data[binding_key] = [{ address: route[:ip] }]
105 elsif interface_binding.none? { |binding| binding[:address] == route[:ip] }
106 interface_binding << { address: route[:ip] }
107 end
108 end
109
110 def retrieve_primary_interface
111 primary_helper = Facter::Util::Resolvers::Networking::PrimaryInterface
112 primary_interface = primary_helper.read_from_proc_route
113 primary_interface ||= primary_helper.read_from_ip_route
114 primary_interface ||= primary_helper.find_in_interfaces(@fact_list[:interfaces])
115
116 @fact_list[:primary_interface] = primary_interface
117 end
118 end
119 end
120 end
121 end
122 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class LoadAverages < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_load_averages(fact_name) }
12 end
13
14 def read_load_averages(fact_name)
15 require 'facter/util/resolvers/ffi/load_averages'
16
17 log.debug('loading cpu load averages')
18 @fact_list[:load_averages] = %w[1m 5m 15m].zip(Facter::Util::Resolvers::Ffi::LoadAverages
19 .read_load_averages).to_h
20
21 @fact_list[fact_name]
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Lpar < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_lpar(fact_name) }
12 end
13
14 def read_lpar(fact_name)
15 output = Facter::Core::Execution.execute('/usr/bin/lparstat -i', logger: log)
16 output.each_line do |line|
17 populate_lpar_data(line.split(':').map(&:strip))
18 end
19 @fact_list[fact_name]
20 end
21
22 def populate_lpar_data(key_value)
23 @fact_list[:lpar_partition_name] = key_value[1] if key_value[0] == 'Partition Name'
24 @fact_list[:lpar_partition_number] = key_value[1].to_i if key_value[0] == 'Partition Number'
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class LsbRelease < BaseResolver
5 # :lsb_version
6 # :distributor_id
7 # :description
8 # :release
9 # :codename
10
11 init_resolver
12
13 class << self
14 private
15
16 def post_resolve(fact_name, _options)
17 @fact_list.fetch(fact_name) { retrieve_facts(fact_name) }
18 end
19
20 def retrieve_facts(fact_name)
21 lsb_release_installed? if @fact_list[:lsb_release_installed].nil?
22 read_lsb_release_file if @fact_list[:lsb_release_installed]
23 @fact_list[fact_name]
24 end
25
26 def lsb_release_installed?
27 @fact_list[:lsb_release_installed] = !Facter::Core::Execution.which('lsb_release').nil?
28 end
29
30 def read_lsb_release_file
31 output = Facter::Core::Execution.execute('lsb_release -a', logger: log)
32 build_fact_list(output)
33 end
34
35 def build_fact_list(info)
36 release_info = info.delete("\t").split("\n").map { |e| e.split(':', 2) }
37
38 result = Hash[*release_info.flatten]
39 result.each { |k, v| @fact_list[k.downcase.gsub(/\s/, '_').to_sym] = v }
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Lspci < BaseResolver
5 init_resolver
6
7 REGEX_VALUES = { 'VirtualBox' => 'virtualbox', 'XenSource' => 'xenhvm',
8 'Microsoft Corporation Hyper-V' => 'hyperv', 'Class 8007: Google, Inc' => 'gce',
9 'VM[wW]are' => 'vmware', '1ab8:' => 'parallels', '[Pp]arallels' => 'parallels',
10 '(?i)(virtio)' => 'kvm' }.freeze
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { lspci_command(fact_name) }
17 end
18
19 def lspci_command(fact_name)
20 output = Facter::Core::Execution.execute('lspci', logger: log)
21 return if output.empty?
22
23 @fact_list[:vm] = retrieve_vm(output)
24 @fact_list[fact_name]
25 end
26
27 def retrieve_vm(output)
28 output.each_line { |line| REGEX_VALUES.each { |key, value| return value if line =~ /#{key}/ } }
29
30 nil
31 end
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class DmiBios < BaseResolver
6 init_resolver
7
8 class << self
9 #:model
10
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_facts }
15 end
16
17 def read_facts
18 # OSX only supports the product name
19 output = Facter::Core::Execution.execute('sysctl -n hw.model', logger: log)
20 @fact_list[:macosx_model] = output&.strip
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class Filesystems < BaseResolver
6 # :macosx_filesystems
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_filesystems(fact_name) }
14 end
15
16 def read_filesystems(fact_name)
17 output = Facter::Core::Execution.execute('mount', logger: log)
18 filesystems = output.scan(/\(([a-z]+)\,*/).flatten
19 @fact_list[:macosx_filesystems] = filesystems.uniq.sort.join(',')
20 @fact_list[fact_name]
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class LoadAverages < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_load_averages_file(fact_name) }
13 end
14
15 def read_load_averages_file(fact_name)
16 output = Facter::Core::Execution.execute('sysctl -n vm.loadavg', logger: log)
17 @fact_list[:load_averages] = {}.tap { |h| _, h['1m'], h['5m'], h['15m'], = output.split.map(&:to_f) }
18
19 @fact_list[fact_name]
20 end
21 end
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class Mountpoints < BaseResolver
6 include Facter::Util::Resolvers::FilesystemHelper
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_mounts }
14 end
15
16 def read_mounts
17 mounts = {}
18
19 Facter::Util::Resolvers::FilesystemHelper.read_mountpoints.each do |fs|
20 device = fs.name
21 filesystem = fs.mount_type
22 path = fs.mount_point
23 options = read_options(fs.options)
24
25 mounts[path] = read_stats(path).tap do |hash|
26 hash[:device] = device
27 hash[:filesystem] = filesystem
28 hash[:options] = options if options.any?
29 end
30 end
31
32 @fact_list[:mountpoints] = mounts
33 end
34
35 def read_stats(path)
36 begin
37 stats = Facter::Util::Resolvers::FilesystemHelper.read_mountpoint_stats(path)
38 size_bytes = stats.bytes_total
39 available_bytes = stats.bytes_available
40 used_bytes = size_bytes - available_bytes
41 rescue Sys::Filesystem::Error
42 size_bytes = used_bytes = available_bytes = 0
43 end
44
45 {
46 size_bytes: size_bytes,
47 used_bytes: used_bytes,
48 available_bytes: available_bytes,
49 capacity: Facter::Util::Resolvers::FilesystemHelper.compute_capacity(used_bytes, size_bytes),
50 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes),
51 available: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(available_bytes),
52 used: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(used_bytes)
53 }
54 end
55
56 def read_options(options)
57 options_map = {
58 'read-only' => 'readonly',
59 'asynchronous' => 'async',
60 'synchronous' => 'noasync',
61 'quotas' => 'quota',
62 'rootfs' => 'root',
63 'defwrite' => 'deferwrites'
64 }
65
66 options.split(',').map(&:strip).map { |o| options_map.key?(o) ? options_map[o] : o }
67 end
68 end
69 end
70 end
71 end
72 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class Processors < BaseResolver
6 init_resolver
7
8 ITEMS = { logical_count: 'hw.logicalcpu_max',
9 physical_count: 'hw.physicalcpu_max',
10 brand: 'machdep.cpu.brand_string',
11 speed: 'hw.cpufrequency_max',
12 cores_per_socket: 'machdep.cpu.core_count',
13 threads_per_core: 'machdep.cpu.thread_count' }.freeze
14
15 class << self
16 # :logicalcount
17 # :models
18 # :physicalcount
19 # :speed
20 # :cores_per_socket
21 # :threads_per_core
22
23 private
24
25 def post_resolve(fact_name, _options)
26 @fact_list.fetch(fact_name) { read_processor_data(fact_name) }
27 end
28
29 def read_processor_data(fact_name)
30 output = Facter::Core::Execution.execute("sysctl #{ITEMS.values.join(' ')}", logger: log)
31 processors_hash = Hash[*output.split("\n").collect { |v| [v.split(': ')[0], v.split(': ')[1]] }.flatten]
32 build_fact_list(processors_hash)
33 @fact_list[fact_name]
34 end
35
36 def build_fact_list(hash)
37 build_logical_count(hash)
38 build_physical_count(hash)
39 build_models(hash)
40 build_speed(hash)
41 build_cores_per_socket(hash)
42 build_threads_per_core(hash)
43 end
44
45 def build_logical_count(hash)
46 @fact_list[:logicalcount] = hash[ITEMS[:logical_count]].to_i
47 end
48
49 def build_physical_count(hash)
50 @fact_list[:physicalcount] = hash[ITEMS[:physical_count]].to_i
51 end
52
53 def build_models(hash)
54 @fact_list[:models] = Array.new(@fact_list[:logicalcount].to_i, hash[ITEMS[:brand]])
55 end
56
57 def build_speed(hash)
58 @fact_list[:speed] = hash[ITEMS[:speed]].to_i
59 end
60
61 def build_cores_per_socket(hash)
62 @fact_list[:cores_per_socket] = hash[ITEMS[:cores_per_socket]].to_i
63 end
64
65 def build_threads_per_core(hash)
66 @fact_list[:threads_per_core] = hash[ITEMS[:threads_per_core]].to_i / hash[ITEMS[:cores_per_socket]].to_i
67 end
68 end
69 end
70 end
71 end
72 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class SwapMemory < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_swap_memory(fact_name) }
13 end
14
15 def read_swap_memory(fact_name) # rubocop:disable Metrics/AbcSize
16 output = Facter::Core::Execution.execute('sysctl -n vm.swapusage', logger: log)
17 data = output.match(/^total = ([\d.]+)M used = ([\d.]+)M free = ([\d.]+)M (\(encrypted\))$/)
18
19 if data[1].to_f.positive?
20 @fact_list[:total_bytes] = megabytes_to_bytes(data[1])
21 @fact_list[:used_bytes] = megabytes_to_bytes(data[2])
22 @fact_list[:available_bytes] = megabytes_to_bytes(data[3])
23 @fact_list[:capacity] = compute_capacity(@fact_list[:used_bytes], @fact_list[:total_bytes])
24 @fact_list[:encrypted] = data[4] == '(encrypted)'
25 end
26
27 @fact_list[fact_name]
28 end
29
30 def megabytes_to_bytes(quantity)
31 (quantity.to_f * 1_048_576).to_i
32 end
33
34 def compute_capacity(used, total)
35 "#{format('%<value>.2f', value: (used / total.to_f * 100))}%"
36 end
37 end
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class SystemMemory < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { calculate_system_memory(fact_name) }
13 end
14
15 def calculate_system_memory(fact_name)
16 read_total_memory_in_bytes
17 read_available_memory_in_bytes
18
19 @fact_list[:used_bytes] = @fact_list[:total_bytes] - @fact_list[:available_bytes]
20 @fact_list[:capacity] = compute_capacity(@fact_list[:used_bytes], @fact_list[:total_bytes])
21
22 @fact_list[fact_name]
23 end
24
25 def read_available_memory_in_bytes
26 output = Facter::Core::Execution.execute('vm_stat', logger: log)
27 page_size = output.match(/page size of (\d+) bytes/)[1].to_i
28 pages_free = output.match(/Pages free:\s+(\d+)/)[1].to_i
29
30 @fact_list[:available_bytes] = page_size * pages_free
31 end
32
33 def read_total_memory_in_bytes
34 @fact_list[:total_bytes] = Facter::Core::Execution.execute('sysctl -n hw.memsize', logger: log).to_i
35 end
36
37 def compute_capacity(used, total)
38 "#{format('%<value>.2f', value: (used / total.to_f * 100))}%"
39 end
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Macosx
5 class SystemProfiler < BaseResolver
6 SP_HARDWARE_DATA_TYPE = %i[model_name model_identifier processor_speed number_of_processors processor_name
7 total_number_of_cores l2_cache_per_core l3_cache memory boot_rom_version
8 smc_version_system serial_number_system hardware_uuid hyper-threading_technology
9 activation_lock_status].freeze
10
11 SP_SOFTWARE_DATA_TYPE = %i[system_version kernel_version boot_volume boot_mode computer_name
12 user_name secure_virtual_memory system_integrity_protection time_since_boot].freeze
13
14 SP_ETHERNET_DATA_TYPE = %i[type bus vendor_id device_id subsystem_vendor_id
15 subsystem_id revision_id bsd_name kext_name location version].freeze
16
17 init_resolver
18
19 class << self
20 private
21
22 def post_resolve(fact_name, _options)
23 @fact_list.fetch(fact_name) { retrieve_system_profiler(fact_name) }
24 end
25
26 def retrieve_system_profiler(fact_name)
27 case fact_name
28 when *SP_HARDWARE_DATA_TYPE
29 @fact_list.merge!(Facter::Util::Macosx::SystemProfileExecutor.execute('SPHardwareDataType'))
30 when *SP_SOFTWARE_DATA_TYPE
31 @fact_list.merge!(Facter::Util::Macosx::SystemProfileExecutor.execute('SPSoftwareDataType'))
32 when *SP_ETHERNET_DATA_TYPE
33 @fact_list.merge!(Facter::Util::Macosx::SystemProfileExecutor.execute('SPEthernetDataType'))
34 end
35
36 @fact_list[fact_name]
37 end
38 end
39 end
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Memory < BaseResolver
6 init_resolver
7
8 @log = Facter::Log.new(self)
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_meminfo_file(fact_name) }
15 end
16
17 def read_meminfo_file(fact_name)
18 meminfo_output = Facter::Util::FileHelper.safe_read('/proc/meminfo', nil)
19 return unless meminfo_output
20
21 read_system(meminfo_output)
22 read_swap(meminfo_output)
23
24 @fact_list[fact_name]
25 end
26
27 def read_system(output)
28 @fact_list[:total] = kilobytes_to_bytes(output.match(/MemTotal:\s+(\d+)\s/)[1])
29 @fact_list[:memfree] = memfree(output)
30 @fact_list[:used_bytes] = compute_used(@fact_list[:total], @fact_list[:memfree])
31 @fact_list[:capacity] = compute_capacity(@fact_list[:used_bytes], @fact_list[:total])
32 end
33
34 def memfree(output)
35 available = output.match(/MemAvailable:\s+(\d+)\s/)
36 return kilobytes_to_bytes(available[1]) if available
37
38 buffers = kilobytes_to_bytes(output.match(/Buffers:\s+(\d+)\s/)[1])
39 cached = kilobytes_to_bytes(output.match(/Cached:\s+(\d+)\s/)[1])
40 memfree = kilobytes_to_bytes(output.match(/MemFree:\s+(\d+)\s/)[1])
41 memfree + buffers + cached
42 end
43
44 def read_swap(output)
45 total = output.match(/SwapTotal:\s+(\d+)\s/)[1]
46 return if total.to_i.zero?
47
48 @fact_list[:swap_total] = kilobytes_to_bytes(total)
49 @fact_list[:swap_free] = kilobytes_to_bytes(output.match(/SwapFree:\s+(\d+)\s/)[1])
50 @fact_list[:swap_used_bytes] = compute_used(@fact_list[:swap_total], @fact_list[:swap_free])
51 @fact_list[:swap_capacity] = compute_capacity(@fact_list[:swap_used_bytes], @fact_list[:swap_total])
52 end
53
54 def kilobytes_to_bytes(quantity)
55 quantity.to_i * 1024
56 end
57
58 def compute_capacity(used, total)
59 format('%<computed_capacity>.2f', computed_capacity: (used / total.to_f * 100)) + '%'
60 end
61
62 def compute_used(total, free)
63 total - free
64 end
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Mountpoints < BaseResolver
5 include Facter::Util::Resolvers::FilesystemHelper
6
7 init_resolver
8
9 @log = Facter::Log.new(self)
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_mounts(fact_name) }
16 end
17
18 def root_device
19 cmdline = Facter::Util::FileHelper.safe_read('/proc/cmdline')
20 match = cmdline.match(/root=([^\s]+)/)
21 root = match&.captures&.first
22
23 if !root.nil? && root.include?('=')
24 # We are dealing with the PARTUUID of the partition. Need to extract partition path.
25 root_partition_path = convert_partuuid_to_path(root)
26 root = root_partition_path unless root_partition_path.nil?
27 end
28 root
29 end
30
31 def convert_partuuid_to_path(root)
32 blkid_content = Facter::Core::Execution.execute('blkid', logger: log)
33 partuuid = root.split('=').last
34 match = blkid_content.match(/(.+)#{partuuid}/)
35 match&.captures&.first&.split(':')&.first
36 end
37
38 def compute_device(device)
39 # If the "root" device, lookup the actual device from the kernel options
40 # This is done because not all systems symlink /dev/root
41 device = root_device if device == '/dev/root'
42 device
43 end
44
45 def read_mounts(fact_name)
46 mounts = []
47 Facter::Util::Resolvers::FilesystemHelper.read_mountpoints.each do |file_system|
48 mount = {}
49 get_mount_data(file_system, mount)
50
51 next if mount[:path] =~ %r{^/(proc|sys)} && mount[:filesystem] != 'tmpfs' || mount[:filesystem] == 'autofs'
52
53 get_mount_sizes(mount)
54 mounts << mount
55 end
56
57 @fact_list[:mountpoints] = mounts
58 @fact_list[fact_name]
59 end
60
61 def get_mount_data(file_system, mount)
62 mount[:device] = compute_device(file_system.name)
63 mount[:filesystem] = file_system.mount_type
64 mount[:path] = file_system.mount_point
65 mount[:options] = file_system.options.split(',').map(&:strip)
66 end
67
68 def get_mount_sizes(mount)
69 begin
70 stats = Facter::Util::Resolvers::FilesystemHelper.read_mountpoint_stats(mount[:path])
71 get_bytes_data(mount, stats)
72 rescue Sys::Filesystem::Error => e
73 @log.debug("Could not get stats for mountpoint #{mount[:path]}, got #{e}")
74 mount[:size_bytes] = mount[:available_bytes] = mount[:used_bytes] = 0
75 end
76
77 populate_mount(mount)
78 end
79
80 def get_bytes_data(mount, stats)
81 mount[:size_bytes] = stats.bytes_total.abs
82 mount[:available_bytes] = stats.bytes_available.abs
83 mount[:used_bytes] = stats.bytes_used.abs
84 end
85
86 def populate_mount(mount)
87 total_bytes = mount[:used_bytes] + mount[:available_bytes]
88 mount[:capacity] = Facter::Util::Resolvers::FilesystemHelper.compute_capacity(mount[:used_bytes], total_bytes)
89
90 mount[:size] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(mount[:size_bytes])
91 mount[:available] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(mount[:available_bytes])
92 mount[:used] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(mount[:used_bytes])
93 end
94 end
95 end
96 end
97 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Networking < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_facts(fact_name) }
12 end
13
14 def read_facts(fact_name)
15 interfaces_data
16 primary_interface
17 Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
18 @fact_list[fact_name]
19 end
20
21 def primary_interface
22 primary_helper = Facter::Util::Resolvers::Networking::PrimaryInterface
23 primary_interface ||= primary_helper.read_from_route
24
25 primary_interface ||= primary_helper.find_in_interfaces(@fact_list[:interfaces])
26
27 @fact_list[:primary_interface] = primary_interface
28 end
29
30 def interfaces_data
31 command_response = Facter::Core::Execution.execute('ifconfig -a', logger: log)
32
33 clean_up_interfaces_response(command_response)
34 parse_interfaces_response(command_response)
35 end
36
37 def clean_up_interfaces_response(response)
38 # convert ip ranges into single ip. eg. 10.16.132.213 --> 10.16.132.213 is converted to 10.16.132.213
39 # convert ip6 ranges into single ip. eg. 2001:db8:cafe::132:213 -->
40 # 2001:db8:cafe::132:213 is converted to 2001:db8:cafe::132:213
41 response.gsub!(/([\da-fA-F]+([\.:]+[\da-fA-F]+)*)\s+-->\s+[\da-fA-F]+([\.:]+[\da-fA-F]+)*/, '\\1')
42 end
43
44 def parse_interfaces_response(response)
45 parsed_interfaces_data = {}
46 interfaces_data = Hash[*response.split(/^([A-Za-z0-9_\.]+): /)[1..-1]]
47
48 interfaces_data.each do |interface_name, raw_data|
49 parsed_interface_data = {}
50
51 extract_mtu(raw_data, parsed_interface_data)
52 extract_mac(raw_data, parsed_interface_data)
53 extract_dhcp(interface_name, raw_data, parsed_interface_data)
54 extract_ip_data(raw_data, parsed_interface_data)
55
56 parsed_interfaces_data[interface_name] = parsed_interface_data
57 end
58 @fact_list[:interfaces] = parsed_interfaces_data unless parsed_interfaces_data.empty?
59 end
60
61 def extract_mtu(raw_data, parsed_interface_data)
62 mtu = raw_data.match(/mtu\s+(\d+)/)&.captures&.first&.to_i
63 parsed_interface_data[:mtu] = mtu unless mtu.nil?
64 end
65
66 def extract_mac(raw_data, parsed_interface_data)
67 mac = raw_data.match(/(?:ether|lladdr)\s+((?:\w?\w:){5}\w?\w)|(?:infiniband)\s+((?:\w?\w:){19}\w?\w)/)
68 &.captures&.compact&.first
69 parsed_interface_data[:mac] = mac unless mac.nil?
70 end
71
72 def extract_dhcp(interface_name, raw_data, parsed_interface_data)
73 return unless raw_data =~ /status:\s+active/
74
75 result = Facter::Core::Execution.execute("ipconfig getoption #{interface_name} " \
76 'server_identifier', logger: log)
77
78 parsed_interface_data[:dhcp] = result.match(/^[\d.a-f:\s]+$/)&.to_s&.strip unless result.empty?
79 end
80
81 def extract_ip_data(raw_data, parsed_interface_data)
82 ip = extract_values(raw_data, /inet (\S+)/)
83 mask = extract_values(raw_data, /netmask (\S+)/).map { |val| val.hex.to_s(2).count('1') }
84
85 ip6 = extract_values(raw_data, /inet6 (\S+)/).map { |val| val.gsub(/%.+/, '') }
86 mask6 = extract_values(raw_data, /prefixlen (\S+)/)
87
88 parsed_interface_data[:bindings] = create_bindings(ip, mask) unless ip.empty?
89 parsed_interface_data[:bindings6] = create_bindings(ip6, mask6) unless ip6.empty?
90 end
91
92 def extract_values(data, regex)
93 results = []
94 data.scan(regex).flatten.each do |val|
95 results << val
96 end
97 results
98 end
99
100 def create_bindings(ips, masks)
101 bindings = []
102 ips.zip(masks).each do |ip, mask|
103 bindings << Facter::Util::Resolvers::Networking.build_binding(ip, mask)
104 end
105 bindings
106 end
107 end
108 end
109 end
110 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class OpenVz < BaseResolver
5 # build
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { check_proc_vz(fact_name) }
14 end
15
16 def check_proc_vz(fact_name)
17 return if !Dir.exist?('/proc/vz') || File.file?('/proc/lve/list') || Dir.entries('/proc/vz').count.equal?(2)
18
19 @fact_list[:vm] = read_proc_status
20 @fact_list[fact_name]
21 end
22
23 def read_proc_status
24 proc_status_content = Facter::Util::FileHelper.safe_readlines('/proc/self/status', nil)
25 return unless proc_status_content
26
27 proc_status_content.each do |line|
28 parts = line.split("\s")
29 next unless parts.size.equal?(2)
30
31 next unless /^envID:/ =~ parts[0]
32
33 @fact_list[:id] = parts[1]
34
35 return 'openvzhn' if parts[1] == '0'
36
37 return 'openvzve'
38 end
39 end
40 end
41 end
42 end
43 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class OsRelease < BaseResolver
5 # :pretty_name
6 # :name
7 # :version_id
8 # :version
9 # :id
10 # :id_like
11 # :ansi_color
12 # :home_url
13 # :support_url
14 # :bug_report_url
15
16 init_resolver
17
18 class << self
19 private
20
21 def post_resolve(fact_name, _options)
22 @fact_list.fetch(fact_name) do
23 # If we get here multiple times per run it's probably because
24 # someone's asking for a os-release value not present in the file
25 # (e.g. VERSION is not a thing on rolling distributions, so this
26 # code will always run if the resolver is being asked for :version,
27 # because it'll never get cached).
28 #
29 # Just return early to avoid reparsing the file.
30 return unless @fact_list.empty?
31
32 pairs = read_and_parse_os_release_file
33 return unless pairs
34
35 fill_fact_list(pairs)
36
37 process_name
38 process_version_id
39 process_id
40
41 @fact_list[fact_name]
42 end
43 end
44
45 def read_and_parse_os_release_file
46 content = Facter::Util::FileHelper.safe_readlines('/etc/os-release')
47 return nil if content.empty?
48
49 pairs = []
50 content.each do |line|
51 pairs << line.strip.delete('"').split('=', 2)
52 end
53
54 pairs
55 end
56
57 def fill_fact_list(pairs)
58 result = Hash[*pairs.flatten]
59 result.each { |k, v| @fact_list[k.downcase.to_sym] = v }
60 end
61
62 def process_version_id
63 return unless @fact_list[:version_id]
64
65 @fact_list[:version_id] = "#{@fact_list[:version_id]}.0" unless @fact_list[:version_id] =~ /\./
66 end
67
68 def process_id
69 return unless @fact_list[:id]
70
71 @fact_list[:id] = 'opensuse' if @fact_list[:id] =~ /opensuse/i
72 end
73
74 def process_name
75 return unless @fact_list[:name]
76
77 join_os_name
78 capitalize_os_name
79 append_linux_to_os_name
80 end
81
82 def join_os_name
83 os_name = @fact_list[:name]
84 @fact_list[:name] = if os_name.downcase.start_with?('red', 'oracle', 'arch', 'manjaro')
85 os_name = os_name.split(' ')[0..1].join
86 os_name
87 elsif os_name.downcase.end_with?('mariner')
88 os_name.split(' ')[-1].strip
89 else
90 os_name.split(' ')[0].strip
91 end
92 end
93
94 def capitalize_os_name
95 os_name = @fact_list[:name]
96 @fact_list[:name] = os_name.capitalize if os_name.downcase.start_with?('arch', 'manjaro')
97 end
98
99 def append_linux_to_os_name
100 os_name = @fact_list[:name]
101 @fact_list[:name] = os_name + 'Linux' if os_name.downcase.start_with?('virtuozzo')
102 end
103 end
104 end
105 end
106 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Partitions < BaseResolver
5 init_resolver
6
7 BLOCK_PATH = '/sys/block'
8 BLOCK_SIZE = 512
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_partitions(fact_name) }
15 end
16
17 def read_partitions(fact_name)
18 return unless File.readable?(BLOCK_PATH)
19
20 block_devices = Dir.entries(BLOCK_PATH).reject { |dir| dir =~ /^\.+/ }
21 @fact_list[:partitions] = {} unless block_devices.empty?
22 blkid_and_lsblk = {}
23
24 block_devices.each do |block_device|
25 block_path = "#{BLOCK_PATH}/#{block_device}"
26 if File.directory?("#{block_path}/device")
27 extract_from_device(block_path, blkid_and_lsblk)
28 elsif File.directory?("#{block_path}/dm")
29 extract_from_dm(block_path, block_device, blkid_and_lsblk)
30 elsif File.directory?("#{block_path}/loop")
31 extract_from_loop(block_path, block_device, blkid_and_lsblk)
32 end
33 end
34
35 @fact_list[fact_name]
36 end
37
38 def extract_from_device(block_path, blkid_and_lsblk)
39 subdirs = browse_subdirectories(block_path)
40 subdirs.each do |subdir|
41 name = "/dev/#{subdir.split('/').last}"
42 populate_partitions(name, subdir, blkid_and_lsblk)
43 end
44 end
45
46 def extract_from_dm(block_path, block_device, blkid_and_lsblk)
47 map_name = Facter::Util::FileHelper.safe_read("#{block_path}/dm/name").chomp
48 if map_name.empty?
49 populate_partitions("/dev/#{block_device}", block_path, blkid_and_lsblk)
50 else
51 populate_partitions("/dev/mapper/#{map_name}", block_path, blkid_and_lsblk)
52 end
53 end
54
55 def extract_from_loop(block_path, block_device, blkid_and_lsblk)
56 backing_file = Facter::Util::FileHelper.safe_read("#{block_path}/loop/backing_file").chomp
57 if backing_file.empty?
58 populate_partitions("/dev/#{block_device}", block_path, blkid_and_lsblk)
59 else
60 populate_partitions("/dev/#{block_device}", block_path, blkid_and_lsblk, backing_file)
61 end
62 end
63
64 def populate_partitions(partition_name, block_path, blkid_and_lsblk, backing_file = nil)
65 size_bytes = Facter::Util::FileHelper.safe_read("#{block_path}/size", '0')
66 .chomp.to_i * BLOCK_SIZE
67 info_hash = { size_bytes: size_bytes,
68 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes),
69 backing_file: backing_file }
70 info_hash.merge!(populate_from_syscalls(partition_name, blkid_and_lsblk))
71 @fact_list[:partitions][partition_name] = info_hash.reject { |_key, value| value.nil? }
72 end
73
74 def populate_from_syscalls(partition_name, blkid_and_lsblk)
75 part_info = populate_from_blkid(partition_name, blkid_and_lsblk)
76
77 return populate_from_lsblk(partition_name, blkid_and_lsblk) if part_info.empty?
78
79 part_info
80 end
81
82 def browse_subdirectories(path)
83 dirs = Dir[File.join(path, '**', '*')].select { |p| File.directory? p }
84 dirs.select { |subdir| subdir.split('/').last.include?(path.split('/').last) }.reject(&:nil?)
85 end
86
87 def populate_from_blkid(partition_name, blkid_and_lsblk)
88 return {} unless available?('blkid', blkid_and_lsblk)
89
90 blkid_and_lsblk[:blkid] ||= execute_and_extract_blkid_info
91
92 partition_data = blkid_and_lsblk[:blkid][partition_name]
93 return {} unless partition_data
94
95 filesys = partition_data['TYPE']
96 uuid = partition_data['UUID']
97 label = partition_data['LABEL']
98 part_uuid = partition_data['PARTUUID']
99 part_label = partition_data['PARTLABEL']
100
101 { filesystem: filesys, uuid: uuid, label: label, partuuid: part_uuid, partlabel: part_label }
102 end
103
104 def available?(command, blkid_and_lsblk)
105 command_exists_key = command == 'blkid' ? :blkid_exists : :lsblk_exists
106
107 return blkid_and_lsblk[command_exists_key] unless blkid_and_lsblk[command_exists_key].nil?
108
109 blkid_and_lsblk[command_exists_key] = !Facter::Core::Execution.which(command).nil?
110 end
111
112 def execute_and_extract_blkid_info
113 stdout = Facter::Core::Execution.execute('blkid', logger: log)
114 output_hash = Hash[*stdout.split(/^([^:]+):/)[1..-1]]
115 output_hash.each do |key, value|
116 output_hash[key] = Hash[*value.delete('"').chomp.rstrip.split(/ ([^= ]+)=/)[1..-1]]
117 end
118 end
119
120 def populate_from_lsblk(partition_name, blkid_and_lsblk)
121 return {} unless available?('lsblk', blkid_and_lsblk)
122
123 blkid_and_lsblk[:lsblk] ||= Facter::Core::Execution.execute('lsblk -fp', logger: log)
124
125 part_info = blkid_and_lsblk[:lsblk].match(/#{partition_name}.*/).to_s.split(' ')
126 return {} if part_info.empty?
127
128 parse_part_info(part_info)
129 end
130
131 def parse_part_info(part_info)
132 result = { filesystem: part_info[1] }
133
134 if part_info.count.eql?(5)
135 result[:label] = part_info[2]
136 result[:uuid] = part_info[3]
137 else
138 result[:uuid] = part_info[2]
139 end
140
141 result
142 end
143 end
144 end
145 end
146 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Path < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_path_from_env }
12 end
13
14 def read_path_from_env
15 @fact_list[:path] = ENV['PATH']
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Processors < BaseResolver
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
10 MHZ_TO_HZ = 1_000_000
11
12 class << self
13 # :count
14 # :models
15 # :physical_count
16 # :speed
17
18 private
19
20 def post_resolve(fact_name, _options)
21 @fact_list.fetch(fact_name) { read_cpuinfo(fact_name) }
22 end
23
24 def read_cpuinfo(fact_name)
25 cpuinfo_output = Facter::Util::FileHelper.safe_readlines('/proc/cpuinfo')
26 return if cpuinfo_output.empty?
27
28 read_processors(cpuinfo_output) # + model names
29
30 @fact_list[:physical_count] = @fact_list[:physical_processors].uniq.length
31 @fact_list[:physical_count] = physical_devices_count if @fact_list[:physical_count].zero?
32 @fact_list[fact_name]
33 end
34
35 def read_processors(cpuinfo_output)
36 @fact_list[:processors] = 0
37 @fact_list[:models] = []
38 @fact_list[:physical_processors] = []
39 cpuinfo_output.each do |line|
40 tokens = line.split(':')
41 count_processors(tokens)
42 construct_models_list(tokens)
43 count_physical_processors(tokens)
44 build_speed(tokens)
45 end
46 end
47
48 def count_processors(tokens)
49 @fact_list[:processors] += 1 if tokens.first.strip == 'processor'
50 end
51
52 def construct_models_list(tokens)
53 return unless tokens.first.strip == 'model name' || tokens.first.strip == 'cpu'
54
55 @fact_list[:models] << tokens.last.strip
56 end
57
58 def count_physical_processors(tokens)
59 @fact_list[:physical_processors] << tokens.last.strip.to_i if tokens.first.strip == 'physical id'
60 end
61
62 def physical_devices_count
63 Dir.entries('/sys/devices/system/cpu')
64 .select { |dir| dir =~ /cpu[0-9]+$/ }
65 .select { |dir| File.exist?("/sys/devices/system/cpu/#{dir}/topology/physical_package_id") }
66 .map do |dir|
67 Facter::Util::FileHelper.safe_read("/sys/devices/system/cpu/#{dir}/topology/physical_package_id").strip
68 end
69 .uniq.count
70 end
71
72 def build_speed(tokens)
73 build_speed_for_power_pc(tokens) if tokens.first.strip == 'clock'
74 build_speed_for_x86(tokens) if tokens.first.strip == 'cpu MHz'
75 end
76
77 def build_speed_for_power_pc(tokens)
78 speed = tokens.last.strip.match(/^(\d+).*MHz/)[1]
79 @fact_list[:speed] = speed.to_i * MHZ_TO_HZ
80 end
81
82 def build_speed_for_x86(tokens)
83 speed = tokens.last.strip.match(/^(\d+).*/)[1]
84 @fact_list[:speed] = speed.to_i * MHZ_TO_HZ
85 end
86 end
87 end
88 end
89 end
90 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Linux
5 class Lscpu < BaseResolver
6 init_resolver
7
8 ITEMS = { threads_per_core: "-e 'Thread(s)'",
9 cores_per_socket: "-e 'Core(s)'" }.freeze
10
11 class << self
12 #:cores_per_socket
13 #:threads_per_core
14
15 private
16
17 def post_resolve(fact_name, _options)
18 @fact_list.fetch(fact_name) { read_cpuinfo(fact_name) }
19 end
20
21 def read_cpuinfo(fact_name)
22 lscpu_output = Facter::Core::Execution.execute("lscpu | grep #{ITEMS.values.join(' ')}", logger: log)
23 build_fact_list(lscpu_output.split("\n"))
24 @fact_list[fact_name]
25 end
26
27 def build_fact_list(processors_data)
28 build_threads_per_core(processors_data[0])
29 build_cores_per_socket(processors_data[1])
30 end
31
32 def build_threads_per_core(index)
33 @fact_list[:threads_per_core] = index.split(': ')[1].to_i
34 end
35
36 def build_cores_per_socket(index)
37 @fact_list[:cores_per_socket] = index.split(': ')[1].to_i
38 end
39 end
40 end
41 end
42 end
43 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class RedHatRelease < BaseResolver
5 # :name
6 # :version
7 # :codename
8 # :description
9 # :distributor_id
10
11 init_resolver
12
13 class << self
14 private
15
16 def post_resolve(fact_name, _options)
17 @fact_list.fetch(fact_name) { read_redhat_release(fact_name) }
18 end
19
20 def read_redhat_release(fact_name)
21 output = Facter::Util::FileHelper.safe_read('/etc/redhat-release', nil)
22 return @fact_list[fact_name] = nil if output.nil?
23
24 build_fact_list(output)
25
26 @fact_list[fact_name]
27 end
28
29 def build_fact_list(output)
30 @fact_list[:description] = output.strip
31 output_strings = output.split('release')
32 output_strings.map!(&:strip)
33
34 @fact_list[:codename] = codename(output)
35 @fact_list[:distributor_id] = distributor_id(output_strings[0])
36 @fact_list[:name] = release_name(output_strings[0])
37 @fact_list[:version] = version(output_strings)
38 @fact_list[:id] = id(@fact_list[:name])
39 end
40
41 def release_name(value)
42 value.split.reject { |el| el.casecmp('linux').zero? }[0..1].join
43 end
44
45 def id(value)
46 id = value.downcase
47 id = 'rhel' if @fact_list[:name].casecmp('Red Hat Enterprise Linux')
48
49 id
50 end
51
52 def codename(value)
53 matched_data = value.match(/.*release.*(\(.*\)).*/)
54 return unless matched_data
55
56 codename = (matched_data[1] || '').gsub(/\(|\)/, '')
57 codename.empty? ? nil : codename
58 end
59
60 def version(value)
61 value[1].split.first
62 end
63
64 def distributor_id(value)
65 value.split.reject { |el| el.casecmp('linux').zero? }.join
66 end
67 end
68 end
69 end
70 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class ReleaseFromFirstLine < BaseResolver
5 # :release
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, options)
13 @fact_list.fetch(fact_name) { read_release_file(fact_name, options) }
14 end
15
16 def read_release_file(fact_name, options)
17 release_file = options[:release_file]
18 return unless release_file
19
20 output = Facter::Util::FileHelper.safe_read(release_file, nil)
21 return @fact_list[fact_name] = nil if output.nil?
22
23 @fact_list[:release] = retrieve_version(output)
24
25 @fact_list[fact_name]
26 end
27
28 def retrieve_version(output)
29 if output[/(Rawhide)$/]
30 'Rawhide'
31 elsif output['release']
32 output.strip =~ /release (\d[\d.]*)/ ? Regexp.last_match(1) : nil
33 else
34 output.strip =~ /Amazon Linux (\d+)/ ? Regexp.last_match(1) : nil
35 end
36 end
37 end
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Ruby < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { retrieve_ruby_information(fact_name) }
12 end
13
14 def retrieve_ruby_information(fact_name)
15 @fact_list[:sitedir] = RbConfig::CONFIG['sitelibdir'] if RbConfig::CONFIG['sitedir']
16 @fact_list[:platform] = RUBY_PLATFORM
17 @fact_list[:version] = RUBY_VERSION
18 @fact_list[fact_name]
19 end
20 end
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class SELinux < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { retrieve_facts(fact_name) }
12 end
13
14 def retrieve_facts(fact_name)
15 mountpoint = selinux_mountpoint
16
17 @fact_list[:enabled] = !mountpoint.empty? && read_selinux_config
18 read_other_selinux_facts(mountpoint) if @fact_list[:enabled]
19
20 @fact_list[fact_name]
21 end
22
23 def selinux_mountpoint
24 output = Facter::Core::Execution.execute('cat /proc/self/mounts', logger: log)
25 mountpoint = ''
26
27 output.each_line do |line|
28 next unless line =~ /selinuxfs/
29
30 mountpoint = line.split("\s")[1]
31 break
32 end
33 mountpoint
34 end
35
36 def read_other_selinux_facts(mountpoint)
37 enforce_file = "#{mountpoint}/enforce"
38 policy_file = "#{mountpoint}/policyvers"
39
40 @fact_list[:policy_version] = Facter::Util::FileHelper.safe_read(policy_file, nil)
41
42 enforce = Facter::Util::FileHelper.safe_read(enforce_file)
43 if enforce.eql?('1')
44 @fact_list[:enforced] = true
45 @fact_list[:current_mode] = 'enforcing'
46 else
47 @fact_list[:enforced] = false
48 @fact_list[:current_mode] = 'permissive'
49 end
50 end
51
52 def read_selinux_config
53 file_lines = Facter::Util::FileHelper.safe_readlines('/etc/selinux/config')
54
55 file_lines.map do |line|
56 @fact_list[:config_mode] = line.split('=').last.strip if line =~ /^SELINUX=/
57 @fact_list[:config_policy] = line.split('=').last.strip if line =~ /^SELINUXTYPE=/
58 end
59
60 !file_lines.empty? ? true : false
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Disks < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_disks_info(fact_name) }
13 end
14
15 def read_disks_info(fact_name)
16 return unless File.executable?('/usr/bin/kstat')
17
18 log.debug('loading disks info')
19
20 kstat_output = Facter::Core::Execution.execute('/usr/bin/kstat sderr', logger: log)
21 return if kstat_output.empty?
22
23 @fact_list[fact_name] = parse(kstat_output)
24 end
25
26 def parse(kstat_output)
27 disks = {}
28
29 names = kstat_output.scan(/name:\s+(\w+)/).flatten
30 products = kstat_output.scan(/Product\s+(.+)/).flatten
31 vendors = kstat_output.scan(/Vendor\s+(\w+)/).flatten
32 sizes = kstat_output.scan(/Size\s+(\w+)/).flatten
33
34 names.each_with_index do |name, index|
35 disk_size = sizes[index].to_i
36 disks[name] = {
37 product: products[index],
38 size: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(disk_size),
39 size_bytes: disk_size,
40 vendor: vendors[index]
41 }
42 end
43 disks
44 end
45 end
46 end
47 end
48 end
49 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Dmi < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_facts(fact_name) }
13 end
14
15 SMBIOS_PARAMS = {
16 'SMB_TYPE_BIOS' => {
17 bios_version: 'Version String: (.+)',
18 bios_vendor: 'Vendor: (.+)',
19 bios_release_date: 'Release Date: (.+)'
20 },
21 'SMB_TYPE_SYSTEM' => {
22 manufacturer: 'Manufacturer: (.+)',
23 product_name: 'Product: (.+)',
24 serial_number: 'Serial Number: (.+)',
25 product_uuid: 'UUID: (.+)'
26 },
27 'SMB_TYPE_CHASSIS' => {
28 chassis_asset_tag: 'Asset Tag: (.+)',
29 chassis_type: '(?:Chassis )?Type: (.+)'
30 }
31 }.freeze
32
33 def read_facts(fact_name)
34 param = SMBIOS_PARAMS.find { |_key, hash| hash[fact_name] }
35 return nil unless param
36
37 output = exec_smbios(param[0])
38 facts = param[1]
39 return unless output
40
41 facts.each do |name, regx|
42 @fact_list[name] = output.match(/#{regx}/)&.captures&.first
43 end
44
45 @fact_list[fact_name]
46 end
47
48 def exec_smbios(args)
49 return unless File.executable?('/usr/sbin/smbios')
50
51 Facter::Core::Execution.execute("/usr/sbin/smbios -t #{args}", logger: log)
52 end
53 end
54 end
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class DmiSparc < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_facts(fact_name) }
13 end
14
15 def read_facts(fact_name)
16 output = exec_prtdiag
17 return unless output
18
19 matches = output.match(/System Configuration:\s+(.+?)\s+sun\d+\S+\s+(.+)/)&.captures
20
21 @fact_list[:manufacturer] = matches[0]&.strip
22 @fact_list[:product_name] = matches[1]&.strip
23
24 sneep = exec_sneep&.strip
25 @fact_list[:serial_number] = sneep
26
27 @fact_list[fact_name]
28 end
29
30 def exec_prtdiag
31 return unless File.executable?('/usr/sbin/prtdiag')
32
33 Facter::Core::Execution.execute('/usr/sbin/prtdiag', logger: log)
34 end
35
36 def exec_sneep
37 return unless File.executable?('/usr/sbin/sneep')
38
39 Facter::Core::Execution.execute('/usr/sbin/sneep', logger: log)
40 end
41 end
42 end
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3 require_relative 'structs.rb'
4 require_relative 'functions.rb'
5
6 module Facter
7 module Resolvers
8 module Solaris
9 module FFI
10 SIOCGLIFNUM = -1_072_928_382
11 SIOCGLIFCONF = -1_072_666_203
12 SIOCGLIFMTU = -1_065_850_502
13 SIOCGLIFNETMASK = -1_065_850_499
14 SIOCGARP = -1_071_355_617
15 AF_INET = 2
16 AF_INET6 = 26
17 AF_UNSPEC = 0
18 SOCK_DGRAM = 1
19 INET_ADDRSTRLEN = 16
20 INET6_ADDRSTRLEN = 46
21 end
22
23 BINDINGS_KEY = {
24 FFI::AF_INET => :bindings,
25 FFI::AF_INET6 => :bindings6
26 }.freeze
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 module FFI
6 module Ioctl
7 extend ::FFI::Library
8 ffi_lib ::FFI::Library::LIBC, 'socket'
9
10 attach_function :ioctl_base, :ioctl, %i[int int pointer], :int
11 attach_function :open_socket, :socket, %i[int int int], :int
12 attach_function :close_socket, :shutdown, %i[int int], :int
13 attach_function :inet_ntop, %i[int pointer pointer uint], :string
14
15 def self.ioctl(call_const, pointer, address_family = AF_INET)
16 fd = Ioctl.open_socket(address_family, SOCK_DGRAM, 0)
17 result = ioctl_base(fd, call_const, pointer)
18 Ioctl.close_socket(fd, 2)
19 result
20 end
21 end
22 end
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 module FFI
6 class SockaddrStorage < ::FFI::Struct
7 layout :ss_family, :int16,
8 :pad, [:char, 254]
9 end
10
11 class Sockaddr < ::FFI::Struct
12 layout :sa_family, :sa_family_t,
13 :sa_data, [:uchar, 14]
14 end
15
16 class Lifnum < ::FFI::Struct
17 layout :lifn_family, :sa_family_t,
18 :lifn_flags, :int,
19 :lifn_count, :int
20 end
21
22 class Arpreq < ::FFI::Struct
23 layout :arp_pa, Sockaddr,
24 :arp_ha, Sockaddr,
25 :arp_flags, :int
26
27 def sa_data_to_mac
28 self[:arp_ha][:sa_data].entries[0, 6].map do |s|
29 s.to_s(16).rjust(2, '0')
30 end.join ':'
31 end
32
33 def self.new_for_ioctl(lifreq)
34 arp = Arpreq.new
35 arp_addr = SockaddrIn.new(arp[:arp_pa].to_ptr)
36 arp_addr[:sin_addr][:s_addr] = SockaddrIn.new(lifreq.lifru_addr.to_ptr).s_addr
37
38 arp
39 end
40 end
41
42 class Lifru1 < ::FFI::Union
43 layout :lifru_addrlen, :int,
44 :lifru_ppa, :uint_t
45 end
46
47 class Lifru < ::FFI::Union
48 layout :lifru_addr, SockaddrStorage,
49 :lifru_dstaddr, SockaddrStorage,
50 :lifru_broadaddr, SockaddrStorage,
51 :lifru_token, SockaddrStorage,
52 :lifru_subnet, SockaddrStorage,
53 :lifru_flags, :uint64,
54 :lifru_metric, :int,
55 :pad, [:char, 80]
56 end
57
58 class Lifreq < ::FFI::Struct
59 layout :lifr_name, [:char, 32],
60 :lifr_lifru1, Lifru1,
61 :lifr_movetoindex, :int,
62 :lifr_lifru, Lifru,
63 :pad, [:char, 80]
64
65 def name
66 self[:lifr_name].to_s
67 end
68
69 def ss_family
70 self[:lifr_lifru][:lifru_addr][:ss_family]
71 end
72
73 def lifru_addr
74 self[:lifr_lifru][:lifru_addr]
75 end
76 end
77
78 class Lifconf < ::FFI::Struct
79 layout :lifc_family, :uint,
80 :lifc_flags, :int,
81 :lifc_len, :int,
82 :lifc_buf, :pointer
83
84 def self.new_for_ioctl(interface_count)
85 lifconf = new
86 lifconf[:lifc_family] = 0
87 lifconf[:lifc_flags] = 0
88 lifconf[:lifc_len] = interface_count * Lifreq.size
89 lifconf[:lifc_buf] = ::FFI::MemoryPointer.new(Lifreq, interface_count)
90 lifconf
91 end
92 end
93
94 class Lifcu < ::FFI::Union
95 layout :lifcu_buf, :caddr_t,
96 :lifcu_req, Lifreq
97 end
98
99 class InAddr < ::FFI::Struct
100 layout :s_addr, :uint32_t
101 end
102
103 class SockaddrIn < ::FFI::Struct
104 layout :sin_family, :sa_family_t,
105 :sin_port, :in_port_t,
106 :sin_addr, InAddr,
107 :sin_zero, [:char, 8]
108
109 def s_addr
110 self[:sin_addr][:s_addr]
111 end
112 end
113
114 class In6Addr < ::FFI::Struct
115 layout :s_addr, [:uint32_t, 4]
116 end
117
118 class SockaddrIn6 < ::FFI::Struct
119 layout :sin6_family, :sa_family_t,
120 :sin6_port, :in_port_t,
121 :sin6_flowinfo, :uint32_t,
122 :sin6_addr, In6Addr,
123 :sin6_scope_id, :uint32_t
124 end
125 end
126 end
127 end
128 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Filesystem < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_sysdef_file(fact_name) }
13 end
14
15 def read_sysdef_file(fact_name)
16 return unless File.executable?('/usr/sbin/sysdef')
17
18 file_content = Facter::Core::Execution.execute('/usr/sbin/sysdef', logger: log)
19 files = file_content.split("\n").map do |line|
20 line.split('/').last if line =~ /^fs\.*/
21 end
22
23 @fact_list[:file_systems] = files.compact.sort.join(',')
24 @fact_list[fact_name]
25 end
26 end
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Ipaddress < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { read_ipaddress(fact_name) }
13 end
14
15 def read_ipaddress(fact_name)
16 ip = nil
17 primary_interface = read_primary_interface
18 unless primary_interface.nil?
19 output = Facter::Core::Execution.execute("ifconfig #{primary_interface}", logger: log)
20 output.each_line do |str|
21 if str.strip =~ /inet\s(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}) .+/
22 @fact_list[:ip] = ip = Regexp.last_match(1)
23 break
24 end
25 end
26 end
27 @fact_list[fact_name]
28 end
29
30 def read_primary_interface
31 output = Facter::Core::Execution.execute('route -n get default | grep interface', logger: log)
32 output.strip =~ /interface:\s(\S+)/ ? Regexp.last_match(1) : nil
33 end
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Ldom < BaseResolver
6 # :chassis_serial
7 # :control_domain
8 # :domain_name
9 # :domain_uuid
10 # :role_control
11 # :role_io
12 # :role_root
13 # :role_service
14 # :role_impl
15
16 init_resolver
17
18 VIRTINFO_MAPPING = {
19 chassis_serial: %w[DOMAINCHASSIS serialno],
20 control_domain: %w[DOMAINCONTROL name],
21 domain_name: %w[DOMAINNAME name],
22 domain_uuid: %w[DOMAINUUID uuid],
23 role_control: %w[DOMAINROLE control],
24 role_io: %w[DOMAINROLE io],
25 role_root: %w[DOMAINROLE root],
26 role_service: %w[DOMAINROLE service],
27 role_impl: %w[DOMAINROLE impl]
28 }.freeze
29
30 class << self
31 private
32
33 def post_resolve(fact_name, _options)
34 @fact_list.fetch(fact_name) { call_virtinfo(fact_name) }
35 end
36
37 def call_virtinfo(fact_name)
38 virtinfo_output = Facter::Core::Execution.execute('/usr/sbin/virtinfo -a -p', logger: log)
39 return if virtinfo_output.empty?
40
41 output_hash = parse_output(virtinfo_output)
42 return if output_hash.empty?
43
44 VIRTINFO_MAPPING.each do |key, value|
45 @fact_list[key] = output_hash.dig(*value)&.strip
46 end
47
48 @fact_list[fact_name]
49 end
50
51 def parse_output(output)
52 result = {}
53 output.each_line do |line|
54 next unless line.include? 'DOMAIN'
55
56 x = line.split('|')
57 result[x.shift] = x.map { |f| f.split('=') }.to_h
58 end
59
60 result
61 end
62 end
63 end
64 end
65 end
66 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Memory < BaseResolver
6 init_resolver
7 BLOCKSIZE = 512
8 # :system
9 # :swap
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { calculate_memory(fact_name) }
16 end
17
18 def calculate_memory(fact_name)
19 @fact_list = { system: sys, swap: swap }
20
21 @fact_list[fact_name]
22 end
23
24 def sys
25 sys = {}
26 output = Facter::Core::Execution.execute('/usr/bin/kstat -m unix -n system_pages', logger: log).strip
27 total, free = parse_sys_output(output)
28
29 return unless total || free
30
31 sys[:total_bytes] = total
32 sys[:available_bytes] = free
33 sys[:used_bytes] = total - free
34 sys[:capacity] = Facter::Util::Resolvers::FilesystemHelper.compute_capacity(total - free, total)
35
36 sys
37 end
38
39 def swap
40 swap_hash = {}
41 output = Facter::Core::Execution.execute('/usr/sbin/swap -l', logger: log).strip
42 total, free = parse_swap_output(output)
43
44 swap_hash[:total_bytes] = total
45 swap_hash[:available_bytes] = free
46 swap_hash[:used_bytes] = total - free
47 swap_hash[:capacity] = Facter::Util::Resolvers::FilesystemHelper.compute_capacity(total - free, total)
48
49 swap_hash if total != 0
50 end
51
52 def parse_sys_output(output)
53 kstats = output.scan(/(physmem|pagesfree)\s+(\d+)/)
54 kstats = kstats.to_h
55 return unless kstats['physmem'] || kstats['pagesfree']
56 return unless pagesize != 0
57
58 total = kstats['physmem'].to_i * pagesize
59 free = kstats['pagesfree'].to_i * pagesize
60
61 [total, free]
62 end
63
64 def parse_swap_output(output)
65 total = 0
66 free = 0
67
68 output.each_line do |line|
69 swap_sizes = line.match(/(\d+)\s+(\d+)$/)
70 next if swap_sizes.nil?
71
72 total += swap_sizes[1].to_i
73 free += swap_sizes[2].to_i
74 end
75 total *= BLOCKSIZE
76 free *= BLOCKSIZE
77
78 [total, free]
79 end
80
81 def pagesize
82 unless @fact_list[:pagesize]
83 @fact_list[:pagesize] = Facter::Core::Execution.execute('pagesize', logger: log).strip.to_i
84 log.debug("Pagesize: #{@fact_list[:pagesize]}")
85 end
86 @fact_list[:pagesize]
87 end
88 end
89 end
90 end
91 end
92 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Mountpoints < BaseResolver
6 include Facter::Util::Resolvers::FilesystemHelper
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_mounts(fact_name) }
14 end
15
16 def exclude_auto_home_mounts!
17 @mounts.reject! do |mount|
18 parent = mount[:path].rpartition('/').first
19 @auto_home_paths.include?(parent)
20 end
21 end
22
23 def read_mounts(fact_name) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
24 @mounts = []
25 @auto_home_paths = []
26
27 Facter::Util::Resolvers::FilesystemHelper.read_mountpoints.each do |fs|
28 if fs.name == 'auto_home'
29 @auto_home_paths << fs.mount_point
30 next
31 end
32
33 next if fs.mount_type == 'autofs'
34
35 device = fs.name
36 filesystem = fs.mount_type
37 path = fs.mount_point
38 options = fs.options.split(',').map(&:strip)
39
40 stats = Facter::Util::Resolvers::FilesystemHelper.read_mountpoint_stats(path)
41 size_bytes = stats.bytes_total.abs
42 available_bytes = stats.bytes_available.abs
43
44 used_bytes = stats.bytes_used.abs
45 total_bytes = used_bytes + available_bytes
46 capacity = Facter::Util::Resolvers::FilesystemHelper.compute_capacity(used_bytes, total_bytes)
47
48 size = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(size_bytes)
49 available = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(available_bytes)
50 used = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(used_bytes)
51
52 @mounts << Hash[Facter::Util::Resolvers::FilesystemHelper::MOUNT_KEYS
53 .zip(Facter::Util::Resolvers::FilesystemHelper::MOUNT_KEYS
54 .map { |v| binding.local_variable_get(v) })]
55 end
56
57 exclude_auto_home_mounts!
58
59 @fact_list[:mountpoints] = @mounts
60 @fact_list[fact_name]
61 end
62 end
63 end
64 end
65 end
66 end
0 # frozen_string_literal: true
1
2 require_relative 'ffi/ffi.rb'
3 module Facter
4 module Resolvers
5 module Solaris
6 class Networking < BaseResolver
7 init_resolver
8 @log = Facter::Log.new(self)
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { read_facts(fact_name) }
15 end
16
17 def read_facts(fact_name)
18 begin
19 lifreqs = load_interfaces
20 @interfaces = {}
21
22 lifreqs.each do |lifreq|
23 obtain_info_for_interface(lifreq)
24 end
25
26 @fact_list[:primary_interface] = Facter::Util::Resolvers::Networking::PrimaryInterface.read_from_route
27
28 unless @interfaces.empty?
29 @fact_list[:interfaces] = @interfaces
30 @fact_list[:primary_interface] ||=
31 Facter::Util::Resolvers::Networking::PrimaryInterface.find_in_interfaces(@interfaces)
32 end
33
34 Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
35 rescue StandardError => e
36 @log.log_exception(e)
37 end
38 @fact_list[fact_name]
39 end
40
41 def obtain_info_for_interface(lifreq)
42 @interfaces[lifreq.name] ||= {}
43
44 add_mac(lifreq)
45 add_bindings(lifreq)
46 add_mtu(lifreq)
47
48 dhcp = Facter::Util::Resolvers::Networking::Dhcp.get(lifreq.name, @log)
49 @interfaces[lifreq.name][:dhcp] = dhcp if dhcp
50 end
51
52 def add_mac(lifreq)
53 arp = FFI::Arpreq.new_for_ioctl(lifreq)
54
55 ioctl = FFI::Ioctl.ioctl(FFI::SIOCGARP, arp, lifreq.ss_family)
56
57 if ioctl == -1
58 @log.debug("Could not read MAC address for interface #{lifreq.name} "\
59 "error code is: #{::FFI::LastError.error}")
60 end
61
62 mac = arp.sa_data_to_mac
63 @interfaces[lifreq.name][:mac] ||= mac if mac.count('0') < 12
64 end
65
66 def add_bindings(lifreq)
67 ip = inet_ntop(lifreq, lifreq.ss_family)
68 _netmask, netmask_length = load_netmask(lifreq)
69
70 bindings = Facter::Util::Resolvers::Networking.build_binding(ip, netmask_length)
71
72 bindings_key = BINDINGS_KEY[lifreq.ss_family]
73 @interfaces[lifreq.name][bindings_key] ||= []
74 @interfaces[lifreq.name][bindings_key] << bindings
75 end
76
77 def add_mtu(lifreq)
78 ioctl = FFI::Ioctl.ioctl(FFI::SIOCGLIFMTU, lifreq, lifreq.ss_family)
79
80 if ioctl == -1
81 @log.error("Cold not read MTU, error code is #{::FFI::LastError.error}")
82 return
83 end
84
85 @interfaces[lifreq.name][:mtu] ||= lifreq[:lifr_lifru][:lifru_metric]
86 end
87
88 def load_netmask(lifreq)
89 netmask_lifreq = FFI::Lifreq.new(lifreq.to_ptr)
90
91 ioctl = FFI::Ioctl.ioctl(FFI::SIOCGLIFNETMASK, netmask_lifreq, lifreq.ss_family)
92
93 if ioctl == -1
94 @log.error("Could not read Netmask, error code is: #{::FFI::LastError.error}")
95 return
96 end
97
98 netmask = inet_ntop(netmask_lifreq, lifreq.ss_family)
99 [netmask, Facter::Util::Resolvers::Networking.calculate_mask_length(netmask)]
100 end
101
102 def inet_ntop(lifreq, ss_family)
103 if ss_family == FFI::AF_INET
104 buffer_size = FFI::INET_ADDRSTRLEN
105 ip = get_ipv4(lifreq)
106 else # FFI::AF_INET6
107 buffer_size = FFI::INET6_ADDRSTRLEN
108 ip = get_ipv6(lifreq)
109 end
110
111 buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
112
113 FFI::Ioctl.inet_ntop(ss_family, ip.to_ptr, buffer.to_ptr, buffer.size)
114 end
115
116 def get_ipv4(lifreq)
117 sockaddr = FFI::Sockaddr.new(lifreq.lifru_addr.to_ptr)
118 sockaddr_in = FFI::SockaddrIn.new(sockaddr.to_ptr)
119 FFI::InAddr.new(sockaddr_in[:sin_addr].to_ptr)
120 end
121
122 def get_ipv6(lifreq)
123 sockaddr = FFI::Sockaddr.new(lifreq.lifru_addr.to_ptr)
124 sockaddr_in6 = FFI::SockaddrIn6.new(sockaddr.to_ptr)
125 FFI::In6Addr.new(sockaddr_in6[:sin6_addr].to_ptr)
126 end
127
128 def count_interfaces
129 lifnum = FFI::Lifnum.new
130 lifnum[:lifn_family] = FFI::AF_UNSPEC
131 lifnum[:lifn_flags] = 0
132 lifnum[:lifn_count] = 0
133
134 ioctl = FFI::Ioctl.ioctl(FFI::SIOCGLIFNUM, lifnum)
135
136 @log.error("Could not read interface count, error code is: #{::FFI::LastError.error}") if ioctl == -1
137
138 lifnum[:lifn_count]
139 end
140
141 def load_interfaces
142 interface_count = count_interfaces
143
144 lifconf = FFI::Lifconf.new_for_ioctl(interface_count)
145
146 ioctl = FFI::Ioctl.ioctl(FFI::SIOCGLIFCONF, lifconf)
147
148 # we need to enlarge the scope of this pointer so that Ruby GC will not free the memory.
149 # If the pointer if freed, Lifreq structures will contain garbage from memory.
150 @long_living_pointer = lifconf
151
152 if ioctl == -1
153 @log.error("Could not read interface information, error code is: #{::FFI::LastError.error}")
154 return []
155 end
156
157 interfaces = []
158 interface_count.times do |i|
159 interfaces << FFI::Lifreq.new(lifconf[:lifc_buf] + (i * FFI::Lifreq.size))
160 end
161
162 interfaces
163 end
164 end
165 end
166 end
167 end
168 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class OsRelease < BaseResolver
6 init_resolver
7
8 OS_VERSION_REGEX_PATTERNS = ['Solaris \d+ \d+/\d+ s(\d+)[sx]?_u(\d+)wos_',
9 'Solaris (\d+)[.](\d+)', 'Solaris (\d+)'].freeze
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { build_release_facts(fact_name) }
16 end
17
18 def build_release_facts(fact_name)
19 result = Facter::Util::FileHelper.safe_read('/etc/release', nil)
20 return @fact_list[fact_name] = nil if result.nil?
21
22 OS_VERSION_REGEX_PATTERNS.each do |os_version_regex|
23 major, minor = search_for_os_version(/#{os_version_regex}/, result)
24 next unless major || minor
25
26 @fact_list[:major] = major
27 @fact_list[:minor] = minor
28 @fact_list[:full] = major == '10' ? major + '_u' + minor : major + '.' + minor
29 break
30 end
31 @fact_list[fact_name]
32 end
33
34 def search_for_os_version(regex_pattern, text)
35 result = text.match(regex_pattern)
36 major, minor = result.captures if result
37 minor = regex_pattern == /Solaris (\d+)/ ? '0' : minor
38 return [major, minor] if major && minor
39 end
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Processors < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { collect_kstat_info(fact_name) }
13 end
14
15 def collect_kstat_info(fact_name)
16 return unless File.executable?('/usr/bin/kstat')
17
18 kstat_output = Facter::Core::Execution.execute('/usr/bin/kstat -m cpu_info', logger: log)
19 return if kstat_output.empty?
20
21 parse_output(kstat_output.chomp)
22 @fact_list[fact_name]
23 end
24
25 def parse_output(output)
26 @fact_list[:logical_count] = output.scan(/module/).size
27 @fact_list[:physical_count] = output.scan(/chip_id .*/).uniq.size
28 @fact_list[:speed] = output.scan(/current_clock_Hz .*/).first.gsub(/[a-zA-z\s]+/, '').to_i
29 @fact_list[:models] = output.scan(/brand .*/).map { |elem| elem.gsub(/brand(\s+)/, '') }
30 calculate_threads_cores(output)
31 end
32
33 def calculate_threads_cores(output)
34 @fact_list[:core_count] = output.scan(/\score_id .*/).uniq.size
35 @fact_list[:threads_per_core] = @fact_list[:logical_count] / @fact_list[:core_count]
36 @fact_list[:cores_per_socket] = @fact_list[:core_count] / @fact_list[:physical_count]
37 end
38 end
39 end
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class Zone < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { build_zone_fact(fact_name) }
13 end
14
15 def build_zone_fact(fact_name)
16 command = '/usr/sbin/zoneadm list -cp'
17 zone_adm_output = Facter::Core::Execution.execute(command, logger: log)
18
19 if zone_adm_output.empty?
20 log.debug("Command #{command} returned an empty result")
21 return
22 end
23 @fact_list[:zone] = create_zone_facts(zone_adm_output)
24
25 @fact_list[fact_name]
26 end
27
28 def create_zone_facts(zones_result)
29 zones_fact = []
30 zones_result.each_line do |zone_line|
31 id, name, status, path, uuid, brand, ip_type = zone_line.split(':')
32 zones_fact << {
33 brand: brand,
34 id: id,
35 iptype: ip_type.chomp,
36 name: name,
37 uuid: uuid,
38 status: status,
39 path: path
40 }
41 end
42 zones_fact
43 end
44 end
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Solaris
5 class ZoneName < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { build_current_zone_name_fact(fact_name) }
13 end
14
15 def build_current_zone_name_fact(fact_name)
16 zone_name_output = Facter::Core::Execution.execute('/bin/zonename', logger: log)
17
18 if zone_name_output.empty?
19 log.debug("Command #{command} returned an empty result")
20 return
21 end
22 @fact_list[:current_zone_name] = zone_name_output.chomp
23 @fact_list[fact_name]
24 end
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class SpecificReleaseFile < BaseResolver
5 # :release
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, options)
13 @fact_list.fetch(fact_name) { read_release_file(fact_name, options) }
14 end
15
16 def read_release_file(fact_name, options)
17 release_file = options[:release_file]
18 return unless release_file
19
20 output = Facter::Util::FileHelper.safe_read(release_file, nil)
21 return @fact_list[fact_name] = nil if output.nil?
22
23 if options[:regex]
24 @fact_list[:release] = output.strip =~ /#{options[:regex]}/ ? Regexp.last_match : nil
25 return @fact_list[fact_name]
26 end
27
28 @fact_list[:release] = output.strip
29 @fact_list[fact_name]
30 end
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Ssh < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 FILE_NAMES = %w[ssh_host_rsa_key.pub ssh_host_dsa_key.pub ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub].freeze
10 FILE_PATHS = %w[/etc/ssh /usr/local/etc/ssh /etc /usr/local/etc /etc/opt/ssh].freeze
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { retrieve_info(fact_name) }
17 end
18
19 def retrieve_info(fact_name)
20 ssh_list = []
21 FILE_PATHS.each do |file_path|
22 next unless File.directory?(file_path)
23
24 FILE_NAMES.each do |file_name|
25 file_content = Facter::Util::FileHelper.safe_read(File.join(file_path, file_name), nil)
26 next unless file_content
27
28 key_type, key = file_content.split(' ')
29 ssh = Facter::Util::Resolvers::SshHelper.create_ssh(key_type, key)
30 ssh_list << ssh if ssh
31 end
32 end
33 @fact_list[:ssh] = ssh_list
34 @fact_list[fact_name]
35 end
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class SuseRelease < BaseResolver
5 # :name
6 # :version
7 # :codename
8
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_suse_release(fact_name) }
16 end
17
18 def read_suse_release(fact_name)
19 output = Facter::Util::FileHelper.safe_read('/etc/SuSE-release', nil)
20 return @fact_list[fact_name] = nil if output.nil?
21
22 output_strings = output.split(' ')
23
24 @fact_list[:name] = output_strings[0]
25 @fact_list[:version] = output_strings[1]
26 @fact_list[:id] = @fact_list[:name].downcase
27
28 @fact_list[fact_name]
29 end
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class SwVers < BaseResolver
5 # :productname
6 # :productversion
7 # :buildversion
8
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { software_version_system_call(fact_name) }
16 end
17
18 def software_version_system_call(fact_name)
19 output = Facter::Core::Execution.execute('sw_vers', logger: log)
20 release_info = output.delete("\t").split("\n").map { |e| e.split(':') }
21 result = Hash[*release_info.flatten]
22 result.each { |k, v| @fact_list[k.downcase.to_sym] = v }
23 @fact_list[fact_name]
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Timezone < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { determine_timezone }
12 end
13
14 def determine_timezone
15 @fact_list[:timezone] = Time.now.localtime.strftime('%Z')
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Uname < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { uname_system_call(fact_name) }
12 end
13
14 def uname_system_call(fact_name)
15 output = Facter::Core::Execution.execute('uname -m &&
16 uname -n &&
17 uname -p &&
18 uname -r &&
19 uname -s &&
20 uname -v', logger: log)
21
22 build_fact_list(output)
23
24 @fact_list[fact_name]
25 end
26
27 def build_fact_list(output)
28 uname_results = output.split("\n")
29
30 @fact_list[:machine] = uname_results[0].strip
31 @fact_list[:nodename] = uname_results[1].strip
32 @fact_list[:processor] = uname_results[2].strip
33 @fact_list[:kernelrelease] = uname_results[3].strip
34 @fact_list[:kernelname] = uname_results[4].strip
35 @fact_list[:kernelversion] = uname_results[5].strip
36 end
37 end
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Uptime < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { uptime_system_call(fact_name) }
12 end
13
14 def uptime_system_call(fact_name)
15 seconds = Facter::Util::Facts::UptimeParser.uptime_seconds_unix
16 build_fact_list(seconds)
17
18 @fact_list[fact_name]
19 end
20
21 def build_fact_list(seconds)
22 return @fact_list[:uptime] = 'unknown' unless seconds
23
24 @fact_list = Facter::Util::Resolvers::UptimeHelper.create_uptime_hash(seconds)
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class VirtWhat < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { retrieve_from_virt_what(fact_name) }
12 end
13
14 def retrieve_from_virt_what(fact_name)
15 output = Facter::Core::Execution.execute('virt-what', logger: log)
16
17 @fact_list[:vm] = determine_xen(output)
18 @fact_list[:vm] ||= determine_other(output)
19 retrieve_vserver unless @fact_list[:vserver]
20
21 @fact_list[fact_name]
22 end
23
24 def determine_xen(output)
25 xen_info = /^xen\n.*/.match(output)
26
27 return unless xen_info
28
29 xen_info = xen_info.to_s
30 return 'xenu' if xen_info =~ /xen-domu/
31 return 'xenhvm' if xen_info =~ /xen-hvm/
32 return 'xen0' if xen_info =~ /xen-dom0/
33 end
34
35 def determine_other(output)
36 values = output.split("\n")
37 other_vm = values.first
38 return unless other_vm
39
40 return 'zlinux' if other_vm =~ /ibm_systemz/
41 return retrieve_vserver if other_vm =~ /linux_vserver/
42 return (values - ['redhat']).first if values.include?('redhat')
43
44 other_vm
45 end
46
47 def retrieve_vserver
48 proc_status_content = Facter::Util::FileHelper.safe_readlines('/proc/self/status', nil)
49 return unless proc_status_content
50
51 proc_status_content.each do |line|
52 parts = line.split("\s")
53 next unless parts.size.equal?(2)
54
55 next unless parts[0] =~ /^s_context:|^VxID:/
56 return @fact_list[:vserver] = 'vserver_host' if parts[1] == '0'
57
58 return @fact_list[:vserver] = 'vserver'
59 end
60 end
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Vmware < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { vmware_command(fact_name) }
12 end
13
14 def vmware_command(fact_name)
15 output = Facter::Core::Execution.execute('vmware -v', logger: log)
16 return if output.empty?
17
18 parts = output.split("\s")
19 return unless parts.size.equal?(2)
20
21 @fact_list[:vm] = "#{parts[0].downcase}_#{parts[1].downcase}"
22 @fact_list[fact_name]
23 end
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Windows
5 class AioAgentVersion < BaseResolver
6 REGISTRY_PATH = 'SOFTWARE\\Puppet Labs\\Puppet'
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_version(fact_name) }
14 end
15
16 def read_version(fact_name)
17 ::Win32::Registry::HKEY_LOCAL_MACHINE.open(REGISTRY_PATH) do |reg|
18 build_fact_list(reg)
19 end
20
21 @fact_list[fact_name]
22 rescue Win32::Registry::Error
23 log.debug("The registry path #{REGISTRY_PATH} does not exist")
24 end
25
26 def build_fact_list(reg)
27 puppet_aio_path = read_for_64_bit(reg) || read_for_32_bit(reg)
28
29 return if puppet_aio_path.nil? || puppet_aio_path.empty?
30
31 puppet_aio_version_path = File.join(puppet_aio_path, 'VERSION')
32 aio_agent_version = Facter::Util::FileHelper.safe_read(puppet_aio_version_path, nil)&.chomp
33
34 @fact_list[:aio_agent_version] = aio_agent_version&.match(/^\d+\.\d+\.\d+(\.\d+){0,2}/)&.to_s
35 end
36
37 def read_for_64_bit(reg)
38 reg.read('RememberedInstallDir64')[1]
39 rescue Win32::Registry::Error
40 log.debug('Could not read Puppet AIO path from 64 bit registry')
41 nil
42 end
43
44 def read_for_32_bit(reg)
45 reg.read('RememberedInstallDir')[1]
46 rescue Win32::Registry::Error
47 log.debug('Could not read Puppet AIO path from 32 bit registry')
48 nil
49 end
50 end
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class DMIBios < BaseResolver
5 @log = Facter::Log.new(self)
6 init_resolver
7
8 class << self
9 # Manufacturer
10 # SerialNumber
11
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_fact_from_bios(fact_name) }
16 end
17
18 def read_fact_from_bios(fact_name)
19 win = Facter::Util::Windows::Win32Ole.new
20
21 bios = win.return_first('SELECT Manufacturer,SerialNumber from Win32_BIOS')
22 unless bios
23 @log.debug 'WMI query returned no results for Win32_BIOS with values Manufacturer and SerialNumber.'
24 return
25 end
26
27 build_fact_list(bios)
28
29 @fact_list[fact_name]
30 end
31
32 def build_fact_list(bios)
33 @fact_list[:manufacturer] = bios.Manufacturer
34 @fact_list[:serial_number] = bios.SerialNumber
35 end
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class DMIComputerSystem < BaseResolver
5 @log = Facter::Log.new(self)
6 init_resolver
7
8 class << self
9 # Name
10 # UUID
11
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_fact_from_computer_system(fact_name) }
16 end
17
18 def read_fact_from_computer_system(fact_name)
19 win = Facter::Util::Windows::Win32Ole.new
20 computersystem = win.return_first('SELECT Name,UUID FROM Win32_ComputerSystemProduct')
21 unless computersystem
22 @log.debug 'WMI query returned no results for Win32_ComputerSystemProduct with values Name and UUID.'
23 return
24 end
25
26 build_fact_list(computersystem)
27
28 @fact_list[fact_name]
29 end
30
31 def build_fact_list(computersys)
32 @fact_list[:name] = computersys.Name
33 @fact_list[:uuid] = computersys.UUID
34 end
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3
4 FFI.typedef :uint16, :word
5 FFI.typedef :uint32, :dword
6 FFI.typedef :uintptr_t, :handle
7 FFI.typedef :buffer_inout, :lpwstr
8 FFI.typedef :pointer, :lpcvoid
9 FFI.typedef :pointer, :lpvoid
10 FFI.typedef :pointer, :lpdword
11 FFI.typedef :pointer, :pdword
12 FFI.typedef :pointer, :phandle
13 FFI.typedef :pointer, :pbool
14 FFI.typedef :pointer, :ulong_ptr
15 FFI.typedef :uint32, :win32_ulong
16 FFI.typedef :int32, :win32_long
17 FFI.typedef :int32, :win32_bool
18 FFI.typedef :uint16, :wchar
19 FFI.typedef :uintptr_t, :hwnd
20
21 ERROR_MORE_DATA = 234
22 MAX_PATH = 32_767
23
24 module FFI
25 WIN32FALSE = 0
26 END_OF_WCHAR_STRING = (+"\0\0").force_encoding(Encoding::UTF_16LE).freeze
27
28 class Pointer
29 def read_wide_string_with_length(char_length)
30 # char_length is number of wide chars (typically excluding NULLs), *not* bytes
31 str = get_bytes(0, char_length * 2).force_encoding(Encoding::UTF_16LE)
32 str.encode(Encoding::UTF_8, str.encoding)
33 end
34
35 def read_wide_string_without_length(replace_invalid_chars: false)
36 wide_character = get_bytes(0, 2)
37 wide_character.force_encoding(Encoding::UTF_16LE)
38 i = 2
39 str = []
40
41 while wide_character != END_OF_WCHAR_STRING
42 str << wide_character
43 wide_character = get_bytes(i, 2)
44 wide_character.force_encoding(Encoding::UTF_16LE)
45 i += 2
46 end
47
48 if replace_invalid_chars
49 str.join.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
50 else
51 str.join.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8)
52 end
53 end
54
55 def read_win32_bool
56 # BOOL is always a 32-bit integer in Win32
57 # some Win32 APIs return 1 for true, while others are non-0
58 read_int32 != WIN32FALSE
59 end
60 end
61
62 class Struct
63 def self.read_list(first_address)
64 instance = new(first_address)
65 while instance.to_ptr != Pointer::NULL
66 yield(instance)
67 instance = new(instance[:Next])
68 end
69 end
70 end
71 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3 require 'facter/resolvers/windows/ffi/system_info'
4
5 module HardwareFFI
6 extend FFI::Library
7
8 ffi_convention :stdcall
9 ffi_lib :kernel32
10 attach_function :GetNativeSystemInfo, [:pointer], :void
11
12 PROCESSOR_ARCHITECTURE_INTEL = 0
13 PROCESSOR_ARCHITECTURE_ARM = 5
14 PROCESSOR_ARCHITECTURE_IA64 = 6
15 PROCESSOR_ARCHITECTURE_AMD64 = 9
16 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3
4 module IdentityFFI
5 extend FFI::Library
6
7 ffi_convention :stdcall
8 ffi_lib :secur32
9 attach_function :GetUserNameExW, %i[uint32 lpwstr pointer], :win32_bool
10
11 ffi_convention :stdcall
12 ffi_lib :shell32
13 attach_function :IsUserAnAdmin, [], :win32_bool
14
15 def self.privileged?
16 result = self.IsUserAnAdmin()
17 result && result != FFI::WIN32FALSE
18 end
19 end
0 # frozen_string_literal: true
1
2 MAX_ADAPTER_ADDRESS_LENGTH = 8
3 MAX_DHCPV6_DUID_LENGTH = 130
4
5 class SockAddr < FFI::Struct
6 layout(
7 :sa_family, :ushort,
8 :sa_data, [:uint8, 14]
9 )
10 end
11
12 class SocketAddress < FFI::Struct
13 layout(
14 :lpSockaddr, :pointer,
15 :iSockaddrLength, :int32
16 )
17 end
18
19 module Enums
20 extend FFI::Library
21
22 IP_PREFIX_ORIGIN = enum(
23 :IpPrefixOriginOther, 0,
24 :IpPrefixOriginManual,
25 :IpPrefixOriginWellKnown,
26 :IpPrefixOriginDhcp,
27 :IpPrefixOriginRouterAdvertisement,
28 :IpPrefixOriginUnchanged
29 )
30
31 IP_SUFFIX_ORIGIN = enum(
32 :NlsoOther, 0,
33 :NlsoManual,
34 :NlsoWellKnown,
35 :NlsoDhcp,
36 :NlsoLinkLayerAddress,
37 :NlsoRandom,
38 :IpSuffixOriginOther,
39 :IpSuffixOriginManual,
40 :IpSuffixOriginWellKnown,
41 :IpSuffixOriginDhcp,
42 :IpSuffixOriginLinkLayerAddress,
43 :IpSuffixOriginRandom,
44 :IpSuffixOriginUnchanged
45 )
46
47 IP_DAD_STATE = enum(
48 :NldsInvalid, 0,
49 :NldsTentative,
50 :NldsDuplicate,
51 :NldsDeprecated,
52 :NldsPreferred,
53 :IpDadStateInvalid,
54 :IpDadStateTentative,
55 :IpDadStateDuplicate,
56 :IpDadStateDeprecated,
57 :IpDadStatePreferred
58 )
59
60 IF_CONNECTION_TYPE = enum(
61 :NET_IF_CONNECTION_DEDICATED, 1,
62 :NET_IF_CONNECTION_PASSIVE,
63 :NET_IF_CONNECTION_DEMAND,
64 :NET_IF_CONNECTION_MAXIMUM
65 )
66
67 TUNNEL_TYPE = enum(
68 :TUNNEL_TYPE_NONE, 0,
69 :TUNNEL_TYPE_OTHER,
70 :TUNNEL_TYPE_DIRECT,
71 :TUNNEL_TYPE_6TO4,
72 :TUNNEL_TYPE_ISATAP,
73 :TUNNEL_TYPE_TEREDO,
74 :TUNNEL_TYPE_IPHTTPS
75 )
76 end
77
78 class IpAdapterUnicastAddressXPUnionStruct < FFI::Struct
79 layout(
80 :Length, :win32_ulong,
81 :Flags, :dword
82 )
83 end
84
85 class IpAdapterUnicastAddressXPUnion < FFI::Union
86 layout(
87 :Aligment, :ulong_long,
88 :Struct, IpAdapterUnicastAddressXPUnionStruct
89 )
90 end
91
92 class IpAdapterUnicastAddressLH < FFI::Struct
93 layout(
94 :Union, IpAdapterUnicastAddressXPUnion,
95 :Next, :pointer,
96 :Address, SocketAddress,
97 :PrefixOrigin, Enums::IP_PREFIX_ORIGIN,
98 :SuffixOrigin, Enums::IP_SUFFIX_ORIGIN,
99 :DadState, Enums::IP_DAD_STATE,
100 :ValidLifetime, :win32_ulong,
101 :PreferredLifetime, :win32_ulong,
102 :LeaseLifetime, :win32_ulong,
103 :OnLinkPrefixLength, :uint8
104 )
105 end
106
107 class AdapterAddressStruct < FFI::Struct
108 layout(
109 :Length, :win32_ulong,
110 :IfIndex, :dword
111 )
112 end
113
114 class AdapterAddressAligmentUnion < FFI::Union
115 layout(
116 :Aligment, :uint64,
117 :Struct, AdapterAddressStruct
118 )
119 end
120
121 class IpAdapterAddressesLh < FFI::Struct
122 layout(
123 :Union, AdapterAddressAligmentUnion,
124 :Next, :pointer,
125 :AdapterName, :pointer,
126 :FirstUnicastAddress, :pointer,
127 :FirstAnycastAddress, :pointer,
128 :FirstMulticastAddress, :pointer,
129 :FirstDnsServerAddress, :pointer,
130 :DnsSuffix, :pointer,
131 :Description, :pointer,
132 :FriendlyName, :pointer,
133 :PhysicalAddress, [:uchar, MAX_ADAPTER_ADDRESS_LENGTH],
134 :PhysicalAddressLength, :win32_ulong,
135 :Flags, :win32_ulong,
136 :Mtu, :win32_ulong,
137 :IfType, :dword,
138 :OperStatus, :uint8,
139 :Ipv6IfIndex, :dword,
140 :ZoneIndices, [:win32_ulong, 16],
141 :FirstPrefix, :pointer,
142 :TransmitLinkSpeed, :ulong_long,
143 :ReceiveLinkSpeed, :ulong_long,
144 :FirstWinsServerAddress, :pointer,
145 :FirstGatewayAddress, :pointer,
146 :Ipv4Metric, :win32_ulong,
147 :Ipv6Metric, :win32_ulong,
148 :Luid, :ulong_long,
149 :Dhcpv4Server, SocketAddress,
150 :CompartmentId, :uint32, # https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/shared/ifdef.h
151 :NetworkGuid, [:uint8, 16], # https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
152 :ConnectionType, Enums::IF_CONNECTION_TYPE,
153 :TunnelType, Enums::TUNNEL_TYPE,
154 :Dhcpv6Server, SocketAddress,
155 :Dhcpv6ClientDuid, [:uchar, MAX_DHCPV6_DUID_LENGTH],
156 :Dhcpv6ClientDuidLength, :win32_ulong,
157 :Dhcpv6Iaid, :win32_ulong,
158 :FirstDnsSuffix, :pointer
159 )
160 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3 require 'facter/resolvers/windows/ffi/os_version_info_ex'
4
5 module KernelFFI
6 extend FFI::Library
7
8 ffi_convention :stdcall
9 ffi_lib :ntdll
10 attach_function :RtlGetVersion, [:pointer], :int32
11
12 STATUS_SUCCESS = 0
13 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3 require 'facter/resolvers/windows/ffi/performance_information'
4
5 module MemoryFFI
6 extend FFI::Library
7
8 ffi_convention :stdcall
9 ffi_lib :psapi
10 attach_function :GetPerformanceInfo, %i[pointer dword], :win32_bool
11 end
0 # frozen_string_literal: true
1
2 class NetworkUtils
3 @log = Facter::Log.new(self)
4 class << self
5 def address_to_string(addr)
6 return if addr[:lpSockaddr] == FFI::Pointer::NULL
7
8 size = FFI::MemoryPointer.new(NetworkingFFI::INET6_ADDRSTRLEN + 1)
9 buffer = FFI::MemoryPointer.new(:wchar, NetworkingFFI::INET6_ADDRSTRLEN + 1)
10 error = nil
11 3.times do
12 error = NetworkingFFI::WSAAddressToStringW(addr[:lpSockaddr], addr[:iSockaddrLength],
13 FFI::Pointer::NULL, buffer, size)
14 break if error.zero?
15 end
16 unless error.zero?
17 @log.debug 'address to string translation failed!'
18 return
19 end
20 extract_address(buffer)
21 end
22
23 def extract_address(addr)
24 addr.read_wide_string_without_length.split('%').first
25 end
26
27 def ignored_ip_address(addr)
28 addr.empty? || addr.start_with?('127.', '169.254.') || addr.start_with?('fe80') || addr.eql?('::1')
29 end
30
31 def find_mac_address(adapter)
32 adapter[:PhysicalAddress].first(adapter[:PhysicalAddressLength])
33 .map { |e| format('%<mac_address>02x', mac_address: e.to_i) }.join(':').upcase
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3 require 'facter/resolvers/windows/ffi/network_utils'
4 require 'facter/resolvers/windows/ffi/ip_adapter_addresses_lh'
5
6 module NetworkingFFI
7 extend FFI::Library
8
9 ffi_convention :stdcall
10 ffi_lib :iphlpapi
11 attach_function :GetAdaptersAddresses, %i[uint32 uint32 pointer pointer pointer], :dword
12
13 ffi_convention :stdcall
14 ffi_lib :ws2_32
15 attach_function :WSAAddressToStringW, %i[pointer dword pointer pointer pointer], :int32
16
17 AF_UNSPEC = 0
18 GAA_FLAG_SKIP_ANYCAST = 2
19 GAA_FLAG_SKIP_MULTICAST = 4
20 GAA_FLAG_SKIP_DNS_SERVER = 8
21 BUFFER_LENGTH = 15_000
22 ERROR_SUCCES = 0
23 ERROR_BUFFER_OVERFLOW = 111
24 ERROR_NO_DATA = 232
25 IF_OPER_STATUS_UP = 1
26 IF_OPER_STATUS_DOWN = 2
27 IF_TYPE_ETHERNET_CSMACD = 6
28 IF_TYPE_IEEE80211 = 71
29 IP_ADAPTER_DHCP_ENABLED = 4
30 INET6_ADDRSTRLEN = 46
31 AF_INET = 2
32 AF_INET6 = 23
33 end
0 # frozen_string_literal: true
1
2 class OsVersionInfoEx < FFI::Struct
3 layout(
4 :dwOSVersionInfoSize, :win32_ulong,
5 :dwMajorVersion, :win32_ulong,
6 :dwMinorVersion, :win32_ulong,
7 :dwBuildNumber, :win32_ulong,
8 :dwPlatformId, :win32_ulong,
9 :szCSDVersion, [:wchar, 128],
10 :wServicePackMajor, :ushort,
11 :wServicePackMinor, :ushort,
12 :wSuiteMask, :ushort,
13 :wProductType, :uchar,
14 :wReserved, :uchar
15 )
16 end
0 # frozen_string_literal: true
1
2 class PerformanceInformation < FFI::Struct
3 layout(
4 :cb, :dword,
5 :CommitTotal, :size_t,
6 :CommitLimit, :size_t,
7 :CommitPeak, :size_t,
8 :PhysicalTotal, :size_t,
9 :PhysicalAvailable, :size_t,
10 :SystemCache, :size_t,
11 :KernelTotal, :size_t,
12 :KernelPaged, :size_t,
13 :KernelNonpaged, :size_t,
14 :PageSize, :size_t,
15 :HandleCount, :dword,
16 :ProcessCount, :dword,
17 :ThreadCount, :dword
18 )
19 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3
4 module System32FFI
5 extend FFI::Library
6
7 ffi_convention :stdcall
8 ffi_lib :kernel32
9 attach_function :IsWow64Process, %i[handle pointer], :win32_bool
10
11 ffi_convention :stdcall
12 ffi_lib :kernel32
13 attach_function :GetCurrentProcess, [], :handle
14
15 CSIDL_WINDOWS = 0x0024
16 H_OK = 0
17 end
0 # frozen_string_literal: true
1
2 class DummyStructName < FFI::Struct
3 layout(
4 :wProcessorArchitecture, :word,
5 :wReserved, :word
6 )
7 end
8
9 class DummyUnionName < FFI::Union
10 layout(
11 :dwOemId, :dword,
12 :dummystructname, DummyStructName
13 )
14 end
15
16 class SystemInfo < FFI::Struct
17 layout(
18 :dummyunionname, DummyUnionName,
19 :dwPageSize, :dword,
20 :lpMinimumApplicationAddress, :pointer,
21 :lpMaximumApplicationAddress, :pointer,
22 :dwActiveProcessorMask, :pdword,
23 :dwNumberOfProcessors, :dword,
24 :dwProcessorType, :dword,
25 :dwAllocationGranularity, :dword,
26 :wProcessorLevel, :word,
27 :wProcessorRevision, :word
28 )
29 end
0 # frozen_string_literal: true
1
2 require 'facter/resolvers/windows/ffi/ffi'
3
4 module WinnlsFFI
5 extend FFI::Library
6
7 ffi_convention :stdcall
8 ffi_lib :kernel32
9 attach_function :GetACP, [], :uint
10 end
0 # frozen_string_literal: true
1
2 require 'win32/registry'
3
4 module Facter
5 module Resolvers
6 module Windows
7 class Fips < BaseResolver
8 # :fips_enabled
9 init_resolver
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { read_fact_from_registry(fact_name) }
16 end
17
18 def read_fact_from_registry(fact_name)
19 reg = ::Win32::Registry::HKEY_LOCAL_MACHINE
20 .open('System\\CurrentControlSet\\Control\\Lsa\\FipsAlgorithmPolicy')
21 @fact_list[:fips_enabled] = reg['Enabled'] != 0 if reg.any? { |name, _value| name == 'Enabled' }
22 reg.close
23
24 @fact_list[:fips_enabled] ||= false
25 @fact_list[fact_name]
26 end
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class HardwareArchitecture < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_hardware_information(fact_name) }
12 end
13
14 def read_hardware_information(fact_name)
15 require 'facter/resolvers/windows/ffi/hardware_ffi'
16
17 sys_info_ptr = FFI::MemoryPointer.new(SystemInfo.size)
18 HardwareFFI::GetNativeSystemInfo(sys_info_ptr)
19 sys_info = SystemInfo.new(sys_info_ptr)
20
21 hard = determine_hardware(sys_info)
22 arch = determine_architecture(hard)
23 build_facts_list(hardware: hard, architecture: arch)
24 @fact_list[fact_name]
25 end
26
27 def determine_hardware(sys_info)
28 union = sys_info[:dummyunionname]
29 struct = union[:dummystructname]
30 case struct[:wProcessorArchitecture]
31 when HardwareFFI::PROCESSOR_ARCHITECTURE_AMD64
32 'x86_64'
33 when HardwareFFI::PROCESSOR_ARCHITECTURE_ARM
34 'arm'
35 when HardwareFFI::PROCESSOR_ARCHITECTURE_IA64
36 'ia64'
37 when HardwareFFI::PROCESSOR_ARCHITECTURE_INTEL
38 family = sys_info[:wProcessorLevel] > 5 ? 6 : sys_info[:wProcessorLevel]
39 "i#{family}86"
40 else
41 'unknown'
42 end
43 end
44
45 def determine_architecture(hardware)
46 case hardware
47 when /i[3456]86/
48 'x86'
49 when 'x86_64'
50 'x64'
51 else
52 hardware
53 end
54 end
55
56 def build_facts_list(facts)
57 @fact_list[:hardware] = facts[:hardware]
58 @fact_list[:architecture] = facts[:architecture]
59 end
60 end
61 end
62 end
63 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Identity < BaseResolver
5 NAME_SAM_COMPATIBLE = 2
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
10 class << self
11 private
12
13 def post_resolve(fact_name, _options)
14 @fact_list.fetch(fact_name) { retrieve_facts(fact_name) }
15 end
16
17 def find_username
18 require 'facter/resolvers/windows/ffi/identity_ffi'
19
20 size_ptr = FFI::MemoryPointer.new(:win32_ulong, 1)
21 IdentityFFI::GetUserNameExW(NAME_SAM_COMPATIBLE, FFI::Pointer::NULL, size_ptr)
22 if FFI.errno != ERROR_MORE_DATA
23 @log.debug "failure resolving identity facts: #{FFI.errno}"
24 return
25 end
26
27 name_ptr = FFI::MemoryPointer.new(:wchar, size_ptr.read_uint32)
28 if IdentityFFI::GetUserNameExW(NAME_SAM_COMPATIBLE, name_ptr, size_ptr) == FFI::WIN32FALSE
29 @log.debug "failure resolving identity facts: #{FFI.errno}"
30 return
31 end
32
33 { user: name_ptr.read_wide_string_with_length(size_ptr.read_uint32), privileged: IdentityFFI.privileged? }
34 end
35
36 def retrieve_facts(fact_name)
37 result = find_username
38 return unless result
39
40 build_fact_list(result)
41 @fact_list[fact_name]
42 end
43
44 def build_fact_list(facts)
45 @fact_list[:user] = facts[:user]
46 @fact_list[:privileged] = facts[:privileged]
47 end
48 end
49 end
50 end
51 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Kernel < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_os_version_information(fact_name) }
14 end
15
16 def read_os_version_information(fact_name)
17 require 'facter/resolvers/windows/ffi/kernel_ffi'
18
19 ver_ptr = FFI::MemoryPointer.new(OsVersionInfoEx.size)
20 ver = OsVersionInfoEx.new(ver_ptr)
21 ver[:dwOSVersionInfoSize] = OsVersionInfoEx.size
22
23 if KernelFFI::RtlGetVersion(ver_ptr) != KernelFFI::STATUS_SUCCESS
24 @log.debug 'Calling Windows RtlGetVersion failed'
25 return
26 end
27
28 result = { major: ver[:dwMajorVersion], minor: ver[:dwMinorVersion], build: ver[:dwBuildNumber] }
29 build_facts_list(result)
30
31 @fact_list[fact_name]
32 end
33
34 def build_facts_list(result)
35 @fact_list[:kernelversion] = "#{result[:major]}.#{result[:minor]}.#{result[:build]}"
36 @fact_list[:kernelmajorversion] = "#{result[:major]}.#{result[:minor]}"
37 @fact_list[:kernel] = 'windows'
38 end
39 end
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Memory < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { validate_info(fact_name) }
14 end
15
16 def read_performance_information
17 require 'facter/resolvers/windows/ffi/memory_ffi'
18
19 state_ptr = FFI::MemoryPointer.new(PerformanceInformation.size)
20 if MemoryFFI::GetPerformanceInfo(state_ptr, state_ptr.size) == FFI::WIN32FALSE
21 @log.debug 'Resolving memory facts failed'
22 return
23 end
24
25 # we need to enlarge the scope of this pointer so that Ruby GC will not free the memory.
26 # If the pointer if freed, Lifreq structures will contain garbage from memory.
27 @long_living_pointer = state_ptr
28
29 PerformanceInformation.new(state_ptr)
30 end
31
32 def calculate_memory
33 state = read_performance_information
34 return unless state
35
36 total_bytes = state[:PhysicalTotal] * state[:PageSize]
37 available_bytes = state[:PhysicalAvailable] * state[:PageSize]
38 if total_bytes.zero? || available_bytes.zero?
39 @log.debug 'Available or Total bytes are zero could not proceed further'
40 return
41 end
42
43 { total_bytes: total_bytes, available_bytes: available_bytes, used_bytes: total_bytes - available_bytes }
44 end
45
46 def validate_info(fact_name)
47 result = calculate_memory
48 return unless result
49
50 build_facts_list(result)
51 @fact_list[fact_name]
52 end
53
54 def build_facts_list(result)
55 @fact_list[:total_bytes] = result[:total_bytes]
56 @fact_list[:available_bytes] = result[:available_bytes]
57 @fact_list[:used_bytes] = result[:used_bytes]
58 @fact_list[:capacity] = format('%<capacity>.2f',
59 capacity: (result[:used_bytes] / result[:total_bytes].to_f * 100)) + '%'
60 end
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 require 'win32/registry'
3
4 module Facter
5 module Resolvers
6 class NetKVM < BaseResolver
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_fact_from_registry(fact_name) }
14 end
15
16 def read_fact_from_registry(fact_name)
17 reg = ::Win32::Registry::HKEY_LOCAL_MACHINE.open('SYSTEM\\CurrentControlSet\\Services')
18 build_fact_list(reg)
19 reg.close
20
21 @fact_list[fact_name]
22 end
23
24 def build_fact_list(reg)
25 # rubocop:disable Performance/InefficientHashSearch
26 @fact_list[:kvm] = reg.keys.include?('netkvm')
27 # rubocop:enable Performance/InefficientHashSearch
28 end
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Windows
5 class Networking < BaseResolver
6 @log = Facter::Log.new(self)
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_network_information(fact_name) }
14 end
15
16 def read_network_information(fact_name)
17 require 'facter/resolvers/windows/ffi/networking_ffi'
18
19 size_ptr = FFI::MemoryPointer.new(NetworkingFFI::BUFFER_LENGTH)
20 adapter_addresses = FFI::MemoryPointer.new(IpAdapterAddressesLh.size, NetworkingFFI::BUFFER_LENGTH)
21 flags = NetworkingFFI::GAA_FLAG_SKIP_ANYCAST |
22 NetworkingFFI::GAA_FLAG_SKIP_MULTICAST | NetworkingFFI::GAA_FLAG_SKIP_DNS_SERVER
23
24 return unless (adapter_addresses = get_adapter_addresses(size_ptr, adapter_addresses, flags))
25
26 retrieve_domain_from_registry
27
28 iterate_list(adapter_addresses)
29
30 Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
31
32 @fact_list[fact_name]
33 end
34
35 def get_adapter_addresses(size_ptr, adapter_addresses, flags)
36 error = nil
37 3.times do
38 error = NetworkingFFI::GetAdaptersAddresses(NetworkingFFI::AF_UNSPEC, flags,
39 FFI::Pointer::NULL, adapter_addresses, size_ptr)
40 break if error == NetworkingFFI::ERROR_SUCCES
41
42 if error == NetworkingFFI::ERROR_BUFFER_OVERFLOW
43 adapter_addresses = FFI::MemoryPointer.new(IpAdapterAddressesLh.size, NetworkingFFI::BUFFER_LENGTH)
44 else
45 @log.debug 'Unable to retrieve networking facts!'
46 return nil
47 end
48 end
49 return nil unless error.zero?
50
51 adapter_addresses
52 end
53
54 def adapter_down?(adapter)
55 adapter[:OperStatus] != NetworkingFFI::IF_OPER_STATUS_UP ||
56 ![NetworkingFFI::IF_TYPE_ETHERNET_CSMACD, NetworkingFFI::IF_TYPE_IEEE80211].include?(adapter[:IfType])
57 end
58
59 def retrieve_dhcp_server(adapter)
60 if !(adapter[:Flags] & NetworkingFFI::IP_ADAPTER_DHCP_ENABLED).zero? &&
61 adapter[:Union][:Struct][:Length] >= IpAdapterAddressesLh.size
62 NetworkUtils.address_to_string(adapter[:Dhcpv4Server])
63 end
64 end
65
66 def iterate_list(adapter_addresses)
67 net_interface = {}
68 IpAdapterAddressesLh.read_list(adapter_addresses) do |adapter_address|
69 if adapter_down?(adapter_address)
70 adapter_address = IpAdapterAddressesLh.new(adapter_address[:Next])
71 next
72 end
73 if !@fact_list[:domain] || @fact_list[:domain].empty?
74 @fact_list[:domain] = adapter_address[:DnsSuffix]
75 .read_wide_string_without_length(replace_invalid_chars: true)
76 end
77 name = adapter_address[:FriendlyName].read_wide_string_without_length(replace_invalid_chars: true)
78 net_interface[name] = build_interface_info(adapter_address, name)
79 end
80
81 @fact_list[:interfaces] = net_interface unless net_interface.empty?
82 end
83
84 def build_interface_info(adapter_address, name)
85 hash = {}
86
87 hash[:dhcp] = retrieve_dhcp_server(adapter_address)
88 hash[:mtu] = adapter_address[:Mtu]
89
90 bindings = find_ip_addresses(adapter_address[:FirstUnicastAddress], name)
91 hash[:bindings] = bindings[:ipv4] unless bindings[:ipv4].empty?
92 hash[:bindings6] = bindings[:ipv6] unless bindings[:ipv6].empty?
93 hash[:mac] = NetworkUtils.find_mac_address(adapter_address)
94 hash
95 end
96
97 def find_ip_addresses(unicast_addresses, name)
98 bindings = {}
99 bindings[:ipv6] = []
100 bindings[:ipv4] = []
101
102 IpAdapterUnicastAddressLH.read_list(unicast_addresses) do |unicast|
103 addr = NetworkUtils.address_to_string(unicast[:Address])
104 unless addr
105 unicast = IpAdapterUnicastAddressLH.new(unicast[:Next])
106 next
107 end
108
109 sock_addr = SockAddr.new(unicast[:Address][:lpSockaddr])
110 add_ip_data(addr, unicast, sock_addr, bindings)
111 find_primary_interface(sock_addr, name, addr)
112 end
113 bindings
114 end
115
116 def add_ip_data(addr, unicast, sock_addr, bindings)
117 result = find_bindings(sock_addr, unicast, addr)
118 return unless result
119
120 bindings[:ipv6] << result if sock_addr[:sa_family] == NetworkingFFI::AF_INET6
121 bindings[:ipv4] << result if sock_addr[:sa_family] == NetworkingFFI::AF_INET
122 end
123
124 def find_bindings(sock_addr, unicast, addr)
125 return unless [NetworkingFFI::AF_INET, NetworkingFFI::AF_INET6].include?(sock_addr[:sa_family])
126
127 Facter::Util::Resolvers::Networking.build_binding(addr, unicast[:OnLinkPrefixLength])
128 end
129
130 def find_primary_interface(sock_addr, name, addr)
131 if !@fact_list[:primary_interface] &&
132 ([NetworkingFFI::AF_INET, NetworkingFFI::AF_INET6].include?(sock_addr[:sa_family]) &&
133 !::Facter::Util::Resolvers::Networking.ignored_ip_address(addr))
134 @fact_list[:primary_interface] = name.to_s
135 end
136 end
137
138 def retrieve_domain_from_registry
139 ::Win32::Registry::HKEY_LOCAL_MACHINE.open(
140 'SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters'
141 ) do |key|
142 domain = key['Domain']
143 @fact_list[:domain] = domain if domain
144 end
145 rescue Win32::Registry::Error
146 @log.debug('Could not read TCPIP Parameters from registry')
147 nil
148 end
149 end
150 end
151 end
152 end
153 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Processors < BaseResolver
5 init_resolver
6
7 class << self
8 # Count
9 # Isa
10 # Models
11 # PhysicalCount
12
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { read_fact_from_win32_processor(fact_name) }
17 end
18
19 def read_fact_from_win32_processor(fact_name)
20 win = Facter::Util::Windows::Win32Ole.new
21 query_string = 'SELECT Name,'\
22 'Architecture,'\
23 'NumberOfLogicalProcessors,'\
24 'NumberOfCores FROM Win32_Processor'
25 proc = win.exec_query(query_string)
26 unless proc
27 log.debug 'WMI query returned no results'\
28 'for Win32_Processor with values Name, Architecture and NumberOfLogicalProcessors.'
29 return
30 end
31 result = iterate_proc(proc)
32 cores_threads = calculate_cores_threads(proc, result)
33 build_fact_list(result, cores_threads)
34 @fact_list[fact_name]
35 end
36
37 def iterate_proc(result)
38 models = []
39 isa = nil
40 logical_count = 0
41 result.each do |proc|
42 models << proc.Name
43 logical_count += proc.NumberOfLogicalProcessors if proc.NumberOfLogicalProcessors
44 isa ||= find_isa(proc.Architecture)
45 end
46
47 { models: models,
48 isa: isa,
49 logical_count: logical_processors_count(logical_count, models.count) }
50 end
51
52 def calculate_cores_threads(result_proc, data_proc)
53 cores = 0
54 threads_per_core = 0
55 result_proc.each do |proc|
56 cores = proc.NumberOfCores
57 threads_per_core = if check_hyperthreading(data_proc[:logical_count], cores) ||
58 cores > data_proc[:logical_count]
59 1
60 else
61 data_proc[:logical_count] / (cores * data_proc[:models].size)
62 end
63 end
64 { cores_per_socket: cores,
65 threads_per_core: threads_per_core }
66 end
67
68 def check_hyperthreading(cores, logical_processors)
69 cores == logical_processors
70 end
71
72 def find_isa(arch)
73 architecture_hash =
74 { 0 => 'x86', 1 => 'MIPS', 2 => 'Alpha', 3 => 'PowerPC', 5 => 'ARM', 6 => 'Itanium', 9 => 'x64' }
75 isa = architecture_hash[arch]
76 return isa if isa
77
78 log.debug 'Unable to determine processor type: unknown architecture'
79 end
80
81 def logical_processors_count(logical_count, models_count)
82 if logical_count.zero?
83 models_count
84 else
85 logical_count
86 end
87 end
88
89 def build_fact_list(result, cores_threads)
90 @fact_list[:count] = result[:logical_count]
91 @fact_list[:isa] = result[:isa]
92 @fact_list[:models] = result[:models]
93 @fact_list[:physicalcount] = result[:models].size
94 @fact_list[:cores_per_socket] = cores_threads[:cores_per_socket]
95 @fact_list[:threads_per_core] = cores_threads[:threads_per_core]
96 end
97 end
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 require 'win32/registry'
3
4 module Facter
5 module Resolvers
6 class ProductRelease < BaseResolver
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_fact_from_registry(fact_name) }
14 end
15
16 def read_fact_from_registry(fact_name)
17 reg = ::Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
18 build_fact_list(reg)
19 reg.close
20
21 @fact_list[fact_name]
22 end
23
24 def build_fact_list(reg)
25 reg.each do |name, _value|
26 case name
27 when 'EditionID'
28 @fact_list[:edition_id] = reg[name]
29 when 'InstallationType'
30 @fact_list[:installation_type] = reg[name]
31 when 'ProductName'
32 @fact_list[:product_name] = reg[name]
33 when 'DisplayVersion'
34 @fact_list[:release_id] = reg[name]
35 @fact_list[:display_version] = reg[name]
36 when 'ReleaseId'
37 @fact_list[:release_id] = reg[name] unless @fact_list[:release_id]
38 end
39 end
40 end
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Windows
5 class Ssh < BaseResolver
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
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, _options)
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 = Facter::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 << Facter::Util::Resolvers::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 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class System32 < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { retrieve_windows_binaries_path }
14 end
15
16 def retrieve_windows_binaries_path
17 require 'facter/resolvers/windows/ffi/system32_ffi'
18
19 windows_path = ENV['SystemRoot']
20
21 if !windows_path || windows_path.empty?
22 @log.debug 'Unable to find correct value for SystemRoot enviroment variable'
23 return nil
24 end
25
26 bool_ptr = FFI::MemoryPointer.new(:win32_bool, 1)
27 if System32FFI::IsWow64Process(System32FFI::GetCurrentProcess(), bool_ptr) == FFI::WIN32FALSE
28 @log.debug 'IsWow64Process failed'
29 return
30 end
31
32 @fact_list[:system32] = construct_path(bool_ptr, windows_path)
33 end
34
35 def construct_path(bool_ptr, windows)
36 if bool_ptr.read_win32_bool
37 "#{windows}\\sysnative"
38 else
39 "#{windows}\\system32"
40 end
41 end
42 end
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Windows
5 class Timezone < BaseResolver
6 init_resolver
7
8 class << self
9 private
10
11 def post_resolve(fact_name, _options)
12 @fact_list.fetch(fact_name) { determine_timezone }
13 end
14
15 def determine_timezone
16 timezone = Time.now.zone
17 @fact_list[:timezone] = timezone.force_encoding("CP#{codepage}").encode('UTF-8', invalid: :replace)
18 rescue ArgumentError
19 @fact_list[:timezone] = timezone
20 end
21
22 def codepage
23 result = codepage_from_api
24 result.empty? ? codepage_from_registry : result
25 end
26
27 def codepage_from_registry
28 require 'win32/registry'
29 ::Win32::Registry::HKEY_LOCAL_MACHINE.open('SYSTEM\CurrentControlSet\Control\Nls\CodePage')['ACP']
30 end
31
32 def codepage_from_api
33 require 'facter/resolvers/windows/ffi/winnls_ffi'
34 WinnlsFFI.GetACP.to_s
35 end
36 end
37 end
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 require 'date'
3
4 module Facter
5 module Resolvers
6 module Windows
7 class Uptime < BaseResolver
8 @log = Facter::Log.new(self)
9
10 init_resolver
11
12 class << self
13 private
14
15 def post_resolve(fact_name, _options)
16 @fact_list.fetch(fact_name) { calculate_system_uptime(fact_name) }
17 end
18
19 def subtract_system_uptime_from_ole
20 win = Facter::Util::Windows::Win32Ole.new
21 opsystem = win.return_first('SELECT LocalDateTime,LastBootUpTime FROM Win32_OperatingSystem')
22 unless opsystem
23 @log.debug 'WMI query returned no results'\
24 'for Win32_OperatingSystem with values LocalDateTime and LastBootUpTime.'
25 return
26 end
27
28 local_time = opsystem.LocalDateTime
29 last_bootup = opsystem.LastBootUpTime
30
31 return DateTime.parse(local_time).to_time - DateTime.parse(last_bootup).to_time if local_time && last_bootup
32
33 nil
34 end
35
36 def calculate_system_uptime(fact_name)
37 seconds = subtract_system_uptime_from_ole&.to_i
38 if !seconds || seconds.negative?
39 @log.debug 'Unable to determine system uptime!'
40 return
41 end
42
43 @fact_list = Facter::Util::Resolvers::UptimeHelper.create_uptime_hash(seconds)
44 @fact_list[fact_name]
45 end
46
47 def build_fact_list(system_uptime)
48 @fact_list[:days] = system_uptime[:days]
49 @fact_list[:hours] = system_uptime[:hours]
50 @fact_list[:seconds] = system_uptime[:seconds]
51 @fact_list[:uptime] = system_uptime[:uptime]
52 end
53 end
54 end
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 module Windows
5 class Virtualization < BaseResolver
6 @log = Facter::Log.new(self)
7
8 init_resolver
9
10 class << self
11 # Virtual
12 # Is_Virtual
13
14 MODEL_HASH = { 'VirtualBox' => 'virtualbox', 'VMware' => 'vmware', 'KVM' => 'kvm',
15 'Bochs' => 'bochs', 'Google' => 'gce', 'OpenStack' => 'openstack' }.freeze
16
17 private
18
19 def post_resolve(fact_name, _options)
20 @fact_list.fetch(fact_name) { read_fact_from_computer_system(fact_name) }
21 end
22
23 def read_fact_from_computer_system(fact_name)
24 win = Facter::Util::Windows::Win32Ole.new
25 comp = win.exec_query('SELECT Manufacturer,Model,OEMStringArray FROM Win32_ComputerSystem')
26 unless comp
27 @log.debug 'WMI query returned no results for Win32_ComputerSystem with values'\
28 ' Manufacturer, Model and OEMStringArray.'
29 return
30 end
31
32 build_fact_list(comp)
33 @fact_list[fact_name]
34 end
35
36 def determine_hypervisor_by_model(comp)
37 MODEL_HASH[MODEL_HASH.keys.find { |key| comp.Model =~ /^#{key}/ }]
38 end
39
40 def determine_hypervisor_by_manufacturer(comp)
41 manufacturer = comp.Manufacturer
42 if comp.Model =~ /^Virtual Machine/ && manufacturer =~ /^Microsoft/
43 'hyperv'
44 elsif manufacturer =~ /^Xen/
45 'xen'
46 elsif manufacturer =~ /^Amazon EC2/
47 'kvm'
48 else
49 'physical'
50 end
51 end
52
53 def build_fact_list(comp)
54 @fact_list[:oem_strings] = []
55 @fact_list[:oem_strings] += comp.to_enum.map(&:OEMStringArray).flatten
56
57 comp = comp.to_enum.first
58 hypervisor = determine_hypervisor_by_model(comp) || determine_hypervisor_by_manufacturer(comp)
59
60 @fact_list[:virtual] = hypervisor
61 @fact_list[:is_virtual] = hypervisor.include?('physical') ? false : true
62 end
63 end
64 end
65 end
66 end
67 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class WinOsDescription < BaseResolver
5 @log = Facter::Log.new(self)
6
7 init_resolver
8
9 class << self
10 private
11
12 def post_resolve(fact_name, _options)
13 @fact_list.fetch(fact_name) { read_from_ole(fact_name) }
14 end
15
16 def read_from_ole(fact_name)
17 win = Facter::Util::Windows::Win32Ole.new
18 op_sys = win.return_first('SELECT ProductType,OtherTypeDescription FROM Win32_OperatingSystem')
19 unless op_sys
20 @log.debug 'WMI query returned no results for Win32_OperatingSystem'\
21 'with values ProductType and OtherTypeDescription.'
22 return
23 end
24 @fact_list[:consumerrel] = (op_sys.ProductType == 1)
25 @fact_list[:description] = op_sys.OtherTypeDescription
26 @fact_list[fact_name]
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Wpar < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { read_wpar(fact_name) }
12 end
13
14 def read_wpar(fact_name)
15 output = Facter::Core::Execution.execute('/usr/bin/lparstat -W', logger: log)
16
17 return if output.empty?
18
19 output.each_line do |line|
20 populate_wpar_data(line.split(':').map(&:strip))
21 end
22 @fact_list[fact_name]
23 end
24
25 def populate_wpar_data(key_value)
26 @fact_list[:wpar_key] = key_value[1].to_i if key_value[0] == 'WPAR Key'
27 @fact_list[:wpar_configured_id] = key_value[1].to_i if key_value[0] == 'WPAR Configured ID'
28 end
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Xen < BaseResolver
5 init_resolver
6
7 XEN_PATH = '/proc/xen/capabilities'
8 XEN_TOOLSTACK = '/usr/lib/xen-common/bin/xen-toolstack'
9 XEN_COMMANDS = ['/usr/sbin/xl', '/usr/sbin/xm'].freeze
10
11 class << self
12 private
13
14 def post_resolve(fact_name, _options)
15 @fact_list.fetch(fact_name) { detect_xen(fact_name) }
16 end
17
18 def detect_xen(fact_name)
19 @fact_list[:vm] = detect_xen_type
20 @fact_list[:privileged] = privileged?(@fact_list[:vm])
21 @fact_list[:domains] = detect_domains
22
23 @fact_list[fact_name]
24 end
25
26 def detect_xen_type
27 xen_type = 'xen0' if File.exist?('/dev/xen/evtchn')
28 xen_type = 'xenu' if !xen_type && (File.exist?('/proc/xen') || File.exist?('/dev/xvda1'))
29
30 xen_type
31 end
32
33 def privileged?(xen_type)
34 content = Facter::Util::FileHelper.safe_read(XEN_PATH, nil)
35 content&.strip == 'control_d' || xen_type == 'xen0'
36 end
37
38 def detect_domains
39 domains = []
40 xen_command = find_command
41 return unless xen_command
42
43 output = Facter::Core::Execution.execute("#{xen_command} list", logger: log)
44 return if output.empty?
45
46 output.each_line do |line|
47 next if line =~ /Domain-0|Name/
48
49 domain = line.match(/^([^\s]*)\s/)
50 domain = domain&.captures&.first
51 domains << domain if domain
52 end
53
54 domains
55 end
56
57 def find_command
58 num_stacks = 0
59 XEN_COMMANDS.each do |command|
60 num_stacks += 1 if File.exist?(command)
61 end
62
63 return XEN_TOOLSTACK if num_stacks > 1 && File.exist?(XEN_TOOLSTACK)
64
65 XEN_COMMANDS.each { |command| return command if File.exist?(command) }
66 end
67 end
68 end
69 end
70 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class ZFS < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { zfs_fact(fact_name) }
12 end
13
14 def zfs_fact(fact_name)
15 build_zfs_facts
16 @fact_list[fact_name]
17 end
18
19 def build_zfs_facts
20 output = Facter::Core::Execution.execute('zfs upgrade -v', logger: log)
21 features_list = output.scan(/^\s+(\d+)/).flatten
22
23 return if features_list.empty?
24
25 @fact_list[:zfs_featurenumbers] = features_list.join(',')
26 @fact_list[:zfs_version] = features_list.last
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Resolvers
4 class Zpool < BaseResolver
5 init_resolver
6
7 class << self
8 private
9
10 def post_resolve(fact_name, _options)
11 @fact_list.fetch(fact_name) { zpool_fact(fact_name) }
12 end
13
14 def zpool_fact(fact_name)
15 build_zpool_facts
16 @fact_list[fact_name]
17 end
18
19 def build_zpool_facts
20 output = Facter::Core::Execution.execute('zpool upgrade -v', logger: log)
21 features_list = output.scan(/^\s+(\d+)/).flatten
22 features_flags = output.scan(/^([a-z0-9_]+)[[:blank:]]*(\(read-only compatible\))?$/).map(&:first)
23
24 return if features_list.empty?
25
26 @fact_list[:zpool_featurenumbers] = features_list.join(',')
27 @fact_list[:zpool_featureflags] = features_flags.join(',')
28 @fact_list[:zpool_version] = features_flags.any? ? '5000' : features_list.last
29 end
30 end
31 end
32 end
33 end
0 <%# encoding: UTF-8%>
1 SYNOPSIS
2 --------
3 facter [options] [query] [query] [...]
4
5 DESCRIPTION
6 -----------
7 Collect and display facts about the current system. The library behind Facter is easy to extend, making Facter an easy way to collect information about a system.
8
9 If no queries are given, then all facts will be returned.
10
11 Many of the command line options can also be set via the HOCON config file. This file can also be used to block or cache certain fact groups.
12
13 OPTIONS
14 -------
15 <% Facter::Cli.class_options.each do |name, option| -%><% next if option.hide%>
16 * `<%= option.aliases[0] + '`, `' if option.aliases.any? %>--<%= 'no-' if negate_options.include?(name.to_s)%><%= name.to_s.gsub('_','-') %>`:
17
18 <%= option.description %>
19
20 <% end -%>
21
22 <% Facter::Cli.commands.select { |_k, command_class| command_class.instance_of?(Thor::Command) }.each do |_, command| -%>
23 * `<%= command.usage %>`:
24
25 <%= command.description %>
26
27
28 <% end -%>
29
30 FILES
31 -----
32 <em>/etc/puppetlabs/facter/facter.conf</em>
33
34 A HOCON config file that can be used to specify directories for custom and external facts, set various command line options, and specify facts to block. See example below for details, or visit the [GitHub README](https://github.com/puppetlabs/puppetlabs-hocon#overview).
35
36 EXAMPLES
37 --------
38 Display all facts:
39
40 ```
41 $ facter
42 disks => {
43 sda => {
44 model => "Virtual disk",
45 size => "8.00 GiB",
46 size_bytes => 8589934592,
47 vendor => "ExampleVendor"
48 }
49 }
50 dmi => {
51 bios => {
52 release_date => "06/23/2013",
53 vendor => "Example Vendor",
54 version => "6.00"
55 }
56 }
57 [...]
58 ```
59
60 Display a single structured fact:
61
62 ```
63 $ facter processors
64 {
65 count => 2,
66 isa => "x86_64",
67 models => [
68 "Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz",
69 "Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz"
70 ],
71 physicalcount => 2
72 }
73 ```
74
75 Display a single fact nested within a structured fact:
76
77 ```
78 $ facter processors.isa
79 x86_64
80 ```
81
82 Display a single legacy fact. Note that non-structured facts existing in previous versions of Facter are still available,
83 but are not displayed by default due to redundancy with newer structured facts:
84
85 ```
86 $ facter processorcount
87 2
88 ```
89
90 Format facts as JSON:
91
92 ```
93 $ facter --json os.name os.release.major processors.isa
94 {
95 "os.name": "Ubuntu",
96 "os.release.major": "14.04",
97 "processors.isa": "x86_64"
98 }
99 ```
100
101 An example config file.
102
103 ```
104 # always loaded (CLI and as Ruby module)
105 global : {
106 external-dir : "~/external/facts",
107 custom-dir : [
108 "~/custom/facts",
109 "~/custom/facts/more-facts"
110 ],
111 no-external-facts : false,
112 no-custom-facts : false,
113 no-ruby : false
114 }
115 # loaded when running from the command line
116 cli : {
117 debug : false,
118 trace : true,
119 verbose : false,
120 log-level : "info"
121 }
122 # always loaded, fact-specific configuration
123 facts : {
124 # for valid blocklist entries, use --list-block-groups
125 blocklist : [ "file system", "EC2" ],
126 # for valid time-to-live entries, use --list-cache-groups
127 ttls : [ { "timezone" : 30 days } ]
128 }
129 ```
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Aix
5 module InfoExtractor
6 MEGABYTES_EXPONENT = 1024**2
7 GIGABYTES_EXPONENT = 1024**3
8 PROPERTIES = {
9 lslv: [
10 'LOGICAL VOLUME:',
11 'VOLUME GROUP:',
12 'LV IDENTIFIER:',
13 'PERMISSION:',
14 'VG STATE:',
15 'LV STATE:',
16 'TYPE:',
17 'WRITE VERIFY:',
18 'MAX LPs:',
19 'PP SIZE:',
20 'COPIES:',
21 'SCHED POLICY:',
22 'LPs:',
23 'PPs:',
24 'STALE PPs:',
25 'BB POLICY:',
26 'INTER-POLICY:',
27 'RELOCATABLE:',
28 'INTRA-POLICY:',
29 'UPPER BOUND:',
30 'MOUNT POINT:',
31 'LABEL:',
32 'MIRROR WRITE CONSISTENCY:',
33 'EACH LP COPY ON A SEPARATE PV ?:',
34 'Serialize IO ?:'
35 ],
36 lspv: [
37 'PHYSICAL VOLUME:',
38 'VOLUME GROUP:',
39 'PV IDENTIFIER:',
40 'VG IDENTIFIER',
41 'PV STATE:',
42 'STALE PARTITIONS:',
43 'ALLOCATABLE:',
44 'PP SIZE:',
45 'LOGICAL VOLUMES:',
46 'TOTAL PPs:',
47 'VG DESCRIPTORS:',
48 'FREE PPs:',
49 'HOT SPARE:',
50 'USED PPs:',
51 'MAX REQUEST:',
52 'FREE DISTRIBUTION:',
53 'USED DISTRIBUTION:',
54 'MIRROR POOL:'
55 ]
56 }.freeze
57
58 def self.extract(content, cmd)
59 property_hash = {}
60 properties = PROPERTIES[cmd]
61 properties.each do |property|
62 str = (properties - [property]).join('|')
63 matcher = content.match(/#{Regexp.escape(property)}([^\n]*?)(#{str}|\n|$)/s)
64 if matcher
65 value = matcher[1].strip
66 property_hash[property.split(':').first] = value
67 end
68 end
69 property_hash
70 end
71 end
72 end
73 end
74 end
0 # frozen_string_literal: true
1
2 # CuAt (Customized Attributes) non-default attribute values
3 # CuDv (Customized Devices) the devices present on this machine
4 # PdAt (Predefined Attributes) default values for all device attributes
5 # PdDv (Predefined Devices) the list of all devices supported by this release of AIX
6
7 module Facter
8 module Util
9 module Aix
10 class ODMQuery
11 REPOS = %w[CuAt CuDv PdAt PdDv].freeze
12
13 def initialize
14 @query = ''
15 @conditions = []
16 @log = Facter::Log.new(self)
17 end
18
19 def equals(field, value)
20 @conditions << "#{field}='#{value}'"
21 self
22 end
23
24 def like(field, value)
25 @conditions << "#{field} like '#{value}'"
26 self
27 end
28
29 def execute
30 result = nil
31 REPOS.each do |repo|
32 break if result && !result.empty?
33
34 result = Facter::Core::Execution.execute("#{query} #{repo}", logger: @log)
35 end
36 result
37 end
38
39 def query
40 'odmget -q "' + @conditions.join(' AND ') + '"'
41 end
42 end
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module ApiDebugger
5 def self.prepended(receiver) # rubocop:disable Metrics/AbcSize
6 exclude, print_caller = parse_options(ENV['API_DEBUG'])
7
8 receiver_methods = receiver.instance_methods - Object.methods
9 receiver_methods.each do |meth|
10 ApiDebugger.class_eval do
11 define_method(meth) do |*args|
12 method_call = super(*args)
13
14 unless exclude.include?(meth)
15 puts '#' * 80
16 puts "Method call: #{meth}"
17 puts "Called with: #{args.inspect}"
18 if print_caller.include?(meth)
19 puts '-' * 80
20 puts caller
21 end
22 puts '#' * 80
23 end
24 method_call
25 end
26 end
27 end
28 end
29
30 def self.parse_options(options)
31 exclude = []
32 print_caller = []
33
34 options.split(',').each do |option|
35 if option.start_with?('-')
36 exclude << option[1..-1].to_sym
37 elsif option.start_with?('+')
38 print_caller << option[1..-1].to_sym
39 end
40 end
41
42 [exclude, print_caller]
43 end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Facts
5 HYPERVISORS_HASH = { 'VMware' => 'vmware', 'VirtualBox' => 'virtualbox', 'Parallels' => 'parallels',
6 'KVM' => 'kvm', 'Virtual Machine' => 'hyperv', 'RHEV Hypervisor' => 'rhev',
7 'oVirt Node' => 'ovirt', 'HVM domU' => 'xenhvm', 'Bochs' => 'bochs', 'OpenBSD' => 'vmm',
8 'BHYVE' => 'bhyve' }.freeze
9
10 PHYSICAL_HYPERVISORS = %w[physical xen0 vmware_server vmware_workstation openvzhn vserver_host].freeze
11 REDHAT_FAMILY = %w[redhat rhel fedora centos scientific ascendos cloudlinux psbm
12 oraclelinux ovs oel amazon xenserver xcp-ng virtuozzo photon mariner].freeze
13 DEBIAN_FAMILY = %w[debian ubuntu huaweios linuxmint devuan kde].freeze
14 SUSE_FAMILY = %w[sles sled suse].freeze
15 GENTOO_FAMILY = ['gentoo'].freeze
16 ARCH_FAMILY = %w[arch manjaro].freeze
17 MANDRAKE_FAMILY = %w[mandrake mandriva mageia].freeze
18 FAMILY_HASH = { 'RedHat' => REDHAT_FAMILY, 'Debian' => DEBIAN_FAMILY, 'Suse' => SUSE_FAMILY,
19 'Gentoo' => GENTOO_FAMILY, 'Archlinux' => ARCH_FAMILY, 'Mandrake' => MANDRAKE_FAMILY }.freeze
20
21 class << self
22 def discover_family(os)
23 FAMILY_HASH.each { |key, array_value| return key if array_value.any? { |os_flavour| os =~ /#{os_flavour}/i } }
24 os
25 end
26
27 def release_hash_from_string(output)
28 return unless output
29
30 versions = output.split('.')
31 {}.tap do |release|
32 release['full'] = output
33 release['major'] = versions[0]
34 release['minor'] = versions[1] if versions[1]
35 end
36 end
37
38 def release_hash_from_matchdata(data)
39 return if data.nil? || data[1].nil?
40
41 release_hash_from_string(data[1].to_s)
42 end
43 end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Facts
5 module Posix
6 module VirtualDetector
7 class << self
8 def platform
9 @@fact_value ||= check_docker_lxc || check_freebsd || check_gce || retrieve_from_virt_what
10 @@fact_value ||= check_vmware || check_open_vz || check_vserver || check_xen || check_other_facts
11 @@fact_value ||= check_lspci || 'physical'
12
13 @@fact_value
14 end
15
16 private
17
18 def check_docker_lxc
19 Facter::Resolvers::Containers.resolve(:vm)
20 end
21
22 def check_gce
23 bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
24 'gce' if bios_vendor&.include?('Google')
25 end
26
27 def check_vmware
28 Facter::Resolvers::Vmware.resolve(:vm)
29 end
30
31 def retrieve_from_virt_what
32 Facter::Resolvers::VirtWhat.resolve(:vm)
33 end
34
35 def check_open_vz
36 Facter::Resolvers::OpenVz.resolve(:vm)
37 end
38
39 def check_vserver
40 Facter::Resolvers::VirtWhat.resolve(:vserver)
41 end
42
43 def check_xen
44 Facter::Resolvers::Xen.resolve(:vm)
45 end
46
47 def check_freebsd
48 return unless Object.const_defined?('Facter::Resolvers::Freebsd::Virtual')
49
50 Facter::Resolvers::Freebsd::Virtual.resolve(:vm)
51 end
52
53 def check_other_facts
54 bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
55 return 'kvm' if bios_vendor&.include?('Amazon EC2')
56
57 product_name = Facter::Resolvers::Linux::DmiBios.resolve(:product_name)
58 return unless product_name
59
60 Facter::Util::Facts::HYPERVISORS_HASH.each { |key, value| return value if product_name.include?(key) }
61
62 nil
63 end
64
65 def check_lspci
66 Facter::Resolvers::Lspci.resolve(:vm)
67 end
68 end
69 end
70 end
71 end
72 end
73 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Facts
5 class UnitConverter
6 class << self
7 def bytes_to_mb(value_in_bytes)
8 return unless value_in_bytes
9
10 value_in_bytes = value_in_bytes.to_i
11
12 (value_in_bytes / (1024.0 * 1024.0))
13 end
14
15 def hertz_to_human_readable(speed)
16 speed = speed.to_i
17 return if !speed || speed.zero?
18
19 validated_speed, metric_prefix = determine_metric_prefix(speed)
20
21 format('%<displayed_speed>.2f', displayed_speed: validated_speed.round(2)).to_s + ' ' + metric_prefix + 'Hz'
22 end
23
24 def bytes_to_human_readable(bytes)
25 return unless bytes
26 return bytes.to_s + ' bytes' if bytes < 1024
27
28 number, multiple = determine_exponent(bytes)
29
30 "#{pad_number(number)} #{multiple}"
31 end
32
33 private
34
35 def pad_number(number)
36 number = number.to_s
37 number << '0' if number.split('.').last.length == 1
38 number
39 end
40
41 def determine_exponent(bytes)
42 prefix = %w[KiB MiB GiB TiB PiB EiB]
43 exp = (Math.log2(bytes) / 10.0).floor
44 converted_number = (100.0 * (bytes / 1024.0**exp)).round / 100.0
45
46 if (converted_number - 1024.0).abs < Float::EPSILON
47 exp += 1
48 converted_number = 1.00
49 end
50 multiple = prefix[exp - 1] || 'bytes'
51
52 converted_number = bytes if multiple == 'bytes'
53 [converted_number, multiple]
54 end
55
56 def determine_metric_prefix(num)
57 metric_prefix = { 0 => '', 3 => 'k', 6 => 'M', 9 => 'G', 12 => 'T' }
58 power = Math.log10(num).floor
59 display_exponent = power - power % 3
60 coefficient = power.zero? ? num.to_f : num.fdiv(10**display_exponent)
61 [coefficient, metric_prefix[display_exponent]]
62 end
63 end
64 end
65 end
66 end
67 end
0 # frozen_string_literal: true
1
2 require 'time'
3
4 module Facter
5 module Util
6 module Facts
7 class UptimeParser
8 SECS_IN_A_DAY = 86_400
9 SECS_IN_AN_HOUR = 3_600
10 SECS_IN_A_MINUTE = 60
11
12 @log = Facter::Log.new(self)
13
14 class << self
15 def uptime_seconds_unix
16 uptime_proc_uptime || uptime_sysctl || uptime_executable
17 end
18
19 private
20
21 def uptime_proc_uptime
22 output = Facter::Core::Execution.execute("/bin/cat #{uptime_file}", logger: @log)
23
24 output.chomp.split(' ').first.to_i unless output.empty?
25 end
26
27 def uptime_sysctl
28 output = Facter::Core::Execution.execute("sysctl -n #{uptime_sysctl_variable}", logger: @log)
29
30 compute_uptime(Time.at(output.match(/\d+/)[0].to_i)) unless output.empty?
31 end
32
33 def uptime_executable
34 output = Facter::Core::Execution.execute(uptime_executable_cmd, logger: @log)
35
36 return unless output
37
38 up = 0
39 output_calculator_methods.find { |method| up = send(method, output) }
40 up || 0
41 end
42
43 def uptime_file
44 '/proc/uptime'
45 end
46
47 def uptime_sysctl_variable
48 'kern.boottime'
49 end
50
51 def uptime_executable_cmd
52 'uptime'
53 end
54
55 def output_calculator_methods
56 %i[
57 calculate_days_hours_minutes
58 calculate_days_hours
59 calculate_days_minutes
60 calculate_days
61 calculate_hours_minutes
62 calculate_hours
63 calculate_minutes
64 ]
65 end
66
67 def compute_uptime(time)
68 (Time.now - time).to_i
69 end
70
71 # Regexp handles Solaris, AIX, HP-UX, and Tru64.
72 # 'day(?:s|\(s\))?' says maybe 'day', 'days',
73 # or 'day(s)', and don't set $2.
74 def calculate_days_hours_minutes(output)
75 return unless output =~ /(\d+) day(?:s|\(s\))?,?\s+(\d+):-?(\d+)/
76
77 SECS_IN_A_DAY * Regexp.last_match(1).to_i +
78 SECS_IN_AN_HOUR * Regexp.last_match(2).to_i +
79 SECS_IN_A_MINUTE * Regexp.last_match(3).to_i
80 end
81
82 def calculate_days_hours(output)
83 return unless output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) hr(?:s|\(s\))?,/
84
85 SECS_IN_A_DAY * Regexp.last_match(1).to_i +
86 SECS_IN_AN_HOUR * Regexp.last_match(2).to_i
87 end
88
89 def calculate_days_minutes(output)
90 return unless output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) min(?:s|\(s\))?,/
91
92 SECS_IN_A_DAY * Regexp.last_match(1).to_i +
93 SECS_IN_A_MINUTE * Regexp.last_match(2).to_i
94 end
95
96 def calculate_days(output)
97 return unless output =~ /(\d+) day(?:s|\(s\))?,/
98
99 SECS_IN_A_DAY * Regexp.last_match(1).to_i
100 end
101
102 # must anchor to 'up' to avoid matching time of day
103 # at beginning of line. Certain versions of uptime on
104 # Solaris may insert a '-' into the minutes field.
105 def calculate_hours_minutes(output)
106 return unless output =~ /up\s+(\d+):-?(\d+),/
107
108 SECS_IN_AN_HOUR * Regexp.last_match(1).to_i +
109 SECS_IN_A_MINUTE * Regexp.last_match(2).to_i
110 end
111
112 def calculate_hours(output)
113 return unless output =~ /(\d+) hr(?:s|\(s\))?,/
114
115 SECS_IN_AN_HOUR * Regexp.last_match(1).to_i
116 end
117
118 def calculate_minutes(output)
119 return unless output =~ /(\d+) min(?:s|\(s\))?,/
120
121 SECS_IN_A_MINUTE * Regexp.last_match(1).to_i
122 end
123 end
124 end
125 end
126 end
127 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Facts
5 class WindowsReleaseFinder
6 class << self
7 def find_release(input)
8 version = input[:version]
9 return unless version
10
11 consumerrel = input[:consumerrel]
12 description = input[:description]
13 kernel_version = input[:kernel_version]
14
15 if version =~ /10.0/
16 check_version_10_11(consumerrel, kernel_version)
17 else
18 check_version_6(version, consumerrel) || check_version_5(version, consumerrel, description) || version
19 end
20 end
21
22 private
23
24 def check_version_10_11(consumerrel, kernel_version)
25 build_number = kernel_version[/([^.]*)$/].to_i
26
27 return '11' if build_number >= 22_000
28 return '10' if consumerrel
29
30 if build_number >= 20_348
31 '2022'
32 elsif build_number >= 17_623
33 '2019'
34 else
35 '2016'
36 end
37 end
38
39 def check_version_6(version, consumerrel)
40 hash = {}
41 hash['6.3'] = consumerrel ? '8.1' : '2012 R2'
42 hash['6.2'] = consumerrel ? '8' : '2012'
43 hash['6.1'] = consumerrel ? '7' : '2008 R2'
44 hash['6.0'] = consumerrel ? 'Vista' : '2008'
45 hash[version]
46 end
47
48 def check_version_5(version, consumerrel, description)
49 return unless version =~ /5.2/
50 return 'XP' if consumerrel
51
52 description == 'R2' ? '2003 R2' : '2003'
53 end
54 end
55 end
56 end
57 end
58 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 class FileHelper
5 @log = Log.new(self)
6
7 class << self
8 DEBUG_MESSAGE = 'File at: %s is not accessible.'
9
10 def safe_read(path, default_return = '')
11 return File.read(path, encoding: Encoding::UTF_8) if File.readable?(path)
12
13 log_failed_to_read(path)
14 default_return
15 end
16
17 def safe_readlines(path, default_return = [])
18 return File.readlines(path, encoding: Encoding::UTF_8) if File.readable?(path)
19
20 log_failed_to_read(path)
21 default_return
22 end
23
24 def dir_children(path)
25 children = if RUBY_VERSION.to_f < 2.5
26 Dir.entries(path).reject { |dir| ['.', '..'].include?(dir) }
27 else
28 Dir.children(path)
29 end
30
31 children
32 end
33
34 private
35
36 def log_failed_to_read(path)
37 @log.debug(DEBUG_MESSAGE % path)
38 end
39 end
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Linux
5 class Dhcp
6 class << self
7 DIRS = %w[/var/lib/dhclient/
8 /var/lib/dhcp/
9 /var/lib/dhcp3/
10 /var/lib/NetworkManager/
11 /var/db/].freeze
12
13 def dhcp(interface_name, interface_index, logger)
14 @log = logger
15 @log.debug("Get DHCP for interface #{interface_name}")
16
17 dhcp = search_systemd_netif_leases(interface_index, interface_name)
18 dhcp ||= search_dhclient_leases(interface_name)
19 dhcp ||= search_internal_leases(interface_name)
20 dhcp ||= search_with_dhcpcd_command(interface_name)
21 dhcp
22 end
23
24 private
25
26 def search_systemd_netif_leases(index, interface_name)
27 return if index.nil?
28
29 @log.debug("Attempt to get DHCP for interface #{interface_name}, from systemd/netif/leases")
30
31 file_content = Facter::Util::FileHelper.safe_read("/run/systemd/netif/leases/#{index}", nil)
32 dhcp = file_content.match(/SERVER_ADDRESS=(.*)/) if file_content
33 dhcp[1] if dhcp
34 end
35
36 def search_dhclient_leases(interface_name)
37 @log.debug("Attempt to get DHCP for interface #{interface_name}, from dhclient leases")
38
39 DIRS.each do |dir|
40 next unless File.readable?(dir)
41
42 lease_files = Dir.entries(dir).select { |file| file =~ /dhclient.*\.lease/ }
43 next if lease_files.empty?
44
45 lease_files.select do |file|
46 content = Facter::Util::FileHelper.safe_read("#{dir}#{file}", nil)
47 next unless content =~ /interface.*#{interface_name}/
48
49 dhcp = content.match(/dhcp-server-identifier ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/)
50 return dhcp[1] if dhcp
51 end
52 end
53
54 nil
55 end
56
57 def search_internal_leases(interface_name)
58 return unless File.readable?('/var/lib/NetworkManager/')
59
60 @log.debug("Attempt to get DHCP for interface #{interface_name}, from NetworkManager leases")
61
62 files = Dir.entries('/var/lib/NetworkManager/').reject { |dir| dir =~ /^\.+$/ }
63 lease_file = files.find { |file| file =~ /internal.*#{interface_name}\.lease/ }
64 return unless lease_file
65
66 dhcp = Facter::Util::FileHelper.safe_read("/var/lib/NetworkManager/#{lease_file}", nil)
67
68 return unless dhcp
69
70 dhcp = dhcp.match(/SERVER_ADDRESS=(.*)/)
71 dhcp[1] if dhcp
72 end
73
74 def search_with_dhcpcd_command(interface_name)
75 @log.debug("Attempt to get DHCP for interface #{interface_name}, from dhcpcd command")
76
77 @dhcpcd_command ||= Facter::Core::Execution.which('dhcpcd')
78 return unless @dhcpcd_command
79
80 output = Facter::Core::Execution.execute("#{@dhcpcd_command} -U #{interface_name}", logger: @log)
81 dhcp = output.match(/dhcp_server_identifier='(.*)'/)
82 dhcp[1] if dhcp
83 end
84 end
85 end
86 end
87 end
88 end
0 # frozen_string_literal: true
1
2 require 'ipaddr'
3
4 module Facter
5 module Util
6 module Linux
7 class IfInet6
8 class << self
9 IFA_FLAGS = {
10 'temporary' => 0x01,
11 'noad' => 0x02,
12 'optimistic' => 0x04,
13 'dadfailed' => 0x08,
14 'homeaddress' => 0x10,
15 'deprecated' => 0x20,
16 'tentative' => 0x40,
17 'permanent' => 0x80
18 # /proc/net/if_inet6 only supports the old 8bit flags
19 # I have been unable to find a simple solution to accesses
20 # the full 32bit flags. netlink is all I can could find but
21 # that will likely be ugly
22 # 'managetempaddr' => 0x100,
23 # 'noprefixroute' => 0x200,
24 # 'mcautojoin' => 0x400,
25 # 'stableprivacy' => 0x800
26 }.freeze
27
28 def read_flags
29 return read_flags_from_proc if File.exist?('/proc/net/if_inet6')
30
31 {}
32 end
33
34 private
35
36 def read_flags_from_proc
37 flags = init_flags
38 Facter::Util::FileHelper.safe_read('/proc/net/if_inet6', nil).each_line do |line|
39 iface = line.split
40 next unless iface.size == 6
41
42 ip = parse_ip(iface[0])
43 flags[iface[5]][ip] = parse_ifa_flags(iface[4])
44 end
45 flags
46 end
47
48 def init_flags
49 Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = [] } }
50 end
51
52 def parse_ifa_flags(flag)
53 flag = flag.hex
54 flags = []
55 IFA_FLAGS.each_pair do |name, value|
56 next if (flag & value).zero?
57
58 flags << name
59 end
60 flags
61 end
62
63 def parse_ip(ip)
64 # The ip address in if_net6 is a long string wit no colons
65 ip = ip.scan(/(\h{4})/).join(':')
66 IPAddr.new(ip).to_s
67 end
68 end
69 end
70 end
71 end
72 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Linux
5 class RoutingTable
6 class << self
7 ROUTE_TYPES = %w[anycast
8 unicast
9 broadcast
10 local
11 nat
12 unreachable
13 prohibit
14 blackhole
15 throw].freeze
16
17 def read_routing_table(logger)
18 ipv4_output = Facter::Core::Execution.execute('ip route show', logger: logger)
19 ipv6_output = Facter::Core::Execution.execute('ip -6 route show', logger: logger)
20 routes4 = parse_routes(ipv4_output, true)
21 routes6 = parse_routes(ipv6_output, false)
22 [routes4, routes6]
23 end
24
25 private
26
27 def parse_routes(output, ipv4_type)
28 routes = []
29 output.each_line do |line|
30 parts = line.split(' ').compact
31 next if parts.include?('linkdown')
32
33 delete_invalid_route_type(parts)
34 next if !ipv4_type && !parts[0].include?(':')
35
36 route = construct_route(parts)
37 routes << route unless route[:ip].nil?
38 end
39 routes.uniq
40 end
41
42 def delete_invalid_route_type(parts)
43 route_type = parts[0]
44 parts.delete_at(0) if ROUTE_TYPES.include?(route_type)
45 end
46
47 def construct_route(parts)
48 route = {}
49 dev_index = parts.find_index { |elem| elem == 'dev' }
50 src_index = parts.find_index { |elem| elem == 'src' }
51 route[:interface] = parts[dev_index + 1] if dev_index
52 route[:ip] = parts[src_index + 1] if src_index
53 route
54 end
55 end
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Linux
5 class SocketParser
6 class << self
7 def retrieve_interfaces(logger)
8 require 'socket'
9 @interfaces = {}
10 @log = logger
11 Socket.getifaddrs.each do |ifaddr|
12 populate_interface_info(ifaddr)
13 end
14
15 @interfaces
16 end
17
18 private
19
20 def populate_interface_info(ifaddr)
21 interface_name = ifaddr.name
22 @interfaces[interface_name] = {} if @interfaces[interface_name].nil?
23
24 mac(ifaddr)
25 ip_info_of(ifaddr)
26 end
27
28 def mac(ifaddr)
29 return unless @interfaces[ifaddr.name][:mac].nil?
30
31 mac = search_for_mac(ifaddr)
32 @interfaces[ifaddr.name][:mac] = mac if mac
33 end
34
35 def search_for_mac(ifaddr)
36 mac = mac_from_bonded_interface(ifaddr.name)
37 mac ||= mac_from(ifaddr)
38 mac if !mac.nil? && mac != '00:00:00:00:00:00' && mac =~ /^([0-9A-Fa-f]{2}[:-]){5,19}([0-9A-Fa-f]{2})$/
39 end
40
41 def mac_from_bonded_interface(interface_name)
42 master = bond_master_of(interface_name)
43 return unless master
44
45 output = Facter::Util::FileHelper.safe_read("/proc/net/bonding/#{master}", nil)
46 return unless output
47
48 found_match = false
49 output.each_line do |line|
50 if line.strip == "Slave Interface: #{interface_name}"
51 found_match = true
52 elsif line.include? 'Slave Interface'
53 # if we reached the data block of another interface belonging to the bonded interface
54 found_match = false
55 end
56 return Regexp.last_match(1) if found_match && line =~ /Permanent HW addr: (\S*)/
57 end
58 end
59
60 def bond_master_of(interface_name)
61 content = get_ip_link_show_data(interface_name)
62 content&.match(/master (\S*) /)&.captures&.first
63 end
64
65 def get_ip_link_show_data(interface_name)
66 @ip_link_show_data ||= read_ip_link_show_data
67 @ip_link_show_data[interface_name]
68 end
69
70 def read_ip_link_show_data
71 ip_link_show_data = {}
72 output = Facter::Core::Execution.execute('ip -o link show', logger: @log)
73 output.each_line do |line|
74 interface_name = line.split(':')[1]&.strip if line
75 ip_link_show_data[interface_name] = line if interface_name
76 end
77 ip_link_show_data
78 end
79
80 def mac_from(ifaddr)
81 if Socket.const_defined? :PF_LINK
82 ifaddr.addr&.getnameinfo&.first # sometimes it returns localhost or ip
83 elsif Socket.const_defined? :PF_PACKET
84 return if ifaddr.addr.nil?
85
86 mac_from_sockaddr_of(ifaddr)
87 end
88 rescue StandardError => e
89 @log.debug("Could not read mac for interface #{ifaddr.name}, got #{e}")
90 end
91
92 def mac_from_sockaddr_of(ifaddr)
93 result = ifaddr.addr.inspect_sockaddr
94 result&.match(/hwaddr=([\h:]+)/)&.captures&.first
95 end
96
97 def ip_info_of(ifaddr)
98 return if ifaddr.addr.nil? || ifaddr.netmask.nil?
99
100 add_binding(ifaddr.name, ifaddr)
101 rescue StandardError => e
102 @log.debug("Could not read binding data, got #{e}")
103 end
104
105 def add_binding(interface_name, ifaddr)
106 ip, netmask, binding_key = binding_data(ifaddr)
107 binding = Facter::Util::Resolvers::Networking.build_binding(ip, netmask)
108 return if binding.nil?
109
110 @interfaces[interface_name][binding_key] = [] if @interfaces[interface_name][binding_key].nil?
111 @interfaces[interface_name][binding_key] << binding
112
113 @log.debug("Adding to interface #{interface_name}, binding:\n#{binding}")
114 end
115
116 def binding_data(ifaddr)
117 # ipv6 ips are retrieved as <ip>%<interface_name>
118 ip = ifaddr.addr.ip_address.split('%').first
119 netmask = ifaddr.netmask.ip_address
120 binding_key = ifaddr.addr.ipv4? ? :bindings : :bindings6
121
122 [ip, netmask, binding_key]
123 end
124 end
125 end
126 end
127 end
128 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Macosx
5 class SystemProfileExecutor
6 @log = Log.new(self)
7
8 class << self
9 def execute(category_name)
10 @log.debug "Executing command: system_profiler #{category_name}"
11 output = Facter::Core::Execution.execute(
12 "system_profiler #{category_name}", logger: @log
13 )&.force_encoding('UTF-8')
14
15 return unless output
16
17 system_profiler_hash = output_to_hash(output)
18
19 normalize_keys(system_profiler_hash)
20 end
21
22 private
23
24 def output_to_hash(output)
25 output.scan(/.*:[ ].*$/).map { |e| e.strip.match(/(.*?): (.*)/).captures }.to_h
26 end
27
28 def normalize_keys(system_profiler_hash)
29 system_profiler_hash.map do |k, v|
30 [k.downcase.tr(' ', '_').delete("\(\)").to_sym, v]
31 end.to_h
32 end
33 end
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module AwsToken
6 attr_reader :token
7
8 @log = Facter::Log.new(self)
9
10 class << self
11 AWS_API_TOKEN_URL = 'http://169.254.169.254/latest/api/token'
12
13 def get(lifetime = 100)
14 @expiry ||= Time.now
15
16 return @token if @token && @expiry > Time.now
17
18 @token = nil
19 @expiry = Time.now + lifetime
20
21 headers = {
22 'X-aws-ec2-metadata-token-ttl-seconds' => lifetime.to_s
23 }
24
25 @token = Facter::Util::Resolvers::Http.put_request(AWS_API_TOKEN_URL, headers)
26 end
27
28 def reset
29 @expiry = nil
30 @token = nil
31 end
32 end
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3
4 module Facter
5 module Util
6 module Resolvers
7 module Ffi
8 class AddrInfo < ::FFI::Struct
9 layout :ai_flags, :int,
10 :ai_family, :int,
11 :ai_socketype, :int,
12 :ai_protocol, :int,
13 :ai_addrlen, :uint,
14 :ai_addr, :pointer,
15 :ai_canonname, :string,
16 :ai_next, :pointer
17 end
18
19 module Hostname
20 HOST_NAME_MAX = 64
21
22 extend ::FFI::Library
23 ffi_lib ::FFI::Library::LIBC
24
25 attach_function :getaddrinfo, %i[string string pointer pointer], :int
26 attach_function :gethostname, %i[pointer int], :int
27 attach_function :freeaddrinfo, [:pointer], :void
28
29 def self.getffihostname
30 raw_hostname = ::FFI::MemoryPointer.new(:char, HOST_NAME_MAX)
31
32 res = Hostname.gethostname(raw_hostname, HOST_NAME_MAX)
33 return unless res.zero?
34
35 raw_hostname.read_string
36 end
37
38 def self.getffiaddrinfo(hostname) # rubocop:disable Metrics/AbcSize
39 ret = FFI::MemoryPointer.new(:pointer)
40
41 hints = Facter::Util::Resolvers::Ffi::AddrInfo.new
42 hints[:ai_family] = Socket::AF_UNSPEC
43 hints[:ai_socketype] = Socket::SOCK_STREAM
44 hints[:ai_flags] = Socket::AI_CANONNAME
45
46 res = Hostname.getaddrinfo(hostname, '', hints.to_ptr, ret)
47 log = Log.new(self)
48 log.debug("FFI Getaddrinfo finished with exit status: #{res}")
49 log.debug("FFI Getaddrinfo returned an AddrInfo struct at address: #{ret}")
50 return unless res.zero?
51
52 return if ret == FFI::Pointer::NULL || ret.read_pointer == FFI::Pointer::NULL
53
54 begin
55 addr = Facter::Util::Resolvers::Ffi::AddrInfo.new(ret.read_pointer)
56 name = addr[:ai_canonname]
57 log.debug("FFI AddrInfo struct has the fqdn: #{name}")
58 return if !name || hostname == name
59
60 name
61 ensure
62 Hostname.freeaddrinfo(ret.read_pointer)
63 end
64 end
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 require 'ffi'
3
4 module Facter
5 module Util
6 module Resolvers
7 module Ffi
8 module LoadAverages
9 extend ::FFI::Library
10 ffi_lib ::FFI::Library::LIBC
11
12 attach_function :getloadavg, %i[pointer int], :int
13
14 def self.read_load_averages
15 raw_loadavg = ::FFI::MemoryPointer.new(:double, 3)
16
17 res = LoadAverages.getloadavg(raw_loadavg, 3)
18 return unless res == 3
19
20 raw_loadavg.read_array_of_double(res)
21 end
22 end
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module FilesystemHelper
6 MOUNT_KEYS = %i[device filesystem path options
7 available available_bytes size
8 size_bytes used used_bytes capacity].freeze
9 class << self
10 def read_mountpoints
11 # TODO: this require must be replaced with "require 'sys/filesystem'" when a new release of
12 # djberg96/sys-filesystem gem is available
13 require_relative '../../patches/sysfilesystem/sys/statvfs.rb'
14 force_utf(Sys::Filesystem.mounts)
15 end
16
17 def read_mountpoint_stats(path)
18 require 'sys/filesystem'
19 Sys::Filesystem.stat(path)
20 end
21
22 def compute_capacity(used, total)
23 if used == total
24 '100%'
25 elsif used.positive?
26 "#{format('%<value>.2f', value: (used / total.to_f * 100))}%"
27 else
28 '0%'
29 end
30 end
31
32 private
33
34 def force_utf(mounts)
35 mounts.each do |mount|
36 mount.name.force_encoding('UTF-8')
37 mount.mount_type.force_encoding('UTF-8')
38 mount.mount_point.force_encoding('UTF-8')
39 mount.options.force_encoding('UTF-8')
40 end
41 end
42 end
43 end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 class FingerPrint
6 attr_accessor :sha1, :sha256
7 def initialize(sha1, sha256)
8 @sha1 = sha1
9 @sha256 = sha256
10 end
11 end
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module Http
6 @log = Facter::Log.new(self)
7
8 class << self
9 CONNECTION_TIMEOUT = 0.6
10 SESSION_TIMEOUT = 5
11
12 # Makes a GET http request and returns its response.
13 #
14 # Params:
15 # url: String which contains the address to which the request will be made
16 # headers: Hash which contains the headers you need to add to your request.
17 # Default headers is an empty hash
18 # Example: { "Accept": 'application/json' }
19 # timeouts: Hash that includes the values for the session and connection timeouts.
20 # Example: { session: 2.4. connection: 5 }
21 #
22 # Return value:
23 # is a string with the response body if the response code is 200.
24 # If the response code is not 200, an empty string is returned.
25 def get_request(url, headers = {}, timeouts = {})
26 make_request(url, headers, timeouts, 'GET')
27 end
28
29 def put_request(url, headers = {}, timeouts = {})
30 make_request(url, headers, timeouts, 'PUT')
31 end
32
33 private
34
35 def make_request(url, headers, timeouts, request_type)
36 require 'net/http'
37
38 uri = URI.parse(url)
39 http = http_obj(uri, timeouts)
40 request = request_obj(headers, uri, request_type)
41
42 # The Windows implementation of sockets does not respect net/http
43 # timeouts, so check if the target is reachable in a different way
44 if Gem.win_platform?
45 Socket.tcp(uri.host, uri.port, connect_timeout: timeouts[:connection] || CONNECTION_TIMEOUT)
46 end
47
48 # Make the request
49 response = http.request(request)
50 response.uri = url
51
52 successful_response?(response) ? response.body : ''
53 rescue StandardError => e
54 @log.debug("Trying to connect to #{url} but got: #{e.message}")
55 ''
56 end
57
58 def http_obj(parsed_url, timeouts)
59 http = Net::HTTP.new(parsed_url.host)
60 http.read_timeout = timeouts[:session] || SESSION_TIMEOUT
61 http.open_timeout = timeouts[:connection] || CONNECTION_TIMEOUT
62
63 http.set_debug_output($stderr) if Options[:http_debug]
64
65 http
66 end
67
68 def request_obj(headers, parsed_url, request_type)
69 Module.const_get("Net::HTTP::#{request_type.capitalize}").new(parsed_url.request_uri, headers)
70 end
71
72 def successful_response?(response)
73 success = response.code.to_i.equal?(200)
74
75 @log.debug("Request to #{response.uri} failed with error code #{response.code}") unless success
76
77 success
78 end
79 end
80 end
81 end
82 end
83 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module Networking
6 module Dhcp
7 class << self
8 def get(interface_name, log = nil)
9 dhcpinfo_command = Facter::Core::Execution.which('dhcpinfo') || '/sbin/dhcpinfo'
10 result = Facter::Core::Execution.execute("#{dhcpinfo_command} -i #{interface_name} ServerID", logger: log)
11 result.chomp!
12
13 result.empty? ? nil : result
14 end
15 end
16 end
17 end
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 require 'ipaddr'
3
4 module Facter
5 module Util
6 module Resolvers
7 module Networking
8 class << self
9 # Creates a hash with IP, netmask and network. Works for IPV4 and IPV6
10 # @param [String] addr The IP address
11 # @param [Integer] mask_length Number of 1 bits the netmask has
12 #
13 # @return [Hash] Hash containing ip address, netmask and network
14 def build_binding(addr, mask_length)
15 return if !addr || !mask_length
16
17 ip = IPAddr.new(addr)
18 mask_helper = nil
19 scope = nil
20 if ip.ipv6?
21 scope = get_scope(addr)
22 mask_helper = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
23 else
24 mask_helper = '255.255.255.255'
25 end
26 mask = IPAddr.new(mask_helper).mask(mask_length)
27
28 result = { address: addr, netmask: mask.to_s, network: ip.mask(mask_length).to_s }
29 result[:scope6] = scope if scope
30 result
31 end
32
33 def expand_main_bindings(networking_facts)
34 primary = networking_facts[:primary_interface]
35 interfaces = networking_facts[:interfaces]
36
37 expand_interfaces(interfaces) unless interfaces.nil?
38 expand_primary_interface(networking_facts, primary) unless primary.nil? || interfaces.nil?
39 end
40
41 def get_scope(ip)
42 require 'socket'
43
44 scope6 = []
45 addrinfo = Addrinfo.new(['AF_INET6', 0, nil, ip], :INET6)
46
47 scope6 << 'compat,' if IPAddr.new(ip).ipv4_compat?
48 scope6 << if addrinfo.ipv6_linklocal?
49 'link'
50 elsif addrinfo.ipv6_sitelocal?
51 'site'
52 elsif addrinfo.ipv6_loopback?
53 'host'
54 else 'global'
55 end
56 scope6.join
57 end
58
59 def find_valid_binding(bindings)
60 bindings.each do |binding|
61 return binding unless ignored_ip_address(binding[:address])
62 end
63 bindings.empty? ? nil : bindings.first
64 end
65
66 def ignored_ip_address(addr)
67 addr.empty? || addr.start_with?('127.', '169.254.') || addr.start_with?('fe80') || addr.eql?('::1')
68 end
69
70 def calculate_mask_length(netmask)
71 ipaddr = IPAddr.new(netmask)
72
73 ipaddr.to_i.to_s(2).count('1')
74 end
75
76 def format_mac_address(address)
77 address.split('.').map { |e| format('%<mac_address>02s', mac_address: e) }.join(':').tr(' ', '0')
78 end
79
80 private
81
82 def expand_interfaces(interfaces)
83 interfaces.each_value do |values|
84 expand_binding(values, values[:bindings]) if values[:bindings]
85 expand_binding(values, values[:bindings6], false) if values[:bindings6]
86 end
87 end
88
89 def expand_primary_interface(networking_facts, primary)
90 networking_facts[:interfaces][primary].each do |key, value|
91 networking_facts[key] = value unless %i[bindings bindings6].include?(key)
92 end
93 end
94
95 def expand_binding(values, bindings, ipv4_type = true)
96 binding = find_valid_binding(bindings)
97 ip_protocol_type = ipv4_type ? '' : '6'
98
99 values["ip#{ip_protocol_type}".to_sym] = binding[:address]
100 values["netmask#{ip_protocol_type}".to_sym] = binding[:netmask]
101 values["network#{ip_protocol_type}".to_sym] = binding[:network]
102 values[:scope6] = get_scope(binding[:address]) unless ipv4_type
103 end
104 end
105 end
106 end
107 end
108 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module Networking
6 module PrimaryInterface
7 @log ||= Log.new(self)
8
9 class << self
10 ROUTE_TABLE_MAPPING = {
11 'Iface' => 0,
12 'Destination' => 1,
13 'Gateway' => 2,
14 'Flags' => 3,
15 'RefCnt' => 4,
16 'Use' => 5,
17 'Metric' => 6,
18 'Mask' => 7,
19 'MTU' => 8,
20 'Window' => 9,
21 'IRTT' => 10
22 }.freeze
23
24 def read_from_route
25 return if Facter::Core::Execution.which('route').nil?
26
27 result = Facter::Core::Execution.execute('route -n get default', logger: @log)
28
29 result.match(/interface: (.+)/)&.captures&.first
30 end
31
32 def read_from_proc_route
33 content = Facter::Util::FileHelper.safe_read('/proc/net/route', '')
34
35 content.each_line.with_index do |line, index|
36 next if index.zero?
37
38 route = line.strip.split("\t")
39
40 return route[ROUTE_TABLE_MAPPING['Iface']] if valid_default_route?(route)
41 end
42
43 nil
44 end
45
46 def read_from_ip_route
47 return if Facter::Core::Execution.which('ip').nil?
48
49 output = Facter::Core::Execution.execute('ip route show default', logger: @log)
50 primary_interface = nil
51 output.each_line do |line|
52 primary_interface = line.strip.split(' ')[4] if line.start_with?('default')
53 end
54
55 primary_interface
56 end
57
58 def find_in_interfaces(interfaces)
59 interfaces.each do |iface_name, interface|
60 interface[:bindings]&.each do |binding|
61 return iface_name unless Facter::Util::Resolvers::Networking.ignored_ip_address(binding[:address])
62 end
63
64 next unless interface[:bindings6]
65
66 interface[:bindings6].each do |binding|
67 return iface_name unless Facter::Util::Resolvers::Networking.ignored_ip_address(binding[:address])
68 end
69 end
70
71 nil
72 end
73
74 private
75
76 def valid_default_route?(route)
77 route.count > 7 &&
78 route[ROUTE_TABLE_MAPPING['Destination']] == '00000000' &&
79 route[ROUTE_TABLE_MAPPING['Mask']] == '00000000' &&
80 route[ROUTE_TABLE_MAPPING['Iface']] != '*' # `*` represents blackhole and not a valid interface
81 end
82 end
83 end
84 end
85 end
86 end
87 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 class Ssh
6 attr_accessor :fingerprint, :type, :key, :name
7 def initialize(fingerprint, type, key, name)
8 @fingerprint = fingerprint
9 @type = type
10 @key = key
11 @name = name
12 end
13 end
14 end
15 end
16 end
0 # frozen_string_literal: true
1
2 require 'base64'
3 require 'digest/sha1'
4
5 module Facter
6 module Util
7 module Resolvers
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 return unless key_name
17
18 decoded_key = Base64.decode64(key)
19 ssh_fp = SSH_FINGERPRINT[key_name]
20 sha1 = "SSHFP #{ssh_fp} 1 #{Digest::SHA1.new.update(decoded_key)}"
21 sha256 = "SSHFP #{ssh_fp} 2 #{Digest::SHA2.new.update(decoded_key)}"
22
23 fingerprint = Facter::Util::Resolvers::FingerPrint.new(sha1, sha256)
24 Facter::Util::Resolvers::Ssh.new(fingerprint, key_type, key, key_name)
25 end
26 end
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Util
4 module Resolvers
5 module UptimeHelper
6 class << self
7 def create_uptime_hash(seconds)
8 results = {}
9 minutes = (seconds / 60) % 60
10
11 results[:seconds] = seconds
12 results[:hours] = seconds / (60 * 60)
13 results[:days] = results[:hours] / 24
14 results[:uptime] = build_uptime_text(results[:days], results[:hours], minutes)
15
16 results
17 end
18
19 def build_uptime_text(days, hours, minutes)
20 case days
21 when 0 then "#{hours}:#{format('%<minutes>02d', minutes: minutes)} hours"
22 when 1 then '1 day'
23 else
24 "#{days} days"
25 end
26 end
27 end
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 module Facter
3 module Utils
4 # Sort nested hash.
5 def self.sort_hash_by_key(hash, recursive = true, &block)
6 hash.keys.sort(&block).each_with_object(hash.class.new) do |key, seed|
7 seed[key] = hash[key]
8 seed[key] = sort_hash_by_key(seed[key], true, &block) if recursive && seed[key].is_a?(Hash)
9
10 seed
11 end
12 end
13
14 def self.split_user_query(user_query)
15 queries = user_query.split('.')
16 queries.map! { |query| query =~ /^[0-9]+$/ ? query.to_i : query }
17 end
18
19 def self.deep_stringify_keys(object)
20 case object
21 when Hash
22 object.each_with_object({}) do |(key, value), result|
23 result[key.to_s] = deep_stringify_keys(value)
24 end
25 when Array
26 object.map { |e| deep_stringify_keys(e) }
27 else
28 object
29 end
30 end
31
32 def self.try_to_bool(value)
33 case value.to_s
34 when 'true'
35 true
36 when 'false'
37 false
38 else
39 value
40 end
41 end
42
43 def self.try_to_int(value)
44 Integer(value)
45 rescue ArgumentError, TypeError
46 value
47 end
48 end
49 end
0 # frozen_string_literal: true
1
2 require 'win32ole'
3
4 module Facter
5 module Util
6 module Windows
7 class Win32Ole
8 RPC_C_IMP_LEVEL_IMPERSONATE = 3
9
10 def initialize
11 locator = WIN32OLE.new('WbemScripting.SWbemLocator')
12 @conn = locator.ConnectServer('.', 'root\\cimv2')
13 @conn.Security_.ImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE
14 end
15
16 def return_first(query)
17 result = exec_query(query)
18 return result.to_enum.first if result
19
20 nil
21 end
22
23 def exec_query(query)
24 @conn.execquery(query)
25 end
26 end
27 end
28 end
29 end
0 # frozen_string_literal: true
1
2 module Facter
3 VERSION = '4.2.13' unless defined?(VERSION)
4 end
0 # frozen_string_literal: true
1
2 require 'pathname'
3 require_relative 'util/api_debugger' if ENV['API_DEBUG']
4
5 require 'facter/version'
6 require 'facter/framework/core/file_loader'
7 require 'facter/framework/core/options/options_validator'
8
9 module Facter
10 class ResolveCustomFactError < StandardError; end
11
12 Options.init
13 Log.output(STDOUT)
14 @already_searched = {}
15
16 class << self
17 # Method used by puppet-agent to retrieve facts
18 # @param args_as_string [string] facter cli arguments
19 #
20 # @return query result
21 #
22 # @api private
23 def resolve(args_as_string)
24 require 'facter/framework/cli/cli_launcher'
25
26 args = args_as_string.split(' ')
27 Facter::OptionsValidator.validate(args)
28 processed_arguments = CliLauncher.prepare_arguments(args, nil)
29 cli = Facter::Cli.new([], processed_arguments)
30 cli_options = cli.options.dup
31
32 # config file options
33 config_file = cli_options.delete(:config)
34 if config_file
35 Facter::OptionStore.set(:config, config_file)
36 Facter::ConfigFileOptions.init(config_file)
37 Facter::Options.store(ConfigFileOptions.get)
38 end
39
40 # user provided options
41 cli_options[:show_legacy] ||= false
42 Facter::Options.store(cli_options)
43
44 queried_facts(cli.args)
45 end
46
47 # Method used by cli to set puppet paths
48 # in order to retrieve puppet custom and external facts
49 #
50 # @return nil
51 #
52 # @api private
53 def puppet_facts
54 require 'puppet'
55
56 # don't allow puppet logger to be injected in Facter
57 Options[:allow_external_loggers] = false
58
59 Puppet.initialize_settings
60 $LOAD_PATH << Puppet[:libdir] unless $LOAD_PATH.include?(Puppet[:libdir])
61 Facter.reset
62 Facter.search_external([Puppet[:pluginfactdest]])
63 if Puppet.respond_to? :initialize_facts
64 Puppet.initialize_facts
65 else
66 Facter.add(:puppetversion) do
67 setcode { Puppet.version.to_s }
68 end
69 end
70 rescue LoadError => e
71 logger.error("Could not load puppet gem, got #{e}")
72 end
73
74 # Alias method for Facter.fact()
75 # @param name [string] fact name
76 #
77 # @return [Facter::Util::Fact, nil] The fact object, or nil if no fact
78 # is found.
79 #
80 # @api public
81 def [](name)
82 fact(name)
83 end
84
85 # Add custom facts to fact collection
86 # @param name [String] Custom fact name
87 # @param options = {} [Hash] optional parameters for the fact - attributes
88 # of {Facter::Util::Fact} and {Facter::Util::Resolution} can be
89 # supplied here
90 # @param block [Proc] a block defining a fact resolution
91 #
92 # @return [Facter::Util::Fact] the fact object, which includes any previously
93 # defined resolutions
94 #
95 # @api public
96 def add(name, options = {}, &block)
97 options[:fact_type] = :custom
98 LegacyFacter.add(name, options, &block)
99 LegacyFacter.collection.invalidate_custom_facts
100 end
101
102 # Clears all cached values and removes all facts from memory.
103 #
104 # @return [nil]
105 #
106 # @api public
107 def clear
108 @already_searched = {}
109 Facter.clear_messages
110 LegacyFacter.clear
111 Options[:custom_dir] = []
112 LegacyFacter.collection.invalidate_custom_facts
113 LegacyFacter.collection.reload_custom_facts
114 SessionCache.invalidate_all_caches
115 nil
116 end
117
118 # Clears the seen state of debug and warning messages.
119 #
120 # @return [nil]
121 def clear_messages
122 Facter::Log.clear_messages
123 end
124
125 # Retrieves the value of a core fact. External or custom facts are
126 # not returned with this call. Returns `nil` if no such fact exists.
127 #
128 # @return [FactCollection] A hash with fact names and values
129 #
130 # @api private
131 def core_value(user_query)
132 user_query = user_query.to_s
133 resolved_facts = Facter::FactManager.instance.resolve_core([user_query])
134 fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
135 splitted_user_query = Facter::Utils.split_user_query(user_query)
136 fact_collection.dig(*splitted_user_query)
137 end
138
139 # Logs debug message when debug option is set to true
140 # @param message [Object] Message object to be logged
141 #
142 # @return [nil]
143 #
144 # @api public
145 def debug(message)
146 return unless debugging?
147
148 logger.debug(message.to_s)
149 nil
150 end
151
152 # Logs the same debug message only once when debug option is set to true
153 # @param message [Object] Message object to be logged
154 #
155 # @return [nil]
156 #
157 # @api public
158 def debugonce(message)
159 logger.debugonce(message)
160 nil
161 end
162
163 # Define a new fact or extend an existing fact.
164 #
165 # @param name [Symbol] The name of the fact to define
166 # @param options [Hash] A hash of options to set on the fact
167 #
168 # @return [Facter::Util::Fact] The fact that was defined
169 #
170 # @api public
171 def define_fact(name, options = {}, &block)
172 options[:fact_type] = :custom
173 LegacyFacter.define_fact(name, options, &block)
174 end
175
176 # Stores a proc that will be used to output custom messages.
177 # The proc must receive one parameter that will be the message to log.
178 # @param block [Proc] a block defining messages handler
179 #
180 # @return [nil]
181 #
182 # @api public
183 def on_message(&block)
184 Facter::Log.on_message(&block)
185 nil
186 end
187
188 # Check whether debugging is enabled
189 #
190 # @return [bool]
191 #
192 # @api public
193 def debugging?
194 Options[:debug]
195 end
196
197 # Enable or disable debugging
198 # @param debug_bool [bool] State which debugging should have
199 #
200 # @return [type] [description]
201 #
202 # @api public
203 def debugging(debug_bool)
204 Facter::Options[:debug] = debug_bool
205 end
206
207 # Check whether http debugging is enabled
208 #
209 # @return [bool]
210 #
211 # @api public
212 def http_debug?
213 Options[:http_debug]
214 end
215
216 # Enable or disable http debugging
217 # @param debug_bool [bool] State which http debugging should have
218 #
219 # @return [type] [description]
220 #
221 # @api public
222 def http_debug(http_debug_bool)
223 Facter::Options[:http_debug] = http_debug_bool
224 end
225
226 # Enable sequential resolving of facts
227 #
228 # @return [bool]
229 #
230 # @api public
231 def enable_sequential
232 Facter::Options[:sequential] = true
233 end
234
235 # Disable sequential resolving of facts
236 #
237 # @return [bool]
238 #
239 # @api public
240 def disable_sequential
241 Facter::Options[:sequential] = false
242 end
243
244 # Check if facts are resolved sequentially or not
245 #
246 # @return [bool]
247 #
248 # @api public
249 def sequential?
250 Facter::Options[:sequential]
251 end
252
253 # Iterates over fact names and values
254 #
255 # @yieldparam [String] name the fact name
256 # @yieldparam [String] value the current value of the fact
257 #
258 # @return [Facter]
259 #
260 # @api public
261 def each
262 log_blocked_facts
263 resolved_facts = Facter::FactManager.instance.resolve_facts
264
265 resolved_facts.each do |fact|
266 yield(fact.name, fact.value)
267 end
268
269 self
270 end
271
272 # Reset search paths for custom and external facts
273 # If config file is set custom and external facts will be reloaded
274 #
275 # @return [nil]
276 #
277 # @api public
278 def reset
279 LegacyFacter.reset
280 Options[:custom_dir] = []
281 Options[:external_dir] = []
282 SessionCache.invalidate_all_caches
283 nil
284 end
285
286 # Flushes cached values for all facts. This does not cause code to be
287 # reloaded; it only clears the cached results.
288 #
289 # @return [void]
290 #
291 # @api public
292 def flush
293 LegacyFacter.flush
294 SessionCache.invalidate_all_caches
295 nil
296 end
297
298 # Loads all facts
299 #
300 # @return [nil]
301 #
302 # @api public
303 def loadfacts
304 LegacyFacter.loadfacts
305 nil
306 end
307
308 # Enables/Disables external facts.
309 # @param enable_external [boolean]
310 #
311 # @return nil
312 #
313 # @api public
314 def load_external(enable_external)
315 # enable_external param needs negation because behind the scene
316 # no_external_facts= method is negating the parameter again.
317 Options[:no_external_facts] = !enable_external
318
319 if enable_external
320 logger.debug('Facter.load_external(true) called. External facts will be loaded')
321 else
322 logger.debug('Facter.load_external(false) called. External facts will NOT be loaded')
323 end
324
325 nil
326 end
327
328 # Register directories to be searched for custom facts. The registered directories
329 # must be absolute paths or they will be ignored.
330 # @param dirs [Array<String>] An array of searched directories
331 #
332 # @return [nil]
333 #
334 # @api public
335 def search(*dirs)
336 Options[:custom_dir] += dirs
337 nil
338 end
339
340 # Registers directories to be searched for external facts.
341 # @param dirs [Array<String>] An array of searched directories
342 #
343 # @return [nil]
344 #
345 # @api public
346 def search_external(dirs)
347 Options[:external_dir] += dirs
348 nil
349 end
350
351 # Returns the registered search directories.for external facts.
352 #
353 # @return [Array<String>] An array of searched directories
354 #
355 # @api public
356 def search_external_path
357 Options.external_dir
358 end
359
360 # Returns the registered search directories for custom facts.
361 #
362 # @return [Array<String>] An array of the directories searched
363 #
364 # @api public
365 def search_path
366 Options.custom_dir
367 end
368
369 # Retrieves a fact's value. Returns `nil` if no such fact exists.
370 #
371 # @param user_query [String] the fact name
372 # @return [String] the value of the fact, or nil if no fact is found
373 #
374 # @api public
375 def to_hash
376 log_blocked_facts
377 logger.debug("Facter version: #{Facter::VERSION}")
378
379 resolved_facts = Facter::FactManager.instance.resolve_facts
380 resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }
381 collection = Facter::FactCollection.new.build_fact_collection!(resolved_facts)
382 Hash[collection]
383 end
384
385 # Check whether printing stack trace is enabled
386 #
387 # @return [bool]
388 #
389 # @api public
390 def trace?
391 Options[:trace]
392 end
393
394 # Enable or disable trace
395 # @param bool [bool] Set trace on debug state
396 #
397 # @return [bool] Value of trace debug state
398 #
399 # @api public
400 def trace(bool)
401 Options[:trace] = bool
402 end
403
404 # Gets the value for a fact. Returns `nil` if no such fact exists.
405 #
406 # @param user_query [String] the fact name
407 # @return [String] the value of the fact, or nil if no fact is found
408 #
409 # @api public
410 def value(user_query)
411 user_query = user_query.to_s.downcase
412 resolve_fact(user_query)
413
414 @already_searched[user_query]&.value
415 end
416
417 # Returns a fact object by name. If you use this, you still have to
418 # call {Facter::Util::Fact#value `value`} on it to retrieve the actual
419 # value.
420 #
421 # @param user_query [String] the name of the fact
422 #
423 # @return [Facter::Util::Fact, nil] The fact object, or nil if no fact
424 # is found.
425 #
426 # @api public
427 def fact(user_query)
428 user_query = user_query.to_s.downcase
429 resolve_fact(user_query)
430
431 @already_searched[user_query]
432 end
433
434 # Returns Facter version
435 #
436 # @return [String] Current version
437 #
438 # @api public
439 def version
440 Facter::VERSION
441 end
442
443 # Gets a hash mapping fact names to their values
444 #
445 # @return [Array] the hash of fact names and values
446 #
447 # @api private
448 def to_user_output(cli_options, *args)
449 init_cli_options(cli_options)
450 logger.info("executed with command line: #{ARGV.drop(1).join(' ')}")
451 logger.debug("Facter version: #{Facter::VERSION}")
452 log_blocked_facts
453 resolved_facts = resolve_facts_for_user_query(args)
454 fact_formatter = Facter::FormatterFactory.build(Facter::Options.get)
455 status = error_check(resolved_facts)
456
457 [fact_formatter.format(resolved_facts), status]
458 end
459
460 # Logs an exception and an optional message
461 #
462 # @return [nil]
463 #
464 # @api public
465 def log_exception(exception, message = nil)
466 error_message = []
467
468 error_message << message.to_s unless message.nil? || (message.is_a?(String) && message.empty?)
469
470 parse_exception(exception, error_message)
471 logger.error(error_message.flatten.join("\n"))
472 nil
473 end
474
475 # Returns a list with the names of all resolved facts
476 # @return [Array] the list with all the fact names
477 #
478 # @api public
479 def list
480 to_hash.keys.sort
481 end
482
483 # Logs the message parameter as a warning.
484 # @param message [Object] the warning object to be displayed
485 #
486 # @return [nil]
487 #
488 # @api public
489 def warn(message)
490 logger.warn(message.to_s)
491 nil
492 end
493
494 # Logs only once the same warning message.
495 # @param message [Object] the warning message object
496 #
497 # @return [nil]
498 #
499 # @api public
500 def warnonce(message)
501 logger.warnonce(message)
502 nil
503 end
504
505 private
506
507 def queried_facts(user_query)
508 log_blocked_facts
509 resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)
510 resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }
511
512 if user_query.count.zero?
513 Facter::FactCollection.new.build_fact_collection!(resolved_facts)
514 else
515 FormatterHelper.retrieve_facts_to_display_for_user_query(user_query, resolved_facts)
516 end
517 end
518
519 def resolve_facts_for_user_query(user_query)
520 resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)
521 user_querie = resolved_facts.uniq(&:user_query).map(&:user_query).first
522
523 resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? } if user_querie&.empty?
524
525 resolved_facts
526 end
527
528 def parse_exception(exception, error_message)
529 if exception.is_a?(Exception)
530 error_message << exception.message if error_message.empty?
531
532 if Options[:trace] && !exception.backtrace.nil?
533 error_message << 'backtrace:'
534 error_message.concat(exception.backtrace)
535 end
536 elsif error_message.empty?
537 error_message << exception.to_s
538 end
539 end
540
541 def logger
542 @logger ||= Log.new(self)
543 end
544
545 def init_cli_options(options)
546 options = options.map { |(k, v)| [k.to_sym, v] }.to_h
547 Facter::Options.init_from_cli(options)
548 end
549
550 def add_fact_to_searched_facts(user_query, value)
551 @already_searched[user_query] ||= ResolvedFact.new(user_query, value)
552 @already_searched[user_query].value = value
553 end
554
555 # Returns a ResolvedFact and saves the result in @already_searched array that is used as a global collection.
556 # @param user_query [String] Fact that needs resolution
557 #
558 # @return [ResolvedFact]
559 def resolve_fact(user_query)
560 user_query = user_query.to_s
561 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
562 # we must make a distinction between custom facts that return nil and nil facts
563 # Nil facts should not be packaged as ResolvedFacts! (add_fact_to_searched_facts packages facts)
564 resolved_facts = resolved_facts.reject { |fact| fact.type == :nil }
565 fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
566
567 begin
568 value = fact_collection.value(user_query)
569 add_fact_to_searched_facts(user_query, value)
570 rescue KeyError, TypeError
571 nil
572 end
573 end
574
575 # Returns exit status when user query contains facts that do
576 # not exist
577 #
578 # @param resolved_facts [Array] List of resolved facts
579 #
580 # @return [1/nil] Will return status 1 if user query contains
581 # facts that are not found or resolved, otherwise it will return nil
582 #
583 # @api private
584 def error_check(resolved_facts)
585 status = 0
586 if Options[:strict]
587 missing_names = resolved_facts.select { |fact| fact.type == :nil }.map(&:user_query)
588
589 if missing_names.count.positive?
590 status = 1
591 log_errors(missing_names)
592 end
593 end
594
595 status
596 end
597
598 # Prints out blocked facts before to_hash or to_user_output is called
599 #
600 # @return [nil]
601 #
602 # @api private
603 def log_blocked_facts
604 block_list = Options[:block_list]
605 return unless block_list.any? && Facter::Options[:block]
606
607 logger.debug("blocking collection of #{block_list.join("\s")} facts")
608 end
609
610 # Used for printing errors regarding CLI user input validation
611 #
612 # @param missing_names [Array] List of facts that were requested
613 # but not found
614 #
615 # @return [nil]
616 #
617 # @api private
618 def log_errors(missing_names)
619 missing_names.each do |missing_name|
620 logger.error("fact \"#{missing_name}\" does not exist.", true)
621 end
622 end
623
624 # Proxy method that catches not yet implemented method calls
625 #
626 # @param name [type] [description]
627 # @param *args [type] [description]
628 # @param &block [type] [description]
629 #
630 # @return [type] [description]
631 #
632 # @api private
633 def method_missing(name, *args, &block)
634 logger.error(
635 "--#{name}-- not implemented but required \n" \
636 'with params: ' \
637 "#{args.inspect} \n" \
638 'with block: ' \
639 "#{block.inspect} \n" \
640 "called by: \n" \
641 "#{caller} \n"
642 )
643 nil
644 end
645
646 prepend ApiDebugger if ENV['API_DEBUG']
647 end
648 end
+0
-49
lib/facter.rb.in less more
0 module Facter
1 if RUBY_PLATFORM == "java"
2 # For JRuby, require 'facter.jar'
3 begin
4 require 'facter.jar'
5 rescue LoadError
6 raise LoadError.new('libfacter was not built with JRuby support.')
7 end
8
9 # Pass value call through to JNI interface
10 def self.value(name)
11 Java::ComPuppetlabs::Facter.lookup(name)
12 end
13
14 def self.search(*paths)
15 # No-op; we don't support custom facts under JRuby
16 end
17
18 def self.reset()
19 # No-op; we treat facts as immutable under JRuby
20 end
21
22 def self.version
23 Java::ComPuppetlabs::Facter.lookup("facterversion")
24 end
25
26 def self.add(*params)
27 raise 'adding facts under JRuby is not implemented.'
28 end
29
30 else
31 # Simply require libfacter.so; this will define all of the Facter API
32 begin
33 facter_dir = ENV['FACTERDIR'] || File.join(File.expand_path("#{File.dirname(__FILE__)}"), '${LIBFACTER_INSTALL_RELATIVE}')
34 #
35 # This is a cmake pre-processor check. On *nix it will end up '' == '1'
36 # On windows, where we want the changes it will be '1' == '1'
37 #
38 # Facter requires the extra inclusion of puppet/bin as the libfacter.so
39 # lib requires libraries and executables from that directory
40 if '${WIN32}' == '1'
41 ENV['PATH'] = "#{File.join(facter_dir, 'bin')}#{File::PATH_SEPARATOR}#{File.join(facter_dir, '../puppet/bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
42 end
43 require "#{facter_dir}/${LIBFACTER_INSTALL_DESTINATION}/libfacter.so"
44 rescue LoadError
45 raise LoadError, "libfacter was not found. Please make sure it was installed to the expected location.\n" + ($!.message || ''), $!.backtrace
46 end
47 end
48 end
+0
-21
lib/gemspec.in less more
0 # -*- encoding: utf-8 -*-
1
2 Gem::Specification.new do |s|
3 s.name = "facter"
4 version = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"
5 mdata = version.match(/(\d+\.\d+\.\d+)/)
6 s.version = mdata ? mdata[1] : version
7
8 s.required_rubygems_version = Gem::Requirement.new(">= 0")
9 s.authors = ["Puppet Labs"]
10 s.date = "2015-10-22"
11 s.description = "You can prove anything with facts!"
12 s.email = "info@puppetlabs.com"
13 s.files = ["bin/facter"]
14 s.homepage = "https://puppetlabs.com"
15 s.require_paths = ["lib"]
16 s.summary = "Facter, a system inventory tool"
17 s.specification_version = 3
18 s.required_ruby_version = '~> 2.1'
19
20 end
+0
-18
lib/inc/facter/cwrapper.hpp less more
0 #pragma once
1
2 #include "stdint.h"
3
4 #ifdef __cplusplus
5 extern "C" {
6 #endif
7
8 /**
9 * Collects all default facts and store them as a C-string in JSON format.
10 * @param result a pointer to the C-string pointer to the collected facts.
11 * @return Returns EXIT_FAILURE if it fails to retrieve or copy facts; EXIT_SUCCESS otherwise.
12 */
13 uint8_t get_default_facts(char **result);
14
15 #ifdef __cplusplus
16 }
17 #endif
+0
-125
lib/inc/facter/facts/array_value.hpp less more
0 /**
1 * @file
2 * Declares the fact value for arrays.
3 */
4 #pragma once
5
6 #include "value.hpp"
7 #include "../export.h"
8 #include <vector>
9 #include <memory>
10 #include <functional>
11
12 namespace facter { namespace facts {
13
14 /**
15 * Represents an array of values.
16 * This type can be moved but cannot be copied.
17 */
18 struct LIBFACTER_EXPORT array_value : value
19 {
20 /**
21 * Constructs an array_value.
22 * @param hidden True if the fact is hidden from output by default or false if not.
23 */
24 array_value(bool hidden = false) :
25 value(hidden)
26 {
27 }
28
29 /**
30 * Prevents the array_value from being copied.
31 */
32 array_value(array_value const&) = delete;
33
34 /**
35 * Prevents the array_value from being copied.
36 * @returns Returns this array_value.
37 */
38 array_value& operator=(array_value const&) = delete;
39
40 /**
41 * Moves the given array_value into this array_value.
42 * @param other The array_value to move into this array_value.
43 */
44 // Visual Studio 12 still doesn't allow default for move constructor.
45 array_value(array_value&& other);
46
47 /**
48 * Moves the given array_value into this array_value.
49 * @param other The array_value to move into this array_value.
50 * @return Returns this array_value.
51 */
52 // Visual Studio 12 still doesn't allow default for move assignment.
53 array_value& operator=(array_value&& other);
54
55 /**
56 * Adds a value to the array.
57 * @param value The value to add to the array.
58 */
59 void add(std::unique_ptr<value> value);
60
61 /**
62 * Checks to see if the array is empty.
63 * @return Returns true if the array is empty or false if it is not.
64 */
65 bool empty() const;
66
67 /**
68 * Gets the size of the array.
69 * @return Returns the number of values in the array.
70 */
71 size_t size() const;
72
73 /**
74 * Enumerates all facts in the array.
75 * @param func The callback function called for each value in the array.
76 */
77 void each(std::function<bool(value const*)> func) const;
78
79 /**
80 * Converts the value to a JSON value.
81 * @param allocator The allocator to use for creating the JSON value.
82 * @param value The returned JSON value.
83 */
84 virtual void to_json(json_allocator& allocator, json_value& value) const override;
85
86 /**
87 * Gets the element at the given index.
88 * @tparam T The expected type of the value.
89 * @param i The index in the array to get the element at.
90 * @return Returns the value at the given index or nullptr if the value is not of the expected type.
91 */
92 template <typename T = value> T const* get(size_t i) const
93 {
94 return dynamic_cast<T const*>(_elements.at(i).get());
95 }
96
97 /**
98 * Gets the value at the given index.
99 * @param i The index in the array to get the element at.
100 * @return Returns the value at the given index.
101 */
102 value const* operator[](size_t i) const;
103
104 /**
105 * Writes the value to the given stream.
106 * @param os The stream to write to.
107 * @param quoted True if string values should be quoted or false if not.
108 * @param level The current indentation level.
109 * @returns Returns the stream being written to.
110 */
111 virtual std::ostream& write(std::ostream& os, bool quoted = true, unsigned int level = 1) const override;
112
113 /**
114 * Writes the value to the given YAML emitter.
115 * @param emitter The YAML emitter to write to.
116 * @returns Returns the given YAML emitter.
117 */
118 virtual YAML::Emitter& write(YAML::Emitter& emitter) const override;
119
120 private:
121 std::vector<std::unique_ptr<value>> _elements;
122 };
123
124 }} // namespace facter::facts
+0
-32
lib/inc/facter/facts/base_resolver.hpp less more
0 #ifndef BASE_RESOLVER_H
1 #define BASE_RESOLVER_H
2
3 #include "../export.h"
4 #include <vector>
5 #include <string>
6
7 namespace facter { namespace facts {
8 struct collection;
9 struct LIBFACTER_EXPORT base_resolver
10 {
11 /**
12 * Gets the name of the fact resolver.
13 * @return Returns the fact resolver's name.
14 */
15 virtual std::string const& name() const = 0;
16
17 /**
18 * Gets the fact names the resolver is responsible for resolving.
19 * @return Returns a list of fact names.
20 */
21 virtual std::vector<std::string> const& names() const = 0;
22
23 /**
24 * Called to resolve all facts the resolver is responsible for.
25 * @param facts The fact collection that is resolving facts.
26 */
27 virtual void resolve(collection& facts) = 0;
28 };
29 }} // namespace facter::facts
30
31 #endif // BASE_RESOLVER_H
+0
-357
lib/inc/facter/facts/collection.hpp less more
0 /**
1 * @file
2 * Declares the fact collection.
3 */
4 #pragma once
5
6 #include "resolver.hpp"
7 #include "value.hpp"
8 #include "external/resolver.hpp"
9 #include "../export.h"
10 #include <list>
11 #include <map>
12 #include <set>
13 #include <unordered_map>
14 #include <vector>
15 #include <string>
16 #include <memory>
17 #include <functional>
18 #include <stdexcept>
19 #include <iostream>
20
21 namespace facter { namespace facts {
22
23 static const std::string cached_custom_facts("cached-custom-facts");
24
25 /**
26 * The supported output format for the fact collection.
27 */
28 enum class format
29 {
30 /**
31 * Use ruby "hash" as the format (default).
32 */
33 hash,
34 /**
35 * Use JSON as the format.
36 */
37 json,
38 /**
39 * Use YAML as the format.
40 */
41 yaml
42 };
43
44 namespace {
45 /**
46 * Stream adapter for using with rapidjson
47 */
48 struct stream_adapter
49 {
50 /**
51 * Constructs an adapter for use with rapidjson around the given stream.
52 * @param stream an output stream to which JSON will be written
53 */
54 explicit stream_adapter(std::ostream& stream) : _stream(stream)
55 {
56 }
57
58 /**
59 * Adds a character to the stream.
60 * @param c the char to add
61 */
62 void Put(char c)
63 {
64 _stream << c;
65 }
66
67 /**
68 * Flushes the stream.
69 */
70 void Flush()
71 {
72 _stream.flush();
73 }
74
75 private:
76 std::ostream& _stream;
77 };
78 }
79
80 /**
81 * Represents the fact collection.
82 * The fact collection is responsible for resolving and storing facts.
83 */
84 struct LIBFACTER_EXPORT collection
85 {
86 /**
87 * Inherent "has_weight" value for external facts.
88 */
89 constexpr static size_t external_fact_weight = 10000;
90
91 /**
92 * Constructs a fact collection.
93 * @param blocklist the names of resolvers that should not be resolved
94 * @param ttls a map of resolver names to cache intervals (times-to-live)
95 * for the facts they resolve
96 * @param ignore_cache true if the cache should not be consulted when resolving facts
97 */
98 collection(std::set<std::string> const& blocklist = std::set<std::string>(),
99 std::unordered_map<std::string, int64_t> const& ttls = std::unordered_map<std::string, int64_t>{},
100 bool ignore_cache = false);
101
102 /**
103 * Destructor for fact collection.
104 */
105 ~collection();
106
107 /**
108 * Prevents the fact collection from being copied.
109 */
110 collection(collection const&) = delete;
111
112 /**
113 * Prevents the fact collection from being copied.
114 * @returns Returns this fact collection.
115 */
116 collection& operator=(collection const&) = delete;
117
118 /**
119 * Moves the given fact collection into this fact collection.
120 * @param other The fact collection to move into this fact collection.
121 */
122 // Visual Studio 12 still doesn't allow default for move constructor.
123 collection(collection&& other);
124
125 /**
126 * Moves the given fact collection into this fact collection.
127 * @param other The fact collection to move into this fact collection.
128 * @return Returns this fact collection.
129 */
130 // Visual Studio 12 still doesn't allow default for move assignment.
131 collection& operator=(collection&& other);
132
133 /**
134 * Adds the default facts to the collection.
135 * @param include_ruby_facts Whether or not to include facts which require Ruby in the collection.
136 */
137 void add_default_facts(bool include_ruby_facts);
138
139 /**
140 * Adds a resolver to the fact collection.
141 * The last resolver that was added for a particular name or pattern will "win" resolution.
142 * @param res The resolver to add to the fact collection.
143 */
144 void add(std::shared_ptr<resolver> const& res);
145
146 /**
147 * Adds a fact to the fact collection.
148 * @param name The name of the fact.
149 * @param value The value of the fact.
150 */
151 void add(std::string name, std::unique_ptr<value> value);
152
153 /**
154 * Adds a custom fact to the fact collection.
155 * @param name The name of the fact.
156 * @param value The value of the fact.
157 * @param weight The weight of the fact.
158 */
159 void add_custom(std::string name, std::unique_ptr<value> value, size_t weight);
160
161 /**
162 * Adds an external fact to the fact collection.
163 * @param name The name of the fact.
164 * @param value The value of the fact.
165 */
166 void add_external(std::string name, std::unique_ptr<value> value);
167
168 /**
169 * Adds external facts to the fact collection.
170 * @param directories The directories to search for external facts. If empty, the default search paths will be used.
171 */
172 void add_external_facts(std::vector<std::string> const& directories = {});
173
174 /**
175 * Adds facts defined via "FACTER_xyz" environment variables.
176 * @param callback The callback that is called with the name of each fact added from the environment.
177 */
178 void add_environment_facts(std::function<void(std::string const&)> callback = nullptr);
179
180 /**
181 * Removes a resolver from the fact collection.
182 * @param res The resolver to remove from the fact collection.
183 */
184 void remove(std::shared_ptr<resolver> const& res);
185
186 /**
187 * Removes a fact by name.
188 * @param name The name of the fact to remove.
189 */
190 void remove(std::string const& name);
191
192 /**
193 * Clears the entire fact collection.
194 * This will remove all built-in facts and resolvers from the fact collection.
195 */
196 void clear();
197
198 /**
199 * Checks to see if the fact collection is empty.
200 * All facts will be resolved to determine if the collection is empty.
201 * @return Returns true if the fact collection is empty or false if it is not.
202 */
203 bool empty();
204
205 /**
206 * Gets the count of facts in the fact collection.
207 * All facts will be resolved to determine the size of the collection.
208 * @return Returns the number of facts in the fact collection.
209 */
210 size_t size();
211
212 /**
213 * Gets a fact value by name.
214 * @tparam T The expected type of the value.
215 * @param name The name of the fact to get the value of.
216 * @return Returns a pointer to the fact value or nullptr if the fact is not in the fact collection or the value is not the expected type.
217 */
218 template <typename T = value>
219 T const* get(std::string const& name)
220 {
221 return dynamic_cast<T const*>(get_value(name));
222 }
223
224 /**
225 * Gets a fact value by name without resolving the fact.
226 * @tparam T The expected type of the value.
227 * @param name The name of the fact to get the value of.
228 * @return Returns a pointer to the fact value or nullptr if the fact is not resolved or the value is not the expected type.
229 */
230 template <typename T = value>
231 T const* get_resolved(std::string const& name) const
232 {
233 // Lookup the fact without resolving
234 auto it = _facts.find(name);
235 return dynamic_cast<T const*>(it == _facts.end() ? nullptr : it->second.get());
236 }
237
238 /**
239 * Gets a fact value by name
240 * @param name The name of the fact to get the value of.
241 * @return Returns a pointer to the fact value or nullptr if the fact is not in the fact collection.
242 */
243 value const* operator[](std::string const& name);
244
245 /**
246 * Query the collection.
247 * @tparam T The expected type of the value.
248 * @param query The query to run.
249 * @return Returns the result of the query or nullptr if the query returned no value.
250 */
251 template <typename T = value>
252 T const* query(std::string const& query)
253 {
254 return dynamic_cast<T const*>(query_value(query, false));
255 }
256
257 /**
258 * Enumerates all facts in the collection.
259 * All facts will be resolved prior to enumeration.
260 * @param func The callback function called for each fact in the collection.
261 */
262 void each(std::function<bool(std::string const&, value const*)> func);
263
264 /**
265 * Writes the contents of the fact collection to the given stream, hiding legacy facts.
266 * All facts will be resolved prior to writing.
267 * @param stream The stream to write the facts to.
268 * @param fmt The output format to use.
269 * @param queries The set of queries to filter the output to. If empty, all facts will be output.
270 * @return Returns the stream being written to.
271 */
272 std::ostream& write(std::ostream& stream, format fmt = format::hash, std::set<std::string> const& queries = std::set<std::string>());
273
274 /**
275 * Writes the contents of the fact collection to the given stream.
276 * All facts will be resolved prior to writing.
277 * @param stream The stream to write the facts to.
278 * @param fmt The output format to use.
279 * @param queries The set of queries to filter the output to. If empty, all facts will be output.
280 * @param show_legacy Show legacy facts when querying all facts.
281 * @param strict_errors Report additional error cases
282 * @return Returns the stream being written to.
283 */
284 std::ostream& write(std::ostream& stream, format fmt, std::set<std::string> const& queries, bool show_legacy, bool strict_errors);
285
286 /**
287 * Resolves all facts in the collection.
288 */
289 void resolve_facts();
290
291 /**
292 * Returns the names of all the resolvers currently in the collection,
293 * along with their associated facts. The group names are used to allow
294 * caching of those facts.
295 * @return a map of group names to their associated fact names
296 */
297 std::map<std::string, std::vector<std::string>> get_fact_groups();
298
299 /**
300 * Returns the time-to-live time for each fact from the facter.conf file
301 * @return a map of fact names to their associated time-to-live value
302 */
303 const std::unordered_map<std::string, int64_t>& get_ttls();
304
305 /**
306 * Returns the names of all blockable resolvers currently in the collection,
307 * along with their associated facts. The group names are used to allow
308 * blocking of those facts.
309 * @return a map of blockable group names to their associated fact names
310 */
311 std::map<std::string, std::vector<std::string>> get_blockable_fact_groups();
312
313 /**
314 * Gets external fact groups (practically files names)
315 * @param directories The directories to search for external facts. If empty, the default search paths will be used.
316 * @return a map of group names to their associated fact names (empty)
317 */
318 std::map<std::string, std::vector<std::string>> get_external_facts_groups(std::vector<std::string> const& directories);
319
320 protected:
321 /**
322 * Gets external fact directories for the current platform.
323 * @return A list of file paths that will be searched for external facts.
324 */
325 virtual std::vector<std::string> get_external_fact_directories() const;
326
327 private:
328 typedef std::list<std::pair<std::string, std::shared_ptr<external::resolver>>> external_files_list;
329 LIBFACTER_NO_EXPORT void resolve_fact(std::string const& name);
330 LIBFACTER_NO_EXPORT value const* get_value(std::string const& name);
331 LIBFACTER_NO_EXPORT value const* query_value(std::string const& query, bool strict_errors);
332 LIBFACTER_NO_EXPORT value const* lookup(value const* value, std::string const& name, bool strict_errors);
333 LIBFACTER_NO_EXPORT void write_hash(std::ostream& stream, std::set<std::string> const& queries, bool show_legacy, bool strict_errors);
334 LIBFACTER_NO_EXPORT void write_json(std::ostream& stream, std::set<std::string> const& queries, bool show_legacy, bool strict_errors);
335 LIBFACTER_NO_EXPORT void write_yaml(std::ostream& stream, std::set<std::string> const& queries, bool show_legacy, bool strict_errors);
336 LIBFACTER_NO_EXPORT void add_common_facts(bool include_ruby_facts);
337 LIBFACTER_NO_EXPORT void get_external_facts_files_from_dir(external_files_list& files,
338 std::string const& directory, bool warn);
339
340 LIBFACTER_NO_EXPORT external_files_list get_external_facts_files(std::vector<std::string> const& directories);
341 LIBFACTER_NO_EXPORT bool try_block(std::shared_ptr<resolver> const& res);
342 LIBFACTER_NO_EXPORT void resolve(std::shared_ptr<resolver> const& res);
343
344 // Platform specific members
345 LIBFACTER_NO_EXPORT void add_platform_facts();
346
347 std::map<std::string, std::unique_ptr<value>> _facts;
348 std::list<std::shared_ptr<resolver>> _resolvers;
349 std::multimap<std::string, std::shared_ptr<resolver>> _resolver_map;
350 std::list<std::shared_ptr<resolver>> _pattern_resolvers;
351 std::set<std::string> _blocklist;
352 std::unordered_map<std::string, int64_t> _ttls;
353 bool _ignore_cache;
354 };
355
356 }} // namespace facter::facts
+0
-70
lib/inc/facter/facts/external/resolver.hpp less more
0 /**
1 * @file
2 * Declares the base external fact resolver.
3 */
4 #pragma once
5
6 #include <stdexcept>
7 #include <string>
8 #include "../../export.h"
9 #include "facter/facts/base_resolver.hpp"
10
11 namespace facter { namespace facts {
12 struct collection;
13 }} // namespace facter::facts
14
15 namespace facter { namespace facts { namespace external {
16
17 /**
18 * Thrown when there is an error processing an external fact.
19 */
20 struct LIBFACTER_EXPORT external_fact_exception : std::runtime_error
21 {
22 /**
23 * Constructs a external_fact_exception.
24 * @param message The exception message.
25 */
26 explicit external_fact_exception(std::string const& message);
27 };
28
29 /**
30 * Thrown when there is no external resolver for a file
31 */
32 struct LIBFACTER_EXPORT external_fact_no_resolver : std::runtime_error
33 {
34 explicit external_fact_no_resolver(std::string const& message);
35 };
36
37 /**
38 * Base class for external resolvers
39 */
40 struct LIBFACTER_EXPORT resolver : facter::facts::base_resolver
41 {
42 resolver(std::string const &path);
43
44 /**
45 * Resolves facts from the given file.
46 * @param path The path to the file to resolve facts from.
47 * @param facts The fact collection to populate the external facts into.
48 */
49 virtual void resolve(collection& facts) = 0;
50
51 /**
52 * Gets the name of the fact resolver.
53 * @return Returns the fact resolver's name.
54 */
55 std::string const& name() const;
56
57 /**
58 * Gets the fact names the resolver is responsible for resolving.
59 * @return Returns a vector of fact names.
60 */
61 std::vector<std::string> const& names() const;
62
63 protected:
64 std::string _path;
65 std::string _name;
66 std::vector<std::string> _names;
67 };
68
69 }}} // namespace facter::facts::external
+0
-23
lib/inc/facter/facts/external_resolvers_factory.hpp less more
0 #ifndef EXTERNAL_RESOLVERS_FACTORY_H
1 #define EXTERNAL_RESOLVERS_FACTORY_H
2
3 #include "../export.h"
4 #include "external/resolver.hpp"
5 #include <memory>
6
7 namespace facter { namespace facts {
8 struct LIBFACTER_NO_EXPORT external_resolvers_factory {
9 std::shared_ptr<external::resolver> get_resolver(const std::string&);
10
11 bool text_resolver_can_resolve(std::string const &path);
12 bool json_resolver_can_resolve(std::string const &path);
13 bool yaml_resolver_can_resolve(std::string const &path);
14
15 bool execution_resolver_can_resolve(std::string const &path);
16 bool powershell_resolver_can_resolve(std::string const &path);
17
18 std::shared_ptr<external::resolver> get_common_resolver(const std::string& path);
19 };
20
21 }} // namespace facter::facts
22 #endif // EXTERNAL_RESOLVERS_FACTORY_H
+0
-698
lib/inc/facter/facts/fact.hpp less more
0 /**
1 * @file
2 * Declares the fact name constants.
3 */
4 #pragma once
5
6 #include "../export.h"
7
8 namespace facter { namespace facts {
9
10 /**
11 * Stores the constant fact names.
12 */
13 struct LIBFACTER_EXPORT fact
14 {
15 /**
16 * The fact for kernel name.
17 */
18 constexpr static char const* kernel = "kernel";
19 /**
20 * The fact for kernel version.
21 */
22 constexpr static char const* kernel_version = "kernelversion";
23 /**
24 * The fact for kernel release.
25 */
26 constexpr static char const* kernel_release = "kernelrelease";
27 /**
28 * The fact for kernel major version.
29 */
30 constexpr static char const* kernel_major_version = "kernelmajversion";
31
32 /**
33 * The structured operating system fact.
34 */
35 constexpr static char const* os = "os";
36 /**
37 * The fact for operating system name.
38 */
39 constexpr static char const* operating_system = "operatingsystem";
40 /**
41 * The fact for operating system family name.
42 */
43 constexpr static char const* os_family = "osfamily";
44 /**
45 * The fact for operating system release.
46 */
47 constexpr static char const* operating_system_release = "operatingsystemrelease";
48 /**
49 * The fact for operating system major release.
50 */
51 constexpr static char const* operating_system_major_release = "operatingsystemmajrelease";
52
53 /**
54 * The fact for LSB distro id.
55 */
56 constexpr static char const* lsb_dist_id = "lsbdistid";
57 /**
58 * The fact for LSB distro release.
59 */
60 constexpr static char const* lsb_dist_release = "lsbdistrelease";
61 /**
62 * The fact for LSB distro codename.
63 */
64 constexpr static char const* lsb_dist_codename = "lsbdistcodename";
65 /**
66 * The fact for LSB distro description.
67 */
68 constexpr static char const* lsb_dist_description = "lsbdistdescription";
69 /**
70 * The fact for LSB distro major release.
71 */
72 constexpr static char const* lsb_dist_major_release = "lsbmajdistrelease";
73 /**
74 * The fact for LSB distro minor release.
75 */
76 constexpr static char const* lsb_dist_minor_release = "lsbminordistrelease";
77 /**
78 * The fact for LSB release.
79 */
80 constexpr static char const* lsb_release = "lsbrelease";
81
82 /**
83 * The structured fact for networking.
84 */
85 constexpr static char const* networking = "networking";
86 /**
87 * The fact for network hostname.
88 */
89 constexpr static char const* hostname = "hostname";
90 /**
91 * The fact for IPv4 address.
92 */
93 constexpr static char const* ipaddress = "ipaddress";
94 /**
95 * The fact for IPv6 address.
96 */
97 constexpr static char const* ipaddress6 = "ipaddress6";
98 /**
99 * The fact for interface MTU.
100 */
101 constexpr static char const* mtu = "mtu";
102 /**
103 * The fact for IPv4 netmask.
104 */
105 constexpr static char const* netmask = "netmask";
106 /**
107 * The fact for IPv6 netmask.
108 */
109 constexpr static char const* netmask6 = "netmask6";
110 /**
111 * The fact for IPv4 network.
112 */
113 constexpr static char const* network = "network";
114 /**
115 * The fact for IPv6 network.
116 */
117 constexpr static char const* network6 = "network6";
118 /**
119 * The fact for IPv6 scope.
120 */
121 constexpr static char const* scope6 = "scope6";
122 /**
123 * The fact for interface MAC address.
124 */
125 constexpr static char const* macaddress = "macaddress";
126 /**
127 * The fact for interface names.
128 */
129 constexpr static char const* interfaces = "interfaces";
130 /**
131 * The fact for domain name.
132 */
133 constexpr static char const* domain = "domain";
134 /**
135 * The fact for fully-qualified domain name (FQDN).
136 */
137 constexpr static char const* fqdn = "fqdn";
138 /**
139 * The fact for DHCP servers.
140 */
141 constexpr static char const* dhcp_servers = "dhcp_servers";
142
143 /**
144 * The fact for block device.
145 */
146 constexpr static char const* block_device = "blockdevice";
147 /**
148 * The fact for the list of block devices.
149 */
150 constexpr static char const* block_devices = "blockdevices";
151
152 /**
153 * The structured processor fact
154 */
155 constexpr static char const* processors = "processors";
156 /**
157 * The fact for processor descriptions.
158 */
159 constexpr static char const* processor = "processor";
160 /**
161 * The fact for logical processor count.
162 */
163 constexpr static char const* processor_count = "processorcount";
164 /**
165 * The fact for physical processor count.
166 */
167 constexpr static char const* physical_processor_count = "physicalprocessorcount";
168 /**
169 * The fact for processor instruction set architecture.
170 */
171 constexpr static char const* hardware_isa = "hardwareisa";
172 /**
173 * The fact for processor hardware model.
174 */
175 constexpr static char const* hardware_model = "hardwaremodel";
176
177 /**
178 * The fact for hardware architecture.
179 */
180 constexpr static char const* architecture = "architecture";
181
182 /**
183 * The structured fact for DMI data.
184 */
185 constexpr static char const* dmi = "dmi";
186 /**
187 * The fact for BIOS vendor.
188 */
189 constexpr static char const* bios_vendor = "bios_vendor";
190 /**
191 * The fact for BIOS version.
192 */
193 constexpr static char const* bios_version = "bios_version";
194 /**
195 * The fact for BIOS release date.
196 */
197 constexpr static char const* bios_release_date = "bios_release_date";
198 /**
199 * The fact for motherboard asset tag.
200 */
201 constexpr static char const* board_asset_tag = "boardassettag";
202 /**
203 * The fact for motherboard manufacturer.
204 */
205 constexpr static char const* board_manufacturer = "boardmanufacturer";
206 /**
207 * The fact for motherboard product name.
208 */
209 constexpr static char const* board_product_name = "boardproductname";
210 /**
211 * The fact for motherboard serial number.
212 */
213 constexpr static char const* board_serial_number = "boardserialnumber";
214 /**
215 * The fact for chassis asset tag.
216 */
217 constexpr static char const* chassis_asset_tag = "chassisassettag";
218 /**
219 * The fact for hardware manufacturer.
220 */
221 constexpr static char const* manufacturer = "manufacturer";
222 /**
223 * The fact for hardware product name.
224 */
225 constexpr static char const* product_name = "productname";
226 /**
227 * The fact for hardware serial number.
228 */
229 constexpr static char const* serial_number = "serialnumber";
230 /**
231 * The fact for hardware UUID.
232 */
233 constexpr static char const* uuid = "uuid";
234 /**
235 * The fact for hardware chassis type.
236 */
237 constexpr static char const* chassis_type = "chassistype";
238
239 /**
240 * The structured uptime fact
241 */
242 constexpr static char const* system_uptime = "system_uptime";
243 /**
244 * The fact for system uptime.
245 */
246 constexpr static char const* uptime = "uptime";
247 /**
248 * The fact for system uptime, in days.
249 */
250 constexpr static char const* uptime_days = "uptime_days";
251 /**
252 * The fact for system uptime, in hours.
253 */
254 constexpr static char const* uptime_hours = "uptime_hours";
255 /**
256 * The fact for system uptime, in seconds.
257 */
258 constexpr static char const* uptime_seconds = "uptime_seconds";
259
260 /**
261 * The fact for selinux state.
262 */
263 constexpr static char const* selinux = "selinux";
264 /**
265 * The fact for selinux enforcement state.
266 */
267 constexpr static char const* selinux_enforced = "selinux_enforced";
268 /**
269 * The fact for selinux policy version.
270 */
271 constexpr static char const* selinux_policyversion = "selinux_policyversion";
272 /**
273 * The fact for current selinux mode.
274 */
275 constexpr static char const* selinux_current_mode = "selinux_current_mode";
276 /**
277 * The fact for selinux config mode.
278 */
279 constexpr static char const* selinux_config_mode = "selinux_config_mode";
280 /**
281 * The fact for selinux config policy.
282 */
283 constexpr static char const* selinux_config_policy = "selinux_config_policy";
284
285 /**
286 * The structured fact for SSH.
287 */
288 constexpr static char const* ssh = "ssh";
289 /**
290 * The fact for SSH DSA public key.
291 */
292 constexpr static char const* ssh_dsa_key = "sshdsakey";
293 /**
294 * The fact for SSH RSA public key.
295 */
296 constexpr static char const* ssh_rsa_key = "sshrsakey";
297 /**
298 * The fact for SSH ECDSA public key.
299 */
300 constexpr static char const* ssh_ecdsa_key = "sshecdsakey";
301 /**
302 * The fact for SSH ED25519 public key.
303 */
304 constexpr static char const* ssh_ed25519_key = "sshed25519key";
305 /**
306 * The fact for SSH fingerprint of the DSA public key.
307 */
308 constexpr static char const* sshfp_dsa = "sshfp_dsa";
309 /**
310 * The fact for SSH fingerprint of the RSA public key.
311 */
312 constexpr static char const* sshfp_rsa = "sshfp_rsa";
313 /**
314 * The fact for SSH fingerprint of the ECDSA public key.
315 */
316 constexpr static char const* sshfp_ecdsa = "sshfp_ecdsa";
317 /**
318 * The fact for SSH fingerprint of the ED25519 public key.
319 */
320 constexpr static char const* sshfp_ed25519 = "sshfp_ed25519";
321
322 /**
323 * The structured fact for OSX system profiler facts.
324 */
325 constexpr static char const* system_profiler = "system_profiler";
326 /**
327 * The fact for OSX system profiler boot mode.
328 */
329 constexpr static char const* sp_boot_mode = "sp_boot_mode";
330 /**
331 * The fact for OSX system profiler boot ROM version.
332 */
333 constexpr static char const* sp_boot_rom_version = "sp_boot_rom_version";
334 /**
335 * The fact for OSX system profiler boot volume.
336 */
337 constexpr static char const* sp_boot_volume = "sp_boot_volume";
338 /**
339 * The fact for OSX system profiler CPU type (processor name).
340 */
341 constexpr static char const* sp_cpu_type = "sp_cpu_type";
342 /**
343 * The fact for OSX system profiler current CPU speed.
344 */
345 constexpr static char const* sp_current_processor_speed = "sp_current_processor_speed";
346 /**
347 * The fact for OSX system profiler kernel version.
348 */
349 constexpr static char const* sp_kernel_version = "sp_kernel_version";
350 /**
351 * The fact for OSX system profiler L2 cache (per core).
352 */
353 constexpr static char const* sp_l2_cache_core = "sp_l2_cache_core";
354 /**
355 * The fact for OSX system profiler L3 cache.
356 */
357 constexpr static char const* sp_l3_cache = "sp_l3_cache";
358 /**
359 * The fact for OSX system profiler local host name (computer name).
360 */
361 constexpr static char const* sp_local_host_name = "sp_local_host_name";
362 /**
363 * The fact for OSX system profiler machine model (model identifier).
364 */
365 constexpr static char const* sp_machine_model = "sp_machine_model";
366 /**
367 * The fact for OSX system profiler machine name (model name).
368 */
369 constexpr static char const* sp_machine_name = "sp_machine_name";
370 /**
371 * The fact for OSX system profiler number of processors (total number of cores).
372 */
373 constexpr static char const* sp_number_processors = "sp_number_processors";
374 /**
375 * The fact for OSX system profiler OS version (system version).
376 */
377 constexpr static char const* sp_os_version = "sp_os_version";
378 /**
379 * The fact for OSX system profiler number of CPU packages (number of physical processors).
380 */
381 constexpr static char const* sp_packages = "sp_packages";
382 /**
383 * The fact for OSX system profiler physical memory.
384 */
385 constexpr static char const* sp_physical_memory = "sp_physical_memory";
386 /**
387 * The fact for OSX system profiler platform UUID (hardware UUID).
388 */
389 constexpr static char const* sp_platform_uuid = "sp_platform_uuid";
390 /**
391 * The fact for OSX system profiler secure virtual memory.
392 */
393 constexpr static char const* sp_secure_vm = "sp_secure_vm";
394 /**
395 * The fact for OSX system profiler serial number (system).
396 */
397 constexpr static char const* sp_serial_number = "sp_serial_number";
398 /**
399 * The fact for OSX system profiler SMC version (system).
400 */
401 constexpr static char const* sp_smc_version_system = "sp_smc_version_system";
402 /**
403 * The fact for OSX system profiler uptime (since boot).
404 */
405 constexpr static char const* sp_uptime = "sp_uptime";
406 /**
407 * The fact for OSX system profiler user name.
408 */
409 constexpr static char const* sp_user_name = "sp_user_name";
410
411 /**
412 * The fact for OSX build version.
413 */
414 constexpr static char const* macosx_buildversion = "macosx_buildversion";
415 /**
416 * The fact for OSX product name.
417 */
418 constexpr static char const* macosx_productname = "macosx_productname";
419 /**
420 * The fact for OSX product version.
421 */
422 constexpr static char const* macosx_productversion = "macosx_productversion";
423 /**
424 * The fact for OSX build major version.
425 */
426 constexpr static char const* macosx_productversion_major = "macosx_productversion_major";
427 /**
428 * The fact for OSX build minor version.
429 */
430 constexpr static char const* macosx_productversion_minor = "macosx_productversion_minor";
431
432 /**
433 * The fact for Windows to specify if is Server or Desktop Edition variant
434 */
435 constexpr static char const* windows_edition_id = "windows_edition_id";
436
437 /**
438 * The fact for Windows to differentiate Server, Server Core, Client (Desktop)
439 */
440 constexpr static char const* windows_installation_type = "windows_installation_type";
441
442 /**
443 * The fact for Windows textual product name
444 */
445 constexpr static char const* windows_product_name = "windows_product_name";
446
447 /**
448 * The fact for Windows Build Version.
449 */
450 constexpr static char const* windows_release_id = "windows_release_id";
451
452 /**
453 * The fact for Windows native system32 directory.
454 */
455 constexpr static char const* windows_system32 = "system32";
456
457 /**
458 * The fact for virtualization hypervisor.
459 */
460 constexpr static char const* virtualization = "virtual";
461 /**
462 * The fact for whether or not the machine is virtual or physical.
463 */
464 constexpr static char const* is_virtual = "is_virtual";
465 /**
466 * The fact for all detected hypervisors
467 */
468 constexpr static char const* hypervisors = "hypervisors";
469 /**
470 * The fact for the cloud info, including provider, for a node.
471 */
472 constexpr static char const* cloud = "cloud";
473 /**
474 * The structured fact for identity information.
475 */
476 constexpr static char const* identity = "identity";
477 /**
478 * The fact for the running user ID
479 */
480 constexpr static char const* id = "id";
481 /**
482 * The fact for the running group ID
483 */
484 constexpr static char const* gid = "gid";
485
486 /**
487 * The fact for the system timezone
488 */
489 constexpr static char const* timezone = "timezone";
490
491 /**
492 * The fact for mountpoints.
493 */
494 constexpr static char const* mountpoints = "mountpoints";
495 /**
496 * The fact for filesystems.
497 */
498 constexpr static char const* filesystems = "filesystems";
499
500 /**
501 * The fact for disks.
502 */
503 constexpr static char const* disks = "disks";
504
505 /**
506 * The fact for partitions.
507 */
508 constexpr static char const* partitions = "partitions";
509
510 /**
511 * The fact for system memory.
512 */
513 constexpr static char const* memory = "memory";
514 /**
515 * The fact for free system memory.
516 */
517 constexpr static char const* memoryfree = "memoryfree";
518 /**
519 * The fact for free system memory in megabytes.
520 */
521 constexpr static char const* memoryfree_mb = "memoryfree_mb";
522 /**
523 * The fact for total system memory.
524 */
525 constexpr static char const* memorysize = "memorysize";
526 /**
527 * The fact for total system memory in megabytes.
528 */
529 constexpr static char const* memorysize_mb = "memorysize_mb";
530 /**
531 * The fact for free swap.
532 */
533 constexpr static char const* swapfree = "swapfree";
534 /**
535 * The fact for free swap in megabytes.
536 */
537 constexpr static char const* swapfree_mb = "swapfree_mb";
538 /**
539 * The fact for total swap.
540 */
541 constexpr static char const* swapsize = "swapsize";
542 /**
543 * The fact for total swap in megabytes.
544 */
545 constexpr static char const* swapsize_mb = "swapsize_mb";
546 /**
547 * The fact for the swap being encrypted or not.
548 */
549 constexpr static char const* swapencrypted = "swapencrypted";
550
551 /**
552 * The ZFS version fact.
553 */
554 constexpr static char const* zfs_version = "zfs_version";
555
556 /**
557 * The ZFS supported version numbers.
558 */
559 constexpr static char const* zfs_versionnumbers = "zfs_featurenumbers";
560
561 /**
562 * The ZFS storage pool (zpool) version fact.
563 */
564 constexpr static char const* zpool_version = "zpool_version";
565
566 /**
567 * The ZFS storage pool supported feature flags.
568 */
569 constexpr static char const* zpool_featureflags = "zpool_featureflags";
570
571 /**
572 * The ZFS storage pool supported version numbers.
573 */
574 constexpr static char const* zpool_versionnumbers = "zpool_featurenumbers";
575
576 /**
577 * The fact for number of Solaris zones.
578 */
579 constexpr static char const* zones = "zones";
580 /**
581 * The fact for the current Solaris zone name.
582 */
583 constexpr static char const* zonename = "zonename";
584 /**
585 * The fact for Solaris zone brand.
586 */
587 constexpr static char const* zone_brand = "brand";
588 /**
589 * The fact for Solaris zone iptype.
590 */
591 constexpr static char const* zone_iptype = "iptype";
592 /**
593 * The fact for Solaris zone uuid.
594 */
595 constexpr static char const* zone_uuid = "uuid";
596 /**
597 * The fact for Solaris zone id.
598 */
599 constexpr static char const* zone_id = "id";
600 /**
601 * The fact for Solaris zone path.
602 */
603 constexpr static char const* zone_path = "path";
604 /**
605 * The fact for Solaris zone status.
606 */
607 constexpr static char const* zone_status = "status";
608 /**
609 * The fact for Solaris zone name.
610 */
611 constexpr static char const* zone_name = "name";
612 /**
613 * The fact for the structured Solaris zone data.
614 */
615 constexpr static char const* solaris_zones = "solaris_zones";
616
617 /**
618 * The fact for EC2 metadata.
619 */
620 constexpr static char const* ec2_metadata = "ec2_metadata";
621 /**
622 * The fact for EC2 user data.
623 */
624 constexpr static char const* ec2_userdata = "ec2_userdata";
625
626 /**
627 * The fact for GCE instance metadata.
628 */
629 constexpr static char const* gce = "gce";
630
631 /**
632 * The fact for Ruby metadata.
633 */
634 constexpr static char const* ruby = "ruby";
635
636 /**
637 * The fact for ruby platform.
638 */
639 constexpr static char const* rubyplatform = "rubyplatform";
640
641 /**
642 * The fact for ruby sitedir.
643 */
644 constexpr static char const* rubysitedir = "rubysitedir";
645
646 /**
647 * The fact for ruby version.
648 */
649 constexpr static char const* rubyversion = "rubyversion";
650
651 /**
652 * The fact for the PATH environment variable.
653 */
654 constexpr static char const* path = "path";
655
656 /**
657 * The fact for cpu load average.
658 */
659 constexpr static char const* load_averages = "load_averages";
660
661 /**
662 * The fact for augeas metadata.
663 */
664 constexpr static char const* augeas = "augeas";
665
666 /**
667 * The fact for augeas version.
668 */
669 constexpr static char const* augeasversion = "augeasversion";
670
671 /**
672 * The fact for Xen metadata.
673 */
674 constexpr static char const* xen = "xen";
675
676 /**
677 * The fact for Xen domains.
678 */
679 constexpr static char const* xendomains = "xendomains";
680
681 /**
682 * The structured fact for Solaris LDom facts.
683 */
684 constexpr static char const* ldom = "ldom";
685
686 /**
687 * The fips mode fact for RHEL/Linux* facts.
688 */
689 constexpr static char const* fips_enabled = "fips_enabled";
690
691 /**
692 * The fact for Aix NIM type (master/client)
693 */
694 constexpr static char const* nim_type = "nim_type";
695 };
696
697 }} // namespace facter::facts
+0
-127
lib/inc/facter/facts/map_value.hpp less more
0 /**
1 * @file
2 * Declares the fact value for maps (associative array).
3 */
4 #pragma once
5
6 #include "value.hpp"
7 #include "../export.h"
8 #include <map>
9 #include <string>
10 #include <memory>
11 #include <functional>
12
13 namespace facter { namespace facts {
14
15 /**
16 * Represents a fact value that maps fact names to values.
17 * This type can be moved but cannot be copied.
18 */
19 struct LIBFACTER_EXPORT map_value : value
20 {
21 /**
22 * Constructs a map value.
23 * @param hidden True if the fact is hidden from output by default or false if not.
24 */
25 map_value(bool hidden = false) :
26 value(hidden)
27 {
28 }
29
30 /**
31 * Prevents the map_value from being copied.
32 */
33 map_value(map_value const&) = delete;
34
35 /**
36 * Prevents the map_value from being copied.
37 * @returns Returns this map_value.
38 */
39 map_value& operator=(map_value const&) = delete;
40
41 /**
42 * Moves the given map_value into this map_value.
43 * @param other The map_value to move into this map_value.
44 */
45 // Visual Studio 12 still doesn't allow default for move constructor.
46 map_value(map_value&& other);
47
48 /**
49 * Moves the given map_value into this map_value.
50 * @param other The map_value to move into this map_value.
51 * @return Returns this map_value.
52 */
53 // Visual Studio 12 still doesn't allow default for move assignment.
54 map_value& operator=(map_value&& other);
55
56 /**
57 * Adds a value to the map.
58 * @param name The name of map element.
59 * @param value The value of the map element.
60 */
61 void add(std::string name, std::unique_ptr<value> value);
62
63 /**
64 * Checks to see if the map is empty.
65 * @return Returns true if the map is empty or false if it is not.
66 */
67 bool empty() const;
68
69 /**
70 * Gets the size of the map.
71 * @return Returns the number of elements in the map.
72 */
73 size_t size() const;
74
75 /**
76 * Enumerates all facts in the map.
77 * @param func The callback function called for each element in the map.
78 */
79 void each(std::function<bool(std::string const&, value const*)> func) const;
80
81 /**
82 * Converts the value to a JSON value.
83 * @param allocator The allocator to use for creating the JSON value.
84 * @param value The returned JSON value.
85 */
86 virtual void to_json(json_allocator& allocator, json_value& value) const override;
87
88 /**
89 * Gets the value in the map of the given name.
90 * @tparam T The expected type of the value.
91 * @param name The name of the value in the map to get.
92 * @return Returns the value in the map or nullptr if the value is not in the map or expected type.
93 */
94 template <typename T = value> T const* get(std::string const& name) const
95 {
96 return dynamic_cast<T const*>(this->operator [](name));
97 }
98
99 /**
100 * Gets the value in the map of the given name.
101 * @param name The name of the value in the map to get.
102 * @return Returns the value in the map or nullptr if the value is not in the map.
103 */
104 value const* operator[](std::string const& name) const;
105
106 /**
107 * Writes the value to the given stream.
108 * @param os The stream to write to.
109 * @param quoted True if string values should be quoted or false if not.
110 * @param level The current indentation level.
111 * @returns Returns the stream being written to.
112 */
113 virtual std::ostream& write(std::ostream& os, bool quoted = true, unsigned int level = 1) const override;
114
115 /**
116 * Writes the value to the given YAML emitter.
117 * @param emitter The YAML emitter to write to.
118 * @returns Returns the given YAML emitter.
119 */
120 virtual YAML::Emitter& write(YAML::Emitter& emitter) const override;
121
122 private:
123 std::map<std::string, std::unique_ptr<value>> _elements;
124 };
125
126 }} // namespace facter::facts
+0
-210
lib/inc/facter/facts/os.hpp less more
0 /**
1 * @file
2 * Declares the operating system constants.
3 */
4 #pragma once
5
6 #include "../export.h"
7
8 namespace facter { namespace facts {
9
10 /**
11 * Stores the constant operating system names.
12 */
13 struct LIBFACTER_EXPORT os
14 {
15 /**
16 * The RedHat operating system.
17 */
18 constexpr static char const* redhat = "RedHat";
19 /**
20 * The Centos operating system.
21 */
22 constexpr static char const* centos = "CentOS";
23 /**
24 * The Fedora operating system.
25 */
26 constexpr static char const* fedora = "Fedora";
27 /**
28 * The Scientific Linux operating system.
29 */
30 constexpr static char const* scientific = "Scientific";
31 /**
32 * The Scientific Linux CERN operating system.
33 */
34 constexpr static char const* scientific_cern = "SLC";
35 /**
36 * The Ascendos Linux operating system.
37 */
38 constexpr static char const* ascendos = "Ascendos";
39 /**
40 * The Cloud Linux operating system.
41 */
42 constexpr static char const* cloud_linux = "CloudLinux";
43 /**
44 * The Virtuozzo Linux operating system.
45 */
46 constexpr static char const* virtuozzo_linux = "VirtuozzoLinux";
47 /**
48 * The Parallels Server Bare Metal operating system.
49 */
50 constexpr static char const* psbm = "PSBM";
51 /**
52 * The Oracle Linux operating system.
53 */
54 constexpr static char const* oracle_linux = "OracleLinux";
55 /**
56 * The Oracle VM Linux operating system.
57 */
58 constexpr static char const* oracle_vm_linux = "OVS";
59 /**
60 * The Oracle Enterprise Linux operating system.
61 */
62 constexpr static char const* oracle_enterprise_linux = "OEL";
63 /**
64 * The Amazon Linux operating system.
65 */
66 constexpr static char const* amazon = "Amazon";
67 /**
68 * The Xen Server Linux operating system.
69 */
70 constexpr static char const* xen_server = "XenServer";
71 /**
72 * The XCP-ng Linux operating system.
73 */
74 constexpr static char const* xcp_ng = "XCP-ng";
75 /**
76 * The Mint Linux operating system.
77 */
78 constexpr static char const* linux_mint = "LinuxMint";
79 /**
80 * The Ubuntu Linux operating system.
81 */
82 constexpr static char const* ubuntu = "Ubuntu";
83 /**
84 * The Debian Linux operating system.
85 */
86 constexpr static char const* debian = "Debian";
87 /**
88 * The Devuan Linux operating system.
89 */
90 constexpr static char const* devuan = "Devuan";
91 /**
92 * The SuSE Linux Enterprise Server operating system.
93 */
94 constexpr static char const* suse_enterprise_server = "SLES";
95 /**
96 * The SuSE Linux Enterprise Desktop operating system.
97 */
98 constexpr static char const* suse_enterprise_desktop = "SLED";
99 /**
100 * The Open SuSE operating system.
101 */
102 constexpr static char const* open_suse = "OpenSuSE";
103 /**
104 * The SuSE operating system.
105 */
106 constexpr static char const* suse = "SuSE";
107 /**
108 * The Solaris operating system.
109 */
110 constexpr static char const* solaris = "Solaris";
111 /**
112 * The SunOS operating system.
113 */
114 constexpr static char const* sunos = "SunOS";
115 /**
116 * The Nexenta operating system.
117 */
118 constexpr static char const* nexenta = "Nexenta";
119 /**
120 * The Omni operating system.
121 */
122 constexpr static char const* omni = "OmniOS";
123 /**
124 * The Open Indiana operating system.
125 */
126 constexpr static char const* open_indiana = "OpenIndiana";
127 /**
128 * The SmartOS operating system.
129 */
130 constexpr static char const* smart = "SmartOS";
131 /**
132 * The Gentoo Linux operating system.
133 */
134 constexpr static char const* gentoo = "Gentoo";
135 /**
136 * The Archlinux operating system.
137 */
138 constexpr static char const* archlinux = "Archlinux";
139 /**
140 * The Manjaro Linux operating system.
141 */
142 constexpr static char const* manjarolinux = "ManjaroLinux";
143 /**
144 * The Mandrake Linux operating system.
145 */
146 constexpr static char const* mandrake = "Mandrake";
147 /**
148 * The Mandriva Linux operating system.
149 */
150 constexpr static char const* mandriva = "Mandriva";
151 /**
152 * The Mageia Linux operating system.
153 */
154 constexpr static char const* mageia = "Mageia";
155 /**
156 * The Open WRT operating system.
157 */
158 constexpr static char const* openwrt = "OpenWrt";
159 /**
160 * The Meego operating system.
161 */
162 constexpr static char const* meego = "MeeGo";
163 /**
164 * The VMWare ESX operating system.
165 */
166 constexpr static char const* vmware_esx = "VMWareESX";
167 /**
168 * The Slackware Linux operating system.
169 */
170 constexpr static char const* slackware = "Slackware";
171 /**
172 * The Alpine Linux operating system.
173 */
174 constexpr static char const* alpine = "Alpine";
175 /**
176 * The CoreOS Linux operating system.
177 */
178 constexpr static char const* coreos = "CoreOS";
179 /**
180 * The Cumulus Linux operating system.
181 */
182 constexpr static char const* cumulus = "CumulusLinux";
183 /**
184 * The Zen Cloud Platform linux operating system.
185 */
186 constexpr static char const* zen_cloud_platform = "XCP";
187 /**
188 * The GNU/kFreeBSD operating system.
189 */
190 constexpr static char const* kfreebsd = "GNU/kFreeBSD";
191 /**
192 * The Windows operating system.
193 */
194 constexpr static char const* windows = "windows";
195 /**
196 * The AristaEOS operating system.
197 */
198 constexpr static char const* arista_eos = "AristaEOS";
199 /**
200 * The HuaweiOS operating system.
201 */
202 constexpr static char const* huawei = "HuaweiOS";
203 /**
204 * The PhotonOS operating system.
205 */
206 constexpr static char const* photon_os = "PhotonOS";
207 };
208
209 }}
+0
-58
lib/inc/facter/facts/os_family.hpp less more
0 /**
1 * @file
2 * Declares the operating system family constants.
3 */
4 #pragma once
5
6 #include "../export.h"
7
8 namespace facter { namespace facts {
9
10 /**
11 * Stores the constant operating system family names.
12 */
13 struct LIBFACTER_EXPORT os_family
14 {
15 /**
16 * The RedHat family of operating systems.
17 */
18 constexpr static char const* redhat = "RedHat";
19 /**
20 * The CoreOS family of operating systems.
21 */
22 constexpr static char const* coreos = "CoreOS";
23 /**
24 * The Debian family of operating systems.
25 */
26 constexpr static char const* debian = "Debian";
27 /**
28 * The SuSE family of operating systems.
29 */
30 constexpr static char const* suse = "Suse";
31 /**
32 * The Solaris family of operating systems.
33 */
34 constexpr static char const* solaris = "Solaris";
35 /**
36 * The SunOS family of operating systems.
37 */
38 constexpr static char const* sunos = "SunOS";
39 /**
40 * The Gentoo family of operating systems.
41 */
42 constexpr static char const* gentoo = "Gentoo";
43 /**
44 * The Archlinux family of operating systems.
45 */
46 constexpr static char const* archlinux = "Archlinux";
47 /**
48 * The Mandrake family of operating systems.
49 */
50 constexpr static char const* mandrake = "Mandrake";
51 /**
52 * The Windows family of operating systems.
53 */
54 constexpr static char const* windows = "windows";
55 };
56
57 }} // namespace facter::facts
+0
-126
lib/inc/facter/facts/resolver.hpp less more
0 /**
1 * @file
2 * Declares the base class for fact resolvers.
3 */
4 #pragma once
5
6 #include "../export.h"
7 #include <memory>
8 #include <stdexcept>
9 #include <string>
10 #include <boost/regex.hpp>
11 #include "facter/facts/base_resolver.hpp"
12
13 namespace facter { namespace facts {
14
15 /**
16 * Thrown when a resolver is constructed with an invalid fact name pattern.
17 */
18 struct LIBFACTER_EXPORT invalid_name_pattern_exception : std::runtime_error
19 {
20 /**
21 * Constructs a invalid_name_pattern_exception.
22 * @param message The exception message.
23 */
24 explicit invalid_name_pattern_exception(std::string const& message);
25 };
26
27 struct collection;
28
29 /**
30 * Base class for fact resolvers.
31 * A fact resolver is responsible for resolving one or more facts.
32 * This type can be moved but cannot be copied.
33 */
34 struct LIBFACTER_EXPORT resolver : base_resolver
35 {
36 /**
37 * Constructs a resolver.
38 * @param name The fact resolver name.
39 * @param names The fact names the resolver is responsible for.
40 * @param patterns Regular expression patterns for additional ("dynamic") facts the resolver is responsible for.
41 */
42 resolver(std::string name, std::vector<std::string> names, std::vector<std::string> const& patterns = {});
43
44 /**
45 * Destructs the resolver.
46 */
47 virtual ~resolver();
48
49 /**
50 * Prevents the resolver from being copied.
51 */
52 resolver(resolver const&) = delete;
53
54 /**
55 * Prevents the resolver from being copied.
56 * @returns Returns this resolver.
57 */
58 resolver& operator=(resolver const&) = delete;
59
60 /**
61 * Moves the given resolver into this resolver.
62 * @param other The resolver to move into this resolver.
63 */
64 // Visual Studio 12 still doesn't allow default for move constructor.
65 resolver(resolver&& other);
66
67 /**
68 * Moves the given resolver into this resolver.
69 * @param other The resolver to move into this resolver.
70 * @return Returns this resolver.
71 */
72 // Visual Studio 12 still doesn't allow default for move assignment.
73 resolver& operator=(resolver&& other);
74
75 /**
76 * Gets the name of the fact resolver.
77 * @return Returns the fact resolver's name.
78 */
79 std::string const& name() const;
80
81 /**
82 * Gets the fact names the resolver is responsible for resolving.
83 * @return Returns a vector of fact names.
84 */
85 std::vector<std::string> const& names() const;
86
87 /**
88 * Determines if the resolver has patterns.
89 * @return Returns true if the resolver has patterns or false if it does not.
90 */
91 bool has_patterns() const;
92
93 /**
94 * Determines if the given name matches a pattern for the resolver.
95 * @param name The fact name to check.
96 * @return Returns true if the name matches a pattern or returns false if it does not.
97 */
98 bool is_match(std::string const& name) const;
99
100 /**
101 * Gets list of languages accepted by the fact resolver for HTTP requests.
102 * @return Returns the fact resolver's accepted languages (comma-separated list of language tags formatted for the HTTP Accept-Language header.)
103 */
104 std::string const& http_langs();
105
106 /**
107 * Called to resolve all facts the resolver is responsible for.
108 * @param facts The fact collection that is resolving facts.
109 */
110 virtual void resolve(collection& facts) = 0;
111
112 /**
113 * Determines if this resolver can be blocked from collecting its facts.
114 * @return Returns true if this resolver can be blocked, false otherwise
115 */
116 virtual bool is_blockable() const;
117
118 private:
119 std::string _name;
120 std::vector<std::string> _names;
121 std::vector<boost::regex> _regexes;
122 std::string _http_langs;
123 };
124
125 }} // namespace facter::facts
+0
-152
lib/inc/facter/facts/scalar_value.hpp less more
0 /**
1 * @file
2 * Declares the fact value for scalar values like strings and integers.
3 */
4 #pragma once
5
6 #include "value.hpp"
7 #include "../export.h"
8 #include <cstdint>
9 #include <string>
10 #include <iostream>
11 #include <yaml-cpp/yaml.h>
12
13 namespace facter { namespace facts {
14
15 /**
16 * Represents a simple scalar value.
17 * This type can be moved but cannot be copied.
18 * @tparam T The underlying scalar type.
19 */
20 template <typename T>
21 #if __clang__ || (__GNUC__ >= 5) // Currently limited to Clang and GCC 5 builds until we can minimally target GCC 5
22 struct LIBFACTER_EXPORT scalar_value : value
23 #else
24 struct scalar_value : value
25 #endif
26 {
27 /**
28 * Constructs a scalar_value.
29 * @param value The scalar value.
30 * @param hidden True if the fact is hidden from output by default or false if not.
31 */
32 scalar_value(T value, bool hidden = false) :
33 facter::facts::value(hidden),
34 _value(std::move(value))
35 {
36 }
37
38 /**
39 * Moves the given scalar_value into this scalar_value.
40 * @param other The scalar_value to move into this scalar_value.
41 */
42 // Visual Studio 12 still doesn't allow default for move constructor.
43 scalar_value(scalar_value&& other)
44 {
45 *this = std::move(other);
46 }
47
48 /**
49 * Moves the given scalar_value into this scalar_value.
50 * @param other The scalar_value to move into this scalar_value.
51 * @return Returns this scalar_value.
52 */
53 // Visual Studio 12 still doesn't allow default for move assignment.
54 scalar_value& operator=(scalar_value&& other)
55 {
56 value::operator=(static_cast<struct value&&>(other));
57 if (this != &other) {
58 _value = std::move(other._value);
59 }
60 return *this;
61 }
62
63 /**
64 * Converts the value to a JSON value.
65 * @param allocator The allocator to use for creating the JSON value.
66 * @param value The returned JSON value.
67 */
68 virtual void to_json(json_allocator& allocator, json_value& value) const override;
69
70 /**
71 * Gets the underlying scalar value.
72 * @return Returns the underlying scalar value.
73 */
74 T const& value() const { return _value; }
75
76 /**
77 * Writes the value to the given stream.
78 * @param os The stream to write to.
79 * @param quoted True if string values should be quoted or false if not.
80 * @param level The current indentation level.
81 * @returns Returns the stream being written to.
82 */
83 virtual std::ostream& write(std::ostream& os, bool quoted = true, unsigned int level = 1) const override
84 {
85 os << _value;
86 return os;
87 }
88
89 /**
90 * Writes the value to the given YAML emitter.
91 * @param emitter The YAML emitter to write to.
92 * @returns Returns the given YAML emitter.
93 */
94 virtual YAML::Emitter& write(YAML::Emitter& emitter) const override
95 {
96 emitter << _value;
97 return emitter;
98 }
99
100 private:
101 scalar_value(scalar_value const&) = delete;
102 scalar_value& operator=(scalar_value const&) = delete;
103
104 T _value;
105 };
106
107 // Declare the specializations for JSON output
108 template <>
109 void scalar_value<std::string>::to_json(json_allocator& allocator, json_value& value) const;
110 template <>
111 void scalar_value<int64_t>::to_json(json_allocator& allocator, json_value& value) const;
112 template <>
113 void scalar_value<bool>::to_json(json_allocator& allocator, json_value& value) const;
114 template <>
115 void scalar_value<double>::to_json(json_allocator& allocator, json_value& value) const;
116
117 // Declare the specializations for YAML output
118 template <>
119 YAML::Emitter& scalar_value<std::string>::write(YAML::Emitter& emitter) const;
120
121 // Declare the specializations for stream output
122 template <>
123 std::ostream& scalar_value<bool>::write(std::ostream& os, bool quoted, unsigned int level) const;
124 template <>
125 std::ostream& scalar_value<std::string>::write(std::ostream& os, bool quoted, unsigned int level) const;
126
127 // Declare the common instantiations as external; defined in scalar_value.cc
128 extern template struct scalar_value<std::string>;
129 extern template struct scalar_value<int64_t>;
130 extern template struct scalar_value<bool>;
131 extern template struct scalar_value<double>;
132
133 // Typedef the common instantiation
134 /**
135 * Represents a string fact value.
136 */
137 typedef scalar_value<std::string> string_value;
138 /**
139 * Represents an integer fact value.
140 */
141 typedef scalar_value<int64_t> integer_value;
142 /**
143 * Represents a boolean fact value.
144 */
145 typedef scalar_value<bool> boolean_value;
146 /**
147 * Represents a double fact value.
148 */
149 typedef scalar_value<double> double_value;
150
151 }} // namespace facter::facts
+0
-164
lib/inc/facter/facts/value.hpp less more
0 /**
1 * @file
2 * Declares the base fact value type.
3 */
4 #pragma once
5
6 #include "../export.h"
7 #include <string>
8 #include <functional>
9 #include <memory>
10 #include <iostream>
11
12 // Forward declare needed yaml-cpp classes.
13 namespace YAML {
14 class Emitter;
15 }
16
17 // Forward delcare needed rapidjson classes.
18 namespace rapidjson {
19 class CrtAllocator;
20 template <typename Encoding, typename Allocator> class GenericValue;
21 template <typename Encoding, typename Allocator, typename StackAllocator> class GenericDocument;
22 template<typename CharType> struct UTF8;
23 }
24
25 extern "C" {
26 /**
27 * Simple structure to store enumeration callbacks.
28 */
29 typedef struct _enumeration_callbacks enumeration_callbacks;
30 }
31
32 namespace facter { namespace facts {
33
34 /**
35 * Typedef for RapidJSON allocator.
36 */
37 typedef typename rapidjson::CrtAllocator json_allocator;
38 /**
39 * Typedef for RapidJSON value.
40 */
41 typedef typename rapidjson::GenericValue<rapidjson::UTF8<char>, json_allocator> json_value;
42 /**
43 * Typedef for RapidJSON document.
44 */
45 typedef typename rapidjson::GenericDocument<rapidjson::UTF8<char>, json_allocator, json_allocator> json_document;
46
47 /**
48 * Base class for values.
49 * This type can be moved but cannot be copied.
50 */
51 struct LIBFACTER_EXPORT value
52 {
53 /**
54 * Constructs a value.
55 * @param hidden True if the fact is hidden from output by default or false if not.
56 */
57 value(bool hidden = false) :
58 _hidden(hidden),
59 _weight(0)
60 {
61 }
62
63 /**
64 * Destructs a value.
65 */
66 virtual ~value() = default;
67
68 /**
69 * Moves the given value into this value.
70 * @param other The value to move into this value.
71 */
72 // Visual Studio 12 still doesn't allow default for move constructor.
73 value(value&& other)
74 {
75 _hidden = other._hidden;
76 _weight = other._weight;
77 }
78
79 /**
80 * Moves the given value into this value.
81 * @param other The value to move into this value.
82 * @return Returns this value.
83 */
84 // Visual Studio 12 still doesn't allow default for move assignment.
85 value& operator=(value&& other)
86 {
87 _hidden = other._hidden;
88 _weight = other._weight;
89 return *this;
90 }
91
92 /**
93 * Determines if the value is hidden from output by default.
94 * @return Returns true if the value is hidden from output by default or false if it is not.
95 */
96 bool hidden() const
97 {
98 return _hidden;
99 }
100
101 /**
102 * Gets the weight of the fact.
103 * @return Returns the weight of the fact.
104 */
105 size_t weight() const
106 {
107 return _weight;
108 }
109
110 /**
111 * Sets the weight of the fact.
112 * @param weight The weight of the fact.
113 */
114 void weight(size_t weight)
115 {
116 _weight = weight;
117 }
118
119 /**
120 * Converts the value to a JSON value.
121 * @param allocator The allocator to use for creating the JSON value.
122 * @param value The returned JSON value.
123 */
124 virtual void to_json(json_allocator& allocator, json_value& value) const = 0;
125
126 /**
127 * Writes the value to the given stream.
128 * @param os The stream to write to.
129 * @param quoted True if string values should be quoted or false if not.
130 * @param level The current indentation level.
131 * @returns Returns the stream being written to.
132 */
133 virtual std::ostream& write(std::ostream& os, bool quoted = true, unsigned int level = 1) const = 0;
134
135 /**
136 * Writes the value to the given YAML emitter.
137 * @param emitter The YAML emitter to write to.
138 * @returns Returns the given YAML emitter.
139 */
140 virtual YAML::Emitter& write(YAML::Emitter& emitter) const = 0;
141
142 private:
143 value(value const&) = delete;
144 value& operator=(value const&) = delete;
145
146 bool _hidden;
147 size_t _weight;
148 };
149
150 /**
151 * Utility function for making a value.
152 * @tparam T The type of the value being constructed.
153 * @tparam Args The variadic types for the value's constructor.
154 * @param args The arguments to the value's constructor.
155 * @return Returns a unique pointer to the constructed value.
156 */
157 template<typename T, typename ...Args>
158 std::unique_ptr<T> make_value(Args&& ...args)
159 {
160 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
161 }
162
163 }} // namespace facter::facts
+0
-162
lib/inc/facter/facts/vm.hpp less more
0 /**
1 * @file
2 * Declares the virtual machine name constants.
3 */
4 #pragma once
5
6 #include "../export.h"
7
8 namespace facter { namespace facts {
9
10 /**
11 * Stores the constant virtual machine names.
12 */
13 struct LIBFACTER_EXPORT vm
14 {
15 /**
16 * The name for VMWare virtual machine.
17 */
18 constexpr static char const* vmware = "vmware";
19
20 /**
21 * The name for VirtualBox virtual machine.
22 */
23 constexpr static char const* virtualbox = "virtualbox";
24
25 /**
26 * The name for Parallels virtual machine.
27 */
28 constexpr static char const* parallels = "parallels";
29
30 /**
31 * The name for VMware Server virtual machine.
32 */
33 constexpr static char const* vmware_server = "vmware_server";
34
35 /**
36 * The name for VMware Workstation virtual machine.
37 */
38 constexpr static char const* vmware_workstation = "vmware_workstation";
39
40 /**
41 * The name for Docker virtual machine.
42 */
43 constexpr static char const* docker = "docker";
44
45 /**
46 * The name for LXC virtual machine.
47 */
48 constexpr static char const* lxc = "lxc";
49
50 /**
51 * The name for Google Compute Engine virtual machine.
52 */
53 constexpr static char const* gce = "gce";
54
55 /**
56 * The name for OpenStack-hosted virtual machine, when OpenStack defines the machine type.
57 */
58 constexpr static char const* openstack = "openstack";
59
60 /**
61 * The name for Xen privileged domain virtual machine.
62 */
63 constexpr static char const* xen_privileged = "xen0";
64
65 /**
66 * The name for Xen unprivileged domain virtual machine.
67 */
68 constexpr static char const* xen_unprivileged = "xenu";
69
70 /**
71 * The name for Xen Hardware virtual machine.
72 */
73 constexpr static char const* xen_hardware = "xenhvm";
74
75 /**
76 * The name for Xen virtual machine (on Windows)
77 */
78 constexpr static char const* xen = "xen";
79
80 /**
81 * The name for IBM System Z virtual machine.
82 */
83 constexpr static char const* zlinux = "zlinux";
84
85 /**
86 * The name for Linux-VServer host virtual machine.
87 */
88 constexpr static char const* vserver_host = "vserver_host";
89
90 /**
91 * The name for Linux-VServer virtual machine.
92 */
93 constexpr static char const* vserver = "vserver";
94
95 /**
96 * The name for OpenVZ Hardware Node virtual machine.
97 */
98 constexpr static char const* openvz_hn = "openvzhn";
99
100 /**
101 * The name for OpenVZ Virtual Environment virtual machine.
102 */
103 constexpr static char const* openvz_ve = "openvzve";
104
105 /**
106 * The name for KVM (QEMU) virtual machine.
107 */
108 constexpr static char const* kvm = "kvm";
109
110 /**
111 * The name for AHV (Nutanix) virtual machine.
112 */
113 constexpr static char const* ahv = "ahv";
114
115 /**
116 * The name for Bochs virtual machine.
117 */
118 constexpr static char const* bochs = "bochs";
119
120 /**
121 * The name for Microsoft Hyper-V virtual machine.
122 */
123 constexpr static char const* hyperv = "hyperv";
124
125 /**
126 * The name for Red Hat Enterprise Virtualization virtual machine.
127 */
128 constexpr static char const* redhat_ev = "rhev";
129
130 /**
131 * The name for oVirt virtual machine.
132 */
133 constexpr static char const* ovirt = "ovirt";
134
135 /**
136 * The name for Solaris zones
137 */
138 constexpr static char const* zone = "zone";
139
140 /**
141 * The name for Solaris ldom
142 */
143 constexpr static char const* ldom = "ldom";
144
145 /**
146 * The name of FreeBSD jails
147 */
148 constexpr static char const* jail = "jail";
149
150 /**
151 * The name for OpenBSD vmm
152 */
153 constexpr static char const* vmm = "vmm";
154
155 /**
156 * The name for FreeBSD bhyve
157 */
158 constexpr static char const* bhyve = "bhyve";
159 };
160
161 }} // namespace facter::facts
+0
-192
lib/inc/facter/logging/logging.hpp less more
0 /**
1 * @file
2 * Declares the Facter logging functions.
3 */
4 #pragma once
5
6 #include <stdexcept>
7 #include <ostream>
8 #include <string>
9 #include <boost/format.hpp>
10 #include "../export.h"
11
12 namespace facter { namespace logging {
13
14 /**
15 * Represents the supported logging levels.
16 */
17 enum class level
18 {
19 /**
20 * No logging level.
21 */
22 none,
23 /**
24 * Trace level.
25 */
26 trace,
27 /**
28 * Debug level.
29 */
30 debug,
31 /**
32 * Info level.
33 */
34 info,
35 /**
36 * Warning level.
37 */
38 warning,
39 /**
40 * Error level.
41 */
42 error,
43 /**
44 * Fatal error level.
45 */
46 fatal
47 };
48
49 /**
50 * Reads a logging level from an input stream.
51 * This is used in boost::lexical_cast<level>.
52 * @param in The input stream.
53 * @param lvl The returned logging level.
54 * @returns Returns the input stream.
55 */
56 LIBFACTER_EXPORT std::istream& operator>>(std::istream& in, level& lvl);
57
58 /**
59 * Produces the printed representation of a logging level.
60 * @param os The stream to write.
61 * @param lvl The logging level to write.
62 * @return Returns the stream after writing to it.
63 */
64 LIBFACTER_EXPORT std::ostream& operator<<(std::ostream& os, level lvl);
65
66 /**
67 * Sets up logging for the given stream.
68 * The logging level is set to warning by default.
69 * @param os The output stream to configure for logging.
70 */
71 LIBFACTER_EXPORT void setup_logging(std::ostream& os);
72
73 /**
74 * Sets the current logging level.
75 * @param lvl The new current logging level to set.
76 */
77 LIBFACTER_EXPORT void set_level(level lvl);
78
79 /**
80 * Gets the current logging level.
81 * @return Returns the current logging level.
82 */
83 LIBFACTER_EXPORT level get_level();
84
85 /**
86 * Sets whether or not log output is colorized.
87 * @param color Pass true if log output is colorized or false if it is not colorized.
88 */
89 LIBFACTER_EXPORT void set_colorization(bool color);
90
91 /**
92 * Gets whether or not the log output is colorized.
93 * @return Returns true if log output is colorized or false if it is not colorized.
94 */
95 LIBFACTER_EXPORT bool get_colorization();
96
97 /**
98 * Determines if the given logging level is enabled.
99 * @param lvl The logging level to check.
100 * @return Returns true if the logging level is enabled or false if it is not.
101 */
102 LIBFACTER_EXPORT bool is_enabled(level lvl);
103
104 /**
105 * Determine if an error has been logged.
106 * @return Returns true if an error or critical message has been logged.
107 */
108 LIBFACTER_EXPORT bool error_logged();
109
110 /**
111 * Clears logged errors.
112 */
113 LIBFACTER_EXPORT void clear_logged_errors();
114
115 /**
116 * Translate text using the locale initialized by this library.
117 * @param msg The string to translate.
118 * @return The translated string.
119 */
120 LIBFACTER_EXPORT std::string translate(std::string const& msg);
121
122 /**
123 * Format a text message.
124 * @tparam TArgs The format argument types.
125 * @param fmt The format string.
126 * @param args The format arguments.
127 * @return The formatted string.
128 */
129 template <typename... TArgs>
130 std::string format(std::string const& fmt, TArgs... args)
131 {
132 boost::format msg{translate(fmt)};
133 (void) std::initializer_list<int>{ ((void)(msg % args), 0)... };
134 return msg.str();
135 }
136
137 /**
138 * Format a text message.
139 * Alias for format(...); Convenience function for adding i18n support.
140 * @tparam TArgs The format argument types.
141 * @param fmt The format string.
142 * @param args The format arguments.
143 * @return The formatted string.
144 */
145 template<typename... TArgs>
146 inline std::string _(std::string const& fmt, TArgs&&... args)
147 {
148 return format(std::forward<decltype(fmt)>(fmt), std::forward<TArgs>(args)...);
149 }
150
151 /**
152 * Log a message.
153 * @param lvl The logging level to log with.
154 * @param msg The message to log.
155 */
156 LIBFACTER_EXPORT void log(level lvl, std::string const& msg);
157
158 /**
159 * Log a formatted message.
160 * @tparam TArgs The format argument types.
161 * @param lvl The logging level to log with.
162 * @param fmt The format string.
163 * @param args The format arguments.
164 */
165 template <typename... TArgs>
166 inline void log(level lvl, std::string const& fmt, TArgs... args)
167 {
168 log(std::forward<decltype(lvl)>(lvl), format(fmt, std::forward<TArgs>(args)...));
169 }
170
171 /**
172 * Starts colorizing for the given logging level.
173 * This is a no-op on platforms that don't natively support terminal colors.
174 * @param os The stream to colorize.
175 * @param lvl The logging level to colorize for. Defaults to none, which resets colorization.
176 */
177 LIBFACTER_EXPORT void colorize(std::ostream &os, level lvl = level::none);
178
179 /**
180 * Exception to indicate that locale setup was not possible.
181 */
182 class locale_error : public std::runtime_error {
183 public:
184 /**
185 * Constructs a locale_error exception.
186 * @param msg The exception message.
187 */
188 explicit locale_error(const std::string& msg) : std::runtime_error(msg) {}
189 };
190
191 }} // namespace facter::logging
+0
-73
lib/inc/facter/ruby/ruby.hpp less more
0 /**
1 * @file
2 * Declares the Facter Ruby functions.
3 */
4 #pragma once
5
6 #include "../facts/collection.hpp"
7 #include "../facts/value.hpp"
8 #include "../export.h"
9 #include <vector>
10 #include <string>
11
12 namespace facter { namespace ruby {
13
14 /**
15 * Initialize Ruby integration in Facter.
16 * Important: this function must be called in main().
17 * Calling this function from an arbitrary stack depth may result in segfaults during Ruby GC.
18 * @param include_stack_trace True if Ruby exception messages should include a stack trace or false if not.
19 * @return Returns true if Ruby was initialized or false if Ruby could not be initialized (likely not found).
20 */
21 LIBFACTER_EXPORT bool initialize(bool include_stack_trace = false);
22
23 /**
24 * Loads custom facts into the given collection.
25 * Important: this function should be called from main().
26 * Calling this function from an arbitrary stack depth may result in segfaults during Ruby GC.
27 * @param facts The collection to populate with custom facts.
28 * @param initialize_puppet Whether puppet should be loaded to find additional facts.
29 * @param redirect_stdout Whether Ruby's stdout should be redirected to stderr
30 * @param paths The paths to search for custom facts.
31 */
32 LIBFACTER_EXPORT void load_custom_facts(facter::facts::collection& facts, bool initialize_puppet, bool redirect_stdout, std::vector<std::string> const& paths = {});
33
34 /**
35 * Loads custom facts into the given collection.
36 * Important: this function should be called from main().
37 * Calling this function from an arbitrary stack depth may result in segfaults during Ruby GC.
38 * @param facts The collection to populate with custom facts.
39 * @param initialize_puppet Whether puppet should be loaded to find additional facts.
40 * @param paths The paths to search for custom facts.
41 */
42 LIBFACTER_EXPORT void load_custom_facts(facter::facts::collection& facts, bool initialize_puppet, std::vector<std::string> const& paths = {});
43
44 /**
45 * Loads custom facts into the given collection.
46 * Important: this function should be called from main().
47 * Calling this function from an arbitrary stack depth may result in segfaults during Ruby GC.
48 * This is provided for backwards compatibility.
49 * @param facts The collection to populate with custom facts.
50 * @param paths The paths to search for custom facts.
51 */
52 LIBFACTER_EXPORT void load_custom_facts(facter::facts::collection& facts, std::vector<std::string> const& paths = {});
53
54 /**
55 * Traverses a ruby fact and returns a new value based on the
56 * query segments passed in the range.
57 * @param value The original value to query
58 * @param segment The beginning of the query segment range
59 * @param end The end of the query segment range
60 * @return Returns a pointer to the value queried, or nullptr if it does not exist.
61 */
62 LIBFACTER_EXPORT facts::value const* lookup(facts::value const* value, std::vector<std::string>::iterator segment, std::vector<std::string>::iterator end);
63
64 /**
65 * Uninitialize Ruby integration in Facter.
66 * This is unneeded if libfacter was loaded from Ruby. If libfacter instead loads Ruby's dynamic library
67 * you should call uninitialize before exiting to avoid dynamic library unload ordering issues with
68 * destructors and atexit handlers.
69 */
70 LIBFACTER_EXPORT void uninitialize();
71
72 }} // namespace facter::ruby
+0
-97
lib/inc/facter/util/config.hpp less more
0 /**
1 * @file
2 * Declares methods for interacting with Facter's config file.
3 */
4 #pragma once
5
6 #include "../export.h"
7 #include <hocon/config.hpp>
8 #include <boost/program_options.hpp>
9
10 namespace facter { namespace util { namespace config {
11 /**
12 * Parses the contents of Facter's config file from its default location
13 * for the current operating system.
14 * @return HOCON config object, or nullptr if no file was found
15 */
16 LIBFACTER_EXPORT hocon::shared_config load_default_config_file();
17
18 /**
19 * Returns the default location of the config file.
20 * @return the absolute path to the default config file
21 */
22 LIBFACTER_EXPORT std::string default_config_location();
23
24 /**
25 * Parses the contents of the config pile at the specified path.
26 * @param config_path the path to the config file
27 * @return HOCON config object, or nullptr if no file was found
28 */
29 LIBFACTER_EXPORT hocon::shared_config load_config_from(std::string config_path);
30
31 /**
32 * Loads the "global" section of the config file into the settings map.
33 * @param hocon_config the config object representing the parsed config file
34 * @param vm the key-value map in which to store the settings
35 */
36 LIBFACTER_EXPORT void load_global_settings(hocon::shared_config hocon_config, boost::program_options::variables_map& vm);
37
38 /**
39 * Loads the "cli" section of the config file into the settings map.
40 * @param hocon_config the config object representing the parsed config file
41 * @param vm the key-value map in which to store the settings
42 */
43 LIBFACTER_EXPORT void load_cli_settings(hocon::shared_config hocon_config, boost::program_options::variables_map& vm);
44
45 /**
46 * Loads the "blocklist" section of the config file into the settings map.
47 * @param hocon_config the config object representing the parsed config file
48 * @param vm the key-value map in which to store the settings
49 */
50 LIBFACTER_EXPORT void load_fact_settings(hocon::shared_config hocon_config, boost::program_options::variables_map& vm);
51
52 /**
53 * Loads the "fact-groups" section of the config file into the settings map.
54 * @param hocon_config the config object representing the parsed config file
55 * @param vm the key-value map in which to store the settings
56 */
57 LIBFACTER_EXPORT void load_fact_groups_settings(hocon::shared_config hocon_config, boost::program_options::variables_map& vm);
58
59 /**
60 * Returns a schema of the valid global options that can appear in the config file.
61 * @return names, values, and descriptions of global Facter options
62 */
63 LIBFACTER_EXPORT boost::program_options::options_description global_config_options();
64
65 /**
66 * Returns a schema of the valid config file options affecting Facter's command line interface.
67 * @return names, values, and descriptions of command line options
68 */
69 LIBFACTER_EXPORT boost::program_options::options_description cli_config_options();
70
71 /**
72 * Returns a schema for options dealing with block fact collection.
73 * @return names, values, and descriptions of fact blocking config options
74 */
75 LIBFACTER_EXPORT boost::program_options::options_description fact_config_options();
76
77 /**
78 * Returns a schema for options dealing with block fact groups collection.
79 * @return names, values, and descriptions of fact groups config options
80 */
81 LIBFACTER_EXPORT boost::program_options::options_description fact_groups_config_options();
82
83 /**
84 * Returns a map of resolver names and durations (in milliseconds). The listed resolvers will
85 * have their output cached, then re-resolved no more frequently than the given interval.
86 * @param hocon_config the config object representing the parsed config file
87 * @return a map of resolvers to time-to-live durations (in milliseconds)
88 */
89 LIBFACTER_EXPORT std::unordered_map<std::string, int64_t> load_ttls(hocon::shared_config hocon_config);
90
91 /**
92 * Returns the directory of the fact cache.
93 * @return the absolute path to the fact cache
94 */
95 LIBFACTER_EXPORT std::string fact_cache_location();
96 }}} // namespace facter::util::config
+0
-72
lib/inc/facter/util/string.hpp less more
0 /**
1 * @file
2 * Declares the utility functions for parsing and manipulating strings.
3 */
4 #pragma once
5
6 #include "../export.h"
7 #include <boost/optional.hpp>
8 #include <string>
9 #include <vector>
10 #include <functional>
11 #include <initializer_list>
12 #include <cctype>
13 #include <cstdint>
14
15 namespace facter { namespace util {
16
17 /**
18 * Converts the given bytes to a hexadecimal string.
19 * @param bytes The pointer to the bytes to convert.
20 * @param length The number of bytes to convert.
21 * @param uppercase True if the hexadecimal string should be uppercase or false if it should be lowercase.
22 * @return Returns the hexadecimal string.
23 */
24 std::string to_hex(uint8_t const* bytes, size_t length, bool uppercase = false);
25
26 /**
27 * Reads each line from the given string.
28 * @param s The string to read.
29 * @param callback The callback function that is passed each line in the string.
30 */
31 void each_line(std::string const& s, std::function<bool(std::string&)> callback);
32
33 /**
34 * Converts a size, in bytes, to a corresponding string using SI-prefixed units.
35 * @param size The size in bytes.
36 * @return Returns the size in largest SI unit greater than 1 (e.g. 4.05 GiB, 5.20 MiB, etc).
37 */
38 std::string si_string(uint64_t size);
39
40 /**
41 * Converts an amount used to a percentage.
42 * @param used The amount used out of the total.
43 * @param total The total amount.
44 * @return Returns the percentage (e.g. "41.53%"), to two decimal places, as a string.
45 */
46 std::string percentage(uint64_t used, uint64_t total);
47
48 /**
49 * Converts the given frequency, in Hz, to a string.
50 * @param freq The frequency to convert, in Hz.
51 * @return Returns the frequency as a string (e.g. "1.24 GHz").
52 */
53 std::string frequency(int64_t freq);
54
55 /**
56 * Checks to see if the given string definitely needs to be quoted for YAML.
57 * This exists as a workaround to yaml-cpp's poor handling of maintaining numerical string types in the output.
58 * @param str The string to check.
59 * @return Returns true if the string needs to be quoted or false if it may not need to be.
60 */
61 bool needs_quotation(std::string const& str);
62
63 /**
64 * Converts the given string to an integer, if it is a valid integer.
65 * @param str The string to convert.
66 * @return Returns the converted string if the string is a valid integer,
67 * otherwise it returns "nothing"
68 */
69 boost::optional<int> maybe_stoi(std::string const& str);
70
71 }} // namespace facter::util
+0
-25
lib/inc/internal/facts/aix/disk_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX disk fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/disk_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving disk facts.
12 */
13 struct disk_resolver : resolvers::disk_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::aix
+0
-41
lib/inc/internal/facts/aix/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX file system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/filesystem_resolver.hpp"
7 #include <map>
8
9 namespace facter { namespace facts { namespace aix {
10
11 /**
12 * Responsible for resolving AIX file system facts.
13 */
14 struct filesystem_resolver : resolvers::filesystem_resolver
15 {
16 protected:
17 /**
18 * Collects the DMI data.
19 * @param facts The fact collection that is resolving facts.
20 * @return Returns the DMI data.
21 */
22 virtual data collect_data(collection& facts) override;
23
24 private:
25 void collect_filesystem_data(data& result);
26 void collect_mountpoint_data(data& result);
27 void collect_partition_data(data& result);
28
29 private:
30 // AIX tracks filesystems as numeric IDs. We need to load up
31 // the human-readable names from /etc before we can print them
32 // out nicely.
33 std::map<int, std::string> _filesystems;
34
35 // This stores which partitions are mounted where, so we don't have
36 // to scan the array of mountpoints when populating partitions.
37 std::map<std::string, std::string> _mounts;
38 };
39
40 }}} // namespace facter::facts::aix
+0
-25
lib/inc/internal/facts/aix/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX kernel fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/kernel_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving kernel facts.
12 */
13 struct kernel_resolver : resolvers::kernel_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::aix
+0
-24
lib/inc/internal/facts/aix/load_average_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX load average fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/load_average_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving the load average facts.
12 */
13 struct load_average_resolver : resolvers::load_average_resolver
14 {
15 protected:
16 /**
17 * Gets the load averages (for 1, 5 and 15 minutes period).
18 * @return The load averages.
19 */
20 virtual boost::optional<std::tuple<double, double, double>> get_load_averages() override;
21 };
22
23 }}} // namespace facter::facts::aix
+0
-25
lib/inc/internal/facts/aix/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Aix memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::aix
+0
-56
lib/inc/internal/facts/aix/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Aix networking fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/networking_resolver.hpp"
7 #include <unordered_map>
8
9 struct kinfo_ndd;
10
11 namespace facter { namespace facts { namespace aix {
12
13 /**
14 * Responsible for resolving networking facts.
15 */
16 struct networking_resolver : posix::networking_resolver
17 {
18 protected:
19 /**
20 * Collects the resolver data.
21 * @param facts The fact collection that is resolving facts.
22 * @return Returns the resolver data.
23 */
24 virtual data collect_data(collection& facts) override;
25
26 /**
27 * Determines if the given sock address is a link layer address.
28 * @param addr The socket address to check.
29 * @returns Returns true if the socket address is a link layer address or false if it is not.
30 */
31 virtual bool is_link_address(sockaddr const* addr) const override;
32
33 /**
34 * Gets the bytes of the link address.
35 * @param addr The socket address representing the link address.
36 * @return Returns a pointer to the address bytes or nullptr if not a link address.
37 */
38 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
39
40 /**
41 * Gets the length of the link address.
42 * @param addr The socket address representing the link address.
43 * @return Returns the length of the address or 0 if not a link address.
44 */
45 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
46
47 private:
48 using mtu_map = std::unordered_map<std::string, std::string>;
49
50 std::string get_primary_interface() const;
51 mtu_map get_mtus() const;
52 std::vector<interface> get_interfaces() const;
53 };
54
55 }}} // namespace facter::facts::aix
+0
-29
lib/inc/internal/facts/aix/nim_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX NIM Type fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving NIM Type fact.
12 */
13 struct nim_resolver : resolver
14 {
15 /*
16 * Constructs the nim_resolver.
17 */
18 nim_resolver();
19
20 /**
21 * Called to resolve all the facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25
26 virtual std::string read_niminfo();
27 };
28 }}} // namespace facter::facts::aix
+0
-24
lib/inc/internal/facts/aix/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the aix operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : posix::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23 }}} // namespace facter::facts::aix
+0
-25
lib/inc/internal/facts/aix/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : posix::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::aix
+0
-27
lib/inc/internal/facts/aix/serial_number_resolver.hpp less more
0 /**
1 * @file
2 * Declares the AIX serial number fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace aix {
9
10 /**
11 * Responsible for resolving serial number fact.
12 */
13 struct serial_number_resolver : resolver
14 {
15 /*
16 * Constructs the serial_number_resolver.
17 */
18 serial_number_resolver();
19
20 /**
21 * Called to resolve all the facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25 };
26 }}} // namespace facter::facts::aix
+0
-30
lib/inc/internal/facts/bsd/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the BSD file system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/filesystem_resolver.hpp"
7
8 struct statfs;
9
10 namespace facter { namespace facts { namespace bsd {
11
12 /**
13 * Responsible for resolving BSD file system facts.
14 */
15 struct filesystem_resolver : resolvers::filesystem_resolver
16 {
17 protected:
18 /**
19 * Collects the file system data.
20 * @param facts The fact collection that is resolving facts.
21 * @return Returns the file system data.
22 */
23 virtual data collect_data(collection& facts) override;
24
25 private:
26 static std::vector<std::string> to_options(struct statfs const& fs);
27 };
28
29 }}} // namespace facter::facts::bsd
+0
-62
lib/inc/internal/facts/bsd/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the BSD networking fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/networking_resolver.hpp"
7 #include <map>
8 #include <ifaddrs.h>
9
10 namespace facter { namespace facts { namespace bsd {
11
12 /**
13 * Responsible for resolving networking facts.
14 */
15 struct networking_resolver : posix::networking_resolver
16 {
17 protected:
18 /**
19 * Collects the resolver data.
20 * @param facts The fact collection that is resolving facts.
21 * @return Returns the resolver data.
22 */
23 virtual data collect_data(collection& facts) override;
24
25 /**
26 * Gets the MTU of the link layer data.
27 * @param interface The name of the link layer interface.
28 * @param data The data pointer from the link layer interface.
29 * @return Returns The MTU of the interface.
30 */
31 virtual boost::optional<uint64_t> get_link_mtu(std::string const& interface, void* data) const = 0;
32
33 /**
34 * Gets the primary interface.
35 * This is typically the interface of the default route.
36 * @return Returns the primary interface or empty string if one could not be determined.
37 */
38 virtual std::string get_primary_interface() const;
39
40 /**
41 * Finds known DHCP servers for all interfaces.
42 * @return Returns a map between interface name and DHCP server.
43 */
44 virtual std::map<std::string, std::string> find_dhcp_servers() const;
45
46 /**
47 * Finds the DHCP server for the given interface.
48 * @param interface The interface to find the DHCP server for.
49 * @returns Returns the DHCP server for the interface or empty string if one isn't found.
50 */
51 virtual std::string find_dhcp_server(std::string const& interface) const;
52
53 private:
54 void find_dhclient_dhcp_servers(std::map<std::string, std::string>& servers) const;
55 void find_networkd_dhcp_servers(std::map<std::string, std::string>& servers) const;
56 void find_nm_internal_dhcp_servers(std::map<std::string, std::string>& servers) const;
57 void populate_binding(interface& iface, ifaddrs const* addr) const;
58 void populate_mtu(interface& iface, ifaddrs const* addr) const;
59 };
60
61 }}} // namespace facter::facts::bsd
+0
-24
lib/inc/internal/facts/bsd/uptime_resolver.hpp less more
0 /**
1 * @file
2 * Declares the BSD uptime fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/uptime_resolver.hpp"
7
8 namespace facter { namespace facts { namespace bsd {
9
10 /**
11 * Responsible for resolving uptime facts.
12 */
13 struct uptime_resolver : posix::uptime_resolver
14 {
15 protected:
16 /**
17 * Gets the system uptime in seconds.
18 * @return Returns the system uptime in seconds.
19 */
20 virtual int64_t get_uptime() override;
21 };
22
23 }}} // namespace facter::facts::bsd
+0
-86
lib/inc/internal/facts/cache.hpp less more
0 #pragma once
1
2 #include <facter/facts/collection.hpp>
3
4 #include <boost/filesystem.hpp>
5
6 namespace facter { namespace facts { namespace cache {
7
8 /**
9 * Adds the cached value to the collection if it both exists and
10 * has not expired. Otherwise, resolves the value afresh and caches it.
11 * @param facts the collection of facts to which to add
12 * @param res the resolver that should be cached
13 * @param ttl the duration in seconds for which the cahced value is considered valid
14 */
15 void use_cache(collection& facts, std::shared_ptr<base_resolver> res, int64_t ttl);
16
17 /**
18 * Checks the given directory for cached facts of the given names.
19 * Each fact found is added to the collection.
20 * @param cache_file the JSON cache file associated with this resolver
21 * @param cached_facts the names of the facts to search for
22 * @param facts to collection of facts to which to add
23 */
24 void load_facts_from_cache(boost::filesystem::path const& cache_file, std::shared_ptr<base_resolver> res, collection& facts);
25
26 /**
27 * Resolve facts from the given resolver and write them out to the cache, one file per resolver.
28 * @param res the resolver that should be cached
29 * @param cache_file the path to the JSON cache file for this resolver
30 * @param facts the collection of facts to which to add
31 */
32 void refresh_cache(std::shared_ptr<base_resolver> res, boost::filesystem::path const& cache_file, collection& facts);
33
34 /**
35 * Returns the location of the fact cache directory.
36 * @return the absolute path to the cache directory
37 */
38 std::string fact_cache_location();
39
40 /**
41 * Returns true if the cache has not expired, false otherwise.
42 * @param cache_file the path to the cache file to be verified
43 * @param ttl a duration in seconds representing the time to live for the cache
44 * @return true if cache is expired, false otherwise
45 */
46 bool cache_is_valid(boost::filesystem::path const& cache_file, int64_t ttl);
47
48 /**
49 * Creates a cache file for the given fact in JSON format.
50 * @param file_path the path to the cache file
51 * @param fact_name the name of the fact to cache
52 */
53 void write_json_cache_file(const collection& facts, boost::filesystem::path const& file_path, std::vector<std::string> const& fact_names);
54
55 /**
56 * Returns the timespan in seconds since the file was last modified.
57 * @param file_path the path to the file
58 * @return the timespan in seconds since last modification
59 */
60 int64_t get_file_lifetime(boost::filesystem::path file_path);
61
62 /**
63 * Removes the cache file for any resolver that does not appear in the
64 * supplied list of fact groups.
65 * @param facts_to_cache the list of fact groups that should be cached
66 * @param cache_location the absolute path to the fact cache directory
67 */
68 void clean_cache(std::unordered_map<std::string, int64_t> const& facts_to_cache,
69 std::string cache_location = fact_cache_location());
70
71 /**
72 * Caches the custom facts from cached_facts
73 * @param collection the collection of facts to which to add
74 * @param ttls the duration in seconds for which the cached custom facts file is considered valid
75 */
76 bool load_cached_custom_facts(collection& collection, int64_t ttl);
77
78 /**
79 * Creates a cache file for the given custom facts list in JSON format.
80 * @param facts the collection of facts
81 * @param cached_custom_facts_list the name of the facts to cache
82 */
83 void write_cached_custom_facts(const collection& facts, const std::vector<std::string>& cached_custom_facts_list);
84
85 }}} // namespace facter::facts::cache
+0
-26
lib/inc/internal/facts/external/execution_resolver.hpp less more
0 /**
1 * @file
2 * Declares the execution external fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/external/resolver.hpp>
7
8 namespace facter { namespace facts { namespace external {
9
10 /**
11 * Responsible for resolving facts from executable files.
12 */
13 struct execution_resolver : resolver
14 {
15 execution_resolver(std::string const &path):resolver(path) {}
16
17 /**
18 * Resolves facts from the given file.
19 * @param path The path to the file to resolve facts from.
20 * @param facts The fact collection to populate the external facts into.
21 */
22 virtual void resolve(collection &facts);
23 };
24
25 }}} // namespace facter::facts::external
+0
-26
lib/inc/internal/facts/external/json_resolver.hpp less more
0 /**
1 * @file
2 * Declares the JSON external fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/external/resolver.hpp>
7
8 namespace facter { namespace facts { namespace external {
9
10 /**
11 * Responsible for resolving facts from JSON files.
12 */
13 struct json_resolver : resolver
14 {
15 json_resolver(std::string const &path):resolver(path) {}
16
17 /**
18 * Resolves facts from the given file.
19 * @param path The path to the file to resolve facts from.
20 * @param facts The fact collection to populate the external facts into.
21 */
22 virtual void resolve(collection& facts);
23 };
24
25 }}} // namespace facter::facts::external
+0
-26
lib/inc/internal/facts/external/text_resolver.hpp less more
0 /**
1 * @file
2 * Declares the text external fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/external/resolver.hpp>
7
8 namespace facter { namespace facts { namespace external {
9
10 /**
11 * Responsible for resolving facts from text files.
12 */
13 struct text_resolver : resolver
14 {
15 text_resolver(std::string const &path):resolver(path) {}
16
17 /**
18 * Resolves facts from the given file.
19 * @param path The path to the file to resolve facts from.
20 * @param facts The fact collection to populate the external facts into.
21 */
22 virtual void resolve(collection& facts);
23 };
24
25 }}} // namespace facter::facts::external
+0
-29
lib/inc/internal/facts/external/windows/powershell_resolver.hpp less more
0 /**
1 * @file
2 * Declares the powershell external fact resolver.
3 */
4 #ifndef FACTER_FACTS_EXTERNAL_POWERSHELL_RESOLVER_HPP_
5 #define FACTER_FACTS_EXTERNAL_POWERSHELL_RESOLVER_HPP_
6
7 #include <facter/facts/external/resolver.hpp>
8
9 namespace facter { namespace facts { namespace external {
10
11 /**
12 * Responsible for resolving facts from powershell scripts.
13 */
14 struct powershell_resolver : resolver
15 {
16 powershell_resolver(std::string const &path):resolver(path) {}
17
18 /**
19 * Resolves facts from the given file.
20 * @param path The path to the file to resolve facts from.
21 * @param facts The fact collection to populate the external facts into.
22 */
23 virtual void resolve(collection& facts);
24 };
25
26 }}} // namespace facter::facts::external
27
28 #endif // FACTER_FACTS_EXTERNAL_POWERSHELL_RESOLVER_HPP_
+0
-26
lib/inc/internal/facts/external/yaml_resolver.hpp less more
0 /**
1 * @file
2 * Declares the YAML external fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/external/resolver.hpp>
7
8 namespace facter { namespace facts { namespace external {
9
10 /**
11 * Responsible for resolving facts from YAML files.
12 */
13 struct yaml_resolver : resolver
14 {
15 yaml_resolver(std::string const &path):resolver(path) {}
16
17 /**
18 * Resolves facts from the given file.
19 * @param path The path to the file to resolve facts from.
20 * @param facts The fact collection to populate the external facts into.
21 */
22 virtual void resolve(collection& facts);
23 };
24
25 }}} // namespace facter::facts::external
+0
-25
lib/inc/internal/facts/freebsd/disk_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD disk fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/disk_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving disk facts.
12 */
13 struct disk_resolver : resolvers::disk_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::freebsd
+0
-28
lib/inc/internal/facts/freebsd/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD Desktop Management Interface (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving DMI facts.
12 */
13 struct dmi_resolver : resolvers::dmi_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22
23 private:
24 static std::string kenv_lookup(const char* file);
25 };
26
27 }}} // namespace facter::facts::freebsd
+0
-25
lib/inc/internal/facts/freebsd/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD file system fact resolver.
3 */
4 #pragma once
5
6 #include "../bsd/filesystem_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving FreeBSD file system facts.
12 */
13 struct filesystem_resolver : bsd::filesystem_resolver
14 {
15 protected:
16 /**
17 * Collects the file system data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the file system data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::freebsd
+0
-25
lib/inc/internal/facts/freebsd/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-49
lib/inc/internal/facts/freebsd/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD networking fact resolver.
3 */
4 #pragma once
5
6 #include "../bsd/networking_resolver.hpp"
7 #include <map>
8 #include <ifaddrs.h>
9
10 namespace facter { namespace facts { namespace freebsd {
11
12 /**
13 * Responsible for resolving networking facts.
14 */
15 struct networking_resolver : bsd::networking_resolver
16 {
17 protected:
18 /**
19 * Gets the MTU of the link layer data.
20 * @param interface The name of the link layer interface.
21 * @param data The data pointer from the link layer interface.
22 * @return Returns The MTU of the interface.
23 */
24 virtual boost::optional<uint64_t> get_link_mtu(std::string const& interface, void* data) const override;
25
26 /**
27 * Determines if the given sock address is a link layer address.
28 * @param addr The socket address to check.
29 * @returns Returns true if the socket address is a link layer address or false if it is not.
30 */
31 virtual bool is_link_address(sockaddr const* addr) const override;
32
33 /**
34 * Gets the bytes of the link address.
35 * @param addr The socket address representing the link address.
36 * @return Returns a pointer to the address bytes or nullptr if not a link address.
37 */
38 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
39
40 /**
41 * Gets the length of the link address.
42 * @param addr The socket address representing the link address.
43 * @return Returns the length of the address or 0 if not a link address.
44 */
45 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
46 };
47
48 }}} // namespace facter::facts::freebsd
+0
-24
lib/inc/internal/facts/freebsd/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : posix::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver's release data.
18 * @param facts The fact collection that is resolving facts.
19 * @param result The current resolver data.
20 */
21 virtual void collect_release_data(collection& facts, data& result) override;
22 };
23 }}} // namespace facter::facts::freebsd
+0
-25
lib/inc/internal/facts/freebsd/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the freebsd processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : posix::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::freebsd
+0
-27
lib/inc/internal/facts/freebsd/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving virtualization facts.
12 */
13 struct virtualization_resolver : resolvers::virtualization_resolver
14 {
15 protected:
16 /**
17 * Gets the name of the hypervisor.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the name of the hypervisor or empty string if no hypervisor.
20 */
21 virtual std::string get_hypervisor(collection& facts) override;
22 private:
23 static std::string get_jail_vm();
24 };
25
26 }}} // namespace facter::facts::freebsd
+0
-24
lib/inc/internal/facts/freebsd/zfs_resolver.hpp less more
0 /**
1 * @file
2 * Declares the ZFS fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/zfs_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving ZFS facts.
12 */
13 struct zfs_resolver : resolvers::zfs_resolver
14 {
15 protected:
16 /**
17 * Gets the platform's ZFS command.
18 * @return Returns the platform's ZFS command.
19 */
20 virtual std::string zfs_command();
21 };
22
23 }}} // namespace facter::facts::freebsd
+0
-24
lib/inc/internal/facts/freebsd/zpool_resolver.hpp less more
0 /**
1 * @file
2 * Declares the FreeBSD ZFS storage pool (zpool) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/zpool_resolver.hpp"
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 /**
11 * Responsible for resolving ZFS storage pool (zpool) facts.
12 */
13 struct zpool_resolver : resolvers::zpool_resolver
14 {
15 protected:
16 /**
17 * Gets the platform's zpool command.
18 * @return Returns the platform's zpool command.
19 */
20 virtual std::string zpool_command();
21 };
22
23 }}} // namespace facter::facts::freebsd
+0
-24
lib/inc/internal/facts/glib/load_average_resolver.hpp less more
0 /**
1 * @file
2 * Declares the glib load average fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/load_average_resolver.hpp"
7
8 namespace facter { namespace facts { namespace glib {
9
10 /**
11 * Responsible for resolving the load average facts.
12 */
13 struct load_average_resolver : resolvers::load_average_resolver
14 {
15 protected:
16 /**
17 * Gets the load averages (for 1, 5 and 15 minutes period).
18 * @return The load averages.
19 */
20 virtual boost::optional<std::tuple<double, double, double>> get_load_averages() override;
21 };
22
23 }}} // namespace facter::facts::glib
+0
-25
lib/inc/internal/facts/linux/disk_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux disk fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/disk_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving disk facts.
12 */
13 struct disk_resolver : resolvers::disk_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::linux
+0
-37
lib/inc/internal/facts/linux/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux Desktop Management Information (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7 #include <string>
8
9 namespace facter { namespace facts { namespace linux {
10
11 /**
12 * Responsible for resolving DMI facts.
13 */
14 struct dmi_resolver : resolvers::dmi_resolver
15 {
16 protected:
17 /**
18 * Collects the resolver data.
19 * @param facts The fact collection that is resolving facts.
20 * @return Returns the resolver data.
21 */
22 virtual data collect_data(collection& facts) override;
23
24 /**
25 * Parses the output of dmidecode.
26 * @param result The resulting data.
27 * @param line The line to parse.
28 * @param dmi_type The current DMI header type.
29 */
30 static void parse_dmidecode_output(data& result, std::string& line, int& dmi_type);
31
32 private:
33 std::string read(std::string const& path);
34 };
35
36 }}} // namespace facter::facts::linux
+0
-40
lib/inc/internal/facts/linux/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux file system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/filesystem_resolver.hpp"
7 #include <map>
8
9 namespace facter { namespace facts { namespace linux {
10
11 /**
12 * Responsible for resolving Linux file system facts.
13 */
14 struct filesystem_resolver : resolvers::filesystem_resolver
15 {
16 /**
17 * Converts a string using the same format as blkid's "safe_print" function.
18 * The format uses M-\<char> (higher than 128) and ^\<char> (control character), while escaping quotes and backslashes.
19 * @param value The value to convert.
20 * @return Returns the converted value.
21 */
22 static std::string safe_convert(char const* value);
23
24 protected:
25 /**
26 * Collects the DMI data.
27 * @param facts The fact collection that is resolving facts.
28 * @return Returns the DMI data.
29 */
30 virtual data collect_data(collection& facts) override;
31
32 private:
33 void collect_mountpoint_data(data& result);
34 void collect_filesystem_data(data& result);
35 void collect_partition_data(data& result);
36 void populate_partition_attributes(partition& part, std::string const& device_directory, void* cache, std::map<std::string, std::string> const& mountpoints);
37 };
38
39 }}} // namespace facter::facts::linux
+0
-31
lib/inc/internal/facts/linux/fips_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux fips fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/fips_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving fips-related facts.
12 */
13 struct fips_resolver : resolvers::fips_resolver
14 {
15 protected:
16 /**
17 * The check consists of the following.
18 * (1) Examining the contents of /proc/sys/crypto/fips_enabled. If it is 1
19 * then fips mode is enabled.
20 */
21
22 /**
23 * Collects the resolver data.
24 * @param facts The fact collection that is resolving facts.
25 * @return Returns the resolver data.
26 */
27 virtual data collect_data(collection& facts) override;
28 };
29
30 }}} // namespace facter::facts::linux
+0
-25
lib/inc/internal/facts/linux/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux kernel fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/kernel_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving kernel facts.
12 */
13 struct kernel_resolver : posix::kernel_resolver
14 {
15 protected:
16 /**
17 * Parses the major and minor kernel versions.
18 * @param version The version to parse.
19 * @return Returns a tuple of major and minor versions.
20 */
21 std::tuple<std::string, std::string> parse_version(std::string const& version) const override;
22 };
23
24 }}} // namespace facter::facts::linux
+0
-25
lib/inc/internal/facts/linux/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::linux
+0
-85
lib/inc/internal/facts/linux/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux networking fact resolver.
3 */
4 #pragma once
5
6 #include "../bsd/networking_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving networking facts.
12 *
13 * The Linux networking_resolver inherits from the BSD networking_resolver.
14 * This is because getifaddrs is a BSD concept that was implemented in Linux.
15 * The only thing this resolver is responsible for is handing link-level
16 * addressing and MTU.
17 */
18 struct networking_resolver : bsd::networking_resolver
19 {
20 protected:
21 /**
22 * Collects the resolver data.
23 * @param facts The fact collection that is resolving facts.
24 * @return Returns the resolver data.
25 */
26 virtual data collect_data(collection& facts) override;
27
28 /**
29 * Determines if the given sock address is a link layer address.
30 * @param addr The socket address to check.
31 * @returns Returns true if the socket address is a link layer address or false if it is not.
32 */
33 virtual bool is_link_address(sockaddr const* addr) const override;
34
35 /**
36 * Gets the bytes of the link address.
37 * @param addr The socket address representing the link address.
38 * @return Returns a pointer to the address bytes or nullptr if not a link address.
39 */
40 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
41
42 /**
43 * Gets the length of the link address.
44 * @param addr The socket address representing the link address.
45 * @return Returns the length of the address or 0 if not a link address.
46 */
47 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
48
49 /**
50 * Gets the MTU of the link layer data.
51 * @param interface The name of the link layer interface.
52 * @param data The data pointer from the link layer interface.
53 * @return Returns The MTU of the interface or -1 if there's no MTU.
54 */
55 virtual boost::optional<uint64_t> get_link_mtu(std::string const& interface, void* data) const override;
56
57 /**
58 * Gets the primary interface.
59 * This is typically the interface of the default route.
60 * @return Returns the primary interface or empty string if one could not be determined.
61 */
62 virtual std::string get_primary_interface() const override;
63
64 private:
65 struct route {
66 // In actuality routes are a destination associated with a
67 // bunch of key-value pairs, but we only require a couple
68 // of those values for our processing of network devices.
69 std::string destination;
70 std::string interface;
71 std::string source;
72 };
73
74 void read_routing_table();
75 void populate_from_routing_table(data&) const;
76 template <typename appender>
77 void associate_src_with_iface(const route&, data&, appender) const;
78 std::string get_bond_master(const std::string& name) const;
79
80 std::vector<route> routes4;
81 std::vector<route> routes6;
82 };
83
84 }}} // namespace facter::facts::linux
+0
-28
lib/inc/internal/facts/linux/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : posix::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22
23 private:
24 static selinux_data collect_selinux_data();
25 };
26
27 }}} // namespace facter::facts::linux
+0
-114
lib/inc/internal/facts/linux/os_cisco.hpp less more
0 /**
1 * @file
2 * Declares the Cisco Linux operating system query helper.
3 */
4 #pragma once
5
6 #include <internal/facts/linux/os_linux.hpp>
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for determining the name/family/release of Cisco operating systems.
12 */
13 struct os_cisco : os_linux
14 {
15 /**
16 * Constructs the os_cisco and reads a release file to gather relevant details.
17 * @param file The release file to read for Cisco-specific OS data.
18 */
19 os_cisco(std::string const& file) : os_linux({"ID", "ID_LIKE", "VERSION"}, file) {}
20
21 /**
22 * Finds ID from the release file contents and returns it as the name.
23 * @param distro_id Unused.
24 * @return Returns the release name.
25 */
26 virtual std::string get_name(std::string const& distro_id) const override
27 {
28 auto val = _release_info.find("ID");
29 return (val != _release_info.end()) ? val->second : std::string();
30 }
31
32 /**
33 * Finds ID_LIKE from the release file contents and returns it as the family.
34 * @param name Unused.
35 * @return Returns the release family.
36 */
37 virtual std::string get_family(std::string const& name) const override
38 {
39 /*
40 * This benefits from some explanation.
41 * Some Cisco platforms have multiple runtime environments.
42 * For these platforms, the name reports as the same regardless of
43 * the environment (e.g., "nexus"), but we want the family to report
44 * appropriately according to the environment (e.g., "cisco-wrlinux"
45 * versus "RedHat").
46 *
47 * In order to achieve this goal, we first check to see what would
48 * be reported if we were a standard Linux environment (e.g., a
49 * Linux distro that detects its name as "centos" would map to
50 * family "RedHat"). Only if a standard Linux family is not
51 * detected do we fall back on the information given in our Cisco
52 * release info file.
53 */
54 auto value = os_linux::get_family(os_linux::get_name(""));
55 if (!value.empty()) {
56 return value;
57 }
58 auto val = _release_info.find("ID_LIKE");
59 if (val != _release_info.end()) {
60 auto& family = val->second;
61 auto pos = family.find(" ");
62 // If multiple values are found in ID_LIKE, only return the
63 // first one (FACT-1246)
64 if (pos != std::string::npos) {
65 return family.substr(0, pos);
66 }
67 return family;
68 }
69 return std::string();
70 }
71
72 /**
73 * Finds VERSION from the release file contents and returns it as the release.
74 * @param name Unused.
75 * @param distro_release Unused.
76 * @return Returns the release version.
77 */
78 virtual std::string get_release(std::string const& name, std::string const& distro_release) const override
79 {
80 auto val = _release_info.find("VERSION");
81 return (val != _release_info.end()) ? val->second : std::string();
82 }
83
84 /**
85 * Parses the release version string to return the major version.
86 * @param name Unused.
87 * @param release The release version determined using get_release.
88 * @return Returns a tuple of the major and minor versions.
89 */
90 virtual std::tuple<std::string, std::string> parse_release(std::string const& name, std::string const& release) const override
91 {
92 /*
93 * Cisco software versions can be idiosyncratic.
94 * NX-OS looks something like '7.0(3)I2(0.455)'
95 * IOS XR looks something like '6.0.0.06I'
96 */
97 auto pos = release.find('.');
98 if (pos != std::string::npos) {
99 auto second = release.find('(', pos + 1);
100 if (second == std::string::npos) {
101 second = release.find('.', pos + 1);
102 }
103 if (second == std::string::npos) {
104 return std::make_tuple(release.substr(0, pos), std::string());
105 }
106 return std::make_tuple(release.substr(0, pos),
107 release.substr(pos + 1, second - (pos + 1)));
108 }
109 return std::make_tuple(release, std::string());
110 }
111 };
112
113 }}} // namespace facter::facts::linux
+0
-72
lib/inc/internal/facts/linux/os_linux.hpp less more
0 /**
1 * @file
2 * Declares the generic Linux operating system query helper.
3 */
4 #pragma once
5
6 #include <internal/facts/linux/release_file.hpp>
7 #include <set>
8 #include <map>
9 #include <tuple>
10 #include <string>
11
12 namespace facter { namespace facts { namespace linux {
13
14 /**
15 * Responsible for determining the name/family/release of common operating systems.
16 */
17 struct os_linux
18 {
19 /**
20 * Constructs the os_linux.
21 * @param items Items to read from the release file; used by inheriting classes.
22 * @param file The release file to read for OS data; used by inheriting classes.
23 */
24 os_linux(std::set<std::string> items = {}, std::string file = release_file::os);
25
26 /**
27 * Returns the name of the operating system.
28 * @param distro_id The distro ID; can be queried as the "Distributor ID" given by lsb_release.
29 * @return Returns the release name.
30 */
31 virtual std::string get_name(std::string const& distro_id) const;
32
33 /**
34 * Returns the family of the operating system.
35 * @param name The release name determined using get_name.
36 * @return Returns the release family.
37 */
38 virtual std::string get_family(std::string const& name) const;
39
40 /**
41 * Returns the release of the operating system.
42 * @param name The release name determined using get_name.
43 * @param distro_release The distro release; can be queried as the "Release" given by lsb_release.
44 * @return Returns the release version.
45 */
46 virtual std::string get_release(std::string const& name, std::string const& distro_release) const;
47
48 /**
49 * Parses the release version string to return the major version.
50 * @param name The release name determined using get_name.
51 * @param release The release version determined using get_release.
52 * @return Returns a tuple of the major and minor versions.
53 */
54 virtual std::tuple<std::string, std::string> parse_release(std::string const& name, std::string const& release) const;
55
56 /**
57 * Parses a file containing key-value pairs separated by an equal (=) sign, one pair per line.
58 * @param file The file to parse.
59 * @param items The keys to save from the file. Only keys given in this parameter will be returned.
60 * @return The key-value pairs identified in the items argument and found in the file.
61 */
62 static std::map<std::string, std::string> key_value_file(std::string file, std::set<std::string> const& items);
63
64 protected:
65 /**
66 * A map of key-value pairs read from the release file.
67 */
68 std::map<std::string, std::string> _release_info;
69 };
70
71 }}} // namespace facter::facts::linux
+0
-102
lib/inc/internal/facts/linux/os_osrelease.hpp less more
0 /**
1 * @file
2 * Declares the Linux operating system query helper based on /etc/os-release.
3 */
4 #pragma once
5
6 #include <internal/facts/linux/os_linux.hpp>
7 #include <facter/facts/os.hpp>
8 #include <facter/facts/os_family.hpp>
9 #include <boost/regex.hpp>
10 #include <iostream>
11 using namespace std;
12
13 namespace facter { namespace facts { namespace linux {
14
15 /**
16 * Responsible for determining the name/family/release of Freedesktop-compliant operating systems.
17 */
18 struct os_osrelease : os_linux
19 {
20 /**
21 * Constructs os_release based on details from /etc/os-release.
22 */
23 os_osrelease() : os_linux({"ID", "VERSION_ID"}) {}
24
25 /**
26 * Returns the release name based on the ID field from /etc/os-release
27 * (which has fewer variations to check for than the NAME field)
28 * @param distro_id Unused.
29 * @return Returns the OS name.
30 */
31 virtual std::string get_name(std::string const& distro_id) const override
32 {
33 auto val = _release_info.find("ID");
34 if (val != _release_info.end()) {
35 auto& id = val->second;
36
37 if (id == "coreos") {
38 return os::coreos;
39 } else if (id == "cumulus-linux") {
40 return os::cumulus;
41 } else if (id == "opensuse" || id == "opensuse-leap") {
42 return os::open_suse;
43 } else if (id == "sled") {
44 return os::suse_enterprise_desktop;
45 } else if (id == "sles") {
46 return os::suse_enterprise_server;
47 } else if (id == "ubuntu") {
48 return os::ubuntu;
49 }
50 }
51 return std::string();
52 }
53
54 /**
55 * Returns the release family based on the ID field from /etc/os-release
56 * (which has fewer variations to check for than the NAME field)
57 * @param name Unused.
58 * @return Returns the OS family.
59 */
60 virtual std::string get_family(std::string const& name) const override
61 {
62 auto val = _release_info.find("ID");
63 if (val != _release_info.end()) {
64 auto& id = val->second;
65
66 if (id == "coreos") {
67 return os_family::coreos;
68 } else if (id == "cumulus-linux") {
69 return os_family::debian;
70 } else if (id == "opensuse" || id == "opensuse-leap" || id == "sled" || id == "sles") {
71 return os_family::suse;
72 } else if (id == "ubuntu") {
73 return os_family::debian;
74 }
75 }
76 return std::string();
77 }
78
79 /**
80 * Returns the OS release version based on the VERSION_ID field from /etc/os-release.
81 * @param name Unused.
82 * @param distro_release Unused.
83 * @return Returns the release version.
84 */
85 virtual std::string get_release(std::string const& name, std::string const& distro_release) const override
86 {
87 auto val = _release_info.find("VERSION_ID");
88 if (val != _release_info.end()) {
89 if (boost::regex_match(val->second, boost::regex("^\\d+$"))) {
90 // FACT-1880: when VERSION_ID doesn't specify a point-release,
91 // return the major version with ".0" appended so that
92 // os.release.minor always returns a value.
93 return val->second + ".0";
94 }
95 return val->second;
96 }
97 return std::string();
98 }
99 };
100
101 }}} // namespace facter::facts::linux
+0
-60
lib/inc/internal/facts/linux/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7 #include <functional>
8
9 namespace facter { namespace facts { namespace linux {
10
11 /**
12 * Responsible for resolving processor-related facts.
13 */
14 struct processor_resolver : posix::processor_resolver
15 {
16 protected:
17 /**
18 * The architecture type of the linux machine.
19 */
20 enum class ArchitectureType {POWER, X86};
21
22 /**
23 * The check consists of the following.
24 * (1) Check the previously computed isa fact. If it starts with
25 * ppc64, then we have a power machine.
26 *
27 * (2) If (1) is empty (possible because exec might have failed to obtain
28 * the isa fact), then we use /proc/cpuinfo by checking whether that file
29 * contains the "cpu", "clock", and "revision" keys -- these keys are only
30 * found in Power machines.
31 *
32 * @param data The currently collected data
33 * @param root Path to the root directory of the system
34 * @return Returns the architecture type of the machine
35 */
36 ArchitectureType architecture_type(data const& data, std::string const& root);
37
38 /**
39 * Adds the cpu-specific data to the currently collected data.
40 * @param data The currently collected data
41 * @param root Path to the root directory of the system
42 */
43 void add_cpu_data(data& data, std::string const& root = "");
44
45 /**
46 * Collects the resolver data.
47 * @param facts The fact collection that is resolving facts.
48 * @return Returns the resolver data.
49 */
50 virtual data collect_data(collection& facts) override;
51
52 private:
53 void maybe_add_speed(data& data, std::string const& speed);
54 bool compute_cpu_counts(data& data, std::string const& root, std::function<bool(std::string const&)> is_valid_id);
55 bool add_x86_cpu_data(data& data, std::string const& root = "");
56 bool add_power_cpu_data(data& data, std::string const& root = "");
57 };
58
59 }}} // namespace facter::facts::linux
+0
-124
lib/inc/internal/facts/linux/release_file.hpp less more
0 /**
1 * @file
2 * Declares the Linux release file constants.
3 */
4 #pragma once
5
6 namespace facter { namespace facts { namespace linux {
7
8 /**
9 * Stores the constant release file names.
10 */
11 struct release_file
12 {
13 /**
14 * Release file for RedHat Linux.
15 */
16 constexpr static char const* redhat = "/etc/redhat-release";
17 /**
18 * Release file for Fedora.
19 */
20 constexpr static char const* fedora = "/etc/fedora-release";
21 /**
22 * Release file for Meego.
23 */
24 constexpr static char const* meego = "/etc/meego-release";
25 /**
26 * Release file for Oracle Linux.
27 */
28 constexpr static char const* oracle_linux = "/etc/oracle-release";
29 /**
30 * Release file for Oracle Enterprise Linux.
31 */
32 constexpr static char const* oracle_enterprise_linux = "/etc/enterprise-release";
33 /**
34 * Release file for Oracle VM Linux.
35 */
36 constexpr static char const* oracle_vm_linux = "/etc/ovs-release";
37 /**
38 * Version file for Debian Linux.
39 */
40 constexpr static char const* debian = "/etc/debian_version";
41 /**
42 * Version file for Devuan Linux.
43 */
44 constexpr static char const* devuan = "/etc/devuan_version";
45 /**
46 * Release file for Alpine Linux.
47 */
48 constexpr static char const* alpine = "/etc/alpine-release";
49 /**
50 * Release file for SuSE Linux.
51 */
52 constexpr static char const* suse = "/etc/SuSE-release";
53 /**
54 * Release file for generic Linux distros.
55 * Also used for:
56 * - Cisco
57 * - CoreOS
58 * - Cumulus
59 */
60 constexpr static char const* os = "/etc/os-release";
61 /**
62 * Release file for LSB distros.
63 */
64 constexpr static char const* lsb = "/etc/lsb-release";
65 /**
66 * Release file for Gentoo Linux.
67 */
68 constexpr static char const* gentoo = "/etc/gentoo-release";
69 /**
70 * Release file for Open WRT.
71 */
72 constexpr static char const* openwrt = "/etc/openwrt_release";
73 /**
74 * Version file for Open WRT.
75 */
76 constexpr static char const* openwrt_version = "/etc/openwrt_version";
77 /**
78 * Release file for Mandriva Linux.
79 */
80 constexpr static char const* mandriva = "/etc/mandriva-release";
81 /**
82 * Release file for Mandrake Linux.
83 */
84 constexpr static char const* mandrake = "/etc/mandrake-release";
85 /**
86 * Release file for Archlinux.
87 */
88 constexpr static char const* archlinux = "/etc/arch-release";
89 /**
90 * Release file for Manjaro Linux.
91 */
92 constexpr static char const* manjarolinux = "/etc/manjaro-release";
93 /**
94 * Release file for VMWare ESX Linux.
95 */
96 constexpr static char const* vmware_esx = "/etc/vmware-release";
97 /**
98 * Version file for Slackware Linux.
99 */
100 constexpr static char const* slackware = "/etc/slackware-version";
101 /**
102 * Release file for Mageia Linux.
103 */
104 constexpr static char const* mageia = "/etc/mageia-release";
105 /**
106 * Release file for Amazon Linux.
107 */
108 constexpr static char const* amazon = "/etc/system-release";
109 /**
110 * Release file for Mint Linux.
111 */
112 constexpr static char const* linux_mint_info = "/etc/linuxmint/info";
113 /**
114 * Release file for AristaEOS.
115 */
116 constexpr static char const* arista_eos = "/etc/Eos-release";
117 /**
118 * Release file for HuaweiOS.
119 */
120 constexpr static char const* huawei = "/etc/huawei-release";
121 };
122
123 }}} // namespace facter::facts::linux
+0
-24
lib/inc/internal/facts/linux/uptime_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux uptime fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/uptime_resolver.hpp"
7
8 namespace facter { namespace facts { namespace linux {
9
10 /**
11 * Responsible for resolving uptime facts.
12 */
13 struct uptime_resolver : posix::uptime_resolver
14 {
15 protected:
16 /**
17 * Gets the system uptime in seconds.
18 * @return Returns the system uptime in seconds.
19 */
20 virtual int64_t get_uptime() override;
21 };
22
23 }}} // namespace facter::facts::linux
+0
-55
lib/inc/internal/facts/linux/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Linux virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7 #include <string>
8
9 namespace facter { namespace facts { namespace linux {
10
11 /**
12 * Responsible for resolving virtualization facts.
13 */
14 struct virtualization_resolver : resolvers::virtualization_resolver
15 {
16 protected:
17 /**
18 * Gets the name of the hypervisor.
19 * @param facts The fact collection that is resolving facts.
20 * @return Returns the name of the hypervisor or empty string if no hypervisor.
21 */
22 std::string get_hypervisor(collection& facts) override;
23 /**
24 * Gets the name of the cloud provider.
25 * @param facts The fact collection that is resolving facts.
26 * @return Returns the name of the cloud provider or empty string if no hypervisor.
27 */
28 std::string get_cloud_provider(collection& facts) override;
29
30 /**
31 * Gets whether the machine is running in Azure.
32 * @return Returns "azure" if running on azure, otherwise an empty string.
33 */
34 static std::string get_azure();
35
36 /**
37 * Checks whether the azure dhcp option (245) is present in leases_file file.
38 * @param leases_file The file containing leases information.
39 * @return Returns "azure" if found, otherwise an empty string.
40 */
41 static std::string get_azure_from_leases_file(std::string leases_file);
42
43 private:
44 static std::string get_cgroup_vm();
45 static std::string get_gce_vm(collection& facts);
46 static std::string get_what_vm();
47 static std::string get_vserver_vm();
48 static std::string get_vmware_vm();
49 static std::string get_openvz_vm();
50 static std::string get_xen_vm();
51 static std::string get_lspci_vm();
52 };
53
54 }}} // namespace facter::facts::linux
+0
-28
lib/inc/internal/facts/openbsd/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OpenBSD Desktop Management Interface (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7
8 namespace facter { namespace facts { namespace openbsd {
9
10 /**
11 * Responsible for resolving DMI facts.
12 */
13 struct dmi_resolver : resolvers::dmi_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22
23 private:
24 std::string sysctl_lookup(int mib);
25 };
26
27 }}} // namespace facter::facts::openbsd
+0
-25
lib/inc/internal/facts/openbsd/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OpenBSD memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace openbsd {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-49
lib/inc/internal/facts/openbsd/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OpenBSD networking fact resolver.
3 */
4 #pragma once
5
6 #include "../bsd/networking_resolver.hpp"
7 #include <map>
8 #include <ifaddrs.h>
9
10 namespace facter { namespace facts { namespace openbsd {
11
12 /**
13 * Responsible for resolving networking facts.
14 */
15 struct networking_resolver : bsd::networking_resolver
16 {
17 protected:
18 /**
19 * Gets the MTU of the link layer data.
20 * @param interface The name of the link layer interface.
21 * @param data The data pointer from the link layer interface.
22 * @return Returns The MTU of the interface.
23 */
24 virtual boost::optional<uint64_t> get_link_mtu(std::string const& interface, void* data) const override;
25
26 /**
27 * Determines if the given sock address is a link layer address.
28 * @param addr The socket address to check.
29 * @returns Returns true if the socket address is a link layer address or false if it is not.
30 */
31 virtual bool is_link_address(sockaddr const* addr) const override;
32
33 /**
34 * Gets the bytes of the link address.
35 * @param addr The socket address representing the link address.
36 * @return Returns a pointer to the address bytes or nullptr if not a link address.
37 */
38 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
39
40 /**
41 * Gets the length of the link address.
42 * @param addr The socket address representing the link address.
43 * @return Returns the length of the address or 0 if not a link address.
44 */
45 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
46 };
47
48 }}} // namespace facter::facts::openbsd
+0
-25
lib/inc/internal/facts/openbsd/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OpenBSD processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace openbsd {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : posix::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::openbsd
+0
-25
lib/inc/internal/facts/openbsd/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OpenBSD virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7
8 namespace facter { namespace facts { namespace openbsd {
9
10 /**
11 * Responsible for resolving virtualization facts.
12 */
13 struct virtualization_resolver : resolvers::virtualization_resolver
14 {
15 protected:
16 /**
17 * Gets the name of the hypervisor.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the name of the hypervisor or empty string if no hypervisor.
20 */
21 virtual std::string get_hypervisor(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::openbsd
+0
-25
lib/inc/internal/facts/osx/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX Desktop Management Interface (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving DMI facts.
12 */
13 struct dmi_resolver : resolvers::dmi_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/osx/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-67
lib/inc/internal/facts/osx/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX networking fact resolver.
3 */
4 #pragma once
5
6 #include "../bsd/networking_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving networking facts.
12 */
13 struct networking_resolver : bsd::networking_resolver
14 {
15 protected:
16 /**
17 * Determines if the given sock address is a link layer address.
18 * @param addr The socket address to check.
19 * @returns Returns true if the socket address is a link layer address or false if it is not.
20 */
21 virtual bool is_link_address(sockaddr const* addr) const override;
22
23 /**
24 * Gets the bytes of the link address.
25 * @param addr The socket address representing the link address.
26 * @return Returns a pointer to the address bytes or nullptr if not a link address.
27 */
28 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
29
30 /**
31 * Gets the length of the link address.
32 * @param addr The socket address representing the link address.
33 * @return Returns the length of the address or 0 if not a link address.
34 */
35 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
36
37 /**
38 * Gets the MTU of the link layer data.
39 * @param interface The name of the link layer interface.
40 * @param data The data pointer from the link layer interface.
41 * @return Returns The MTU of the interface or -1 if there's no MTU.
42 */
43 virtual boost::optional<uint64_t> get_link_mtu(std::string const& interface, void* data) const override;
44
45 /**
46 * Gets the primary interface.
47 * This is typically the interface of the default route.
48 * @return Returns the primary interface or empty string if one could not be determined.
49 */
50 virtual std::string get_primary_interface() const override;
51
52 /**
53 * Finds known DHCP servers for all interfaces.
54 * @return Returns a map between interface name and DHCP server.
55 */
56 virtual std::map<std::string, std::string> find_dhcp_servers() const override;
57
58 /**
59 * Finds the DHCP server for the given interface.
60 * @param interface The interface to find the DHCP server for.
61 * @returns Returns the DHCP server for the interface or empty string if one isn't found.
62 */
63 virtual std::string find_dhcp_server(std::string const& interface) const override;
64 };
65
66 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/osx/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : posix::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/osx/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : posix::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/osx/system_profiler_resolver.hpp less more
0 /**
1 * @file
2 * Declares the system profiler fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/system_profiler_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving system profiler facts.
12 */
13 struct system_profiler_resolver : resolvers::system_profiler_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/osx/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the OSX virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7
8 namespace facter { namespace facts { namespace osx {
9
10 /**
11 * Responsible for resolving virtualization facts.
12 */
13 struct virtualization_resolver : resolvers::virtualization_resolver
14 {
15 protected:
16 /**
17 * Gets the name of the hypervisor.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the name of the hypervisor or empty string if no hypervisor.
20 */
21 virtual std::string get_hypervisor(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::osx
+0
-25
lib/inc/internal/facts/posix/identity_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX user and group resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/identity_resolver.hpp"
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving the user and group facts.
12 */
13 struct identity_resolver : resolvers::identity_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::posix
+0
-25
lib/inc/internal/facts/posix/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX kernel fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/kernel_resolver.hpp"
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving kernel facts.
12 */
13 struct kernel_resolver : resolvers::kernel_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::posix
+0
-55
lib/inc/internal/facts/posix/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX networking fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/networking_resolver.hpp"
7 #include <sys/socket.h>
8
9 namespace facter { namespace facts { namespace posix {
10
11 /**
12 * Responsible for resolving networking facts.
13 */
14 struct networking_resolver : resolvers::networking_resolver
15 {
16 /**
17 * Utility function to convert a socket address to a IPv4 or IPv6 string representation.
18 * @param addr The socket address to convert to a string.
19 * @param mask The mask to apply to the address.
20 * @return Returns the IPv4 or IPv6 representation or an empty string if the family isn't supported.
21 */
22 std::string address_to_string(sockaddr const* addr, sockaddr const* mask = nullptr) const;
23
24 protected:
25 /**
26 * Determines if the given sock address is a link layer address.
27 * @param addr The socket address to check.
28 * @returns Returns true if the socket address is a link layer address or false if it is not.
29 */
30 virtual bool is_link_address(sockaddr const* addr) const = 0;
31
32 /**
33 * Gets the bytes of the link address.
34 * @param addr The socket address representing the link address.
35 * @return Returns a pointer to the address bytes or nullptr if not a link address.
36 */
37 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const = 0;
38
39 /**
40 * Gets the length of the link address.
41 * @param addr The socket address representing the link address.
42 * @return Returns the length of the address or 0 if not a link address.
43 */
44 virtual uint8_t get_link_address_length(sockaddr const* addr) const = 0;
45
46 /**
47 * Collects the resolver data.
48 * @param facts The fact collection that is resolving facts.
49 * @return Returns the resolver data.
50 */
51 virtual data collect_data(collection& facts) override;
52 };
53
54 }}} // namespace facter::facts::posix
+0
-25
lib/inc/internal/facts/posix/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : resolvers::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::posix
+0
-25
lib/inc/internal/facts/posix/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX processor fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : resolvers::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::posix
+0
-24
lib/inc/internal/facts/posix/timezone_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX timezone fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/timezone_resolver.hpp"
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving time zone facts.
12 */
13 struct timezone_resolver : resolvers::timezone_resolver
14 {
15 protected:
16 /**
17 * Gets the system timezone.
18 * @return Returns the system timezone.
19 */
20 virtual std::string get_timezone() override;
21 };
22
23 }}} // namespace facter::facts::posix
+0
-32
lib/inc/internal/facts/posix/uptime_resolver.hpp less more
0 /**
1 * @file
2 * Declares the POSIX uptime fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/uptime_resolver.hpp"
7 #include <string>
8
9 namespace facter { namespace facts { namespace posix {
10
11 /**
12 * Responsible for resolving uptime facts.
13 */
14 struct uptime_resolver : resolvers::uptime_resolver
15 {
16 /**
17 * Utility function to parse the output of the uptime executable.
18 * @param output The output of the uptime executable.
19 * @return Returns the number of uptime seconds.
20 */
21 static int64_t parse_uptime(std::string const& output);
22
23 protected:
24 /**
25 * Gets the system uptime in seconds.
26 * @return Returns the system uptime in seconds.
27 */
28 virtual int64_t get_uptime() override;
29 };
30
31 }}} // namespace facter::facts::posix
+0
-24
lib/inc/internal/facts/posix/xen_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Xen fact resolver on POSIX systems.
3 */
4 #pragma once
5
6 #include <internal/facts/resolvers/xen_resolver.hpp>
7
8 namespace facter { namespace facts { namespace posix {
9
10 /**
11 * Responsible for resolving Xen facts.
12 */
13 struct xen_resolver : resolvers::xen_resolver
14 {
15 protected:
16 /**
17 * Gets the Xen management command.
18 * @return Returns the Xen management command.
19 */
20 virtual std::string xen_command();
21 };
22
23 }}} // namespace facter::facts::posix
+0
-35
lib/inc/internal/facts/resolvers/augeas_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base augeas fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving augeas fact, when an augeas runtime is available.
12 */
13 struct augeas_resolver : resolver
14 {
15 /**
16 * Constructs the augeas_resolver.
17 */
18 augeas_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25
26 protected:
27 /**
28 * Gets the augeas version.
29 * @return The augeas version string.
30 */
31 virtual std::string get_version();
32 };
33
34 }}} // namespace facter::facts::resolvers
+0
-86
lib/inc/internal/facts/resolvers/disk_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base disk resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <cstdint>
9 #include <vector>
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 /**
14 * Responsible for resolving disk facts.
15 */
16 struct disk_resolver : resolver
17 {
18 /**
19 * Constructs the disk_resolver.
20 */
21 disk_resolver();
22
23 /**
24 * Called to resolve all facts the resolver is responsible for.
25 * @param facts The fact collection that is resolving facts.
26 */
27 virtual void resolve(collection& facts) override;
28
29 protected:
30 /**
31 * Represents a disk.
32 */
33 struct disk
34 {
35 /**
36 * Stores the name of the disk.
37 */
38 std::string name;
39
40 /**
41 * Stores the vendor of the disk.
42 */
43 std::string vendor;
44
45 /**
46 * Stores the model of the disk.
47 */
48 std::string model;
49
50 /**
51 * Stores the product of the disk.
52 */
53 std::string product;
54
55 /**
56 * Stores the size of the disk.
57 */
58 uint64_t size;
59
60 /**
61 * Stores the serial number of the disk
62 */
63 std::string serial_number;
64 };
65
66 /**
67 * Represents the resolver's data.
68 */
69 struct data
70 {
71 /**
72 * Stores the disks.
73 */
74 std::vector<disk> disks;
75 };
76
77 /**
78 * Collects the resolver data.
79 * @param facts The fact collection that is resolving facts.
80 * @return Returns the resolver data.
81 */
82 virtual data collect_data(collection& facts) = 0;
83 };
84
85 }}} // namespace facter::facts::resolvers
+0
-115
lib/inc/internal/facts/resolvers/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base Desktop Management Interface (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving DMI facts.
13 */
14 struct dmi_resolver : resolver
15 {
16 /**
17 * Constructs the dmi_resolver.
18 */
19 dmi_resolver();
20
21 /**
22 * Converts the given chassis type identifier to a description string.
23 * @param type The chassis type identifier.
24 * @return Returns the chassis description string.
25 */
26 static std::string to_chassis_description(std::string const& type);
27
28 /**
29 * Called to resolve all facts the resolver is responsible for.
30 * @param facts The fact collection that is resolving facts.
31 */
32 virtual void resolve(collection& facts) override;
33
34 protected:
35 /**
36 * Represents DMI data.
37 */
38 struct data
39 {
40 /**
41 * Stores the BIOS vendor.
42 */
43 std::string bios_vendor;
44
45 /**
46 * Stores the BIOS version.
47 */
48 std::string bios_version;
49
50 /**
51 * Stores the BIOS release date.
52 */
53 std::string bios_release_date;
54
55 /**
56 * Stores the board asset tag.
57 */
58 std::string board_asset_tag;
59
60 /**
61 * Stores the board manufacturer.
62 */
63 std::string board_manufacturer;
64
65 /**
66 * Stores the board product name.
67 */
68 std::string board_product_name;
69
70 /**
71 * Stores the board serial number.
72 */
73 std::string board_serial_number;
74
75 /**
76 * Stores the chassis asset tag.
77 */
78 std::string chassis_asset_tag;
79
80 /**
81 * Stores the system manufacturer.
82 */
83 std::string manufacturer;
84
85 /**
86 * Stores the system product name.
87 */
88 std::string product_name;
89
90 /**
91 * Stores the system serial number.
92 */
93 std::string serial_number;
94
95 /**
96 * Stores the system product UUID.
97 */
98 std::string uuid;
99
100 /**
101 * Stores the system chassis type.
102 */
103 std::string chassis_type;
104 };
105
106 /**
107 * Collects the resolver data.
108 * @param facts The fact collection that is resolving facts.
109 * @return Returns resolver DMI data.
110 */
111 virtual data collect_data(collection& facts) = 0;
112 };
113
114 }}} // namespace facter::facts::resolvers
+0
-30
lib/inc/internal/facts/resolvers/ec2_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base EC2 fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving EC2 facts.
12 */
13 struct ec2_resolver : resolver
14 {
15 /**
16 * Constructs the ec2_resolver.
17 */
18 ec2_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25
26 bool is_blockable() const override;
27 };
28
29 }}} // namespace facter::facts::resolvers
+0
-172
lib/inc/internal/facts/resolvers/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base file system fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9 #include <set>
10 #include <cstdint>
11
12 namespace facter { namespace facts { namespace resolvers {
13
14 /**
15 * Responsible for resolving file system facts.
16 */
17 struct filesystem_resolver : resolver
18 {
19 /**
20 * Constructs the filesystem_resolver.
21 */
22 filesystem_resolver();
23
24 /**
25 * Called to resolve all facts the resolver is responsible for.
26 * @param facts The fact collection that is resolving facts.
27 */
28 virtual void resolve(collection& facts) override;
29
30 bool is_blockable() const override;
31 protected:
32 /**
33 * Represents data about a mountpoint.
34 */
35 struct mountpoint
36 {
37 /**
38 * Constructs a mountpoint.
39 */
40 mountpoint() :
41 size(0),
42 available(0),
43 free(0)
44 {
45 }
46
47 /**
48 * Stores the mountpoint name (the mount location).
49 */
50 std::string name;
51
52 /**
53 * Stores the mounted device.
54 */
55 std::string device;
56
57 /**
58 * Stores the filesystem of the mountpoint.
59 */
60 std::string filesystem;
61
62 /**
63 * Stores the total size of the mountpoint.
64 */
65 uint64_t size;
66
67 /**
68 * Stores the available size of the mountpoint.
69 */
70 uint64_t available;
71
72 /**
73 * Stores the free size of the mountpoint.
74 */
75 uint64_t free;
76
77 /**
78 * Stores the mountpoint options.
79 */
80 std::vector<std::string> options;
81 };
82
83 /**
84 * Represents data about a partition.
85 */
86 struct partition
87 {
88 /**
89 * Constructs a partition.
90 */
91 partition() :
92 size(0)
93 {
94 }
95
96 /**
97 * Stores the name of the partition.
98 */
99 std::string name;
100
101 /**
102 * Stores the file system of the partition.
103 */
104 std::string filesystem;
105
106 /**
107 * Stores the size of the partition.
108 */
109 uint64_t size;
110
111 /**
112 * Stores the UUID of the file system.
113 */
114 std::string uuid;
115
116 /**
117 * Stores the UUID of the GPT partition.
118 */
119 std::string partition_uuid;
120
121 /**
122 * Stores the label of the file system.
123 */
124 std::string label;
125
126 /**
127 * Stores the label of the GPT partition.
128 */
129 std::string partition_label;
130
131 /**
132 * Stores the partition mountpoint.
133 */
134 std::string mount;
135
136 /**
137 * Stores the backing file for partitions backed by a file.
138 */
139 std::string backing_file;
140 };
141
142 /**
143 * Represents file system data.
144 */
145 struct data
146 {
147 /**
148 * Stores the mountpoint data.
149 */
150 std::vector<mountpoint> mountpoints;
151
152 /**
153 * Stores the filesystems.
154 */
155 std::set<std::string> filesystems;
156
157 /**
158 * Stores the partitions data.
159 */
160 std::vector<partition> partitions;
161 };
162
163 /**
164 * Collects the file system data.
165 * @param facts The fact collection that is resolving facts.
166 * @return Returns the file system data.
167 */
168 virtual data collect_data(collection& facts) = 0;
169 };
170
171 }}} // namespace facter::facts::resolvers
+0
-46
lib/inc/internal/facts/resolvers/fips_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base fips fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10
11 /**
12 * Responsible for resolving fips facts.
13 */
14 struct fips_resolver : resolver
15 {
16 fips_resolver();
17
18 /**
19 * Called to resolve all facts the resolver is responsible for.
20 * @param facts The fact collection that is resolving facts.
21 */
22 virtual void resolve(collection& facts) override;
23
24 protected:
25 /**
26 * Represents fips data.
27 */
28 struct data
29 {
30 /**
31 * Stores the is_fips_mode_enabled data.
32 */
33 bool is_fips_mode_enabled;
34 };
35
36 /**
37 *
38 * Collects fips data.
39 * @param facts The fact collection that is resolving facts.
40 * @return Returns the fips data.
41 */
42 virtual data collect_data(collection& facts) = 0;
43 };
44
45 }}} // namespace facter::facts::resolvers
+0
-28
lib/inc/internal/facts/resolvers/gce_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base Google Compute Engine (GCE) fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving GCE facts.
12 */
13 struct gce_resolver : resolver
14 {
15 /**
16 * Constructs the gce_resolver.
17 */
18 gce_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25 };
26
27 }}} // namespace facter::facts::resolvers
+0
-69
lib/inc/internal/facts/resolvers/hypervisors_resolver.hpp less more
0 /**
1 * @file
2 * Declares the hypervisors fact resolver
3 */
4 #pragma once
5
6 #include <facter/facts/map_value.hpp>
7 #include <facter/facts/array_value.hpp>
8 #include <facter/facts/scalar_value.hpp>
9 #include <facter/facts/resolver.hpp>
10 #include <facter/facts/fact.hpp>
11 #include <boost/variant.hpp>
12 #include <string>
13 #include <unordered_map>
14
15 namespace facter { namespace facts { namespace resolvers {
16
17 /**
18 * Represents hypervisor data collected from libwhereami
19 */
20 using hypervisor_data = std::unordered_map<std::string, std::unordered_map<std::string, boost::variant<std::string, bool, int>>>;
21
22 /**
23 * This base resolver exists to allow libfacter schema tests to pass when libwhereami is not included in the build.
24 */
25 struct hypervisors_resolver_base : resolver
26 {
27 /**
28 * Default constructor
29 */
30 hypervisors_resolver_base() :
31 resolver(
32 "hypervisors",
33 {
34 fact::hypervisors
35 })
36 {
37 }
38
39 /**
40 * Called to resolve all facts the resolver is responsible for
41 * @param facts The fact collection that is resolving the facts
42 */
43 virtual void resolve(collection& facts) override;
44
45 bool is_blockable() const override;
46 protected:
47 /**
48 * Collects hypervisor data
49 * @param facts
50 * @return Returns the hypervisor data.
51 */
52 virtual hypervisor_data collect_data(collection& facts) = 0;
53 };
54
55 /**
56 * Hypervisors resolver for use when libwhereami is included
57 */
58 struct hypervisors_resolver : hypervisors_resolver_base
59 {
60 /**
61 * Collects hypervisor data
62 * @param facts
63 * @return Returns the hypervisor data.
64 */
65 virtual hypervisor_data collect_data(collection& facts) override;
66 };
67
68 }}} // namespace facter::facts::resolvers
+0
-73
lib/inc/internal/facts/resolvers/identity_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base user and group resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <boost/optional.hpp>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving the user and group facts.
14 */
15 struct identity_resolver : resolver
16 {
17 /**
18 * Constructs the identity_resolver.
19 */
20 identity_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Represents user information data.
31 */
32 struct data
33 {
34 /**
35 * Stores the id of the user.
36 */
37 boost::optional<int64_t> user_id;
38
39 /**
40 * Stores the name of the user.
41 */
42 std::string user_name;
43
44 /**
45 * Stores id of the user's primary group.
46 */
47 boost::optional<int64_t> group_id;
48
49 /**
50 * Stores the name of the user's primary group.
51 */
52 std::string group_name;
53
54 /**
55 * Stores whether facter is running as a privileged
56 * process: With the effective UID of 0 on *NIX systems
57 * or with elevated privileges on Windows (or with the
58 * local Administrators group privileges on older versions
59 * of windows not supporting privileges elevation).
60 */
61 boost::optional<bool> privileged;
62 };
63
64 /**
65 * Collects the resolver data.
66 * @param facts The fact collection that is resolving facts.
67 * @return Returns the resolver data.
68 */
69 virtual data collect_data(collection& facts) = 0;
70 };
71
72 }}} // namespace facter::facts::resolvers
+0
-66
lib/inc/internal/facts/resolvers/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base kernel fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <tuple>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving kernel facts.
14 */
15 struct kernel_resolver : resolver
16 {
17 /**
18 * Constructs the kernel_resolver.
19 */
20 kernel_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Represents kernel data.
31 */
32 struct data
33 {
34 /**
35 * Stores the name of the kernel (e.g. Linux, Darwin, etc).
36 */
37 std::string name;
38
39 /**
40 * Stores the release of the kernel.
41 */
42 std::string release;
43
44 /**
45 * Stores the version of the kernel.
46 */
47 std::string version;
48 };
49
50 /**
51 * Collects the resolver data.
52 * @param facts The fact collection that is resolving facts.
53 * @return Returns the resolver data.
54 */
55 virtual data collect_data(collection& facts) = 0;
56
57 /**
58 * Parses the major and minor kernel versions.
59 * @param version The version to parse.
60 * @return Returns a tuple of major and minor versions.
61 */
62 virtual std::tuple<std::string, std::string> parse_version(std::string const& version) const;
63 };
64
65 }}} // namespace facter::facts::resolvers
+0
-64
lib/inc/internal/facts/resolvers/ldom_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base LDom (Logical Domain) fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <map>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving LDom facts.
13 */
14 struct ldom_resolver : resolver
15 {
16 /**
17 * Constructs the ldom_resolver.
18 */
19 ldom_resolver();
20
21 /**
22 * Called to resolve all facts the resolver is responsible for.
23 * @param facts The fact collection that is resolving facts.
24 */
25 virtual void resolve(collection& facts) override;
26
27 protected:
28 /**
29 * Represents dynamic sub-keys consisting of LDom data.
30 */
31 struct ldom_info
32 {
33 /**
34 * Stores the top-level name of this category of LDom information.
35 */
36 std::string key;
37
38 /**
39 * Stores related LDom information.
40 */
41 std::map<std::string, std::string> values;
42 };
43
44 /**
45 * Represents the resolver's data.
46 */
47 struct data
48 {
49 /**
50 * Stores all gathered LDom data.
51 */
52 std::vector<ldom_info> ldom;
53 };
54
55 /**
56 * Collects the resolver data.
57 * @param facts The fact collection that is resolving facts.
58 * @return Returns the resolver data.
59 */
60 virtual data collect_data(collection& facts) = 0;
61 };
62
63 }}} // namespace facter::facts::resolvers
+0
-37
lib/inc/internal/facts/resolvers/load_average_resolver.hpp less more
0 /**
1 * @file
2 * Declares the load average fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <tuple>
8 #include <boost/optional.hpp>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving load_average facts.
14 */
15 struct load_average_resolver : resolver
16 {
17 /**
18 * Constructs the disk_resolver.
19 */
20 load_average_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Get the system load averages (1, 5 or 15 minutes).
31 * @return Returns the system load averages.
32 */
33 virtual boost::optional<std::tuple<double, double, double> > get_load_averages() = 0;
34 };
35
36 }}} // namespace facter::facts::resolvers
+0
-99
lib/inc/internal/facts/resolvers/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base memory fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <cstdint>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving memory facts.
13 */
14 struct memory_resolver : resolver
15 {
16 /**
17 * Constructs the memory_resolver.
18 */
19 memory_resolver();
20
21 /**
22 * Called to resolve all facts the resolver is responsible for.
23 * @param facts The fact collection that is resolving facts.
24 */
25 virtual void resolve(collection& facts) override;
26
27 protected:
28 /**
29 * Represents the possible swap encryption status.
30 */
31 enum class encryption_status
32 {
33 /**
34 * The swap encryption status is unknown.
35 */
36 unknown,
37 /**
38 * The swap is encrypted.
39 */
40 encrypted,
41 /**
42 * The swap is not encrypted.
43 */
44 not_encrypted
45 };
46
47 /**
48 * Represents data about system memory.
49 */
50 struct data
51 {
52 /**
53 * Constructs the data.
54 */
55 data() :
56 mem_free(0),
57 mem_total(0),
58 swap_free(0),
59 swap_total(0),
60 swap_encryption(encryption_status::unknown)
61 {
62 }
63
64 /**
65 * Stores the free memory, in bytes.
66 */
67 uint64_t mem_free;
68
69 /**
70 * Stores the total memory, in bytes.
71 */
72 uint64_t mem_total;
73
74 /**
75 * Stores the free swap, in bytes.
76 */
77 uint64_t swap_free;
78
79 /**
80 * Stores the total swap, in bytes.
81 */
82 uint64_t swap_total;
83
84 /**
85 * Stores the swap encryption status.
86 */
87 encryption_status swap_encryption;
88 };
89
90 /**
91 * Collects the resolver data.
92 * @param facts The fact collection that is resolving facts.
93 * @return Returns the resolver data.
94 */
95 virtual data collect_data(collection& facts) = 0;
96 };
97
98 }}} // namespace facter::facts::resolvers
+0
-158
lib/inc/internal/facts/resolvers/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base networking fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <facter/facts/map_value.hpp>
8 #include <string>
9 #include <vector>
10 #include <boost/optional.hpp>
11
12 namespace facter { namespace facts { namespace resolvers {
13
14 /**
15 * Responsible for resolving networking facts.
16 */
17 struct networking_resolver : resolver
18 {
19 /**
20 * Constructs the networking_resolver.
21 */
22 networking_resolver();
23
24 /**
25 * Utility function to convert the bytes of a MAC address to a string.
26 * @param bytes The bytes of the MAC address; accepts 6-byte and 20-byte addresses.
27 * @param byte_count The number of bytes in the MAC address; defaults to be 6 bytes long.
28 * @returns Returns the MAC address as a string or an empty string if the address is the "NULL" MAC address.
29 */
30 static std::string macaddress_to_string(uint8_t const* bytes, uint8_t byte_count = 6);
31
32 /**
33 * Returns whether the address is an ignored IPv4 address.
34 * Ignored addresses are local or auto-assigned private IP.
35 * @param addr The string representation of an IPv4 address.
36 * @return Returns true if an ignored IPv4 address.
37 */
38 static bool ignored_ipv4_address(std::string const& addr);
39
40 /**
41 * Returns whether the address is an ignored IPv6 address. Ignored addresses are local or link-local.
42 * @param addr The string representation of an IPv6 address.
43 * @return Returns true if an ignored IPv6 address.
44 */
45 static bool ignored_ipv6_address(std::string const& addr);
46
47 /**
48 * Called to resolve all facts the resolver is responsible for.
49 * @param facts The fact collection that is resolving facts.
50 */
51 virtual void resolve(collection& facts) override;
52
53 protected:
54 /**
55 * Represents an address binding.
56 */
57 struct binding
58 {
59 /**
60 * Stores the interface's address.
61 */
62 std::string address;
63
64 /**
65 * Stores the netmask represented as an address.
66 */
67 std::string netmask;
68
69 /**
70 * Stores the network address.
71 */
72 std::string network;
73 };
74
75 /**
76 * Represents a network interface.
77 */
78 struct interface
79 {
80 /**
81 * Stores the name of the interface.
82 */
83 std::string name;
84
85 /**
86 * Stores the DHCP server address.
87 */
88 std::string dhcp_server;
89
90 /**
91 * Stores the IPv4 bindings for the interface.
92 */
93 std::vector<binding> ipv4_bindings;
94
95 /**
96 * Stores the IPv6 bindings for the interface.
97 */
98 std::vector<binding> ipv6_bindings;
99
100 /**
101 * Stores the link layer (MAC) address.
102 */
103 std::string macaddress;
104
105 /**
106 * Stores the interface MTU.
107 */
108 boost::optional<uint64_t> mtu;
109 };
110
111 /**
112 * Represents the resolver's data.
113 */
114 struct data
115 {
116 /**
117 * Stores the hostname.
118 */
119 std::string hostname;
120
121 /**
122 * Stores the domain.
123 */
124 std::string domain;
125
126 /**
127 * Stores the FQDN.
128 */
129 std::string fqdn;
130
131 /**
132 * Stores the name of the primary interface.
133 */
134 std::string primary_interface;
135
136 /**
137 * Stores the interface.
138 */
139 std::vector<interface> interfaces;
140 };
141
142 /**
143 * Collects the resolver data.
144 * @param facts The fact collection that is resolving facts.
145 * @return Returns the resolver data.
146 */
147 virtual data collect_data(collection& facts) = 0;
148
149 private:
150 static binding const* find_default_binding(std::vector<binding> const& bindings, std::function<bool(std::string const&)> const& ignored);
151 static void add_bindings(interface& iface, bool primary, bool ipv4, collection& facts, map_value& networking, map_value& iface_value);
152 interface const* find_primary_interface(std::vector<interface> const& interfaces);
153
154 static std::string get_scope(const std::string& address);
155 };
156
157 }}} // namespace facter::facts::resolvers
+0
-272
lib/inc/internal/facts/resolvers/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base operating system fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving operating system facts.
13 */
14 struct operating_system_resolver : resolver
15 {
16 /**
17 * Constructs the operating_system_resolver.
18 */
19 operating_system_resolver();
20
21 /**
22 * Called to resolve all facts the resolver is responsible for.
23 * @param facts The fact collection that is resolving facts.
24 */
25 virtual void resolve(collection& facts) override;
26
27 /**
28 * Parses the major and minor OS release versions for Linux distros.
29 * @param name The name of the OS.
30 * @param release The release to parse.
31 * @return Returns a tuple of major and minor release versions.
32 */
33 static std::tuple<std::string, std::string> parse_distro(std::string const& name, std::string const& release);
34
35 protected:
36 /**
37 * Represents information about an operating system distribution.
38 */
39 struct distribution
40 {
41 /**
42 * Stores the distribution id.
43 */
44 std::string id;
45
46 /**
47 * Stores the distribution release.
48 */
49 std::string release;
50
51 /**
52 * Stores the distribution codename.
53 */
54 std::string codename;
55
56 /**
57 * Stores the distribution description.
58 */
59 std::string description;
60 };
61
62 /**
63 * Represents information about FreeBSD.
64 */
65 struct freebsd_data
66 {
67 /**
68 * Stores FreeBSD branch.
69 */
70 std::string branch;
71
72 /**
73 * Stores FreeBSD patchlevel.
74 */
75 std::string patchlevel;
76 };
77
78 /**
79 * Represents information about Mac OSX.
80 */
81 struct mac
82 {
83 /**
84 * Stores the OSX product name.
85 */
86 std::string product;
87 /**
88 * Stores the OSX build number.
89 */
90 std::string build;
91 /**
92 * Stores the OSX version.
93 */
94 std::string version;
95 };
96
97 /**
98 * Represents information about Windows.
99 */
100 struct windows
101 {
102 /**
103 * Stores the Windows Server or Desktop Edition variant
104 */
105 std::string edition_id;
106 /**
107 * Stores the Windows differentiate Server, Server Core, Client (Desktop)
108 */
109 std::string installation_type;
110 /**
111 * Stores the Windows textual product name
112 */
113 std::string product_name;
114 /**
115 * Stores the Windows Build Version.
116 */
117 std::string release_id;
118 /**
119 * Stores the native system32 directory, the location native OS executables can be found.
120 * For 32-bit facter on 32-bit Windows, typically: 'C:\\Windows\\system32'.
121 * For 32-bit facter on 64-bit Windows, typically: 'C:\\Windows\\sysnative'.
122 * For 64-bit facter on 64-bit Windows, typically: 'C:\\Windows\\system32'.
123 */
124 std::string system32;
125 };
126
127 /**
128 * Represents information about SELinux.
129 */
130 struct selinux_data
131 {
132 /**
133 * Default constructor for selinux data.
134 */
135 selinux_data() :
136 supported(false),
137 enabled(false),
138 enforced(false)
139 {
140 }
141
142 /**
143 * Stores whether or not SELinux is supported.
144 */
145 bool supported;
146
147 /**
148 * Stores whether or not SELinux is enabled.
149 */
150 bool enabled;
151
152 /**
153 * Stores whether or not SELinux is enforced.
154 */
155 bool enforced;
156
157 /**
158 * Stores the SELinux policy version.
159 */
160 std::string policy_version;
161
162 /**
163 * Stores the current SELinux mode.
164 */
165 std::string current_mode;
166
167 /**
168 * Stores the configured SELinux mode.
169 */
170 std::string config_mode;
171
172 /**
173 * Stores the configured SELinux policy.
174 */
175 std::string config_policy;
176 };
177
178 /**
179 * Represents operating system data.
180 */
181 struct data
182 {
183 /**
184 * Stores the OS name (e.g. CentOS).
185 */
186 std::string name;
187
188 /**
189 * Stores the OS family name (e.g. Debian).
190 */
191 std::string family;
192
193 /**
194 * Stores the OS release.
195 */
196 std::string release;
197
198 /**
199 * Stores the OS major release.
200 */
201 std::string major;
202
203 /**
204 * Stores the OS minor release.
205 */
206 std::string minor;
207
208 /**
209 * Stores the processor hardware model.
210 */
211 std::string hardware;
212
213 /**
214 * Stores the system architecture.
215 */
216 std::string architecture;
217
218 /**
219 * Stores the distribution specification version.
220 */
221 std::string specification_version;
222
223 /**
224 * Stores information about the OS distribution.
225 */
226 distribution distro;
227
228 /**
229 * Stores information about FreeBSD.
230 */
231 freebsd_data freebsd;
232
233 /**
234 * Stores information about Mac OSX.
235 */
236 mac osx;
237
238 /**
239 * Stores information about Windows.
240 */
241 windows win;
242
243 /**
244 * Stores information about SELinux.
245 */
246 selinux_data selinux;
247 };
248
249 /**
250 * Collects the resolver data.
251 * @param facts The fact collection that is resolving facts.
252 * @return Returns the resolver data.
253 */
254 virtual data collect_data(collection& facts);
255
256 /**
257 * Collects the resolver's kernel data.
258 * @param facts The fact collection that is resolving facts.
259 * @param result The current resolver data.
260 */
261 virtual void collect_kernel_data(collection& facts, data &result);
262
263 /**
264 * Collects the resolver's release data.
265 * @param facts The fact collection that is resolving facts.
266 * @param result The current resolver data.
267 */
268 virtual void collect_release_data(collection& facts, data &result);
269 };
270
271 }}} // namespace facter::facts::resolvers
+0
-28
lib/inc/internal/facts/resolvers/path_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base PATH fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving path facts.
12 */
13 struct path_resolver : resolver
14 {
15 /**
16 * Constructs the path_resolver.
17 */
18 path_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25 };
26
27 }}} // namespace facter::facts::resolvers
+0
-80
lib/inc/internal/facts/resolvers/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base processor fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9 #include <cstdint>
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 /**
14 * Responsible for resolving processor-related facts.
15 */
16 struct processor_resolver : resolver
17 {
18 /**
19 * Constructs the processor_resolver.
20 */
21 processor_resolver();
22
23 /**
24 * Called to resolve all facts the resolver is responsible for.
25 * @param facts The fact collection that is resolving facts.
26 */
27 virtual void resolve(collection& facts) override;
28
29 protected:
30 /**
31 * Represents processor resolver data.
32 */
33 struct data
34 {
35 /**
36 * Constructs the processor resolver data.
37 */
38 data():
39 physical_count(0),
40 logical_count(0),
41 speed(0)
42 {
43 }
44
45 /**
46 * Stores the physical count of processors.
47 */
48 int physical_count;
49
50 /**
51 * Stores the logical count of processors.
52 */
53 int logical_count;
54
55 /**
56 * Stores the processor model strings.
57 */
58 std::vector<std::string> models;
59
60 /**
61 * Stores the processor speed, in Hz.
62 */
63 int64_t speed;
64
65 /**
66 * Stores the processor instruction set architecture.
67 */
68 std::string isa;
69 };
70
71 /**
72 * Collects the resolver data.
73 * @param facts The fact collection that is resolving facts.
74 * @return Returns the resolver data.
75 */
76 virtual data collect_data(collection& facts) = 0;
77 };
78
79 }}} // namespace facter::facts::resolvers
+0
-56
lib/inc/internal/facts/resolvers/ruby_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base Ruby fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving Ruby facts, when a Ruby runtime is available.
12 */
13 struct ruby_resolver : resolver
14 {
15 /**
16 * Constructs the ruby_resolver.
17 */
18 ruby_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25
26 protected:
27 /**
28 * Represents Ruby metadata.
29 */
30 struct data {
31 /**
32 * Stores RUBY_PLATFORM.
33 */
34 std::string platform;
35
36 /**
37 * Stores the Ruby sitelibdir.
38 */
39 std::string sitedir;
40
41 /**
42 * Stores RUBY_VERSION.
43 */
44 std::string version;
45 };
46
47 /**
48 * Collects the resolver data.
49 * @param facts The fact collection that is resolving facts.
50 * @return Returns the resolver data.
51 */
52 virtual data collect_data(collection& facts);
53 };
54
55 }}} // namespace facter::facts::resolvers
+0
-114
lib/inc/internal/facts/resolvers/ssh_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base SSH fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <facter/facts/map_value.hpp>
8 #include <boost/filesystem.hpp>
9 #include <string>
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 /**
14 * Responsible for resolving ssh facts.
15 */
16 struct ssh_resolver : resolver
17 {
18 /**
19 * Constructs the ssh_resolver.
20 */
21 ssh_resolver();
22
23 /**
24 * Called to resolve all facts the resolver is responsible for.
25 * @param facts The fact collection that is resolving facts.
26 */
27 virtual void resolve(collection& facts) override;
28
29 protected:
30 /**
31 * Represents an SSH fingerprint.
32 */
33 struct fingerprint
34 {
35 /**
36 * Stores the SHA1 fingerprint.
37 */
38 std::string sha1;
39
40 /**
41 * Stores the SHA256 fingerprint.
42 */
43 std::string sha256;
44 };
45
46 /**
47 * Represents information about a SSH key.
48 */
49 struct ssh_key
50 {
51 /**
52 * Stores the SSH key.
53 */
54 std::string key;
55
56 /**
57 * Stores the SSH key's fingerprint.
58 */
59 fingerprint digest;
60
61 /**
62 * Stores the SSH key type. One of ssh-dss, ssh-rsa, ssh-ed25519,
63 * ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp512
64 */
65 std::string type;
66 };
67
68 /**
69 * Represents SSH resolver data.
70 */
71 struct data
72 {
73 /**
74 * Stores the DSA key.
75 */
76 ssh_key dsa;
77
78 /**
79 * Stores the RSA key.
80 */
81 ssh_key rsa;
82
83 /**
84 * Stores the ECDSA key.
85 */
86 ssh_key ecdsa;
87
88 /**
89 * Stores the ED25519 key
90 */
91 ssh_key ed25519;
92 };
93
94 /**
95 * Retrieves the fact's key file
96 * @param filename The searched key file name.
97 * @return Returns the key file's path
98 */
99 virtual boost::filesystem::path retrieve_key_file(std::string const& filename) = 0;
100
101 /**
102 * Collects the resolver data.
103 * @param facts The fact collection that is resolving facts.
104 * @return Returns the resolver data.
105 */
106 virtual data collect_data(collection& facts);
107
108 private:
109 void add_key(collection& facts, map_value& value, ssh_key& key, std::string const& name, std::string const& key_fact_name, std::string const& fingerprint_fact_name);
110 void populate_key(std::string const& filename, int type, ssh_key& key);
111 };
112
113 }}} // namespace facter::facts::resolvers
+0
-127
lib/inc/internal/facts/resolvers/system_profiler_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base system profiler fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 /**
11 * Responsible for resolving system profiler facts.
12 */
13 struct system_profiler_resolver : resolver
14 {
15 /**
16 * Constructs the system_profiler_resolver.
17 */
18 system_profiler_resolver();
19
20 /**
21 * Called to resolve all facts the resolver is responsible for.
22 * @param facts The fact collection that is resolving facts.
23 */
24 virtual void resolve(collection& facts) override;
25
26 protected:
27 /**
28 * Represents the resolver's data.
29 */
30 struct data
31 {
32 /**
33 * Stores the boot mode.
34 */
35 std::string boot_mode;
36 /**
37 * Stores the boot ROM version.
38 */
39 std::string boot_rom_version;
40 /**
41 * Stores the boot volume.
42 */
43 std::string boot_volume;
44 /**
45 * Stores the processor name.
46 */
47 std::string processor_name;
48 /**
49 * Stores the processor speed.
50 */
51 std::string processor_speed;
52 /**
53 * Stores the kernel version.
54 */
55 std::string kernel_version;
56 /**
57 * Stores the L2 cache per core.
58 */
59 std::string l2_cache_per_core;
60 /**
61 * Stores the L3 cache.
62 */
63 std::string l3_cache;
64 /**
65 * Stores the computer name.
66 */
67 std::string computer_name;
68 /**
69 * Stores the model identifier.
70 */
71 std::string model_identifier;
72 /**
73 * Stores the model name.
74 */
75 std::string model_name;
76 /**
77 * Stores the number of cores.
78 */
79 std::string cores;
80 /**
81 * Stores the system version.
82 */
83 std::string system_version;
84 /**
85 * Stores the number of processors.
86 */
87 std::string processors;
88 /**
89 * Stores the physical memory amount.
90 */
91 std::string memory;
92 /**
93 * Stores the hardware UUID.
94 */
95 std::string hardware_uuid;
96 /**
97 * Stores whether or not secure virtual memory is enabled.
98 */
99 std::string secure_virtual_memory;
100 /**
101 * Stores the system serial number.
102 */
103 std::string serial_number;
104 /**
105 * Stores the SMC version.
106 */
107 std::string smc_version;
108 /**
109 * Stores the system uptime.
110 */
111 std::string uptime;
112 /**
113 * Stores the user name.
114 */
115 std::string username;
116 };
117
118 /**
119 * Collects the resolver data.
120 * @param facts The fact collection that is resolving facts.
121 * @return Returns the resolver data.
122 */
123 virtual data collect_data(collection& facts) = 0;
124 };
125
126 }}} // namespace facter::facts::resolvers
+0
-36
lib/inc/internal/facts/resolvers/timezone_resolver.hpp less more
0 /**
1 * @file
2 * Declares the timezone fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving time zone facts.
13 */
14 struct timezone_resolver : resolver
15 {
16 /**
17 * Constructs the timezone resolver.
18 */
19 timezone_resolver();
20
21 /**
22 * Called to resolve all facts the resolver is responsible for.
23 * @param facts The fact collection that is resolving facts.
24 */
25 virtual void resolve(collection& facts) override;
26
27 protected:
28 /**
29 * Gets the system timezone.
30 * @return Returns the system timezone.
31 */
32 virtual std::string get_timezone() = 0;
33 };
34
35 }}} // namespace facter::facts::resolvers
+0
-36
lib/inc/internal/facts/resolvers/uptime_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base uptime fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <cstdint>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Responsible for resolving uptime facts.
13 */
14 struct uptime_resolver : resolver
15 {
16 /**
17 * Constructs the uptime_resolver.
18 */
19 uptime_resolver();
20
21 /**
22 * Called to resolve all facts the resolver is responsible for.
23 * @param facts The fact collection that is resolving facts.
24 */
25 virtual void resolve(collection& facts) override;
26
27 protected:
28 /**
29 * Gets the system uptime in seconds.
30 * @return Returns the system uptime in seconds.
31 */
32 virtual int64_t get_uptime() = 0;
33 };
34
35 }}} // namespace facter::facts::resolvers
+0
-95
lib/inc/internal/facts/resolvers/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base virtualization fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 /**
12 * Represents cloud data.
13 */
14 struct cloud_
15 {
16 /**
17 * Stores the cloud provider.
18 */
19 std::string provider;
20 };
21
22 /**
23 * Represents virtualization data.
24 */
25 struct data
26 {
27 /**
28 * Stores the cloud data.
29 */
30 cloud_ cloud;
31 /**
32 * Stores the hypervisor data.
33 */
34 std::string hypervisor;
35 /**
36 * Stores the is_virtual data.
37 */
38 bool is_virtual;
39 };
40
41 /**
42 * Responsible for resolving virtualization facts.
43 */
44 struct virtualization_resolver : resolver
45 {
46 /**
47 * Constructs the virtualization_resolver.
48 */
49 virtualization_resolver();
50
51 /**
52 * Called to resolve all facts the resolver is responsible for.
53 * @param facts The fact collection that is resolving facts.
54 */
55 virtual void resolve(collection& facts) override;
56
57 protected:
58 /**
59 * Gets the name of the hypervisor.
60 * @param facts The fact collection that is resolving facts.
61 * @return Returns the name of the hypervisor or empty string if no hypervisor.
62 */
63 virtual std::string get_hypervisor(collection& facts) = 0;
64
65 /**
66 * Gets the name of the cloud provider.
67 * @param facts The fact collection that is resolving facts.
68 * @return Returns the name of the cloud provider or empty string if no cloud provider.
69 */
70 virtual std::string get_cloud_provider(collection& facts);
71
72 /**
73 * Determines if the given hypervisor is considered to be virtual.
74 * @param hypervisor The hypervisor to check.
75 * @return Returns true if the hypervisor is virtual or false if it is physical.
76 */
77 virtual bool is_virtual(std::string const& hypervisor);
78
79 /**
80 * Gets the name of the hypervisor based on other facts and a set of matching strings
81 * @param facts The fact collection that is resolving facts
82 * @return Returns the hypervisor name if matched.
83 */
84 static std::string get_fact_vm(collection& facts);
85
86 /**
87 * Collects the virtualization data.
88 * @param facts The fact collection that is resolving facts.
89 * @return Returns the virtualization data.
90 */
91 virtual data collect_data(collection& facts);
92 };
93
94 }}} // namespace facter::facts::resolvers
+0
-55
lib/inc/internal/facts/resolvers/xen_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Xen fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving Xen facts.
14 */
15 struct xen_resolver : resolver
16 {
17 /**
18 * Constructs the xen_resolver.
19 */
20 xen_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Gets the Xen management command.
31 * @return Returns the Xen management command.
32 */
33 virtual std::string xen_command() = 0;
34
35 /**
36 * Represents the resolver's data.
37 */
38 struct data
39 {
40 /**
41 * Stores the Xen domains.
42 */
43 std::vector<std::string> domains;
44 };
45
46 /**
47 * Collects the resolver data.
48 * @param facts The fact collection that is resolving facts.
49 * @return Returns the resolver data.
50 */
51 virtual data collect_data(collection& facts);
52 };
53
54 }}} // namespace facter::facts::resolvers
+0
-59
lib/inc/internal/facts/resolvers/zfs_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris ZFS fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving ZFS facts.
14 */
15 struct zfs_resolver : resolver
16 {
17 /**
18 * Constructs the zfs_resolver.
19 */
20 zfs_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Gets the platform's ZFS command.
31 * @return Returns the platform's ZFS command.
32 */
33 virtual std::string zfs_command() = 0;
34
35 /**
36 * Represents the resolver's data.
37 */
38 struct data
39 {
40 /**
41 * Stores the ZFS version.
42 */
43 std::string version;
44 /**
45 * Stores the ZFS version numbers.
46 */
47 std::vector<std::string> versions;
48 };
49
50 /**
51 * Collects the resolver data.
52 * @param facts The fact collection that is resolving facts.
53 * @return Returns the resolver data.
54 */
55 virtual data collect_data(collection& facts);
56 };
57
58 }}} // namespace facter::facts::resolvers
+0
-95
lib/inc/internal/facts/resolvers/zone_resolver.hpp less more
0 /**
1 * @file
2 * Declares the base Solaris zone fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving Solaris zone facts.
14 */
15 struct zone_resolver : resolver
16 {
17 /**
18 * Constructs the zone_resolver.
19 */
20 zone_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Represents a Solaris zone.
31 */
32 struct zone
33 {
34 /**
35 * Stores the zone id.
36 */
37 std::string id;
38
39 /**
40 * Stores the zone name.
41 */
42 std::string name;
43
44 /**
45 * Stores the zone status.
46 */
47 std::string status;
48
49 /**
50 * Stores the zone path.
51 */
52 std::string path;
53
54 /**
55 * Stores the zone unique identifier.
56 */
57 std::string uuid;
58
59 /**
60 * Stores the zone brand.
61 */
62 std::string brand;
63
64 /**
65 * Stores the zone IP type.
66 */
67 std::string ip_type;
68 };
69
70 /**
71 * Represents the resolver data.
72 */
73 struct data
74 {
75 /**
76 * Stores the Solaris zones.
77 */
78 std::vector<zone> zones;
79
80 /**
81 * Stores the current zone name.
82 */
83 std::string current_zone_name;
84 };
85
86 /**
87 * Collects the resolver data.
88 * @param facts The fact collection that is resolving facts.
89 * @return Returns the resolver data.
90 */
91 virtual data collect_data(collection& facts) = 0;
92 };
93
94 }}} // namespace facter::facts::resolvers
+0
-63
lib/inc/internal/facts/resolvers/zpool_resolver.hpp less more
0 /**
1 * @file
2 * Declares the ZFS storage pool (zpool) fact resolver.
3 */
4 #pragma once
5
6 #include <facter/facts/resolver.hpp>
7 #include <string>
8 #include <vector>
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 /**
13 * Responsible for resolving ZFS storage pool (zpool) facts.
14 */
15 struct zpool_resolver : resolver
16 {
17 /**
18 * Constructs the zpool_resolver.
19 */
20 zpool_resolver();
21
22 /**
23 * Called to resolve all facts the resolver is responsible for.
24 * @param facts The fact collection that is resolving facts.
25 */
26 virtual void resolve(collection& facts) override;
27
28 protected:
29 /**
30 * Gets the platform's zpool command.
31 * @return Returns the platform's zpool command.
32 */
33 virtual std::string zpool_command() = 0;
34
35 /**
36 * Represents the resolver's data.
37 */
38 struct data
39 {
40 /**
41 * Stores the zpool version.
42 */
43 std::string version;
44 /**
45 * Stores the zpool feature flags.
46 */
47 std::vector<std::string> feature_flags;
48 /**
49 * Stores the zpool version numbers.
50 */
51 std::vector<std::string> versions;
52 };
53
54 /**
55 * Collects the resolver data.
56 * @param facts The fact collection that is resolving facts.
57 * @return Returns the resolver data.
58 */
59 virtual data collect_data(collection& facts);
60 };
61
62 }}} // namespace facter::facts::resolvers
+0
-25
lib/inc/internal/facts/solaris/disk_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris disk fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/disk_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving disk facts.
12 */
13 struct disk_resolver : resolvers::disk_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris Desktop Management Interface (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving DMI facts.
12 */
13 struct dmi_resolver : resolvers::dmi_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-29
lib/inc/internal/facts/solaris/filesystem_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris file system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/filesystem_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving Solaris file system facts.
12 */
13 struct filesystem_resolver : resolvers::filesystem_resolver
14 {
15 protected:
16 /**
17 * Collects the file system data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the file system data.
20 */
21 virtual data collect_data(collection& facts) override;
22
23 private:
24 void collect_mountpoint_data(data& result);
25 void collect_filesystem_data(data& result);
26 };
27
28 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris kernel fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/kernel_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving kernel facts.
12 */
13 struct kernel_resolver : resolvers::kernel_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/ldom_resolver.hpp less more
0 /**
1 * @file
2 * Declares the ldom fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/ldom_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving ldom facts.
12 */
13 struct ldom_resolver : resolvers::ldom_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-55
lib/inc/internal/facts/solaris/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris networking fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/networking_resolver.hpp"
7 #include <map>
8 #include <net/if.h>
9
10 namespace facter { namespace facts { namespace solaris {
11
12 /**
13 * Responsible for resolving networking facts.
14 */
15 struct networking_resolver : posix::networking_resolver
16 {
17 protected:
18 /**
19 * Collects the resolver data.
20 * @param facts The fact collection that is resolving facts.
21 * @return Returns the resolver data.
22 */
23 virtual data collect_data(collection& facts) override;
24
25 /**
26 * Determines if the given sock address is a link layer address.
27 * @param addr The socket address to check.
28 * @returns Returns true if the socket address is a link layer address or false if it is not.
29 */
30 virtual bool is_link_address(sockaddr const* addr) const override;
31
32 /**
33 * Gets the bytes of the link address.
34 * @param addr The socket address representing the link address.
35 * @return Returns a pointer to the address bytes or nullptr if not a link address.
36 */
37 virtual uint8_t const* get_link_address_bytes(sockaddr const* addr) const override;
38
39 /**
40 * Gets the length of the link address.
41 * @param addr The socket address representing the link address.
42 * @return Returns the length of the address or 0 if not a link address.
43 */
44 virtual uint8_t get_link_address_length(sockaddr const* addr) const override;
45
46 private:
47 void populate_binding(interface& iface, lifreq const* addr) const;
48 void populate_macaddress(interface& iface, lifreq const* addr) const;
49 void populate_mtu(interface& iface, lifreq const* addr) const;
50 std::string find_dhcp_server(std::string const& interface) const;
51 std::string get_primary_interface() const;
52 };
53
54 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the solaris operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/operating_system_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving operating system facts.
12 */
13 struct operating_system_resolver : posix::operating_system_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris processor fact resolver.
3 */
4 #pragma once
5
6 #include "../posix/processor_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving processor-related facts.
12 */
13 struct processor_resolver : posix::processor_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving virtualization facts.
12 */
13 struct virtualization_resolver : resolvers::virtualization_resolver
14 {
15 protected:
16 /**
17 * Gets the name of the hypervisor.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the name of the hypervisor or empty string if no hypervisor.
20 */
21 virtual std::string get_hypervisor(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-24
lib/inc/internal/facts/solaris/zfs_resolver.hpp less more
0 /**
1 * @file
2 * Declares the ZFS fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/zfs_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving ZFS facts.
12 */
13 struct zfs_resolver : resolvers::zfs_resolver
14 {
15 protected:
16 /**
17 * Gets the platform's ZFS command.
18 * @return Returns the platform's ZFS command.
19 */
20 virtual std::string zfs_command();
21 };
22
23 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/solaris/zone_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris zone fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/zone_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving Solaris zone facts.
12 */
13 struct zone_resolver : resolvers::zone_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts);
22 };
23
24 }}} // namespace facter::facts::solaris
+0
-24
lib/inc/internal/facts/solaris/zpool_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Solaris ZFS storage pool (zpool) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/zpool_resolver.hpp"
7
8 namespace facter { namespace facts { namespace solaris {
9
10 /**
11 * Responsible for resolving ZFS storage pool (zpool) facts.
12 */
13 struct zpool_resolver : resolvers::zpool_resolver
14 {
15 protected:
16 /**
17 * Gets the platform's zpool command.
18 * @return Returns the platform's zpool command.
19 */
20 virtual std::string zpool_command();
21 };
22
23 }}} // namespace facter::facts::solaris
+0
-25
lib/inc/internal/facts/ssh_resolver.hpp less more
0 /**
1 * @file
2 * Declares the SSH fact resolver.
3 */
4 #pragma once
5
6 #include "resolvers/ssh_resolver.hpp"
7
8 namespace facter { namespace facts {
9
10 /**
11 * Responsible for resolving ssh facts.
12 */
13 struct ssh_resolver : resolvers::ssh_resolver
14 {
15 protected:
16 /**
17 * Retrieves the fact's key file
18 * @param filename The searched key file name.
19 * @return Returns the key file's path
20 */
21 virtual boost::filesystem::path retrieve_key_file(std::string const& filename) override;
22 };
23
24 }} // namespace facter::facts
+0
-38
lib/inc/internal/facts/windows/dmi_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows Desktop Management Information (DMI) fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/dmi_resolver.hpp"
7 #include <leatherman/windows/wmi.hpp>
8 #include <string>
9 #include <memory>
10
11 namespace facter { namespace facts { namespace windows {
12
13 /**
14 * Responsible for resolving DMI facts.
15 */
16 struct dmi_resolver : resolvers::dmi_resolver
17 {
18 /**
19 * Constructs the dmi_resolver.
20 * @param wmi_conn The WMI connection to use when resolving facts.
21 */
22 dmi_resolver(std::shared_ptr<leatherman::windows::wmi> wmi_conn = std::make_shared<leatherman::windows::wmi>());
23
24 protected:
25 /**
26 * Collects the resolver data.
27 * @param facts The fact collection that is resolving facts.
28 * @return Returns the resolver data.
29 */
30 virtual data collect_data(collection& facts) override;
31
32 private:
33 std::string read(std::string const& path);
34 std::shared_ptr<leatherman::windows::wmi> _wmi;
35 };
36
37 }}} // namespace facter::facts::windows
+0
-33
lib/inc/internal/facts/windows/fips_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows fips fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/fips_resolver.hpp"
7
8 namespace facter { namespace facts { namespace windows {
9
10 /**
11 * Responsible for resolving fips-related facts.
12 */
13 struct fips_resolver : resolvers::fips_resolver
14 {
15 protected:
16 /**
17 * The check consists of the following.
18 * (1) Examining the contents of
19 * HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Lsa/FipsAlgorithmPolicy/Enabled
20 *
21 * If it is 1 then fips mode is enabled.
22 */
23
24 /**
25 * Collects the resolver data.
26 * @param facts The fact collection that is resolving facts.
27 * @return Returns the resolver data.
28 */
29 virtual data collect_data(collection& facts) override;
30 };
31
32 }}} // namespace facter::facts::windows
+0
-25
lib/inc/internal/facts/windows/identity_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows user and group resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/identity_resolver.hpp"
7
8 namespace facter { namespace facts { namespace windows {
9
10 /**
11 * Responsible for resolving the user and group facts.
12 */
13 struct identity_resolver : resolvers::identity_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::windows
+0
-25
lib/inc/internal/facts/windows/kernel_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows kernel fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/kernel_resolver.hpp"
7
8 namespace facter { namespace facts { namespace windows {
9
10 /**
11 * Responsible for resolving kernel facts.
12 */
13 struct kernel_resolver : resolvers::kernel_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::windows
+0
-25
lib/inc/internal/facts/windows/memory_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows memory fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/memory_resolver.hpp"
7
8 namespace facter { namespace facts { namespace windows {
9
10 /**
11 * Responsible for resolving memory facts.
12 */
13 struct memory_resolver : resolvers::memory_resolver
14 {
15 protected:
16 /**
17 * Collects the resolver data.
18 * @param facts The fact collection that is resolving facts.
19 * @return Returns the resolver data.
20 */
21 virtual data collect_data(collection& facts) override;
22 };
23
24 }}} // namespace facter::facts::windows
+0
-74
lib/inc/internal/facts/windows/networking_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows networking fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/networking_resolver.hpp"
7 #include <vector>
8 #include <string>
9 #include <functional>
10
11 /*
12 * Forward declarations from winsock2.h, ws2tcpip.h, windows.h
13 * To use these APIs, include those headers. The headers are not included here
14 * to avoid polluting the global namespace.
15 */
16 struct sockaddr;
17 struct sockaddr_in;
18 struct sockaddr_in6;
19
20 namespace facter { namespace facts { namespace windows {
21
22 /**
23 * Responsible for resolving networking facts.
24 */
25 struct networking_resolver : resolvers::networking_resolver
26 {
27 /**
28 * Constructs a Windows networking resolver.
29 */
30 networking_resolver();
31
32 protected:
33 /**
34 * Collects the resolver data.
35 * @param facts The fact collection that is resolving facts.
36 * @return Returns the resolver data.
37 */
38 virtual data collect_data(collection& facts) override;
39
40 /**
41 * Creates an IPv4 sockaddr_in of the mask. If masklen is too large, returns a full mask.
42 * Windows only allows contiguous subnet masks, representing them by their length.
43 * @param masklen Length of the contiguous mask.
44 * @return The sockaddr_in representation of the mask.
45 */
46 sockaddr_in create_ipv4_mask(uint8_t masklen);
47
48 /**
49 * Creates an IPv6 sockaddr_in6 of the mask. If masklen is too large, returns a full mask.
50 * Windows only allows contiguous subnet masks, representing them by their length.
51 * @param masklen Length of the contiguous mask.
52 * @return The sockaddr_in6 representation of the mask.
53 */
54 sockaddr_in6 create_ipv6_mask(uint8_t masklen);
55
56 /**
57 * Applies a mask to an IPv4 address, returning a new sockaddr_in.
58 * @param addr A sockaddr structure defining a valid IPv4 address.
59 * @param mask A sockaddr_in structure defining a valid IPv4 mask.
60 * @return A new sockaddr_in structure representing the masked IPv4 address.
61 */
62 static sockaddr_in mask_ipv4_address(sockaddr const* addr, sockaddr_in const& mask);
63
64 /**
65 * Applies a mask to an IPv6 address, returning a new sockaddr_in6.
66 * @param addr A sockaddr structure defining a valid IPv6 address.
67 * @param mask A sockaddr_in6 structure defining a valid IPv6 mask.
68 * @return A new sockaddr_in6 structure representing the masked IPv6 address.
69 */
70 static sockaddr_in6 mask_ipv6_address(sockaddr const* addr, sockaddr_in6 const& mask);
71 };
72
73 }}} // namespace facter::facts::windows
+0
-36
lib/inc/internal/facts/windows/operating_system_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows operating system fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/operating_system_resolver.hpp"
7 #include <leatherman/windows/wmi.hpp>
8 #include <memory>
9
10 namespace facter { namespace facts { namespace windows {
11
12 /**
13 * Responsible for resolving operating system facts.
14 */
15 struct operating_system_resolver : resolvers::operating_system_resolver
16 {
17 /**
18 * Constructs the operating_system_resolver.
19 * @param wmi_conn The WMI connection to use when resolving facts.
20 */
21 operating_system_resolver(std::shared_ptr<leatherman::windows::wmi> wmi_conn = std::make_shared<leatherman::windows::wmi>());
22
23 protected:
24 /**
25 * Collects the resolver data.
26 * @param facts The fact collection that is resolving facts.
27 * @return Returns the resolver data.
28 */
29 virtual data collect_data(collection& facts) override;
30
31 private:
32 std::shared_ptr<leatherman::windows::wmi> _wmi;
33 };
34
35 }}} // namespace facter::facts::windows
+0
-36
lib/inc/internal/facts/windows/processor_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows processor fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/processor_resolver.hpp"
7 #include <leatherman/windows/wmi.hpp>
8 #include <memory>
9
10 namespace facter { namespace facts { namespace windows {
11
12 /**
13 * Responsible for resolving processor-related facts.
14 */
15 struct processor_resolver : resolvers::processor_resolver
16 {
17 /**
18 * Constructs the processor_resolver.
19 * @param wmi_conn The WMI connection to use when resolving facts.
20 */
21 processor_resolver(std::shared_ptr<leatherman::windows::wmi> wmi_conn = std::make_shared<leatherman::windows::wmi>());
22
23 protected:
24 /**
25 * Collects the resolver data.
26 * @param facts The fact collection that is resolving facts.
27 * @return Returns the resolver data.
28 */
29 virtual data collect_data(collection& facts) override;
30
31 private:
32 std::shared_ptr<leatherman::windows::wmi> _wmi;
33 };
34
35 }}} // namespace facter::facts::windows
+0
-24
lib/inc/internal/facts/windows/timezone_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows timezone fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/timezone_resolver.hpp"
7
8 namespace facter { namespace facts { namespace windows {
9
10 /**
11 * Responsible for resolving time zone facts.
12 */
13 struct timezone_resolver : resolvers::timezone_resolver
14 {
15 protected:
16 /**
17 * Gets the system timezone.
18 * @return Returns the system timezone.
19 */
20 virtual std::string get_timezone() override;
21 };
22
23 }}} // namespace facter::facts::windows
+0
-32
lib/inc/internal/facts/windows/uptime_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows uptime fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/uptime_resolver.hpp"
7 #include <leatherman/windows/wmi.hpp>
8 #include <string>
9 #include <memory>
10
11 namespace facter { namespace facts { namespace windows {
12
13 /**
14 * Responsible for resolving uptime facts.
15 */
16 struct uptime_resolver : resolvers::uptime_resolver
17 {
18 /**
19 * Constructs the uptime_resolver.
20 */
21 uptime_resolver();
22
23 protected:
24 /**
25 * Gets the system uptime in seconds.
26 * @return Returns the system uptime in seconds.
27 */
28 virtual int64_t get_uptime() override;
29 };
30
31 }}} // namespace facter::facts::windows
+0
-37
lib/inc/internal/facts/windows/virtualization_resolver.hpp less more
0 /**
1 * @file
2 * Declares the Windows virtualization fact resolver.
3 */
4 #pragma once
5
6 #include "../resolvers/virtualization_resolver.hpp"
7 #include <leatherman/windows/wmi.hpp>
8 #include <string>
9 #include <memory>
10
11 namespace facter { namespace facts { namespace windows {
12
13 /**
14 * Responsible for resolving virtualization facts.
15 */
16 struct virtualization_resolver : resolvers::virtualization_resolver
17 {
18 /**
19 * Constructs the virtualization_resolver.
20 * @param wmi_conn The WMI connection to use when resolving facts.
21 */
22 virtualization_resolver(std::shared_ptr<leatherman::windows::wmi> wmi_conn = std::make_shared<leatherman::windows::wmi>());
23
24 protected:
25 /**
26 * Gets the name of the hypervisor.
27 * @param facts The fact collection that is resolving facts.
28 * @return Returns the name of the hypervisor or empty string if no hypervisor.
29 */
30 virtual std::string get_hypervisor(collection& facts) override;
31
32 private:
33 std::shared_ptr<leatherman::windows::wmi> _wmi;
34 };
35
36 }}} // namespace facter::facts::windows
+0
-77
lib/inc/internal/ruby/aggregate_resolution.hpp less more
0 /**
1 * @file
2 * Declares the Ruby Facter::Core::Aggregate class.
3 */
4 #pragma once
5
6 #include "resolution.hpp"
7 #include "chunk.hpp"
8 #include <string>
9 #include <map>
10
11 namespace facter { namespace ruby {
12
13 /**
14 * Represents the Ruby Facter::Core::Aggregate class.
15 */
16 struct aggregate_resolution : resolution
17 {
18 /**
19 * Defines the Facter::Core::Aggregate class.
20 * @return Returns the Facter::Core::Aggregate class.
21 */
22 static leatherman::ruby::VALUE define();
23
24 /**
25 * Creates an instance of the Facter::Core::Aggregate class.
26 * @return Returns the new instance.
27 */
28 static leatherman::ruby::VALUE create();
29
30 /**
31 * Gets the value of the resolution.
32 * @return Returns the value of the resolution or nil if the value did not resolve.
33 */
34 virtual leatherman::ruby::VALUE value();
35
36 /**
37 * Finds the value of the given chunk.
38 * @param name The name of the chunk to find the value of.
39 * @return Returns the value of the chunk or nil if the chunk is not found.
40 */
41 leatherman::ruby::VALUE find_chunk(leatherman::ruby::VALUE name);
42
43 /**
44 * Defines a chunk.
45 * @param name The name of the chunk.
46 * @param options The options for defining the chunk.
47 */
48 void define_chunk(leatherman::ruby::VALUE name, leatherman::ruby::VALUE options);
49
50 private:
51 // Construction and assignment
52 aggregate_resolution();
53 aggregate_resolution(aggregate_resolution const&) = delete;
54 aggregate_resolution& operator=(aggregate_resolution const&) = delete;
55 aggregate_resolution(aggregate_resolution&& other) = delete;
56 aggregate_resolution& operator=(aggregate_resolution&& other) = delete;
57
58 // Ruby lifecycle functions
59 static leatherman::ruby::VALUE alloc(leatherman::ruby::VALUE klass);
60 static void mark(void* data);
61 static void free(void* data);
62
63 // Methods called from Ruby
64 static leatherman::ruby::VALUE ruby_chunk(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
65 static leatherman::ruby::VALUE ruby_aggregate(leatherman::ruby::VALUE self);
66 static leatherman::ruby::VALUE ruby_merge_hashes(leatherman::ruby::VALUE obj, leatherman::ruby::VALUE context, int argc, leatherman::ruby::VALUE* argv);
67
68 // Helper functions
69 static leatherman::ruby::VALUE deep_merge(leatherman::ruby::api const& ruby, leatherman::ruby::VALUE left, leatherman::ruby::VALUE right);
70
71 leatherman::ruby::VALUE _self;
72 leatherman::ruby::VALUE _block;
73 std::map<leatherman::ruby::VALUE, ruby::chunk> _chunks;
74 };
75
76 }} // namespace facter::ruby
+0
-83
lib/inc/internal/ruby/chunk.hpp less more
0 /**
1 * @file
2 * Declares the class for aggregate resolution chunks.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7
8 namespace facter { namespace ruby {
9
10 struct aggregate_resolution;
11
12 /**
13 * Represents a aggregate resolution chunk.
14 */
15 struct chunk
16 {
17 /**
18 * Constructs an aggregate resolution chunk.
19 * @param dependencies The symbol or array of symbols this chunk depends on.
20 * @param block The block to run to resolve the chunk.
21 */
22 chunk(leatherman::ruby::VALUE dependencies, leatherman::ruby::VALUE block);
23
24 /**
25 * Moves the given chunk into this chunk.
26 * @param other The chunk to move into this chunk.
27 */
28 chunk(chunk&& other);
29
30 /**
31 * Moves the given chunk into this chunk.
32 * @param other The chunk to move into this chunk.
33 * @return Returns this chunk.
34 */
35 chunk& operator=(chunk&& other);
36
37 /**
38 * Gets the value of the chunk.
39 * @param resolution The aggregate resolution being resolved.
40 * @return Returns the value of the chunk.
41 */
42 leatherman::ruby::VALUE value(aggregate_resolution& resolution);
43
44 /**
45 * Gets the chunk's dependencies.
46 * @return Returns the chunk's dependencies.
47 */
48 leatherman::ruby::VALUE dependencies() const;
49
50 /**
51 * Sets the chunk's dependencies.
52 * @param dependencies The chunk's dependencies.
53 */
54 void dependencies(leatherman::ruby::VALUE dependencies);
55
56 /**
57 * Gets the chunk's block.
58 * @return Returns the chunk's block.
59 */
60 leatherman::ruby::VALUE block() const;
61
62 /**
63 * Sets the chunk's block.
64 * @param block The chunk's block.
65 */
66 void block(leatherman::ruby::VALUE block);
67
68 private:
69 chunk(chunk const&) = delete;
70 chunk& operator=(chunk const&) = delete;
71 void mark() const;
72
73 friend struct aggregate_resolution;
74
75 leatherman::ruby::VALUE _dependencies;
76 leatherman::ruby::VALUE _block;
77 leatherman::ruby::VALUE _value;
78 bool _resolved;
79 bool _resolving;
80 };
81
82 }} // namespace facter::ruby
+0
-60
lib/inc/internal/ruby/confine.hpp less more
0 /**
1 * @file
2 * Declares the class for Ruby fact confines.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7 #include <string>
8 #include <vector>
9
10 namespace facter { namespace ruby {
11
12 struct module;
13
14 /**
15 * Represents a Ruby API confine.
16 */
17 struct confine
18 {
19 /**
20 * Confines a fact resolution based on a fact name and vector of expected values.
21 * @param fact The fact name to confine to. Can be nil if a block is given.
22 * @param expected The expected value or values for the given fact. Can be nil if no fact given.
23 * @param block The block to call for the confine. Can be nil.
24 */
25 confine(leatherman::ruby::VALUE fact, leatherman::ruby::VALUE expected, leatherman::ruby::VALUE block);
26
27 /**
28 * Moves the given confine into this confine.
29 * @param other The confine to move into this confine.
30 */
31 confine(confine&& other);
32
33 /**
34 * Moves the given confine into this confine.
35 * @param other The confine to move into this confine.
36 * @return Returns this confine.
37 */
38 confine& operator=(confine&& other);
39
40 /**
41 * Determines if the confine is suitable or not.
42 * @param facter The Ruby Facter module to resolve facts with.
43 * @return Returns true if the confine is suitable or false if it is not.
44 */
45 bool suitable(module& facter) const;
46
47 private:
48 confine(confine const&) = delete;
49 confine& operator=(confine const&) = delete;
50 void mark() const;
51
52 friend struct resolution;
53
54 leatherman::ruby::VALUE _fact;
55 leatherman::ruby::VALUE _expected;
56 leatherman::ruby::VALUE _block;
57 };
58
59 }} // namespace facter::ruby
+0
-108
lib/inc/internal/ruby/fact.hpp less more
0 /**
1 * @file
2 * Declares the Ruby Facter::Util::Fact class.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7 #include "resolution.hpp"
8 #include <vector>
9 #include <memory>
10
11 namespace facter { namespace facts {
12
13 struct value;
14
15 }} // namespace facter::facts
16
17 namespace facter { namespace ruby {
18
19 struct module;
20
21 /**
22 * Represents the Ruby Facter::Util::Fact class.
23 */
24 struct fact
25 {
26 /**
27 * Defines the Facter::Util::Fact class.
28 * @return Returns the Facter::Util::Fact class.
29 */
30 static leatherman::ruby::VALUE define();
31
32 /**
33 * Creates an instance of the Facter::Util::Fact class.
34 * @param name The name of the fact.
35 * @return Returns the new instance.
36 */
37 static leatherman::ruby::VALUE create(leatherman::ruby::VALUE name);
38
39 /**
40 * Gets the name of the fact.
41 * @return Returns the name of the fact.
42 */
43 leatherman::ruby::VALUE name() const;
44
45 /**
46 * Gets the value of the fact.
47 * @return Returns the value of the fact.
48 */
49 leatherman::ruby::VALUE value();
50
51 /**
52 * Sets the value of the fact.
53 * @param v The value of the fact.
54 */
55 void value(leatherman::ruby::VALUE v);
56
57 /**
58 * Finds a resolution.
59 * @param name The name of the resolution.
60 * @return Returns the resolution or nil if the resolution was not found.
61 */
62 leatherman::ruby::VALUE find_resolution(leatherman::ruby::VALUE name) const;
63
64 /**
65 * Defines a resolution.
66 * @param name The name of the resolution.
67 * @param options The resolution options.
68 * @return Returns the resolution instance.
69 */
70 leatherman::ruby::VALUE define_resolution(leatherman::ruby::VALUE name, leatherman::ruby::VALUE options);
71
72 /**
73 * Flushes all resolutions for the fact and resets the value.
74 */
75 void flush();
76
77 private:
78 // Construction and assignment
79 fact();
80 fact(fact const&) = delete;
81 fact& operator=(fact const&) = delete;
82 fact(fact&& other) = delete;
83 fact& operator=(fact&& other) = delete;
84
85 // Ruby lifecycle functions
86 static leatherman::ruby::VALUE alloc(leatherman::ruby::VALUE klass);
87 static void mark(void* data);
88 static void free(void* data);
89
90 // Methods called from Ruby
91 static leatherman::ruby::VALUE ruby_initialize(leatherman::ruby::VALUE self, leatherman::ruby::VALUE name);
92 static leatherman::ruby::VALUE ruby_name(leatherman::ruby::VALUE self);
93 static leatherman::ruby::VALUE ruby_value(leatherman::ruby::VALUE self);
94 static leatherman::ruby::VALUE ruby_resolution(leatherman::ruby::VALUE self, leatherman::ruby::VALUE name);
95 static leatherman::ruby::VALUE ruby_define_resolution(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
96 static leatherman::ruby::VALUE ruby_flush(leatherman::ruby::VALUE self);
97
98 leatherman::ruby::VALUE _self;
99 leatherman::ruby::VALUE _name;
100 leatherman::ruby::VALUE _value;
101 std::vector<leatherman::ruby::VALUE> _resolutions;
102 bool _resolved;
103 bool _resolving;
104 size_t _weight;
105 };
106
107 }} // namespace facter::ruby
+0
-165
lib/inc/internal/ruby/module.hpp less more
0 /**
1 * @file
2 * Declares the Ruby Facter module.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7 #include <boost/program_options.hpp>
8 #include "fact.hpp"
9 #include <map>
10 #include <set>
11 #include <string>
12
13 namespace facter { namespace facts {
14
15 struct collection;
16
17 }} // namespace facter::facts
18
19 namespace leatherman { namespace logging {
20
21 enum class log_level;
22
23 }} // namespace leatherman::logging
24
25 namespace facter { namespace ruby {
26
27 /**
28 * Represents the Ruby Facter module.
29 */
30 struct module
31 {
32 /**
33 * Constructs the Ruby Facter module.
34 * @param facts The collection of facts to populate.
35 * @param paths The search paths for loading custom facts.
36 * @param logging_hooks True if the logging hooks should be defined in the Facter API or false if not.
37 */
38 module(facter::facts::collection& facts, std::vector<std::string> const& paths = {}, bool logging_hooks = true);
39
40 /**
41 * Destructs the Facter module.
42 */
43 ~module();
44
45 /**
46 * Add additional search paths for ruby custom facts
47 * @param paths The search paths for loading custom facts
48 */
49 void search(std::vector<std::string> const& paths);
50
51 /**
52 * Loads all custom facts.
53 */
54 void load_facts();
55
56 /**
57 * Resolves all custom facts.
58 */
59 void resolve_facts();
60
61 /**
62 * Clears the facts.
63 * @param clear_collection True if the underlying collection should be cleared or false if not.
64 */
65 void clear_facts(bool clear_collection = true);
66
67 /**
68 * Gets the value of the given fact name.
69 * @param name The name of the fact to get the value of.
70 * @return Returns the fact's value or nil if the fact isn't found.
71 */
72 leatherman::ruby::VALUE fact_value(leatherman::ruby::VALUE name);
73
74 /**
75 * Converts the given value to a corresponding Ruby object.
76 * @param val The value to convert.
77 * @return Returns a Ruby object for the value.
78 */
79 leatherman::ruby::VALUE to_ruby(facter::facts::value const* val) const;
80
81 /**
82 * Normalizes the given fact name.
83 * @param name The fact name to normalize.
84 * @return Returns the normalized fact name.
85 */
86 leatherman::ruby::VALUE normalize(leatherman::ruby::VALUE name) const;
87
88 /**
89 * Gets the collection associated with the module.
90 * @return Returns the collection associated with the Facter module.
91 */
92 facter::facts::collection& facts();
93
94 /**
95 * Gets the module's self.
96 * @return Returns the module's self.
97 */
98 leatherman::ruby::VALUE self() const;
99
100 /**
101 * Gets the current module.
102 * @return Returns the current module.
103 */
104 static module* current();
105
106 private:
107 // Methods called from Ruby
108 static leatherman::ruby::VALUE ruby_version(leatherman::ruby::VALUE self);
109 static leatherman::ruby::VALUE ruby_add(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
110 static leatherman::ruby::VALUE ruby_define_fact(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
111 static leatherman::ruby::VALUE ruby_value(leatherman::ruby::VALUE self, leatherman::ruby::VALUE name);
112 static leatherman::ruby::VALUE ruby_fact(leatherman::ruby::VALUE self, leatherman::ruby::VALUE name);
113 static leatherman::ruby::VALUE ruby_debug(leatherman::ruby::VALUE self, leatherman::ruby::VALUE message);
114 static leatherman::ruby::VALUE ruby_debugonce(leatherman::ruby::VALUE self, leatherman::ruby::VALUE message);
115 static leatherman::ruby::VALUE ruby_warn(leatherman::ruby::VALUE self, leatherman::ruby::VALUE message);
116 static leatherman::ruby::VALUE ruby_warnonce(leatherman::ruby::VALUE self, leatherman::ruby::VALUE message);
117 static leatherman::ruby::VALUE ruby_log_exception(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
118 static leatherman::ruby::VALUE ruby_set_debugging(leatherman::ruby::VALUE self, leatherman::ruby::VALUE value);
119 static leatherman::ruby::VALUE ruby_get_debugging(leatherman::ruby::VALUE self);
120 static leatherman::ruby::VALUE ruby_set_trace(leatherman::ruby::VALUE self, leatherman::ruby::VALUE value);
121 static leatherman::ruby::VALUE ruby_get_trace(leatherman::ruby::VALUE self);
122 static leatherman::ruby::VALUE ruby_flush(leatherman::ruby::VALUE self);
123 static leatherman::ruby::VALUE ruby_list(leatherman::ruby::VALUE self);
124 static leatherman::ruby::VALUE ruby_to_hash(leatherman::ruby::VALUE self);
125 static leatherman::ruby::VALUE ruby_each(leatherman::ruby::VALUE self);
126 static leatherman::ruby::VALUE ruby_clear(leatherman::ruby::VALUE self);
127 static leatherman::ruby::VALUE ruby_reset(leatherman::ruby::VALUE self);
128 static leatherman::ruby::VALUE ruby_loadfacts(leatherman::ruby::VALUE self);
129 static leatherman::ruby::VALUE ruby_search(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
130 static leatherman::ruby::VALUE ruby_search_path(leatherman::ruby::VALUE self);
131 static leatherman::ruby::VALUE ruby_search_external(leatherman::ruby::VALUE self, leatherman::ruby::VALUE paths);
132 static leatherman::ruby::VALUE ruby_search_external_path(leatherman::ruby::VALUE self);
133 static leatherman::ruby::VALUE ruby_which(leatherman::ruby::VALUE self, leatherman::ruby::VALUE binary);
134 static leatherman::ruby::VALUE ruby_exec(leatherman::ruby::VALUE self, leatherman::ruby::VALUE command);
135 static leatherman::ruby::VALUE ruby_execute(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
136 static leatherman::ruby::VALUE ruby_on_message(leatherman::ruby::VALUE self);
137
138 // Helper functions
139 static module* from_self(leatherman::ruby::VALUE self);
140 static leatherman::ruby::VALUE execute_command(std::string const& command, leatherman::ruby::VALUE failure_default, bool raise, uint32_t timeout = 0, bool expand = true);
141
142 void initialize_search_paths(std::vector<std::string> const& paths);
143 leatherman::ruby::VALUE load_fact(leatherman::ruby::VALUE value);
144 void load_file(std::string const& path);
145 leatherman::ruby::VALUE create_fact(leatherman::ruby::VALUE name);
146 static leatherman::ruby::VALUE level_to_symbol(leatherman::logging::log_level level);
147
148 boost::program_options::variables_map _config_file_settings;
149 facter::facts::collection& _collection;
150 std::map<std::string, leatherman::ruby::VALUE> _facts;
151 std::set<std::string> _debug_messages;
152 std::set<std::string> _warning_messages;
153 std::vector<std::string> _search_paths;
154 std::vector<std::string> _additional_search_paths;
155 std::vector<std::string> _external_search_paths;
156 std::set<std::string> _loaded_files;
157 bool _loaded_all;
158 leatherman::ruby::VALUE _self;
159 leatherman::ruby::VALUE _on_message_block;
160
161 static std::map<leatherman::ruby::VALUE, module*> _instances;
162 };
163
164 }} // namespace facter::ruby
+0
-126
lib/inc/internal/ruby/resolution.hpp less more
0 /**
1 * @file
2 * Declares the base class for Ruby resolution classes.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7 #include "confine.hpp"
8 #include <vector>
9 #include <memory>
10
11 namespace facter { namespace facts {
12
13 struct value;
14
15 }} // namespace facter::facts
16
17 namespace facter { namespace ruby {
18
19 struct module;
20
21 /**
22 * The base class for Ruby resolution classes.
23 */
24 struct resolution
25 {
26 /**
27 * Gets the name of the resolution.
28 * @return Returns the name of the resolution or nil if the resolution has no name.
29 */
30 leatherman::ruby::VALUE name() const;
31
32 /**
33 * Sets the name of the resolution.
34 * @param name The name of the resolution.
35 */
36 void name(leatherman::ruby::VALUE name);
37
38 /**
39 * Gets the weight of the resolution.
40 * The higher the weight value, the more precedence is given to the resolution.
41 * @return Returns the weight of the resolution.
42 */
43 size_t weight() const;
44
45 /**
46 * Sets the weight of the resolution.
47 * @param weight The weight of the resolution.
48 */
49 void weight(size_t weight);
50
51 /**
52 * Gets the value of the resolution.
53 * @return Returns the value of the resolution or nil if the value did not resolve.
54 */
55 virtual leatherman::ruby::VALUE value();
56
57 /**
58 * Sets the value of the resolution.
59 * @param v The value of the resolution.
60 */
61 void value(leatherman::ruby::VALUE v);
62
63 /**
64 * Determines if the resolution is suitable.
65 * @param facter The Ruby facter module to resolve facts with.
66 * @returns Returns true if the resolution is allowed or false if it is not.
67 */
68 bool suitable(module& facter) const;
69
70 /**
71 * Confines the resolution.
72 * @param confines The confines for the resolution.
73 */
74 void confine(leatherman::ruby::VALUE confines);
75
76 /**
77 * Calls the on_flush block for the resolution, if there is one.
78 */
79 void flush() const;
80
81 protected:
82 /**
83 * Constructs the resolution.
84 */
85 resolution();
86
87 /**
88 * Destructs the resolution.
89 */
90 virtual ~resolution();
91
92 /**
93 * Defines the base methods on the given class.
94 * @param klass The Ruby class to define the base methods on.
95 */
96 static void define(leatherman::ruby::VALUE klass);
97
98 /**
99 * Called to mark this object's values during GC.
100 */
101 void mark() const;
102
103 private:
104 // Construction and assignment
105 resolution(resolution const&) = delete;
106 resolution& operator=(resolution const&) = delete;
107 resolution(resolution&& other) = delete;
108 resolution& operator=(resolution&& other) = delete;
109
110 // Methods called from Ruby
111 static leatherman::ruby::VALUE ruby_confine(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
112 static leatherman::ruby::VALUE ruby_has_weight(leatherman::ruby::VALUE self, leatherman::ruby::VALUE value);
113 static leatherman::ruby::VALUE ruby_name(leatherman::ruby::VALUE self);
114 static leatherman::ruby::VALUE ruby_timeout(leatherman::ruby::VALUE self, leatherman::ruby::VALUE timeout);
115 static leatherman::ruby::VALUE ruby_on_flush(leatherman::ruby::VALUE self);
116
117 leatherman::ruby::VALUE _name;
118 leatherman::ruby::VALUE _value;
119 leatherman::ruby::VALUE _flush_block;
120 std::vector<ruby::confine> _confines;
121 bool _has_weight;
122 size_t _weight;
123 };
124
125 }} // namespace facter::ruby
+0
-119
lib/inc/internal/ruby/ruby_value.hpp less more
0 /**
1 * @file
2 * Declares the Ruby fact value.
3 */
4 #pragma once
5
6 #include <leatherman/ruby/api.hpp>
7 #include "fact.hpp"
8 #include <facter/facts/value.hpp>
9
10 #include <unordered_map>
11
12 namespace facter { namespace ruby {
13
14 /**
15 * Represents a value for a Ruby fact.
16 */
17 struct ruby_value : facter::facts::value
18 {
19 /**
20 * Constructs a ruby_value.
21 * @param value The Ruby value.
22 */
23 ruby_value(leatherman::ruby::VALUE value);
24
25 /**
26 * Destructs a ruby_value.
27 */
28 ~ruby_value();
29
30 /**
31 * Prevents the ruby_value from being copied.
32 */
33 ruby_value(ruby_value const&) = delete;
34
35 /**
36 * Prevents the ruby_value from being copied.
37 * @returns Returns this ruby_value.
38 */
39 ruby_value& operator=(ruby_value const&) = delete;
40
41 /**
42 * Moves the given ruby_value into this ruby_value.
43 * @param other The ruby_value to move into this ruby_value.
44 */
45 ruby_value(ruby_value&& other);
46
47 /**
48 * Moves the given ruby_value into this ruby_value.
49 * @param other The ruby_value to move into this ruby_value.
50 * @return Returns this ruby_value.
51 */
52 ruby_value& operator=(ruby_value&& other);
53
54 /**
55 * Converts the value to a JSON value.
56 * @param allocator The allocator to use for creating the JSON value.
57 * @param value The returned JSON value.
58 */
59 virtual void to_json(facts::json_allocator& allocator, facts::json_value& value) const override;
60
61 /**
62 * Writes the value to the given stream.
63 * @param os The stream to write to.
64 * @param quoted True if string values should be quoted or false if not.
65 * @param level The current indentation level.
66 * @returns Returns the stream being written to.
67 */
68 virtual std::ostream& write(std::ostream& os, bool quoted = true, unsigned int level = 1) const override;
69
70 /**
71 * Writes the value to the given YAML emitter.
72 * @param emitter The YAML emitter to write to.
73 * @returns Returns the given YAML emitter.
74 */
75 virtual YAML::Emitter& write(YAML::Emitter& emitter) const override;
76
77 /**
78 * Gets the Ruby value.
79 * @return Returns the Ruby value.
80 */
81 leatherman::ruby::VALUE value() const;
82
83 /**
84 * Exposes an owned ruby VALUE as a facter ruby_value
85 * @param child the owned child object to wrap
86 * @param key the query string for the child
87 * @return pointer to the ruby_value wrapper for the child object
88 */
89 ruby_value const* wrap_child(leatherman::ruby::VALUE child, std::string key) const;
90
91 /**
92 * Get a cached ruby_value wrapper for a child VALUE
93 * @param key the query string for the child
94 * @return pointer to the ruby_value wrapper, or nullptr if none exists
95 */
96 ruby_value const* child(const std::string& key) const;
97
98 private:
99 static void to_json(leatherman::ruby::api const& ruby, leatherman::ruby::VALUE value, facts::json_allocator& allocator, facts::json_value& json);
100 static void write(leatherman::ruby::api const& ruby, leatherman::ruby::VALUE value, std::ostream& os, bool quoted, unsigned int level);
101 static void write(leatherman::ruby::api const& ruby, leatherman::ruby::VALUE value, YAML::Emitter& emitter);
102
103 leatherman::ruby::VALUE _value;
104
105 // This is mutable because of constness that's passed down
106 // from the collection object during dot-syntax fact
107 // querying. That query is const over the collection, which
108 // means the collection's actions on values also need to be
109 // const. Unfortunately, we need a place to stash the owning
110 // pointer to the new value that we extract from a ruby object
111 // during lookup. The logical place for that ownership is the
112 // parent ruby object (right here). So we get this cool
113 // mutable field that owns the C++ wrappers for looked-up ruby
114 // values.
115 mutable std::unordered_map<std::string, std::unique_ptr<ruby_value>> _children;
116 };
117
118 }} // namespace facter::ruby
+0
-56
lib/inc/internal/ruby/simple_resolution.hpp less more
0 /**
1 * @file
2 * Declares the Ruby Facter::Util::Resolution class.
3 */
4 #pragma once
5
6 #include "resolution.hpp"
7
8 namespace facter { namespace ruby {
9
10 /**
11 * Represents the Ruby Facter::Util::Resolution class.
12 */
13 struct simple_resolution : resolution
14 {
15 /**
16 * Defines the Facter::Util::Resolution class.
17 * @return Returns theFacter::Util::Resolution class.
18 */
19 static leatherman::ruby::VALUE define();
20
21 /**
22 * Creates an instance of the Facter::Util::Resolution class.
23 * @return Returns the new instance.
24 */
25 static leatherman::ruby::VALUE create();
26
27 /**
28 * Gets the value of the resolution.
29 * @return Returns the value of the resolution or nil if the value did not resolve.
30 */
31 virtual leatherman::ruby::VALUE value();
32
33 private:
34 // Construction and assignment
35 simple_resolution();
36 simple_resolution(simple_resolution const&) = delete;
37 simple_resolution& operator=(simple_resolution const&) = delete;
38 simple_resolution(simple_resolution&& other) = delete;
39 simple_resolution& operator=(simple_resolution&& other) = delete;
40
41 // Ruby lifecycle functions
42 static leatherman::ruby::VALUE alloc(leatherman::ruby::VALUE klass);
43 static void mark(void* data);
44 static void free(void* data);
45
46 static leatherman::ruby::VALUE ruby_setcode(int argc, leatherman::ruby::VALUE* argv, leatherman::ruby::VALUE self);
47 static leatherman::ruby::VALUE ruby_which(leatherman::ruby::VALUE self, leatherman::ruby::VALUE binary);
48 static leatherman::ruby::VALUE ruby_exec(leatherman::ruby::VALUE self, leatherman::ruby::VALUE command);
49
50 leatherman::ruby::VALUE _self;
51 leatherman::ruby::VALUE _block;
52 leatherman::ruby::VALUE _command;
53 };
54
55 }} // namespace facter::ruby
+0
-40
lib/inc/internal/util/agent.hpp less more
0 /**
1 * @file
2 * Declares methods for Facter's integration with the puppet-agent package.
3 */
4 #pragma once
5
6 #include <leatherman/execution/execution.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <string>
9
10 namespace facter { namespace util { namespace agent {
11 /**
12 * Looks for an executable in Facter's built-in PATH. Falls back
13 * to the system PATH (with a warning) if the requested executable
14 * doesn't exist in Facter's PATH.
15 * @param exe the name of the executable
16 * @return the path to the executable
17 */
18 inline std::string which(const std::string& exe) {
19 #ifdef FACTER_PATH
20 std::string fixed = leatherman::execution::which(exe, {FACTER_PATH}, true);
21 if (!fixed.empty()) {
22 return fixed;
23 }
24 LOG_WARNING("{1} not found at configured location {2}, using PATH instead", exe, FACTER_PATH);
25 #endif
26 return exe;
27 }
28
29 inline std::string which(const std::string& exe, bool expand) {
30 #ifdef FACTER_PATH
31 std::string fixed = leatherman::execution::which(exe, {FACTER_PATH}, expand);
32 if (!fixed.empty()) {
33 return fixed;
34 }
35 LOG_WARNING("{1} not found at configured location {2}, using PATH instead, parameter expand is {3}", exe, FACTER_PATH, expand);
36 #endif
37 return exe;
38 }
39 }}} // namespace facter::util::agent
+0
-392
lib/inc/internal/util/aix/odm.hpp less more
0 /**
1 * @file
2 * Declares an iterator interface for examining the AIX object data manager
3 */
4
5 #include <odmi.h>
6
7 #include <cstdint>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <leatherman/locale/locale.hpp>
12
13 namespace facter { namespace util { namespace aix {
14
15 /**
16 * Singleton representing the ODM subsystem.
17 * The ODM subsystem needs to be explicitly initialized and terminated when
18 * it is used. This wraps that initialization/cleanup such that ODM is
19 * initialized when it is first used, and cleaned up when all consumers
20 * have released their references to it.
21 *
22 * some helper methods for interacting with the ODM are also defined here,
23 * since we can't have a `namespace odm` here.
24 */
25 class odm {
26 public:
27 /// shared_ptr to the odm reference
28 using ptr = std::shared_ptr<odm>;
29
30 /**
31 * Grab a reference to the ODM subsystem to keep it open.
32 * If no references currently exist, this will initialize the ODM
33 * subsystem.
34 * @return
35 */
36 static ptr open() {
37 static std::weak_ptr<odm> self;
38
39 auto result = self.lock();
40 if (!result) {
41 result = ptr(new odm);
42 self = result;
43 }
44 return result;
45 }
46
47 /// deleted copy constructor
48 odm(const odm&) = delete;
49
50 /// deleted assignment operator
51 /// @return nothing
52 odm& operator=(const odm&) = delete;
53
54 /**
55 * Clean up the odm library when there are no more users
56 */
57 ~odm() {
58 odm_terminate();
59 }
60
61 /**
62 * Get the error string for an ODM error state.
63 * @return an error string owned by the ODM subsystem
64 */
65 static std::string error_string() {
66 char* msg;
67 int result = odm_err_msg(odmerrno, &msg);
68 if (result < 0) {
69 return leatherman::locale::format("failed to retrieve ODM error message");
70 } else {
71 return std::string(msg);
72 }
73 }
74
75 private:
76 /**
77 * Initialize the ODM library
78 */
79 odm() {
80 if (odm_initialize() < 0) {
81 throw std::runtime_error(odm::error_string());
82 }
83 }
84 };
85
86 /**
87 * This represents an ODM class as an iterable thing. An ODM
88 * class can be throught of as a table in a typical database.
89 * Each templatized version of this has a singleton instance,
90 * which represents a process' handle to that table. Each process
91 * can open a single ODM class only once, and concurrent
92 * operations are not supported.
93 *
94 * @tparam T the struct that is stored in the ODM class.
95 */
96 template <typename T>
97 class odm_class : public std::enable_shared_from_this<odm_class<T>> {
98 public:
99 /// shared_ptr to an odm_class
100 using ptr = std::shared_ptr<odm_class<T>>;
101
102 /**
103 * Implements the standard C++ iterator interface for an
104 * odm_class. Iterators can be incremented and compared for
105 * inequality. This is the minimum to make range-based for
106 * loops work.
107 *
108 * Additional members may need to be added to make other
109 * algorithms play nice.
110 */
111 class iterator {
112 public:
113 /**
114 * inequality comparison.
115 * @param rhs the other iterator to compare to
116 * @return true if the iterators are not equal
117 */
118 bool operator != (const iterator& rhs) {
119 return _owner != rhs._owner || _data != rhs._data;
120 }
121
122 /**
123 * equality comparison.
124 * @param rhs the other iterator to compare to
125 * @return true if the iterators are equal
126 */
127 bool operator == (const iterator& rhs) {
128 // We could do !(*this != rhs) here, but it's better to inline
129 // the expression for performance reasons. It's simple enough.
130 return _owner == rhs._owner && _data == rhs._data;
131 }
132
133 /**
134 * pre-increment operator. This invalidates any
135 * references held to the current data of this iterator.
136 * @return the new value of the iterator
137 */
138 iterator& operator++() {
139 if (!_data || !_owner) {
140 return *this;
141 }
142 free(_data);
143 _data = static_cast<T*>(odm_get_next(_owner->_class, nullptr));
144 // If data == nullptr, we have reached the end of this query
145 if (!_data) {
146 _owner->_locked = false;
147 _owner = ptr(nullptr);
148 }
149 if ((intptr_t)_data < 0) {
150 throw std::runtime_error(odm::error_string());
151 }
152 return *this;
153 }
154
155 /**
156 * dereference operator
157 * @return a reference to the held ODM data structure
158 */
159 const T& operator*() const{
160 return *_data;
161 }
162
163 /**
164 * arrow operator
165 * @return a pointer to the held ODM data structure
166 */
167 const T* operator->() const{
168 return _data;
169 }
170
171 /**
172 * Destructor. Frees any held ODM data.
173 */
174 ~iterator() {
175 if (_data) {
176 free(_data);
177 }
178 }
179
180 protected: // Constructor is protected so iterators must come from an odm_class<T> or its associated query_proxy
181 /**
182 * Construct an iterator from an odm_class ptr and the first ODM data pointer
183 * @param data the ODM data we point to
184 * @param owner the odm_class object that owns this iterator
185 */
186 iterator(T* data, ptr owner) : _data(data), _owner(owner) {
187 if (_data) {
188 if (!_owner) {
189 throw std::logic_error(leatherman::locale::format("Tried to construct an iterator with valid data but no owner. Naughty naughty."));
190 }
191 _owner->_locked = true;
192 } else {
193 // In theory nobody should be constructing us with
194 // null data and valid owner, but why take the risk?
195 _owner = ptr(nullptr);
196 }
197 }
198
199 private:
200 T *_data;
201 ptr _owner;
202
203 friend class odm_class::query_proxy;
204 friend class odm_class;
205 };
206 friend class iterator; // iterator is our friend so it can lock/unlock us.
207
208 /**
209 * A query_proxy instance represents a query of an ODM
210 * class. The proxy has begin and end methods to allow it to
211 * be used in context of a range-based for loop or other
212 * algorithm.
213 */
214 class query_proxy {
215 public:
216 /**
217 * Begin the actual ODM query. This locks the odm_class until all valid iterators from this query are destroyed.
218 * @return first iterator of the query
219 */
220 iterator begin() {
221 if (_owner->_locked) {
222 throw std::logic_error(leatherman::locale::format("Cannot iterate over the same ODM class concurrently"));
223 }
224 auto data = static_cast<T*>(odm_get_first(_owner->_class, const_cast<char*>(_query.c_str()), nullptr));
225 if ((intptr_t)data < 0) {
226 throw std::runtime_error(odm::error_string());
227 }
228 return iterator(data, _owner);
229 }
230
231 /// @return an end iterator
232 iterator end() {
233 return iterator(nullptr, nullptr);
234 }
235
236 protected:
237 /**
238 * Construct a query_proxy for an odm_class query
239 * @param query the query string
240 * @param owner the odm_class that owns this query
241 */
242 template <typename Arg>
243 query_proxy(Arg&& query, ptr owner) : _query(std::forward<Arg>(query)), _owner(owner) {}
244
245 private:
246 std::string _query;
247 ptr _owner;
248
249 friend class odm_class;
250 };
251 friend class query_proxy; // query proxy needs to know if we're locked so it can begin() properly.
252
253 /**
254 * This class exists purely to allow nicer syntax when
255 * iterating over an odm_class. Using the proxy, it's possible
256 * to use `.begin()` and `.end()`, isntead of `->begin()`.
257 */
258 class proxy {
259 public:
260 /**
261 * Begin iterating over the entire odm_class. This could
262 * potentially be MANY values, so use wisely. You
263 * probably want query(). This will lock the odm_class
264 * until iteration is complete or all valid iterators are
265 * destructed.
266 * @return an iterator
267 */
268 iterator begin() {
269 return _self->begin();
270 }
271
272 /**
273 * Get the end iterator. All end iterators are identical
274 * @return the end iterator
275 */
276 iterator end() {
277 return _self->end();
278 }
279
280 /**
281 * Begin a query. This does not look the odm_class until
282 * query_proxy::begin() is called.
283 * @param query the query string
284 * @return a query_proxy representing this query
285 */
286 template <typename Arg>
287 query_proxy query(Arg&& query) {
288 return _self->query(std::forward<Arg>(query));
289 }
290
291 protected:
292 /**
293 * Construct a proxy for an odm_class
294 * @param self The odm_class that we proxy for
295 */
296 proxy(ptr self) : _self(self) {}
297
298 private:
299 ptr _self;
300
301 friend class odm_class;
302 };
303 friend class proxy; // proxy needs to, well, proxy to us.
304
305 /**
306 * Get a reference to an odm_class named "name". This will
307 * open the class if it is not yet open. The class will be
308 * closed when all existing references are released.
309 * @param name The name of the ODM class. Usually the string form of T
310 * @return an odm_class::proxy object that represents the requested class
311 */
312 static proxy open(std::string name) {
313 static std::weak_ptr<odm_class<T>> self;
314
315 auto result = self.lock();
316 if (!result) {
317 result = ptr(new odm_class<T>(name));
318 self = result;
319 }
320 return proxy { result };
321 }
322
323 /// deleted default constructor
324 odm_class() = delete;
325
326 /// deleted copy constructor
327 odm_class(const odm_class&) = delete;
328
329 /// deleted assignment operator
330 /// @return nothing
331 odm& operator=(const odm&) = delete;
332
333 /**
334 * Releases an ODM class when there are no more users of it.
335 */
336 ~odm_class() {
337 odm_close_class(_class);
338 }
339
340 protected:
341 /**
342 * @see proxy::query
343 * @param query the query string
344 * @return a query_proxy representing the provided query
345 */
346 template <typename Arg>
347 query_proxy query(Arg&& query) {
348 return query_proxy(std::forward<Arg>(query), this->shared_from_this());
349 }
350
351 /**
352 * @see proxy::begin
353 * @return an iterator
354 */
355 iterator begin() {
356 return query("").begin();
357 }
358
359 /**
360 * @see proxy::end
361 * @return the end/invalid iterator
362 */
363 iterator end() {
364 return iterator(nullptr, nullptr);
365 }
366
367 private:
368 odm_class(std::string name) : _locked(false) {
369 _the_odm = odm::open();
370 _class = odm_mount_class(const_cast<char*>(name.c_str()));
371 if (reinterpret_cast<intptr_t>(_class) < 0) {
372 throw std::runtime_error(odm::error_string());
373 }
374 _class = odm_open_class_rdonly(_class);
375 if (reinterpret_cast<intptr_t>(_class) < 0) {
376 throw std::runtime_error(odm::error_string());
377 }
378 }
379
380 protected:
381 /// The ODM class that we are wrapping
382 CLASS_SYMBOL _class;
383
384 /// whether we are currently locked for iteration
385 bool _locked;
386
387 private:
388 odm::ptr _the_odm;
389 };
390
391 }}} // namespace facter::util::aix
+0
-139
lib/inc/internal/util/aix/vmount.hpp less more
0 /**
1 * @file
2 * Declares an iterator interface for examining AIX mountpoints
3 */
4
5 #include <leatherman/logging/logging.hpp>
6
7 // AIX doesn't properly wrap these function declarations as C-style
8 extern "C" {
9 #include <sys/vmount.h>
10 }
11
12 #include <cstdint>
13 #include <vector>
14
15 namespace facter { namespace util { namespace aix {
16
17 /**
18 * This wraps the AIX mntctl(MCTL_QUERY, ...) function in an
19 * iterator interface. Each item is a mountctl struct.
20 */
21 class mountctl {
22 public:
23 /**
24 * Implements the standard C++ iterator interface for mounted
25 * filesystems. Iterators can be incremented and compared for
26 * inequality. This is the minimum to make range-based for
27 * loops work.
28 *
29 * Additional members may need to be added to make other
30 * algorithms play nice.
31 */
32 class iterator {
33 mountctl* _parent;
34 size_t _idx;
35 struct vmount* _current;
36 protected:
37 /**
38 * Construct an iterator from a mountctl class and the given
39 * vmount object index.
40 * @param parent the mountctl instance that this iterator points to
41 * @param idx the index into the list of vmount instances
42 */
43 iterator(mountctl* parent, size_t idx)
44 : _parent(parent), _idx(0) {
45 _current = reinterpret_cast<struct vmount*>(_parent->_buffer.data());
46 while (_idx < idx) {
47 ++(*this);
48 }
49 }
50
51 public:
52 iterator() = delete;
53
54 /// Defaulted move constructor
55 iterator(iterator&&) = default;
56
57 /// Defaulted move assignment
58 /// @return myself
59 iterator& operator=(iterator&&) = default;
60
61 /**
62 * inequality comparison
63 * @param rhs the other iterator to compare us to
64 * @return true if the iterators are not equal
65 */
66 bool operator != (const iterator& rhs) {
67 return _parent != rhs._parent || _idx != rhs._idx;
68 }
69
70 /**
71 * pre-increment operator. This moves the iterator to
72 * the next position in the list of vmount structs
73 * @return A reference to this iterator after it has advanced.
74 */
75 iterator& operator++() {
76 // Only increment if we're not at the end
77 // thus, end()++ == end();
78 if (_idx != _parent->_count) {
79 uintptr_t pos = reinterpret_cast<uintptr_t>(_current);
80 pos += _current->vmt_length;
81 _current = reinterpret_cast<struct vmount*>(pos);
82 _idx++;
83 }
84 return *this;
85 }
86
87 /**
88 * dereference operator
89 * @return a reference to the held vmount structure
90 */
91 const struct vmount& operator*() const {
92 return *_current;
93 }
94
95 friend class mountctl;
96 };
97 friend class iterator; // iterator is our friend so it can access data
98
99 /**
100 * Query the mounted filesystems and return an iterable instance
101 */
102 mountctl() {
103 uint32_t buf_sz;
104 int result = mntctl(MCTL_QUERY, sizeof(uint32_t), reinterpret_cast<char*>(&buf_sz));
105 if (result < 0) {
106 throw std::system_error(errno, std::system_category());
107 }
108 LOG_DEBUG("Required space for mountpoints is {1}", buf_sz);
109
110 _buffer.reserve(buf_sz);
111 result = mntctl(MCTL_QUERY, buf_sz, _buffer.data());
112 if (result < 0) {
113 throw std::system_error(errno, std::system_category());
114 }
115 _count = result;
116 LOG_DEBUG("Got {1} mountpoints", _count);
117 }
118
119 /// Start the iteration
120 /// @return an iterator that references me. I must outlive this iterator
121 iterator begin() {
122 return iterator(this, 0);
123 }
124
125 /// Retrieve iterator sentinel value
126 /// @return an iterator that references me. I must outlive this iterator
127 iterator end() {
128 return iterator(this, _count);
129 }
130
131 protected:
132 /// The data returned from mntctl. Used by the iterator
133 std::vector<char> _buffer;
134
135 /// The number of elements returned from mntctl
136 size_t _count;
137 };
138 }}} // namespace facter::util::aix
+0
-33
lib/inc/internal/util/bsd/scoped_ifaddrs.hpp less more
0 /**
1 * @file
2 * Declares the scoped ifaddrs resource.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <ifaddrs.h>
8
9 namespace facter { namespace util { namespace bsd {
10
11 /**
12 * Represents a scoped ifaddrs pointer that automatically is freed when it goes out of scope.
13 */
14 struct scoped_ifaddrs : leatherman::util::scoped_resource<ifaddrs*>
15 {
16 /**
17 * Default constructor.
18 * This constructor will handle calling getifaddrs.
19 */
20 scoped_ifaddrs();
21
22 /**
23 * Constructs a scoped_descriptor.
24 * @param addrs The ifaddrs pointer to free when destroyed
25 */
26 explicit scoped_ifaddrs(ifaddrs* addrs);
27
28 private:
29 static void free(ifaddrs* addrs);
30 };
31
32 }}} // namespace facter::util::bsd
+0
-150
lib/inc/internal/util/freebsd/geom.hpp less more
0 /**
1 * @file
2 * Declares geom.
3 */
4 #pragma once
5
6 #include <stdexcept>
7 #include <string>
8 #include <vector>
9
10 #include <libgeom.h>
11
12 namespace facter { namespace util { namespace freebsd {
13
14 /**
15 * geom exceptions
16 */
17 struct geom_exception : std::runtime_error
18 {
19 /**
20 * Constructs a geom_exception.
21 * @param message the exception message.
22 */
23 explicit geom_exception(std::string const& message);
24 };
25
26 /**
27 * GEOM configuration.
28 * This is a wrapper for struct gconfig.
29 */
30 class geom_config {
31 private:
32 std::string _name;
33 std::string _value;
34 public:
35 /**
36 * Constructs a GEOM configuration item.
37 * @param name the name of the item.
38 * @param value the valure of the item.
39 */
40 geom_config(std::string name, std::string value);
41 /**
42 * Returns the name of the item.
43 * @return the name of the item.
44 */
45 std::string name();
46 /**
47 * Returns the value of the item.
48 * @return the value of the item.
49 */
50 std::string value();
51 };
52
53 /**
54 * Base GEOM class capable of storing configuration.
55 */
56 class geom_object_with_config {
57 private:
58 std::vector<geom_config> _configs;
59 protected:
60 /**
61 * Loads GEOM configuration.
62 * @param conf the first configuration item.
63 */
64 geom_object_with_config(struct gconf *conf);
65 public:
66 /**
67 * Fetches a configuration value from the object.
68 * @param name the name of the configuration to get.
69 * @return the value of the configuration.
70 */
71 std::string config(std::string name);
72 };
73
74 /**
75 * GEOM providers.
76 * This is a wrapper for struct gprovider.
77 */
78 class geom_provider : public geom_object_with_config {
79 private:
80 std::string _name;
81 std::string _mode;
82 off_t _mediasize;
83 u_int _sectorsize;
84 off_t _stripeoffset;
85 off_t _stripesize;
86 public:
87 /**
88 * Loads a GEOM provider.
89 * @param provider the provider to load.
90 */
91 geom_provider(struct gprovider* provider);
92 /**
93 * Returns the provider name.
94 * @return the name of the provider.
95 */
96 std::string name();
97 /**
98 * Returns the provider media size.
99 * @return the media size in bytes.
100 */
101 off_t mediasize();
102 };
103
104 /**
105 * GEOM geoms (sic).
106 * This is a wrapper for struct ggeom.
107 */
108 class geom_geom : public geom_object_with_config {
109 private:
110 std::string _name;
111 public:
112 /**
113 * Loads a GEOM Geom.
114 * @param geom the Geom to load.
115 */
116 geom_geom(struct ggeom *geom);
117 /**
118 * Providers attached to this Geom.
119 */
120 std::vector<geom_provider> providers;
121 /**
122 * Returns the name of the Geom.
123 * @return the name of the Geom.
124 */
125 std::string name();
126 };
127
128 /**
129 * GEOM classes.
130 * This is a wrapper for struct gclass.
131 */
132 class geom_class {
133 private:
134 struct gmesh _mesh;
135 struct gclass *_class;
136 public:
137 /**
138 * Loads a GEOM class. Throws a geom_exception on failure.
139 * @param type the GEOM class to load.
140 */
141 geom_class(std::string type);
142 ~geom_class();
143 /**
144 * Geoms attached to this class.
145 */
146 std::vector<geom_geom> geoms;
147 };
148
149 }}} // namespace facter::util::freebsd
+0
-45
lib/inc/internal/util/posix/scoped_addrinfo.hpp less more
0 /**
1 * @file
2 * Declares the scoped addrinfo resource.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <netdb.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <string>
11 #include <cstring>
12
13 namespace facter { namespace util { namespace posix {
14
15 /**
16 * Represents a scoped addrinfo for POSIX systems.
17 * Automatically frees the address information pointer when it goes out of scope.
18 */
19 struct scoped_addrinfo : leatherman::util::scoped_resource<addrinfo*>
20 {
21 /**
22 * Constructs a scoped_addrinfo.
23 * @param hostname The hostname to get the address information of.
24 */
25 explicit scoped_addrinfo(std::string const& hostname);
26
27 /**
28 * Constructs a scoped_addrinfo.
29 * @param info The address info to free when destroyed.
30 */
31 explicit scoped_addrinfo(addrinfo* info);
32
33 /**
34 * Returns the result of any call to getaddrinfo.
35 * @returns Returns the result of any call to getaddrinfo.
36 */
37 int result() const;
38
39 private:
40 static void free(addrinfo* info);
41 int _result;
42 };
43
44 }}} // namespace facter::util::posix
+0
-27
lib/inc/internal/util/posix/scoped_descriptor.hpp less more
0 /**
1 * @file
2 * Declares the scoped descriptor resource for managing file/socket descriptors.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <unistd.h>
8
9 namespace facter { namespace util { namespace posix {
10 /**
11 * Represents a scoped file descriptor for POSIX systems.
12 * Automatically closes the file descriptor when it goes out of scope.
13 */
14 struct scoped_descriptor : leatherman::util::scoped_resource<int>
15 {
16 /**
17 * Constructs a scoped_descriptor.
18 * @param descriptor The file descriptor to close when destroyed.
19 */
20 explicit scoped_descriptor(int descriptor);
21
22 private:
23 static void close(int descriptor);
24 };
25
26 }}} // namespace facter::util::posix
+0
-57
lib/inc/internal/util/posix/utmpx_file.hpp less more
0 /**
1 * @file
2 * Declares an interface for querying the contents of the utmpx file
3 */
4 #pragma once
5
6 #include <utmpx.h>
7
8 namespace facter { namespace util { namespace posix {
9
10 /**
11 * Class representing a utmpx file. We create only one instance at a time since
12 * the utmpx API calls deal with global state. See https://linux.die.net/man/3/getutxid
13 * for the documentation.
14 */
15 class utmpx_file {
16 public:
17 /**
18 * Constructs a utmpx_file instance. We only do this if no other utmpx_file instance exists,
19 * which we can determine by querying the 'instance_exists' static variable. Otherwise,
20 * we throw an std::logic_error.
21 */
22 utmpx_file();
23
24 /// deleted copy constructor
25 utmpx_file(const utmpx_file&) = delete;
26
27 /// deleted assignment operator
28 /// @return nothing
29 utmpx_file& operator=(const utmpx_file&) = delete;
30
31 /**
32 * Destroys our utmpx_file instance. Here, we also set `instance_exists` to false so that another
33 * utmpx_file instance can be created.
34 */
35 ~utmpx_file();
36
37 /**
38 * Returns a pointer to the utmpx entry corresponding to the passed-in query. Make sure
39 * that the calling instance does not go out of scope after invoking this method, otherwise
40 * the data in the returned utmpx entry will be garbage. Note that this will move the
41 * underlying utmpx file pointer forward, so be sure to call reset() if you want subsequent
42 * calls to this routine to always start from the beginning of the utmpx file.
43 * @param query the utmpx query. See https://www.systutorials.com/docs/linux/man/5-utmpx/
44 * @return pointer to the utmpx entry satisfying the query
45 */
46 const utmpx* query(utmpx const& query) const;
47
48 /**
49 * Resets the utmpx file.
50 */
51 void reset() const;
52
53 private:
54 static bool instance_exists; // set to true if a utmpx_file instance exists, false otherwise
55 };
56 }}} // namespace facter::util::posix
+0
-34
lib/inc/internal/util/scoped_bio.hpp less more
0 /**
1 * @file
2 * Declares the scoped BIO (OpenSSL) resource.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <openssl/bio.h>
8
9 namespace facter { namespace util {
10
11 /**
12 * Represents a scoped OpenSSL BIO object.
13 * Automatically frees the BIO when it goes out of scope.
14 */
15 struct scoped_bio : leatherman::util::scoped_resource<BIO*>
16 {
17 /**
18 * Constructs a scoped_bio.
19 * @param method The BIO_METHOD to use.
20 */
21 explicit scoped_bio(const BIO_METHOD* method);
22
23 /**
24 * Constructs a scoped_bio.
25 * @param bio The BIO to free when destroyed.
26 */
27 explicit scoped_bio(BIO* bio);
28
29 private:
30 static void free(BIO* bio);
31 };
32
33 }} // namespace facter::util
+0
-36
lib/inc/internal/util/scoped_file.hpp less more
0 /**
1 * @file
2 * Declares the scoped file resource for managing FILE pointers.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <string>
8 #include <cstdio>
9
10 namespace facter { namespace util {
11
12 /**
13 * Represents a scoped file.
14 * Automatically closes the file when it goes out of scope.
15 */
16 struct scoped_file : leatherman::util::scoped_resource<std::FILE*>
17 {
18 /**
19 * Constructs a scoped_file.
20 * @param path The path to the file.
21 * @param mode The open mode.
22 */
23 explicit scoped_file(std::string const& path, std::string const& mode);
24
25 /**
26 * Constructs a scoped_file.
27 * @param file The existing file pointer.
28 */
29 explicit scoped_file(std::FILE* file);
30
31 private:
32 static void close(std::FILE* file);
33 };
34
35 }} // namespace facter::util
+0
-176
lib/inc/internal/util/solaris/k_stat.hpp less more
0 /**
1 * @file
2 * Declares the k_stat resource.
3 */
4 #pragma once
5
6 #include "scoped_kstat.hpp"
7 #include <vector>
8 #include <string>
9
10 namespace facter { namespace util { namespace solaris {
11
12 /**
13 * Wrapper around the kstat_t structure.
14 */
15 struct k_stat_entry
16 {
17 /**
18 * Default constructor. Should be created only by k_stat[] call.
19 * @param kp The kstat_t pointer we wrap.
20 *
21 */
22 k_stat_entry(kstat_t* kp);
23
24 /**
25 * Get a value out of our kstat_named_t
26 * @param attrib The attribute we are looking up
27 * @tparam T the datatype of the attribute result
28 * @return Returns the looked up value.
29 */
30 template <typename T>
31 T value(const std::string& attrib) const;
32
33 /**
34 * Gets the name of the entry.
35 * @return Returns the name of entry.
36 */
37 std::string name();
38
39 /**
40 * Gets the class of the entry.
41 * @return Returns the class of entry.
42 */
43 std::string klass();
44
45 /**
46 * Gets the module of the entry.
47 * @return Returns the module of entry.
48 */
49 std::string module();
50
51 /**
52 * Gets the instance of the entry.
53 * @return Returns the instance of entry.
54 */
55 int instance();
56
57 private:
58 /**
59 * Lookup the given attribute in the kstat structure.
60 * @param attrib The attribute we are looking up
61 * @return Returns the looked up value.
62 */
63 kstat_named_t* lookup(const std::string& attrib) const;
64
65 /**
66 * Lookup the given attribute in the kstat structure, and verify that
67 * the datatype is correct.
68 * @param datatype The datatype of attribute we are looking up
69 * @param attrib The attribute we are looking up
70 * @return Returns the looked up value.
71 */
72 kstat_named_t* lookup(const int datatype, const std::string& attrib) const;
73
74 /**
75 * The main data in the struct, obtained by a lookup
76 */
77 kstat_t* k_stat;
78 };
79
80 /**
81 * The template specializations for k_stat_entry::value with ulong_t.
82 * @param attrib The attribute we are looking up.
83 * @return Returns the looked up value.
84 */
85 template<>
86 ulong_t k_stat_entry::value(const std::string& attrib) const;
87
88 /**
89 * The template specializations for k_stat_entry::value with long.
90 * @param attrib The attribute we are looking up.
91 * @return Returns the looked up value.
92 */
93 template<>
94 long k_stat_entry::value(const std::string& attrib) const;
95
96 /**
97 * The template specializations for k_stat_entry::value with int32_t.
98 * @param attrib The attribute we are looking up.
99 * @return Returns the looked up value.
100 */
101 template<>
102 int32_t k_stat_entry::value(const std::string& attrib) const;
103
104 /**
105 * The template specializations for k_stat_entry::value with uint32_t.
106 * @param attrib The attribute we are looking up.
107 * @return Returns the looked up value.
108 */
109 template<>
110 uint32_t k_stat_entry::value(const std::string& attrib) const;
111
112 /**
113 * The template specializations for k_stat_entry::value with int64_t.
114 * @param attrib The attribute we are looking up.
115 * @return Returns the looked up value.
116 */
117 template<>
118 int64_t k_stat_entry::value(const std::string& attrib) const;
119
120 /**
121 * The template specializations for k_stat_entry::value with uint64_t.
122 * @param attrib The attribute we are looking up.
123 * @return Returns the looked up value.
124 */
125 template<>
126 uint64_t k_stat_entry::value(const std::string& attrib) const;
127
128 /**
129 * The template specializations for k_stat_entry::value with string.
130 * @param attrib The attribute we are looking up.
131 * @return Returns the looked up value.
132 */
133 template<>
134 std::string k_stat_entry::value(const std::string& attrib) const;
135
136 /**
137 * Wrapper around the kstat_ctl structure. It represents our
138 * link to kernel stats, and controls the lifetime of any kstat
139 * structures associated. (They go away when it is closed)
140 */
141 struct k_stat
142 {
143 /**
144 * Default constructor.
145 * This constructor will handle calling kstat_open.
146 */
147 k_stat();
148
149 /**
150 * Function for looking up a module.
151 * @param module The module name.
152 * @return Returns the vector containing all entries.
153 */
154 std::vector<k_stat_entry> operator[](std::string const& module);
155
156 /**
157 * Function for looking up a module, and an entry name.
158 * @param entry A pair containing module name an entry name.
159 * @return Returns the vector containing all entries.
160 */
161 std::vector<k_stat_entry> operator[](std::pair<std::string, std::string> const& entry);
162
163 /**
164 * Function for looking up a module, and an instance id.
165 * @param entry A pair containing module name an instance id.
166 * @return Returns the vector containing all entries.
167 */
168 std::vector<k_stat_entry> operator[](std::pair<std::string, int> const& entry);
169
170 private:
171 std::vector<k_stat_entry> lookup(std::string const& module, int instance, std::string const& name);
172 scoped_kstat ctrl;
173 };
174
175 }}} // namespace facter::util::solaris
+0
-45
lib/inc/internal/util/solaris/scoped_kstat.hpp less more
0 /**
1 * @file
2 * Declares the scoped kstat resource.
3 */
4 #pragma once
5
6 #include <leatherman/util/scoped_resource.hpp>
7 #include <kstat.h>
8
9 namespace facter { namespace util { namespace solaris {
10
11 /**
12 * kstat exceptions
13 */
14 struct kstat_exception : std::runtime_error
15 {
16 /**
17 * Constructs a kstat_exception.
18 * @param message The exception message.
19 */
20 explicit kstat_exception(std::string const& message);
21 };
22
23 /**
24 * Represents a scoped kstat pointer that automatically is freed when it goes out of scope.
25 */
26 struct scoped_kstat : leatherman::util::scoped_resource<kstat_ctl*>
27 {
28 /**
29 * Default constructor.
30 * This constructor will handle calling kstat_open.
31 */
32 scoped_kstat();
33
34 /**
35 * Constructs a scoped_descriptor.
36 * @param ctrl The kstat pointer to free when destroyed
37 */
38 explicit scoped_kstat(kstat_ctl* ctrl);
39
40 private:
41 static void close(kstat_ctl* ctrl);
42 };
43
44 }}} // namespace facter::util::solaris
+0
-34
lib/inc/internal/util/versions.hpp less more
0 /**
1 * @file
2 * Defines helpers for parsing various version strings
3 */
4 #pragma once
5
6 #include <string>
7 #include <tuple>
8
9 namespace facter { namespace util { namespace versions {
10 /**
11 * Helper function for parsing X.Y from an arbitrary version
12 * string. If there is a .Z component, it will be ignored.
13 * @param version the version string to parse
14 * @return A tuple of <maj, min>
15 */
16 inline std::tuple<std::string, std::string> major_minor(const std::string& version)
17 {
18 std::string major, minor;
19 auto pos = version.find('.');
20 if (pos != std::string::npos) {
21 auto second = version.find('.', pos+1);
22 decltype(second) end;
23 major = version.substr(0, pos);
24 if (second != std::string::npos) {
25 end = second - (pos + 1);
26 } else {
27 end = std::string::npos;
28 }
29 minor = version.substr(pos+1, end);
30 }
31 return std::make_tuple(std::move(major), std::move(minor));
32 }
33 }}} // namespace facter::util::versions
+0
-92
lib/inc/internal/util/windows/wsa.hpp less more
0 /**
1 * @file
2 * Declares utility functions for interacting with Winsock
3 */
4 #pragma once
5
6 #include <leatherman/windows/windows.hpp>
7 #include <string>
8 #include <stdexcept>
9
10 namespace facter { namespace util { namespace windows {
11
12 /**
13 * Exception thrown when wsa initialization fails.
14 */
15 struct wsa_exception : std::runtime_error
16 {
17 /**
18 * Constructs a wsa_exception.
19 * @param message The exception message.
20 */
21 explicit wsa_exception(std::string const& message);
22 };
23
24 /**
25 * A class for initiating use of the Winsock DLL, providing wrappers for WSA calls.
26 */
27 struct wsa
28 {
29 /**
30 * Initializes Winsock. Throws a wsa_exception on failure.
31 */
32 wsa();
33
34 /**
35 * Do WSA cleanup.
36 */
37 ~wsa();
38
39 /**
40 * Use default move constructer.
41 */
42 wsa(wsa&&) = default;
43
44 /**
45 * Use default move assignment.
46 * @return Returns this instance.
47 */
48 wsa& operator=(wsa&&) = default;
49
50 /**
51 * This wraps calling WSAAddressToString to translate a sockaddr structure to an IPv4 or IPv6 string.
52 * Throws an exception if passed an IPv6 argument on Windows Server 2003 and IPv6 support isn't installed.
53 * See https://social.technet.microsoft.com/Forums/windowsserver/en-US/7166dcbe-d493-4da1-8441-5b5d6aa0d21c/ipv6-and-windows-server-2003
54 * @param addr The socket address structure.
55 * @return An IPv4 or IPv6 string, or an empty string if addr is uninitialized.
56 */
57 std::string saddress_to_string(SOCKET_ADDRESS const& addr) const;
58
59 /**
60 * This adapts sockaddr structs to the SOCKET_ADDRESS wrapper.
61 * @param addr A sockaddr-like structure (sockaddr, sockaddr_in, sockaddr_in6).
62 * @return An IPv4 or IPv6 string.
63 */
64 template<typename T>
65 std::string address_to_string(T &addr) const
66 {
67 return saddress_to_string(SOCKET_ADDRESS{reinterpret_cast<sockaddr *>(&addr), sizeof(T)});
68 }
69
70 /**
71 * This wraps calling WSAStringToAddress to translate a an IPv4 or IPv6 string to a sockaddr structure.
72 * @tparam T The expected sockaddr structure, either sockaddr_in or sockaddr_in6.
73 * @tparam ADDRESS_FAMILY The expected address family, either AF_INET or AF_INET6.
74 * @param addr An IPv4 or IPv6 string.
75 * @return A sockaddr structure containing the IPv4 or IPv6 sockaddr data.
76 */
77 template<typename T, int ADDRESS_FAMILY>
78 T string_to_address(std::string const& addr) const
79 {
80 T sock = {ADDRESS_FAMILY};
81 string_fill_sockaddr(reinterpret_cast<sockaddr*>(&sock), addr, sizeof(T));
82 return sock;
83 }
84
85 private:
86 wsa(wsa const&) = delete;
87 wsa& operator=(wsa const&) = delete;
88 void string_fill_sockaddr(sockaddr *sock, std::string const& addr, int size) const;
89 };
90
91 }}} // namespace facter::util::windows
+0
-27
lib/inc/internal/util/yaml.hpp less more
0 /**
1 * @file
2 * Declares helper methods for dealing with YAML
3 */
4 #pragma once
5
6 #include <string>
7 #include <vector>
8
9 namespace YAML {
10 class Node;
11 }
12
13 namespace facter { namespace facts {
14 struct collection;
15 struct array_value;
16 struct map_value;
17 }}
18
19 namespace facter { namespace util { namespace yaml {
20 /**
21 * Adds a YAML value into a Facter collection.
22 */
23 void add_value(std::string const& name, YAML::Node const& node,
24 facts::collection& facts, std::vector<std::string>& names,
25 facts::array_value* array_parent = nullptr, facts::map_value* map_parent = nullptr);
26 }}}
+0
-2017
lib/schema/core_facts.pot less more
0 # CORE FACTS SCHEMA
1 # Copyright (C) 2016 Puppet, LLC
2 # This file is distributed under the same license as the FACTER package.
3 # FIRST AUTHOR <docs@puppet.com>, 2016.
4 #
5 #, fuzzy
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: FACTER \n"
9 "Report-Msgid-Bugs-To: docs@puppet.com\n"
10 "POT-Creation-Date: \n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language: \n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19
20
21 #. aio_agent_version description
22 msgid "Return the version of the puppet-agent package that installed facter."
23 msgstr ""
24
25 #. aio_agent_version resolution
26 msgid "All platforms: use the compile-time enabled version definition."
27 msgstr ""
28
29 #. architecture description
30 msgid "Return the operating system's hardware architecture."
31 msgstr ""
32
33 #. architecture resolution
34 msgid "POSIX platforms: use the `uname` function to retrieve the OS hardware architecture."
35 msgstr ""
36
37 #. architecture resolution
38 msgid "Windows: use the `GetNativeSystemInfo` function to retrieve the OS hardware architecture."
39 msgstr ""
40
41 #. architecture caveats
42 msgid "Linux: Debian, Gentoo, kFreeBSD, and Ubuntu use "amd64" for "x86_64" and Gentoo uses "x86" for "i386"."
43 msgstr ""
44
45 #. augeas description
46 msgid "Return information about augeas."
47 msgstr ""
48
49 #. augeas resolution
50 msgid "All platforms: query augparse for augeas metadata."
51 msgstr ""
52
53 #. augeasversion description
54 msgid "Return the version of augeas."
55 msgstr ""
56
57 #. augeasversion resolution
58 msgid "All platforms: query augparse for the augeas version."
59 msgstr ""
60
61 #. blockdevices description
62 msgid "Return a comma-separated list of block devices."
63 msgstr ""
64
65 #. blockdevices resolution
66 msgid "Linux: parse the contents of `/sys/block/<device>/`."
67 msgstr ""
68
69 #. blockdevices resolution
70 msgid "Solaris: use the `kstat` function to query disk information."
71 msgstr ""
72
73 #. blockdevices caveats
74 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
75 msgstr ""
76
77 #. blockdevice_<devicename>_model description
78 msgid "Return the model name of block devices attached to the system."
79 msgstr ""
80
81 #. blockdevice_<devicename>_model resolution
82 msgid "Linux: parse the contents of `/sys/block/<device>/device/model` to retrieve the model name/number for a device."
83 msgstr ""
84
85 #. blockdevice_<devicename>_model resolution
86 msgid "Solaris: use the `kstat` function to query disk information."
87 msgstr ""
88
89 #. blockdevice_<devicename>_model caveats
90 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
91 msgstr ""
92
93 #. blockdevice_<devicename>_size description
94 msgid "Return the size of a block device in bytes."
95 msgstr ""
96
97 #. blockdevice_<devicename>_size resolution
98 msgid "Linux: parse the contents of `/sys/block/<device>/size` to receive the size (multiplying by 512 to correct for blocks-to-bytes)."
99 msgstr ""
100
101 #. blockdevice_<devicename>_size resolution
102 msgid "Solaris: use the `kstat` function to query disk information."
103 msgstr ""
104
105 #. blockdevice_<devicename>_size caveats
106 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
107 msgstr ""
108
109 #. blockdevice_<devicename>_vendor description
110 msgid "Return the vendor name of block devices attached to the system."
111 msgstr ""
112
113 #. blockdevice_<devicename>_vendor resolution
114 msgid "Linux: parse the contents of `/sys/block/<device>/device/vendor` to retrieve the vendor for a device."
115 msgstr ""
116
117 #. blockdevice_<devicename>_vendor resolution
118 msgid "Solaris: use the `kstat` function to query disk information."
119 msgstr ""
120
121 #. blockdevice_<devicename>_vendor caveats
122 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
123 msgstr ""
124
125 #. bios_release_date description
126 msgid "Return the release date of the system BIOS."
127 msgstr ""
128
129 #. bios_release_date resolution
130 msgid "Linux: parse the contents of `/sys/class/dmi/id/bios_date` to retrieve the system BIOS release date."
131 msgstr ""
132
133 #. bios_release_date resolution
134 msgid "Solaris: use the `smbios` utility to retrieve the system BIOS release date."
135 msgstr ""
136
137 #. bios_release_date caveats
138 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
139 msgstr ""
140
141 #. bios_vendor description
142 msgid "Return the vendor of the system BIOS."
143 msgstr ""
144
145 #. bios_vendor resolution
146 msgid "Linux: parse the contents of `/sys/class/dmi/id/bios_vendor` to retrieve the system BIOS vendor."
147 msgstr ""
148
149 #. bios_vendor resolution
150 msgid "Solaris: use the `smbios` utility to retrieve the system BIOS vendor."
151 msgstr ""
152
153 #. bios_vendor caveats
154 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
155 msgstr ""
156
157 #. bios_version description
158 msgid "Return the version of the system BIOS."
159 msgstr ""
160
161 #. bios_version resolution
162 msgid "Linux: parse the contents of `/sys/class/dmi/id/bios_version` to retrieve the system BIOS version."
163 msgstr ""
164
165 #. bios_version resolution
166 msgid "Solaris: use the `smbios` utility to retrieve the system BIOS version."
167 msgstr ""
168
169 #. bios_version caveats
170 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
171 msgstr ""
172
173 #. boardassettag description
174 msgid "Return the system board asset tag."
175 msgstr ""
176
177 #. boardassettag resolution
178 msgid "Linux: parse the contents of `/sys/class/dmi/id/board_asset_tag` to retrieve the system board asset tag."
179 msgstr ""
180
181 #. boardassettag caveats
182 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
183 msgstr ""
184
185 #. boardmanufacturer description
186 msgid "Return the system board manufacturer."
187 msgstr ""
188
189 #. boardmanufacturer resolution
190 msgid "Linux: parse the contents of `/sys/class/dmi/id/board_vendor` to retrieve the system board manufacturer."
191 msgstr ""
192
193 #. boardmanufacturer caveats
194 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
195 msgstr ""
196
197 #. boardproductname description
198 msgid "Return the system board product name."
199 msgstr ""
200
201 #. boardproductname resolution
202 msgid "Linux: parse the contents of `/sys/class/dmi/id/board_name` to retrieve the system board product name."
203 msgstr ""
204
205 #. boardproductname caveats
206 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
207 msgstr ""
208
209 #. boardserialnumber description
210 msgid "Return the system board serial number."
211 msgstr ""
212
213 #. boardserialnumber resolution
214 msgid "Linux: parse the contents of `/sys/class/dmi/id/board_serial` to retrieve the system board serial number."
215 msgstr ""
216
217 #. boardserialnumber caveats
218 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
219 msgstr ""
220
221 #. chassisassettag description
222 msgid "Return the system chassis asset tag."
223 msgstr ""
224
225 #. chassisassettag resolution
226 msgid "Linux: parse the contents of `/sys/class/dmi/id/chassis_asset_tag` to retrieve the system chassis asset tag."
227 msgstr ""
228
229 #. chassisassettag resolution
230 msgid "Solaris: use the `smbios` utility to retrieve the system chassis asset tag."
231 msgstr ""
232
233 #. chassisassettag caveats
234 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
235 msgstr ""
236
237 #. chassistype description
238 msgid "Return the system chassis type."
239 msgstr ""
240
241 #. chassistype resolution
242 msgid "Linux: parse the contents of `/sys/class/dmi/id/chassis_type` to retrieve the system chassis type."
243 msgstr ""
244
245 #. chassistype resolution
246 msgid "Solaris: use the `smbios` utility to retrieve the system chassis type."
247 msgstr ""
248
249 #. chassistype caveats
250 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
251 msgstr ""
252
253 #. dhcp_servers description
254 msgid "Return the DHCP servers for the system."
255 msgstr ""
256
257 #. dhcp_servers resolution
258 msgid "Linux: parse `dhclient` lease files or use the `dhcpcd` utility to retrieve the DHCP servers."
259 msgstr ""
260
261 #. dhcp_servers resolution
262 msgid "Mac OSX: use the `ipconfig` utility to retrieve the DHCP servers."
263 msgstr ""
264
265 #. dhcp_servers resolution
266 msgid "Solaris: use the `dhcpinfo` utility to retrieve the DHCP servers."
267 msgstr ""
268
269 #. dhcp_servers resolution
270 msgid "Windows: use the `GetAdaptersAddresses` (Windows Server 2003: `GetAdaptersInfo`) function to retrieve the DHCP servers."
271 msgstr ""
272
273 #. disks description
274 msgid "Return the disk (block) devices attached to the system."
275 msgstr ""
276
277 #. disks resolution
278 msgid "Linux: parse the contents of `/sys/block/<device>/`."
279 msgstr ""
280
281 #. disks resolution
282 msgid "Solaris: use the `kstat` function to query disk information."
283 msgstr ""
284
285 #. disks caveats
286 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
287 msgstr ""
288
289 #. dmi description
290 msgid "Return the system management information."
291 msgstr ""
292
293 #. dmi resolution
294 msgid "Linux: parse the contents of `/sys/class/dmi/id/` to retrieve system management information."
295 msgstr ""
296
297 #. dmi resolution
298 msgid "Mac OSX: use the `sysctl` function to retrieve system management information."
299 msgstr ""
300
301 #. dmi resolution
302 msgid "Solaris: use the `smbios`, `prtconf`, and `uname` utilities to retrieve system management information."
303 msgstr ""
304
305 #. dmi resolution
306 msgid "Windows: use WMI to retrieve system management information."
307 msgstr ""
308
309 #. dmi caveats
310 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
311 msgstr ""
312
313 #. domain description
314 msgid "Return the network domain of the system."
315 msgstr ""
316
317 #. domain resolution
318 msgid "POSIX platforms: use the `getaddrinfo` function to retrieve the network domain."
319 msgstr ""
320
321 #. domain resolution
322 msgid "Windows: query the registry to retrieve the network domain; falls back to the primary interface's domain if not set in the registry."
323 msgstr ""
324
325 #. ec2_metadata description
326 msgid "Return the Amazon Elastic Compute Cloud (EC2) instance metadata."
327 msgstr ""
328
329 #. ec2_metadata description
330 msgid "Please see the [EC2 instance metadata documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) for the contents of this fact."
331 msgstr ""
332
333 #. ec2_metadata resolution
334 msgid "EC2: query the EC2 metadata endpoint and parse the response."
335 msgstr ""
336
337 #. ec2_metadata caveats
338 msgid "All platforms: `libfacter` must be built with `libcurl` support."
339 msgstr ""
340
341 #. ec2_userdata description
342 msgid "Return the Amazon Elastic Compute Cloud (EC2) instance user data."
343 msgstr ""
344
345 #. ec2_userdata description
346 msgid "Please see the [EC2 instance user data documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) for the contents of this fact."
347 msgstr ""
348
349 #. ec2_userdata resolution
350 msgid "EC2: query the EC2 user data endpoint and parse the response."
351 msgstr ""
352
353 #. ec2_userdata caveats
354 msgid "All platforms: `libfacter` must be built with `libcurl` support."
355 msgstr ""
356
357 #. env_windows_installdir description
358 msgid "Return the path of the directory in which Puppet was installed."
359 msgstr ""
360
361 #. env_windows_installdir resolution
362 msgid "Windows: This fact is specific to the Windows MSI generated environment, and is"
363 msgstr ""
364
365 #. env_windows_installdir resolution
366 msgid " set using the `environment.bat` script that configures the runtime environment"
367 msgstr ""
368
369 #. env_windows_installdir resolution
370 msgid " for all Puppet executables. Please see [the original commit in the puppet_for_the_win repo](https://github.com/puppetlabs/puppet_for_the_win/commit/0cc32c1a09550c13d725b200d3c0cc17d93ec262) for more information."
371 msgstr ""
372
373 #. env_windows_installdir caveats
374 msgid "This fact is specific to Windows, and will not resolve on any other platform."
375 msgstr ""
376
377 #. facterversion description
378 msgid "Return the version of facter."
379 msgstr ""
380
381 #. facterversion resolution
382 msgid "All platforms: use the built-in version of libfacter."
383 msgstr ""
384
385 #. filesystems description
386 msgid "Return the usable file systems for block or disk devices."
387 msgstr ""
388
389 #. filesystems resolution
390 msgid "Linux: parse the contents of `/proc/filesystems` to retrieve the usable file systems."
391 msgstr ""
392
393 #. filesystems resolution
394 msgid "Mac OSX: use the `getfsstat` function to retrieve the usable file systems."
395 msgstr ""
396
397 #. filesystems resolution
398 msgid "Solaris: use the `sysdef` utility to retrieve the usable file systems."
399 msgstr ""
400
401 #. filesystems caveats
402 msgid "Linux: The proc file system must be mounted."
403 msgstr ""
404
405 #. filesystems caveats
406 msgid "Mac OSX: The usable file systems is limited to the file system of mounted devices."
407 msgstr ""
408
409 #. fqdn description
410 msgid "Return the fully qualified domain name (FQDN) of the system."
411 msgstr ""
412
413 #. fqdn resolution
414 msgid "POSIX platforms: use the `getaddrinfo` function to retrieve the FQDN or use host and domain names."
415 msgstr ""
416
417 #. fqdn resolution
418 msgid "Windows: use the host and domain names to build the FQDN."
419 msgstr ""
420
421 #. gce description
422 msgid "Return the Google Compute Engine (GCE) metadata."
423 msgstr ""
424
425 #. gce description
426 msgid "Please see the [GCE metadata documentation](https://cloud.google.com/compute/docs/metadata) for the contents of this fact."
427 msgstr ""
428
429 #. gce resolution
430 msgid "GCE: query the GCE metadata endpoint and parse the response."
431 msgstr ""
432
433 #. gce caveats
434 msgid "All platforms: `libfacter` must be built with `libcurl` support."
435 msgstr ""
436
437 #. gid description
438 msgid "Return the group identifier (GID) of the user running facter."
439 msgstr ""
440
441 #. gid resolution
442 msgid "POSIX platforms: use the `getegid` fuction to retrieve the group identifier."
443 msgstr ""
444
445 #. hardwareisa description
446 msgid "Return the hardware instruction set architecture (ISA)."
447 msgstr ""
448
449 #. hardwareisa resolution
450 msgid "POSIX platforms: use `uname` to retrieve the hardware ISA."
451 msgstr ""
452
453 #. hardwareisa resolution
454 msgid "Windows: use WMI to retrieve the hardware ISA."
455 msgstr ""
456
457 #. hardwaremodel description
458 msgid "Return the operating system's hardware model."
459 msgstr ""
460
461 #. hardwaremodel resolution
462 msgid "POSIX platforms: use the `uname` function to retrieve the OS hardware model."
463 msgstr ""
464
465 #. hardwaremodel resolution
466 msgid "Windows: use the `GetNativeSystemInfo` function to retrieve the OS hardware model."
467 msgstr ""
468
469 #. hostname description
470 msgid "Return the host name of the system."
471 msgstr ""
472
473 #. hostname resolution
474 msgid "POSIX platforms: use the `gethostname` function to retrieve the host name"
475 msgstr ""
476
477 #. hostname resolution
478 msgid "Windows: use the `GetComputerNameExW` function to retrieve the host name."
479 msgstr ""
480
481 #. id description
482 msgid "Return the user identifier (UID) of the user running facter."
483 msgstr ""
484
485 #. id resolution
486 msgid "POSIX platforms: use the `geteuid` fuction to retrieve the user identifier."
487 msgstr ""
488
489 #. identity description
490 msgid "Return the identity information of the user running facter."
491 msgstr ""
492
493 #. identity resolution
494 msgid "POSIX platforms: use the `getegid`, `getpwuid_r`, `geteuid`, and `getgrgid_r` functions to retrieve the identity information; use the result of the `geteuid() == 0` test as the value of the privileged element"
495 msgstr ""
496
497 #. identity resolution
498 msgid "Windows: use the `GetUserNameExW` function to retrieve the identity information; use the `GetTokenInformation` to get the current process token elevation status and use it as the value of the privileged element on versions of Windows supporting the token elevation, on older versions of Windows use the `CheckTokenMembership` to test whether the well known local Administrators group SID is enabled in the current thread impersonation token and use the test result as the value of the privileged element"
499 msgstr ""
500
501 #. interfaces description
502 msgid "Return the comma-separated list of network interface names."
503 msgstr ""
504
505 #. interfaces resolution
506 msgid "Linux: use the `getifaddrs` function to retrieve the network interface names."
507 msgstr ""
508
509 #. interfaces resolution
510 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface names."
511 msgstr ""
512
513 #. interfaces resolution
514 msgid "Solaris: use the `ioctl` function to retrieve the network interface names."
515 msgstr ""
516
517 #. interfaces resolution
518 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface names."
519 msgstr ""
520
521 #. ipaddress description
522 msgid "Return the IPv4 address for the default network interface."
523 msgstr ""
524
525 #. ipaddress resolution
526 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
527 msgstr ""
528
529 #. ipaddress resolution
530 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
531 msgstr ""
532
533 #. ipaddress resolution
534 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
535 msgstr ""
536
537 #. ipaddress resolution
538 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
539 msgstr ""
540
541 #. ipaddress6 description
542 msgid "Return the IPv6 address for the default network interface."
543 msgstr ""
544
545 #. ipaddress6 resolution
546 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
547 msgstr ""
548
549 #. ipaddress6 resolution
550 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
551 msgstr ""
552
553 #. ipaddress6 resolution
554 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
555 msgstr ""
556
557 #. ipaddress6 resolution
558 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
559 msgstr ""
560
561 #. ipaddress6_<interface> description
562 msgid "Return the IPv6 address for a network interface."
563 msgstr ""
564
565 #. ipaddress6_<interface> resolution
566 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
567 msgstr ""
568
569 #. ipaddress6_<interface> resolution
570 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
571 msgstr ""
572
573 #. ipaddress6_<interface> resolution
574 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
575 msgstr ""
576
577 #. ipaddress6_<interface> resolution
578 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
579 msgstr ""
580
581 #. ipaddress_<interface> description
582 msgid "Return the IPv4 address for a network interface."
583 msgstr ""
584
585 #. ipaddress_<interface> resolution
586 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
587 msgstr ""
588
589 #. ipaddress_<interface> resolution
590 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
591 msgstr ""
592
593 #. ipaddress_<interface> resolution
594 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
595 msgstr ""
596
597 #. ipaddress_<interface> resolution
598 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
599 msgstr ""
600
601 #. is_virtual description
602 msgid "Return whether or not the host is a virtual machine."
603 msgstr ""
604
605 #. is_virtual resolution
606 msgid "Linux: use procfs or utilities such as `vmware` and `virt-what` to retrieve virtual machine status."
607 msgstr ""
608
609 #. is_virtual resolution
610 msgid "Mac OSX: use the system profiler to retrieve virtual machine status."
611 msgstr ""
612
613 #. is_virtual resolution
614 msgid "Solaris: use the `zonename` utility to retrieve virtual machine status."
615 msgstr ""
616
617 #. is_virtual resolution
618 msgid "Windows: use WMI to retrieve virtual machine status."
619 msgstr ""
620
621 #. kernel description
622 msgid "Return the kernel's name."
623 msgstr ""
624
625 #. kernel resolution
626 msgid "POSIX platforms: use the `uname` function to retrieve the kernel name."
627 msgstr ""
628
629 #. kernel resolution
630 msgid "Windows: use the value of `windows` for all Windows versions."
631 msgstr ""
632
633 #. kernelmajversion description
634 msgid "Return the kernel's major version."
635 msgstr ""
636
637 #. kernelmajversion resolution
638 msgid "POSIX platforms: use the `uname` function to retrieve the kernel's major version."
639 msgstr ""
640
641 #. kernelmajversion resolution
642 msgid "Windows: use the file version of `kernel32.dll` to retrieve the kernel's major version."
643 msgstr ""
644
645 #. kernelrelease description
646 msgid "Return the kernel's release."
647 msgstr ""
648
649 #. kernelrelease resolution
650 msgid "POSIX platforms: use the `uname` function to retrieve the kernel's release."
651 msgstr ""
652
653 #. kernelrelease resolution
654 msgid "Windows: use the file version of `kernel32.dll` to retrieve the kernel's release."
655 msgstr ""
656
657 #. kernelversion description
658 msgid "Return the kernel's version."
659 msgstr ""
660
661 #. kernelversion resolution
662 msgid "POSIX platforms: use the `uname` function to retrieve the kernel's version."
663 msgstr ""
664
665 #. kernelversion resolution
666 msgid "Windows: use the file version of `kernel32.dll` to retrieve the kernel's version."
667 msgstr ""
668
669 #. ldom description
670 msgid "Return Solaris LDom information from the `virtinfo` utility."
671 msgstr ""
672
673 #. ldom resolution
674 msgid "Solaris: use the `virtinfo` utility to retrieve LDom information."
675 msgstr ""
676
677 #. ldom_<name> description
678 msgid "Return Solaris LDom information."
679 msgstr ""
680
681 #. ldom_<name> resolution
682 msgid "Solaris: use the `virtinfo` utility to retrieve LDom information."
683 msgstr ""
684
685 #. load_averages description
686 msgid "Return the load average over the last 1, 5 and 15 minutes."
687 msgstr ""
688
689 #. load_averages resolution
690 msgid "POSIX platforms: use `getloadavg` function to retrieve the system load averages."
691 msgstr ""
692
693 #. lsbdistcodename description
694 msgid "Return the Linux Standard Base (LSB) distribution code name."
695 msgstr ""
696
697 #. lsbdistcodename resolution
698 msgid "Linux: use the `lsb_release` utility to retrieve the LSB distribution code name."
699 msgstr ""
700
701 #. lsbdistcodename caveats
702 msgid "Linux: Requires that the `lsb_release` utility be installed."
703 msgstr ""
704
705 #. lsbdistdescription description
706 msgid "Return the Linux Standard Base (LSB) distribution description."
707 msgstr ""
708
709 #. lsbdistdescription resolution
710 msgid "Linux: use the `lsb_release` utility to retrieve the LSB distribution description."
711 msgstr ""
712
713 #. lsbdistdescription caveats
714 msgid "Linux: Requires that the `lsb_release` utility be installed."
715 msgstr ""
716
717 #. lsbdistid description
718 msgid "Return the Linux Standard Base (LSB) distribution identifier."
719 msgstr ""
720
721 #. lsbdistid resolution
722 msgid "Linux: use the `lsb_release` utility to retrieve the LSB distribution identifier."
723 msgstr ""
724
725 #. lsbdistid caveats
726 msgid "Linux: Requires that the `lsb_release` utility be installed."
727 msgstr ""
728
729 #. lsbdistrelease description
730 msgid "Return the Linux Standard Base (LSB) distribution release."
731 msgstr ""
732
733 #. lsbdistrelease resolution
734 msgid "Linux: use the `lsb_release` utility to retrieve the LSB distribution release."
735 msgstr ""
736
737 #. lsbdistrelease caveats
738 msgid "Linux: Requires that the `lsb_release` utility be installed."
739 msgstr ""
740
741 #. lsbmajdistrelease description
742 msgid "Return the Linux Standard Base (LSB) major distribution release."
743 msgstr ""
744
745 #. lsbmajdistrelease resolution
746 msgid "Linux: use the `lsb_release` utility to retrieve the LSB major distribution release."
747 msgstr ""
748
749 #. lsbmajdistrelease caveats
750 msgid "Linux: Requires that the `lsb_release` utility be installed."
751 msgstr ""
752
753 #. lsbminordistrelease description
754 msgid "Return the Linux Standard Base (LSB) minor distribution release."
755 msgstr ""
756
757 #. lsbminordistrelease resolution
758 msgid "Linux: use the `lsb_release` utility to retrieve the LSB minor distribution release."
759 msgstr ""
760
761 #. lsbminordistrelease caveats
762 msgid "Linux: Requires that the `lsb_release` utility be installed."
763 msgstr ""
764
765 #. lsbrelease description
766 msgid "Return the Linux Standard Base (LSB) release."
767 msgstr ""
768
769 #. lsbrelease resolution
770 msgid "Linux: use the `lsb_release` utility to retrieve the LSB release."
771 msgstr ""
772
773 #. lsbrelease caveats
774 msgid "Linux: Requires that the `lsb_release` utility be installed."
775 msgstr ""
776
777 #. macaddress description
778 msgid "Return the MAC address for the default network interface."
779 msgstr ""
780
781 #. macaddress resolution
782 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
783 msgstr ""
784
785 #. macaddress resolution
786 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
787 msgstr ""
788
789 #. macaddress resolution
790 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
791 msgstr ""
792
793 #. macaddress resolution
794 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
795 msgstr ""
796
797 #. macaddress_<interface> description
798 msgid "Return the MAC address for a network interface."
799 msgstr ""
800
801 #. macaddress_<interface> resolution
802 msgid "Linux: use the `getifaddrs` function to retrieve the network interface address."
803 msgstr ""
804
805 #. macaddress_<interface> resolution
806 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface address."
807 msgstr ""
808
809 #. macaddress_<interface> resolution
810 msgid "Solaris: use the `ioctl` function to retrieve the network interface address."
811 msgstr ""
812
813 #. macaddress_<interface> resolution
814 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface address."
815 msgstr ""
816
817 #. macosx_buildversion description
818 msgid "Return the Mac OSX build version."
819 msgstr ""
820
821 #. macosx_buildversion resolution
822 msgid "Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX build version."
823 msgstr ""
824
825 #. macosx_productname description
826 msgid "Return the Mac OSX product name."
827 msgstr ""
828
829 #. macosx_productname resolution
830 msgid "Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product name."
831 msgstr ""
832
833 #. macosx_productversion description
834 msgid "Return the Mac OSX product version."
835 msgstr ""
836
837 #. macosx_productversion resolution
838 msgid "Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product version."
839 msgstr ""
840
841 #. macosx_productversion_major description
842 msgid "Return the Mac OSX product major version."
843 msgstr ""
844
845 #. macosx_productversion_major resolution
846 msgid "Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product major version."
847 msgstr ""
848
849 #. macosx_productversion_minor description
850 msgid "Return the Mac OSX product minor version."
851 msgstr ""
852
853 #. macosx_productversion_minor resolution
854 msgid "Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product minor version."
855 msgstr ""
856
857 #. manufacturer description
858 msgid "Return the system manufacturer."
859 msgstr ""
860
861 #. manufacturer resolution
862 msgid "Linux: parse the contents of `/sys/class/dmi/id/sys_vendor` to retrieve the system manufacturer."
863 msgstr ""
864
865 #. manufacturer resolution
866 msgid "Solaris: use the `prtconf` utility to retrieve the system manufacturer."
867 msgstr ""
868
869 #. manufacturer resolution
870 msgid "Windows: use WMI to retrieve the system manufacturer."
871 msgstr ""
872
873 #. manufacturer caveats
874 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
875 msgstr ""
876
877 #. memory description
878 msgid "Return the system memory information."
879 msgstr ""
880
881 #. memory resolution
882 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the system memory information."
883 msgstr ""
884
885 #. memory resolution
886 msgid "Mac OSX: use the `sysctl` function to retrieve the system memory information."
887 msgstr ""
888
889 #. memory resolution
890 msgid "Solaris: use the `kstat` function to retrieve the system memory information."
891 msgstr ""
892
893 #. memory resolution
894 msgid "Windows: use the `GetPerformanceInfo` function to retrieve the system memory information."
895 msgstr ""
896
897 #. memoryfree description
898 msgid "Return the display size of the free system memory (e.g. "1 GiB")."
899 msgstr ""
900
901 #. memoryfree resolution
902 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the free system memory."
903 msgstr ""
904
905 #. memoryfree resolution
906 msgid "Mac OSX: use the `sysctl` function to retrieve the free system memory."
907 msgstr ""
908
909 #. memoryfree resolution
910 msgid "Solaris: use the `kstat` function to retrieve the free system memory."
911 msgstr ""
912
913 #. memoryfree resolution
914 msgid "Windows: use the `GetPerformanceInfo` function to retrieve the free system memory."
915 msgstr ""
916
917 #. memoryfree_mb description
918 msgid "Return the size of the free system memory, in mebibytes."
919 msgstr ""
920
921 #. memoryfree_mb resolution
922 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the free system memory."
923 msgstr ""
924
925 #. memoryfree_mb resolution
926 msgid "Mac OSX: use the `sysctl` function to retrieve the free system memory."
927 msgstr ""
928
929 #. memoryfree_mb resolution
930 msgid "Solaris: use the `kstat` function to retrieve the free system memory."
931 msgstr ""
932
933 #. memoryfree_mb resolution
934 msgid "Windows: use the `GetPerformanceInfo` function to retrieve the free system memory."
935 msgstr ""
936
937 #. memorysize description
938 msgid "Return the display size of the total system memory (e.g. "1 GiB")."
939 msgstr ""
940
941 #. memorysize resolution
942 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the total system memory."
943 msgstr ""
944
945 #. memorysize resolution
946 msgid "Mac OSX: use the `sysctl` function to retrieve the total system memory."
947 msgstr ""
948
949 #. memorysize resolution
950 msgid "Solaris: use the `kstat` function to retrieve the total system memory."
951 msgstr ""
952
953 #. memorysize resolution
954 msgid "Windows: use the `GetPerformanceInfo` function to retrieve the total system memory."
955 msgstr ""
956
957 #. memorysize_mb description
958 msgid "Return the size of the total system memory, in mebibytes."
959 msgstr ""
960
961 #. memorysize_mb resolution
962 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the total system memory."
963 msgstr ""
964
965 #. memorysize_mb resolution
966 msgid "Mac OSX: use the `sysctl` function to retrieve the total system memory."
967 msgstr ""
968
969 #. memorysize_mb resolution
970 msgid "Solaris: use the `kstat` function to retrieve the total system memory."
971 msgstr ""
972
973 #. memorysize_mb resolution
974 msgid "Windows: use the `GetPerformanceInfo` function to retrieve the total system memory."
975 msgstr ""
976
977 #. mountpoints description
978 msgid "Return the current mount points of the system."
979 msgstr ""
980
981 #. mountpoints resolution
982 msgid "Linux: use the `setmntent` function to retrieve the mount points."
983 msgstr ""
984
985 #. mountpoints resolution
986 msgid "Mac OSX: use the `getfsstat` function to retrieve the mount points."
987 msgstr ""
988
989 #. mountpoints resolution
990 msgid "Solaris: parse the contents of `/etc/mnttab` to retrieve the mount points."
991 msgstr ""
992
993 #. mtu_<interface> description
994 msgid "Return the Maximum Transmission Unit (MTU) for a network interface."
995 msgstr ""
996
997 #. mtu_<interface> resolution
998 msgid "Linux: use the `ioctl` function to retrieve the network interface MTU."
999 msgstr ""
1000
1001 #. mtu_<interface> resolution
1002 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface MTU."
1003 msgstr ""
1004
1005 #. mtu_<interface> resolution
1006 msgid "Solaris: use the `ioctl` function to retrieve the network interface MTU."
1007 msgstr ""
1008
1009 #. mtu_<interface> resolution
1010 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface MTU."
1011 msgstr ""
1012
1013 #. netmask description
1014 msgid "Return the IPv4 netmask for the default network interface."
1015 msgstr ""
1016
1017 #. netmask resolution
1018 msgid "Linux: use the `getifaddrs` function to retrieve the network interface netmask."
1019 msgstr ""
1020
1021 #. netmask resolution
1022 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface netmask."
1023 msgstr ""
1024
1025 #. netmask resolution
1026 msgid "Solaris: use the `ioctl` function to retrieve the network interface netmask."
1027 msgstr ""
1028
1029 #. netmask resolution
1030 msgid "Windows: use the `GetAdaptersAddresses` (Windows Server 2003: `GetAdaptersInfo`) function to retrieve the network interface netmask."
1031 msgstr ""
1032
1033 #. netmask6 description
1034 msgid "Return the IPv6 netmask for the default network interface."
1035 msgstr ""
1036
1037 #. netmask6 resolution
1038 msgid "Linux: use the `getifaddrs` function to retrieve the network interface netmask."
1039 msgstr ""
1040
1041 #. netmask6 resolution
1042 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface netmask."
1043 msgstr ""
1044
1045 #. netmask6 resolution
1046 msgid "Solaris: use the `ioctl` function to retrieve the network interface netmask."
1047 msgstr ""
1048
1049 #. netmask6 resolution
1050 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface netmask."
1051 msgstr ""
1052
1053 #. netmask6 caveats
1054 msgid "Windows Server 2003: IPv6 netmasks are not supported."
1055 msgstr ""
1056
1057 #. netmask6_<interface> description
1058 msgid "Return the IPv6 netmask for a network interface."
1059 msgstr ""
1060
1061 #. netmask6_<interface> resolution
1062 msgid "Linux: use the `getifaddrs` function to retrieve the network interface netmask."
1063 msgstr ""
1064
1065 #. netmask6_<interface> resolution
1066 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface netmask."
1067 msgstr ""
1068
1069 #. netmask6_<interface> resolution
1070 msgid "Solaris: use the `ioctl` function to retrieve the network interface netmask."
1071 msgstr ""
1072
1073 #. netmask6_<interface> resolution
1074 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface netmask."
1075 msgstr ""
1076
1077 #. netmask6_<interface> caveats
1078 msgid "Windows Server 2003: IPv6 netmasks are not supported."
1079 msgstr ""
1080
1081 #. netmask_<interface> description
1082 msgid "Return the IPv4 netmask for a network interface."
1083 msgstr ""
1084
1085 #. netmask_<interface> resolution
1086 msgid "Linux: use the `getifaddrs` function to retrieve the network interface netmask."
1087 msgstr ""
1088
1089 #. netmask_<interface> resolution
1090 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface netmask."
1091 msgstr ""
1092
1093 #. netmask_<interface> resolution
1094 msgid "Solaris: use the `ioctl` function to retrieve the network interface netmask."
1095 msgstr ""
1096
1097 #. netmask_<interface> resolution
1098 msgid "Windows: use the `GetAdaptersAddresses` (Windows Server 2003: `GetAdaptersInfo`) function to retrieve the network interface netmask."
1099 msgstr ""
1100
1101 #. network description
1102 msgid "Return the IPv4 network for the default network interface."
1103 msgstr ""
1104
1105 #. network resolution
1106 msgid "Linux: use the `getifaddrs` function to retrieve the network interface network."
1107 msgstr ""
1108
1109 #. network resolution
1110 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface network."
1111 msgstr ""
1112
1113 #. network resolution
1114 msgid "Solaris: use the `ioctl` function to retrieve the network interface network."
1115 msgstr ""
1116
1117 #. network resolution
1118 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface network."
1119 msgstr ""
1120
1121 #. network6 description
1122 msgid "Return the IPv6 network for the default network interface."
1123 msgstr ""
1124
1125 #. network6 resolution
1126 msgid "Linux: use the `getifaddrs` function to retrieve the network interface network."
1127 msgstr ""
1128
1129 #. network6 resolution
1130 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface network."
1131 msgstr ""
1132
1133 #. network6 resolution
1134 msgid "Solaris: use the `ioctl` function to retrieve the network interface network."
1135 msgstr ""
1136
1137 #. network6 resolution
1138 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface network."
1139 msgstr ""
1140
1141 #. network6_<interface> description
1142 msgid "Return the IPv6 network for a network interface."
1143 msgstr ""
1144
1145 #. network6_<interface> resolution
1146 msgid "Linux: use the `getifaddrs` function to retrieve the network interface network."
1147 msgstr ""
1148
1149 #. network6_<interface> resolution
1150 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface network."
1151 msgstr ""
1152
1153 #. network6_<interface> resolution
1154 msgid "Solaris: use the `ioctl` function to retrieve the network interface network."
1155 msgstr ""
1156
1157 #. network6_<interface> resolution
1158 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface network."
1159 msgstr ""
1160
1161 #. network_<interface> description
1162 msgid "Return the IPv4 network for a network interface."
1163 msgstr ""
1164
1165 #. network_<interface> resolution
1166 msgid "Linux: use the `getifaddrs` function to retrieve the network interface network."
1167 msgstr ""
1168
1169 #. network_<interface> resolution
1170 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interface network."
1171 msgstr ""
1172
1173 #. network_<interface> resolution
1174 msgid "Solaris: use the `ioctl` function to retrieve the network interface network."
1175 msgstr ""
1176
1177 #. network_<interface> resolution
1178 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interface network."
1179 msgstr ""
1180
1181 #. networking description
1182 msgid "Return the networking information for the system."
1183 msgstr ""
1184
1185 #. networking resolution
1186 msgid "Linux: use the `getifaddrs` function to retrieve the network interfaces."
1187 msgstr ""
1188
1189 #. networking resolution
1190 msgid "Mac OSX: use the `getifaddrs` function to retrieve the network interfaces."
1191 msgstr ""
1192
1193 #. networking resolution
1194 msgid "Solaris: use the `ioctl` function to retrieve the network interfaces."
1195 msgstr ""
1196
1197 #. networking resolution
1198 msgid "Windows: use the `GetAdaptersAddresses` function to retrieve the network interfaces."
1199 msgstr ""
1200
1201 #. networking caveats
1202 msgid "Windows Server 2003: the `GetAdaptersInfo` function is used for DHCP and netmask lookup. This function does not support IPv6 netmasks."
1203 msgstr ""
1204
1205 #. operatingsystem description
1206 msgid "Return the name of the operating system."
1207 msgstr ""
1208
1209 #. operatingsystem resolution
1210 msgid "All platforms: default to the kernel name."
1211 msgstr ""
1212
1213 #. operatingsystem resolution
1214 msgid "Linux: use various release files in `/etc` to retrieve the OS name."
1215 msgstr ""
1216
1217 #. operatingsystemmajrelease description
1218 msgid "Return the major release of the operating system."
1219 msgstr ""
1220
1221 #. operatingsystemmajrelease resolution
1222 msgid "All platforms: default to the major version of the kernel release."
1223 msgstr ""
1224
1225 #. operatingsystemmajrelease resolution
1226 msgid "Linux: parse the contents of release files in `/etc` to retrieve the OS major release."
1227 msgstr ""
1228
1229 #. operatingsystemmajrelease resolution
1230 msgid "Solaris: parse the contents of `/etc/release` to retrieve the OS major release."
1231 msgstr ""
1232
1233 #. operatingsystemmajrelease resolution
1234 msgid "Windows: use WMI to retrieve the OS major release."
1235 msgstr ""
1236
1237 #. operatingsystemmajrelease caveats
1238 msgid "Linux: for Ubuntu, the major release is X.Y (e.g. "10.4")."
1239 msgstr ""
1240
1241 #. operatingsystemrelease description
1242 msgid "Return the release of the operating system."
1243 msgstr ""
1244
1245 #. operatingsystemrelease resolution
1246 msgid "All platforms: default to the kernel release."
1247 msgstr ""
1248
1249 #. operatingsystemrelease resolution
1250 msgid "Linux: parse the contents of release files in `/etc` to retrieve the OS release."
1251 msgstr ""
1252
1253 #. operatingsystemrelease resolution
1254 msgid "Solaris: parse the contents of `/etc/release` to retrieve the OS release."
1255 msgstr ""
1256
1257 #. operatingsystemrelease resolution
1258 msgid "Windows: use WMI to retrieve the OS release."
1259 msgstr ""
1260
1261 #. os description
1262 msgid "Return information about the host operating system."
1263 msgstr ""
1264
1265 #. os resolution
1266 msgid "Linux: use the `lsb_release` utility and parse the contents of release files in `/etc` to retrieve the OS information."
1267 msgstr ""
1268
1269 #. os resolution
1270 msgid "OSX: use the `sw_vers` utility to retrieve the OS information."
1271 msgstr ""
1272
1273 #. os resolution
1274 msgid "Solaris: parse the contents of `/etc/release` to retrieve the OS information."
1275 msgstr ""
1276
1277 #. os resolution
1278 msgid "Windows: use WMI to retrieve the OS information."
1279 msgstr ""
1280
1281 #. osfamily description
1282 msgid "Return the family of the operating system."
1283 msgstr ""
1284
1285 #. osfamily resolution
1286 msgid "All platforms: default to the kernel name."
1287 msgstr ""
1288
1289 #. osfamily resolution
1290 msgid "Linux: map various Linux distributions to their base distribution (e.g. Ubuntu is a "Debian" distro)."
1291 msgstr ""
1292
1293 #. osfamily resolution
1294 msgid "Solaris: map various Solaris-based operating systems to the "Solaris" family."
1295 msgstr ""
1296
1297 #. osfamily resolution
1298 msgid "Windows: use "windows" as the family name."
1299 msgstr ""
1300
1301 #. partitions description
1302 msgid "Return the disk partitions of the system."
1303 msgstr ""
1304
1305 #. partitions resolution
1306 msgid "Linux: use `libblkid` to retrieve the disk partitions."
1307 msgstr ""
1308
1309 #. partitions caveats
1310 msgid "Linux: `libfacter` must be built with `libblkid` support."
1311 msgstr ""
1312
1313 #. path description
1314 msgid "Return the PATH environment variable."
1315 msgstr ""
1316
1317 #. path resolution
1318 msgid "All platforms: retrieve the value of the PATH environment variable."
1319 msgstr ""
1320
1321 #. physicalprocessorcount description
1322 msgid "Return the count of physical processors."
1323 msgstr ""
1324
1325 #. physicalprocessorcount resolution
1326 msgid "Linux: parse the contents `/sys/devices/system/cpu/` and `/proc/cpuinfo` to retrieve the count of physical processors."
1327 msgstr ""
1328
1329 #. physicalprocessorcount resolution
1330 msgid "Mac OSX: use the `sysctl` function to retrieve the count of physical processors."
1331 msgstr ""
1332
1333 #. physicalprocessorcount resolution
1334 msgid "Solaris: use the `kstat` function to retrieve the count of physical processors."
1335 msgstr ""
1336
1337 #. physicalprocessorcount resolution
1338 msgid "Windows: use WMI to retrieve the count of physical processors."
1339 msgstr ""
1340
1341 #. physicalprocessorcount caveats
1342 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
1343 msgstr ""
1344
1345 #. processor<N> description
1346 msgid "Return the model string of processor N."
1347 msgstr ""
1348
1349 #. processor<N> resolution
1350 msgid "Linux: parse the contents of `/proc/cpuinfo` to retrieve the processor model string."
1351 msgstr ""
1352
1353 #. processor<N> resolution
1354 msgid "Mac OSX: use the `sysctl` function to retrieve the processor model string."
1355 msgstr ""
1356
1357 #. processor<N> resolution
1358 msgid "Solaris: use the `kstat` function to retrieve the processor model string."
1359 msgstr ""
1360
1361 #. processor<N> resolution
1362 msgid "Windows: use WMI to retrieve the processor model string."
1363 msgstr ""
1364
1365 #. processorcount description
1366 msgid "Return the count of logical processors."
1367 msgstr ""
1368
1369 #. processorcount resolution
1370 msgid "Linux: parse the contents `/sys/devices/system/cpu/` and `/proc/cpuinfo` to retrieve the count of logical processors."
1371 msgstr ""
1372
1373 #. processorcount resolution
1374 msgid "Mac OSX: use the `sysctl` function to retrieve the count of logical processors."
1375 msgstr ""
1376
1377 #. processorcount resolution
1378 msgid "Solaris: use the `kstat` function to retrieve the count of logical processors."
1379 msgstr ""
1380
1381 #. processorcount resolution
1382 msgid "Windows: use WMI to retrieve the count of logical processors."
1383 msgstr ""
1384
1385 #. processorcount caveats
1386 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
1387 msgstr ""
1388
1389 #. processors description
1390 msgid "Return information about the system's processors."
1391 msgstr ""
1392
1393 #. processors resolution
1394 msgid "Linux: parse the contents `/sys/devices/system/cpu/` and `/proc/cpuinfo` to retrieve the processor information."
1395 msgstr ""
1396
1397 #. processors resolution
1398 msgid "Mac OSX: use the `sysctl` function to retrieve the processor information."
1399 msgstr ""
1400
1401 #. processors resolution
1402 msgid "Solaris: use the `kstat` function to retrieve the processor information."
1403 msgstr ""
1404
1405 #. processors resolution
1406 msgid "Windows: use WMI to retrieve the processor information."
1407 msgstr ""
1408
1409 #. productname description
1410 msgid "Return the system product name."
1411 msgstr ""
1412
1413 #. productname resolution
1414 msgid "Linux: parse the contents of `/sys/class/dmi/id/product_name` to retrieve the system product name."
1415 msgstr ""
1416
1417 #. productname resolution
1418 msgid "Mac OSX: use the `sysctl` function to retrieve the system product name."
1419 msgstr ""
1420
1421 #. productname resolution
1422 msgid "Solaris: use the `smbios` utility to retrieve the system product name."
1423 msgstr ""
1424
1425 #. productname resolution
1426 msgid "Windows: use WMI to retrieve the system product name."
1427 msgstr ""
1428
1429 #. productname caveats
1430 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
1431 msgstr ""
1432
1433 #. ruby description
1434 msgid "Return information about the Ruby loaded by facter."
1435 msgstr ""
1436
1437 #. ruby resolution
1438 msgid "All platforms: Use `RbConfig`, `RUBY_PLATFORM`, and `RUBY_VERSION` to retrieve information about Ruby."
1439 msgstr ""
1440
1441 #. ruby caveats
1442 msgid "All platforms: facter must be able to locate `libruby`."
1443 msgstr ""
1444
1445 #. rubyplatform description
1446 msgid "Return the platform Ruby was built for."
1447 msgstr ""
1448
1449 #. rubyplatform resolution
1450 msgid "All platforms: use `RUBY_PLATFORM` from the Ruby loaded by facter."
1451 msgstr ""
1452
1453 #. rubyplatform caveats
1454 msgid "All platforms: facter must be able to locate `libruby`."
1455 msgstr ""
1456
1457 #. rubysitedir description
1458 msgid "Return the path to Ruby's site library directory."
1459 msgstr ""
1460
1461 #. rubysitedir resolution
1462 msgid "All platforms: use `RbConfig` from the Ruby loaded by facter."
1463 msgstr ""
1464
1465 #. rubysitedir caveats
1466 msgid "All platforms: facter must be able to locate `libruby`."
1467 msgstr ""
1468
1469 #. rubyversion description
1470 msgid "Return the version of Ruby."
1471 msgstr ""
1472
1473 #. rubyversion resolution
1474 msgid "All platforms: use `RUBY_VERSION` from the Ruby loaded by facter."
1475 msgstr ""
1476
1477 #. rubyversion caveats
1478 msgid "All platforms: facter must be able to locate `libruby`."
1479 msgstr ""
1480
1481 #. selinux description
1482 msgid "Return whether Security-Enhanced Linux (SELinux) is enabled."
1483 msgstr ""
1484
1485 #. selinux resolution
1486 msgid "Linux: parse the contents of `/proc/self/mounts` to determine if SELinux is enabled."
1487 msgstr ""
1488
1489 #. selinux_config_mode description
1490 msgid "Return the configured Security-Enhanced Linux (SELinux) mode."
1491 msgstr ""
1492
1493 #. selinux_config_mode resolution
1494 msgid "Linux: parse the contents of `/etc/selinux/config` to retrieve the configured SELinux mode."
1495 msgstr ""
1496
1497 #. selinux_config_policy description
1498 msgid "Return the configured Security-Enhanced Linux (SELinux) policy."
1499 msgstr ""
1500
1501 #. selinux_config_policy resolution
1502 msgid "Linux: parse the contents of `/etc/selinux/config` to retrieve the configured SELinux policy."
1503 msgstr ""
1504
1505 #. selinux_current_mode description
1506 msgid "Return the current Security-Enhanced Linux (SELinux) mode."
1507 msgstr ""
1508
1509 #. selinux_current_mode resolution
1510 msgid "Linux: parse the contents of `<mountpoint>/enforce` to retrieve the current SELinux mode."
1511 msgstr ""
1512
1513 #. selinux_enforced description
1514 msgid "Return whether Security-Enhanced Linux (SELinux) is enforced."
1515 msgstr ""
1516
1517 #. selinux_enforced resolution
1518 msgid "Linux: parse the contents of `<mountpoint>/enforce` to retrieve the current SELinux mode."
1519 msgstr ""
1520
1521 #. selinux_policyversion description
1522 msgid "Return the Security-Enhanced Linux (SELinux) policy version."
1523 msgstr ""
1524
1525 #. selinux_policyversion resolution
1526 msgid "Linux: parse the contents of `<mountpoint>/policyvers` to retrieve the SELinux policy version."
1527 msgstr ""
1528
1529 #. serialnumber description
1530 msgid "Return the system product serial number."
1531 msgstr ""
1532
1533 #. serialnumber resolution
1534 msgid "Linux: parse the contents of `/sys/class/dmi/id/product_name` to retrieve the system product serial number."
1535 msgstr ""
1536
1537 #. serialnumber resolution
1538 msgid "Solaris: use the `smbios` utility to retrieve the system product serial number."
1539 msgstr ""
1540
1541 #. serialnumber resolution
1542 msgid "Windows: use WMI to retrieve the system product serial number."
1543 msgstr ""
1544
1545 #. serialnumber caveats
1546 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
1547 msgstr ""
1548
1549 #. solaris_zones description
1550 msgid "Return information about Solaris zones."
1551 msgstr ""
1552
1553 #. solaris_zones resolution
1554 msgid "Solaris: use the `zoneadm` and `zonename` utilities to retrieve information about the Solaris zones."
1555 msgstr ""
1556
1557 #. sp_<name> description
1558 msgid "Return Mac OSX system profiler information."
1559 msgstr ""
1560
1561 #. sp_<name> resolution
1562 msgid "Mac OSX: use the `system_profiler` utility to retrieve system profiler information."
1563 msgstr ""
1564
1565 #. ssh description
1566 msgid "Return SSH public keys and fingerprints."
1567 msgstr ""
1568
1569 #. ssh resolution
1570 msgid "POSIX platforms: parse SSH public key files and derive fingerprints."
1571 msgstr ""
1572
1573 #. ssh caveats
1574 msgid "POSIX platforms: facter must be built with OpenSSL support."
1575 msgstr ""
1576
1577 #. ssh<algorithm>key description
1578 msgid "Return the SSH public key for the algorithm."
1579 msgstr ""
1580
1581 #. ssh<algorithm>key resolution
1582 msgid "POSIX platforms: parse SSH public key files."
1583 msgstr ""
1584
1585 #. ssh<algorithm>key caveats
1586 msgid "POSIX platforms: facter must be built with OpenSSL support."
1587 msgstr ""
1588
1589 #. sshfp_<algorithm> description
1590 msgid "Return the SSH fingerprints for the algorithm's public key."
1591 msgstr ""
1592
1593 #. sshfp_<algorithm> resolution
1594 msgid "POSIX platforms: derive the SHA1 and SHA256 fingerprints; delimit with a new line character."
1595 msgstr ""
1596
1597 #. sshfp_<algorithm> caveats
1598 msgid "POSIX platforms: facter must be built with OpenSSL support."
1599 msgstr ""
1600
1601 #. swapencrypted description
1602 msgid "Return whether or not the swap is encrypted."
1603 msgstr ""
1604
1605 #. swapencrypted resolution
1606 msgid "Mac OSX: use the `sysctl` function to retrieve swap encryption status."
1607 msgstr ""
1608
1609 #. swapfree description
1610 msgid "Return the display size of the free swap memory (e.g. "1 GiB")."
1611 msgstr ""
1612
1613 #. swapfree resolution
1614 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the free swap memory."
1615 msgstr ""
1616
1617 #. swapfree resolution
1618 msgid "Mac OSX: use the `sysctl` function to retrieve the free swap memory."
1619 msgstr ""
1620
1621 #. swapfree resolution
1622 msgid "Solaris: use the `swapctl` function to retrieve the free swap memory."
1623 msgstr ""
1624
1625 #. swapfree_mb description
1626 msgid "Return the size of the free swap memory, in mebibytes."
1627 msgstr ""
1628
1629 #. swapfree_mb resolution
1630 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the free swap memory."
1631 msgstr ""
1632
1633 #. swapfree_mb resolution
1634 msgid "Mac OSX: use the `sysctl` function to retrieve the free swap memory."
1635 msgstr ""
1636
1637 #. swapfree_mb resolution
1638 msgid "Solaris: use the `swapctl` function to retrieve the free swap memory."
1639 msgstr ""
1640
1641 #. swapsize description
1642 msgid "Return the display size of the total swap memory (e.g. "1 GiB")."
1643 msgstr ""
1644
1645 #. swapsize resolution
1646 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the total swap memory."
1647 msgstr ""
1648
1649 #. swapsize resolution
1650 msgid "Mac OSX: use the `sysctl` function to retrieve the total swap memory."
1651 msgstr ""
1652
1653 #. swapsize resolution
1654 msgid "Solaris: use the `swapctl` function to retrieve the total swap memory."
1655 msgstr ""
1656
1657 #. swapsize_mb description
1658 msgid "Return the size of the total swap memory, in mebibytes."
1659 msgstr ""
1660
1661 #. swapsize_mb resolution
1662 msgid "Linux: parse the contents of `/proc/meminfo` to retrieve the total swap memory."
1663 msgstr ""
1664
1665 #. swapsize_mb resolution
1666 msgid "Mac OSX: use the `sysctl` function to retrieve the total swap memory."
1667 msgstr ""
1668
1669 #. swapsize_mb resolution
1670 msgid "Solaris: use the `swapctl` function to retrieve the total swap memory."
1671 msgstr ""
1672
1673 #. system32 description
1674 msgid "Return the path to the System32 directory on Windows."
1675 msgstr ""
1676
1677 #. system32 resolution
1678 msgid "Windows: use the `SHGetFolderPath` function to retrieve the path to the System32 directory."
1679 msgstr ""
1680
1681 #. system_profiler description
1682 msgid "Return information from the Mac OSX system profiler."
1683 msgstr ""
1684
1685 #. system_profiler resolution
1686 msgid "Mac OSX: use the `system_profiler` utility to retrieve system profiler information."
1687 msgstr ""
1688
1689 #. system_uptime description
1690 msgid "Return the system uptime information."
1691 msgstr ""
1692
1693 #. system_uptime resolution
1694 msgid "Linux: use the `sysinfo` function to retrieve the system uptime."
1695 msgstr ""
1696
1697 #. system_uptime resolution
1698 msgid "POSIX platforms: use the `uptime` utility to retrieve the system uptime."
1699 msgstr ""
1700
1701 #. system_uptime resolution
1702 msgid "Solaris: use the `kstat` function to retrieve the system uptime."
1703 msgstr ""
1704
1705 #. system_uptime resolution
1706 msgid "Windows: use WMI to retrieve the system uptime."
1707 msgstr ""
1708
1709 #. timezone description
1710 msgid "Return the system timezone."
1711 msgstr ""
1712
1713 #. timezone resolution
1714 msgid "POSIX platforms: use the `localtime_r` function to retrieve the system timezone."
1715 msgstr ""
1716
1717 #. timezone resolution
1718 msgid "Windows: use the `localtime_s` function to retrieve the system timezone."
1719 msgstr ""
1720
1721 #. uptime description
1722 msgid "Return the system uptime."
1723 msgstr ""
1724
1725 #. uptime resolution
1726 msgid "Linux: use the `sysinfo` function to retrieve the system uptime."
1727 msgstr ""
1728
1729 #. uptime resolution
1730 msgid "POSIX platforms: use the `uptime` utility to retrieve the system uptime."
1731 msgstr ""
1732
1733 #. uptime resolution
1734 msgid "Solaris: use the `kstat` function to retrieve the system uptime."
1735 msgstr ""
1736
1737 #. uptime resolution
1738 msgid "Windows: use WMI to retrieve the system uptime."
1739 msgstr ""
1740
1741 #. uptime_days description
1742 msgid "Return the system uptime days."
1743 msgstr ""
1744
1745 #. uptime_days resolution
1746 msgid "Linux: use the `sysinfo` function to retrieve the system uptime days."
1747 msgstr ""
1748
1749 #. uptime_days resolution
1750 msgid "POSIX platforms: use the `uptime` utility to retrieve the system uptime days."
1751 msgstr ""
1752
1753 #. uptime_days resolution
1754 msgid "Solaris: use the `kstat` function to retrieve the system uptime days."
1755 msgstr ""
1756
1757 #. uptime_days resolution
1758 msgid "Windows: use WMI to retrieve the system uptime days."
1759 msgstr ""
1760
1761 #. uptime_hours description
1762 msgid "Return the system uptime hours."
1763 msgstr ""
1764
1765 #. uptime_hours resolution
1766 msgid "Linux: use the `sysinfo` function to retrieve the system uptime hours."
1767 msgstr ""
1768
1769 #. uptime_hours resolution
1770 msgid "POSIX platforms: use the `uptime` utility to retrieve the system uptime hours."
1771 msgstr ""
1772
1773 #. uptime_hours resolution
1774 msgid "Solaris: use the `kstat` function to retrieve the system uptime hours."
1775 msgstr ""
1776
1777 #. uptime_hours resolution
1778 msgid "Windows: use WMI to retrieve the system uptime hours."
1779 msgstr ""
1780
1781 #. uptime_seconds description
1782 msgid "Return the system uptime seconds."
1783 msgstr ""
1784
1785 #. uptime_seconds resolution
1786 msgid "Linux: use the `sysinfo` function to retrieve the system uptime seconds."
1787 msgstr ""
1788
1789 #. uptime_seconds resolution
1790 msgid "POSIX platforms: use the `uptime` utility to retrieve the system uptime seconds."
1791 msgstr ""
1792
1793 #. uptime_seconds resolution
1794 msgid "Solaris: use the `kstat` function to retrieve the system uptime seconds."
1795 msgstr ""
1796
1797 #. uptime_seconds resolution
1798 msgid "Windows: use WMI to retrieve the system uptime seconds."
1799 msgstr ""
1800
1801 #. uuid description
1802 msgid "Return the system product unique identifier."
1803 msgstr ""
1804
1805 #. uuid resolution
1806 msgid "Linux: parse the contents of `/sys/class/dmi/id/product_uuid` to retrieve the system product unique identifier."
1807 msgstr ""
1808
1809 #. uuid resolution
1810 msgid "Solaris: use the `smbios` utility to retrieve the system product unique identifier."
1811 msgstr ""
1812
1813 #. uuid caveats
1814 msgid "Linux: kernel 2.6+ is required due to the reliance on sysfs."
1815 msgstr ""
1816
1817 #. virtual description
1818 msgid "Return the hypervisor name for virtual machines or "physical" for physical machines."
1819 msgstr ""
1820
1821 #. virtual resolution
1822 msgid "Linux: use procfs or utilities such as `vmware` and `virt-what` to retrieve virtual machine name."
1823 msgstr ""
1824
1825 #. virtual resolution
1826 msgid "Mac OSX: use the system profiler to retrieve virtual machine name."
1827 msgstr ""
1828
1829 #. virtual resolution
1830 msgid "Solaris: use the `zonename` utility to retrieve virtual machine name."
1831 msgstr ""
1832
1833 #. virtual resolution
1834 msgid "Windows: use WMI to retrieve virtual machine name."
1835 msgstr ""
1836
1837 #. xen description
1838 msgid "Return metadata for the Xen hypervisor."
1839 msgstr ""
1840
1841 #. xen resolution
1842 msgid "POSIX platforms: use `/usr/lib/xen-common/bin/xen-toolstack` to locate xen admin commands if available, otherwise fallback to `/usr/sbin/xl` or `/usr/sbin/xm`. Use the found command to execute the `list` query."
1843 msgstr ""
1844
1845 #. xen caveats
1846 msgid "POSIX platforms: confined to Xen privileged virtual machines."
1847 msgstr ""
1848
1849 #. xendomains description
1850 msgid "Return a list of comma-separated active Xen domain names."
1851 msgstr ""
1852
1853 #. xendomains resolution
1854 msgid "POSIX platforms: see the `xen` structured fact."
1855 msgstr ""
1856
1857 #. xendomains caveats
1858 msgid "POSIX platforms: confined to Xen privileged virtual machines."
1859 msgstr ""
1860
1861 #. zfs_featurenumbers description
1862 msgid "Return the comma-delimited feature numbers for ZFS."
1863 msgstr ""
1864
1865 #. zfs_featurenumbers resolution
1866 msgid "Solaris: use the `zfs` utility to retrieve the feature numbers for ZFS"
1867 msgstr ""
1868
1869 #. zfs_featurenumbers caveats
1870 msgid "Solaris: the `zfs` utility must be present."
1871 msgstr ""
1872
1873 #. zfs_version description
1874 msgid "Return the version for ZFS."
1875 msgstr ""
1876
1877 #. zfs_version resolution
1878 msgid "Solaris: use the `zfs` utility to retrieve the version for ZFS"
1879 msgstr ""
1880
1881 #. zfs_version caveats
1882 msgid "Solaris: the `zfs` utility must be present."
1883 msgstr ""
1884
1885 #. zone_<name>_brand description
1886 msgid "Return the brand for the Solaris zone."
1887 msgstr ""
1888
1889 #. zone_<name>_brand resolution
1890 msgid "Solaris: use the `zoneadm` utility to retrieve the brand for the Solaris zone."
1891 msgstr ""
1892
1893 #. zone_<name>_brand caveats
1894 msgid "Solaris: the `zoneadm` utility must be present."
1895 msgstr ""
1896
1897 #. zone_<name>_iptype description
1898 msgid "Return the IP type for the Solaris zone."
1899 msgstr ""
1900
1901 #. zone_<name>_iptype resolution
1902 msgid "Solaris: use the `zoneadm` utility to retrieve the IP type for the Solaris zone."
1903 msgstr ""
1904
1905 #. zone_<name>_iptype caveats
1906 msgid "Solaris: the `zoneadm` utility must be present."
1907 msgstr ""
1908
1909 #. zone_<name>_name description
1910 msgid "Return the name for the Solaris zone."
1911 msgstr ""
1912
1913 #. zone_<name>_name resolution
1914 msgid "Solaris: use the `zoneadm` utility to retrieve the name for the Solaris zone."
1915 msgstr ""
1916
1917 #. zone_<name>_name caveats
1918 msgid "Solaris: the `zoneadm` utility must be present."
1919 msgstr ""
1920
1921 #. zone_<name>_uuid description
1922 msgid "Return the unique identifier for the Solaris zone."
1923 msgstr ""
1924
1925 #. zone_<name>_uuid resolution
1926 msgid "Solaris: use the `zoneadm` utility to retrieve the unique identifier for the Solaris zone."
1927 msgstr ""
1928
1929 #. zone_<name>_uuid caveats
1930 msgid "Solaris: the `zoneadm` utility must be present."
1931 msgstr ""
1932
1933 #. zone_<name>_id description
1934 msgid "Return the zone identifier for the Solaris zone."
1935 msgstr ""
1936
1937 #. zone_<name>_id resolution
1938 msgid "Solaris: use the `zoneadm` utility to retrieve the zone identifier for the Solaris zone."
1939 msgstr ""
1940
1941 #. zone_<name>_id caveats
1942 msgid "Solaris: the `zoneadm` utility must be present."
1943 msgstr ""
1944
1945 #. zone_<name>_path description
1946 msgid "Return the zone path for the Solaris zone."
1947 msgstr ""
1948
1949 #. zone_<name>_path resolution
1950 msgid "Solaris: use the `zoneadm` utility to retrieve the zone path for the Solaris zone."
1951 msgstr ""
1952
1953 #. zone_<name>_path caveats
1954 msgid "Solaris: the `zoneadm` utility must be present."
1955 msgstr ""
1956
1957 #. zone_<name>_status description
1958 msgid "Return the zone state for the Solaris zone."
1959 msgstr ""
1960
1961 #. zone_<name>_status resolution
1962 msgid "Solaris: use the `zoneadm` utility to retrieve the zone state for the Solaris zone."
1963 msgstr ""
1964
1965 #. zone_<name>_status caveats
1966 msgid "Solaris: the `zoneadm` utility must be present."
1967 msgstr ""
1968
1969 #. zonename description
1970 msgid "Return the name of the current Solaris zone."
1971 msgstr ""
1972
1973 #. zonename resolution
1974 msgid "Solaris: use the `zonename` utility to retrieve the current zone name."
1975 msgstr ""
1976
1977 #. zonename caveats
1978 msgid "Solaris: the `zonename` utility must be present."
1979 msgstr ""
1980
1981 #. zones description
1982 msgid "Return the count of Solaris zones."
1983 msgstr ""
1984
1985 #. zones resolution
1986 msgid "Solaris: use the `zoneadm` utility to retrieve the count of Solaris zones."
1987 msgstr ""
1988
1989 #. zones caveats
1990 msgid "Solaris: the `zoneadm` utility must be present."
1991 msgstr ""
1992
1993 #. zpool_featurenumbers description
1994 msgid "Return the comma-delimited feature numbers for ZFS storage pools."
1995 msgstr ""
1996
1997 #. zpool_featurenumbers resolution
1998 msgid "Solaris: use the `zpool` utility to retrieve the feature numbers for ZFS storage pools"
1999 msgstr ""
2000
2001 #. zpool_featurenumbers caveats
2002 msgid "Solaris: the `zpool` utility must be present."
2003 msgstr ""
2004
2005 #. zpool_version description
2006 msgid "Return the version for ZFS storage pools."
2007 msgstr ""
2008
2009 #. zpool_version resolution
2010 msgid "Solaris: use the `zpool` utility to retrieve the version for ZFS storage pools"
2011 msgstr ""
2012
2013 #. zpool_version caveats
2014 msgid "Solaris: the `zpool` utility must be present."
2015 msgstr ""
2016
5454 resolution: |
5555 All platforms: query augparse for the augeas version.
5656
57 az_metadata:
58 type: map
59 description: |
60 Return the Microsoft Azure instance metadata.
61 Please see the [Microsoft Azure instance metadata documentation](http://https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service) for the contents of this fact.
62 resolution: |
63 Azure: query the Azure metadata endpoint and parse the response.
64 caveats: |
65 All platforms: `libfacter` must be built with `libcurl` support.
66 validate: false
67
5768 blockdevices:
5869 type: string
5970 hidden: true
185196
186197 cloud:
187198 type: map
188 description: Information about the cloud instance of the node. This is currently only populated on Linux nodes running in Microsoft Azure.
199 description: Information about the cloud instance of the node. This is currently only populated on nodes running in Microsoft Azure, Amazon Web Services, and Google Compute Engine.
189200 elements:
190201 provider:
191202 type: string
243254 vendor:
244255 type: string
245256 description: The vendor of the disk or block device.
257 type:
258 type: string
259 description: The type of disk or block device (sshd or hdd). This fact is available only on Linux.
246260
247261 dmi:
248262 type: map
379393 Windows: check if key `HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Lsa/FipsAlgorithmPolicy/Enabled` is 1 or 0
380394 caveats: |
381395 Linux: Limited to linux redhat family only
396 details: |
397 Only available on Windows and Redhat linux family
382398
383399 fqdn:
384400 type: string
706722 description: Return the Mac OSX product minor version.
707723 resolution: |
708724 Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product minor version.
725
726 macosx_productversion_patch:
727 type: string
728 hidden: true
729 description: Return the Mac OSX product patch version.
730 resolution: |
731 Mac OSX: use the `sw_vers` utility to retrieve the Mac OSX product patch version.
709732
710733 manufacturer:
711734 type: string
11671190 minor:
11681191 type: string
11691192 description: The minor Mac OSX version number.
1193 patch:
1194 type: string
1195 description: The patch Mac OSX version number.
11701196 name:
11711197 type: string
11721198 description: The operating system's name.
13511377 speed:
13521378 type: string
13531379 description: The speed of the processors, such as "2.0 GHz".
1380 cores:
1381 type: integer
1382 description: The number of cores per processor socket.
1383 threads:
1384 type: integer
1385 description: The number of threads per processor core.
13541386
13551387 productname:
13561388 type: string
18771909 type: map
18781910 description: Return metadata for the Xen hypervisor.
18791911 resolution: |
1880 POSIX platforms: use `/usr/lib/xen-common/bin/xen-toolstack` to locate xen admin commands if available, otherwise fallback to `/usr/sbin/xl` or `/usr/sbin/xm`. Use the found command to execute the `list` query.
1912 POSIX platforms: if `/usr/sbin/xl` and `/usr/sbin/xm` is installed use `/usr/lib/xen-common/bin/xen-toolstack` to choose, otherwise try `/usr/sbin/xl` then `/usr/sbin/xm` in order. Use the found command to execute the `list` query.
18811913 caveats: |
18821914 POSIX platforms: confined to Xen privileged virtual machines.
18831915 elements:
20212053 Solaris: use the `zpool` utility to retrieve the version for ZFS storage pools
20222054 caveats: |
20232055 Solaris: the `zpool` utility must be present.
2056
2057 nim_type:
2058 type: string
2059 description: Tells if the node is master or standalone inside an AIX Nim environment.
2060 details: |
2061 Is Available only on AIX.
+0
-67
lib/schema/translation_tooling.rb less more
0 require 'yaml'
1
2 fact_schema = YAML.load_file(File.join(File.dirname(__FILE__), "facter.yaml"))
3 facts = []
4
5 fact_schema.each do |fact|
6 fact_name = fact[0]
7 fact_details = fact[1]
8
9 fact_data = {:name => fact_name,
10 :description => fact_details['description'],
11 :resolution => fact_details['resolution'],
12 :caveats => fact_details['caveats']}
13
14 facts << fact_data
15 end
16
17 File.open(File.join(File.dirname(__FILE__), "core_facts.pot"), 'w') do |file|
18 file.puts <<-HEADER
19 # CORE FACTS SCHEMA
20 # Copyright (C) 2016 Puppet, LLC
21 # This file is distributed under the same license as the FACTER package.
22 # FIRST AUTHOR <docs@puppet.com>, 2016.
23 #
24 #, fuzzy
25 msgid ""
26 msgstr ""
27 "Project-Id-Version: FACTER \\n"
28 "Report-Msgid-Bugs-To: docs@puppet.com\\n"
29 "POT-Creation-Date: \\n"
30 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
31 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
32 "Language-Team: LANGUAGE <LL@li.org>\\n"
33 "Language: \\n"
34 "MIME-Version: 1.0\\n"
35 "Content-Type: text/plain; charset=UTF-8\\n"
36 "Content-Transfer-Encoding: 8bit\\n"
37 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
38 \n
39 HEADER
40
41 facts.each do |fact|
42 if fact[:description]
43 descriptions = fact[:description].split("\n")
44
45 descriptions.each do |string|
46 file.puts "#. #{fact[:name]} description\nmsgid \"#{string}\"\nmsgstr \"\"\n\n"
47 end
48 end
49
50 if fact[:resolution]
51 resolutions = fact[:resolution].split("\n")
52
53 resolutions.each do |string|
54 file.puts "#. #{fact[:name]} resolution\nmsgid \"#{string}\"\nmsgstr \"\"\n\n"
55 end
56 end
57
58 if fact[:caveats]
59 caveats = fact[:caveats].split("\n")
60
61 caveats.each do |string|
62 file.puts "#. #{fact[:name]} caveats\nmsgid \"#{string}\"\nmsgstr \"\"\n\n"
63 end
64 end
65 end
66 end
+0
-136
lib/spec/unit/facter_spec.rb less more
0 # encoding: UTF-8
1 require 'spec_helper'
2
3 Facter.on_message do |level, message|
4 puts message if level == :fatal
5 end
6
7 describe Facter do
8
9 it 'should provide a version' do
10 expect(Facter.version).to_not be_nil
11 end
12
13 describe 'without resetting' do
14
15 before :all do
16 Facter.reset
17 end
18
19 it 'should not be an empty hash' do
20 expect(Facter.to_hash).to_not be_empty
21 end
22
23 it 'should return a fact for []' do
24 fact = Facter[:facterversion]
25 expect(fact).to_not be_nil
26 expect(fact.name).to eq('facterversion')
27 expect(fact.value).to eq(Facter.version)
28 end
29
30 it 'should return nil value for [] with unknown fact' do
31 expect(Facter[:not_a_fact]).to be_nil
32 end
33
34 it 'should return nil for value with unknown fact' do
35 expect(Facter.value(:not_a_fact)).to be_nil
36 end
37
38 it 'should contain a matching facter version' do
39 version = Facter.value('facterversion')
40 expect(version).to eq(Facter.version)
41 expect(version).to eq(Facter::FACTERVERSION)
42 end
43 end
44
45 describe 'with resetting' do
46 before :each do
47 Facter.reset
48 end
49
50 it 'should load external facts' do
51 # Check for windows vs posix for executable external facts
52 windows = Facter.value('osfamily') == 'windows'
53 Facter.reset
54
55 Facter.search_external([
56 File.expand_path('../../../lib/tests/fixtures/facts/external/yaml', File.dirname(__FILE__)),
57 File.expand_path('../../../lib/tests/fixtures/facts/external/json', File.dirname(__FILE__)),
58 File.expand_path('../../../lib/tests/fixtures/facts/external/text', File.dirname(__FILE__)),
59 File.expand_path("../../../lib/tests/fixtures/facts/external/#{ if windows then 'windows' else 'posix' end }/execution", File.dirname(__FILE__))
60 ])
61
62 facts = Facter.to_hash
63 expect(facts['yaml_fact1']).to be_a(String)
64 expect(facts['yaml_fact2']).to be_a(Integer)
65 expect(facts['yaml_fact3']).to(satisfy { |v| v == true || v == false })
66 expect(facts['yaml_fact4']).to be_a(Float)
67 expect(facts['yaml_fact5']).to be_a(Array)
68 expect(facts['yaml_fact6']).to be_a(Hash)
69 expect(facts['yaml_fact7']).to be_a(String)
70 expect(facts['not_bool']).to be_a(String)
71 expect(facts['not_int']).to be_a(String)
72 expect(facts['not_double']).to be_a(String)
73 expect(facts['json_fact1']).to be_a(String)
74 expect(facts['json_fact2']).to be_a(Integer)
75 expect(facts['json_fact3']).to(satisfy { |v| v == true || v == false })
76 expect(facts['json_fact4']).to be_a(Float)
77 expect(facts['json_fact5']).to be_a(Array)
78 expect(facts['json_fact6']).to be_a(Hash)
79 expect(facts['exe_fact1']).to be_a(String)
80 expect(facts['exe_fact2']).to be_a(String)
81 expect(facts['exe_fact3']).to be_nil
82 expect(facts['txt_fact1']).to be_a(String)
83 expect(facts['txt_fact2']).to be_a(String)
84 expect(facts['txt_fact3']).to be_nil
85 end
86
87 it 'should set search paths' do
88 Facter.search('foo', 'bar', 'baz')
89 expect(Facter.search_path).to include('foo', 'bar', 'baz')
90 Facter.reset
91 expect(Facter.search_path).to eq([])
92 end
93
94 it 'should set external search paths' do
95 Facter.search_external(['foo', 'bar', 'baz'])
96 expect(Facter.search_external_path).to include('foo', 'bar', 'baz')
97 end
98
99 it 'should find encoded search paths' do
100 snowman_path = File.expand_path('../../../lib/tests/fixtures/facts/external/zö', File.dirname(__FILE__))
101 encoded_path = snowman_path.encode("Windows-1252")
102 Facter.search(encoded_path)
103 expect(Facter.search_path).to include(snowman_path)
104 expect(Facter.value('snowman_fact')).to eq('olaf')
105 end
106
107 it 'should find encoded external search paths' do
108 snowman_path = File.expand_path('../../../lib/tests/fixtures/facts/external/zö', File.dirname(__FILE__))
109 encoded_path = snowman_path.encode("Windows-1252")
110 Facter.search_external([encoded_path])
111 expect(Facter.search_external_path).to include(snowman_path)
112 expect(Facter.value('snowman_fact')).to eq('olaf')
113 end
114
115 it 'should support stubbing for confine testing' do
116 Facter.fact(:osfamily).expects(:value).at_least(1).returns 'foo'
117 expect(Facter.fact(:osfamily).value).to eq('foo')
118 Facter.add(:test) do
119 confine osfamily: 'foo'
120 setcode do
121 'bar'
122 end
123 end
124 expect(Facter.value(:test)).to eq('bar')
125 end
126
127 it 'should allow stubbing on which and exec' do
128 Facter::Util::Resolution.expects(:which).with("foo").returns('/usr/bin/foo')
129 Facter::Util::Resolution.expects(:exec).with("foo").returns('bar')
130 expect(Facter::Util::Resolution.which('foo')).to eq('/usr/bin/foo')
131 expect(Facter::Util::Resolution.exec('foo')).to eq('bar')
132 end
133 end
134
135 end
+0
-27
lib/spec_helper.rb.in less more
0 # Set FACTERDIR so that facter.rb can find libfacter.so
1 ENV['FACTERDIR'] = '${CMAKE_BINARY_DIR}'
2
3 # Add the location of libfacter.so to the path for Windows
4 ENV['PATH'] = '${CMAKE_BINARY_DIR}/${LIBFACTER_INSTALL_DESTINATION}' + File::PATH_SEPARATOR + ENV['PATH']
5
6 require 'mocha'
7 require 'rspec'
8 require '${CMAKE_BINARY_DIR}/lib/facter.rb'
9
10 RSpec.configure do |config|
11 config.mock_with :mocha
12
13 config.before :each do
14 # Store any environment variables away to be restored later
15 @old_env = {}
16 ENV.each_key {|k| @old_env[k] = ENV[k]}
17 end
18
19 config.after :each do
20 # Restore environment variables after execution of each test
21 @old_env.each_pair {|k, v| ENV[k] = v}
22 to_remove = ENV.keys.reject {|key| @old_env.include? key }
23 to_remove.each {|key| ENV.delete key }
24 end
25 end
26
+0
-45
lib/src/cwrapper.cc less more
0 #include <facter/cwrapper.hpp>
1
2 #include <facter/facts/collection.hpp>
3 #include <facter/util/config.hpp>
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <sstream>
8
9 uint8_t get_default_facts(char **result) {
10 try {
11 // NB: ssh resolver cannot be blocked
12 facter::facts::collection facts {{}, // blocklist set
13 {}, // ttls map (resolvers - ttl)
14 true}; // ignore_cache flag
15
16 // The boolean arg is meant to avoid including ruby facts
17 facts.add_default_facts(false);
18
19 // NB: skipping the add_environment_facts() call
20
21 // TODO: consider iterating only the facts we're interested
22 // in by using the 'queries' arg
23 std::ostringstream stream;
24 facts.write(stream,
25 facter::facts::format::json,
26 {}, // queries vector
27 true, // show_legacy flag
28 true); // strict_errors flag
29
30 auto json_facts = stream.str();
31 auto l = json_facts.length()+1;
32
33 *result = static_cast<char*>(malloc(sizeof(char)*l));
34 if (*result == nullptr) {
35 return EXIT_FAILURE;
36 }
37
38 strncpy(*result, json_facts.c_str(), l);
39 } catch (const std::exception&) {
40 return EXIT_FAILURE;
41 }
42
43 return EXIT_SUCCESS;
44 }
+0
-36
lib/src/facts/aix/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/aix/disk_resolver.hpp>
2 #include <internal/facts/aix/filesystem_resolver.hpp>
3 #include <internal/facts/aix/kernel_resolver.hpp>
4 #include <internal/facts/aix/load_average_resolver.hpp>
5 #include <internal/facts/aix/memory_resolver.hpp>
6 #include <internal/facts/aix/nim_resolver.hpp>
7 #include <internal/facts/aix/networking_resolver.hpp>
8 #include <internal/facts/aix/operating_system_resolver.hpp>
9 #include <internal/facts/aix/processor_resolver.hpp>
10 #include <internal/facts/aix/serial_number_resolver.hpp>
11 #include <internal/facts/ssh_resolver.hpp>
12 #include <internal/facts/posix/identity_resolver.hpp>
13 #include <internal/facts/posix/timezone_resolver.hpp>
14 #include <internal/facts/posix/uptime_resolver.hpp>
15
16 using namespace std;
17
18 namespace facter { namespace facts {
19 void collection::add_platform_facts() {
20 add(make_shared<aix::disk_resolver>());
21 add(make_shared<aix::filesystem_resolver>());
22 add(make_shared<aix::kernel_resolver>());
23 add(make_shared<aix::load_average_resolver>());
24 add(make_shared<aix::memory_resolver>());
25 add(make_shared<aix::networking_resolver>());
26 add(make_shared<aix::nim_resolver>());
27 add(make_shared<aix::operating_system_resolver>());
28 add(make_shared<aix::processor_resolver>());
29 add(make_shared<aix::serial_number_resolver>());
30 add(make_shared<ssh_resolver>());
31 add(make_shared<posix::identity_resolver>());
32 add(make_shared<posix::timezone_resolver>());
33 add(make_shared<posix::uptime_resolver>());
34 }
35 }} // namespace facter::facts
+0
-75
lib/src/facts/aix/disk_resolver.cc less more
0 #include <internal/facts/aix/disk_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2 #include <internal/util/posix/scoped_descriptor.hpp>
3 #include <leatherman/logging/logging.hpp>
4
5 #include <fcntl.h>
6 #include <odmi.h>
7 #include <sys/cfgodm.h>
8 #include <sys/ioctl.h>
9 #include <sys/devinfo.h>
10
11 using namespace facter::util::aix;
12 using namespace facter::util::posix;
13 using namespace std;
14
15 namespace facter { namespace facts { namespace aix {
16
17 disk_resolver::data disk_resolver::collect_data(collection& facts)
18 {
19 data result;
20 vector<string> disk_types;
21 auto pd_dv_query = odm_class<PdDv>::open("PdDv").query("class=disk");
22 for (auto& pd_dv : pd_dv_query) {
23 LOG_DEBUG("got a disk type: {1}", pd_dv.uniquetype);
24 disk_types.push_back(pd_dv.uniquetype);
25 }
26
27 auto cu_dv = odm_class<CuDv>::open("CuDv");
28 for (string& type : disk_types) {
29 string query = (boost::format("PdDvLn=%1%") % type).str();
30 auto cu_dv_query = cu_dv.query(query);
31 for (auto& cu_dv : cu_dv_query) {
32 LOG_DEBUG("got a disk: {1}", cu_dv.name);
33 disk d;
34 d.name = cu_dv.name;
35
36 {
37 string device = (boost::format("/dev/%1%") % d.name).str();
38 auto descriptor = open(device.c_str(), O_RDONLY);
39 if (descriptor < 0) {
40 LOG_DEBUG("Could not open device %1% for reading: %2% (%3%). Disk facts will not be populated for this device", d.name, strerror(errno), errno);
41 continue;
42 }
43 scoped_descriptor fd(descriptor);
44 devinfo info;
45 auto result = ioctl(fd, IOCINFO, &info);
46 if (result < 0) {
47 LOG_DEBUG("Ioctl IOCINFO failed for device %1%: %2% (%3%). Disk facts will not be populated for this device", d.name, strerror(errno), errno);
48 continue;
49 }
50 switch (info.devtype) {
51 case DD_DISK:
52 case DD_SCDISK:
53 if (info.flags & DF_LGDSK) {
54 d.size = (uint32_t)info.un.scdk64.hi_numblks;
55 d.size <<= 32;
56 d.size |= (uint32_t)info.un.scdk64.lo_numblks;
57 d.size *= info.un.scdk64.blksize;
58 } else {
59 d.size = (uint32_t)info.un.scdk.numblks;
60 d.size *= info.un.scdk.blksize;
61 }
62 break;
63 default:
64 LOG_WARNING("Expected a Disk or SCSI disk device, got device code '{1}'. This is probably a Facter bug. Please report it, and include this error message.", info.devtype);
65 break;
66 }
67 }
68 result.disks.emplace_back(move(d));
69 }
70 }
71
72 return result;
73 }
74 }}} // namespace facter::facts::aix
+0
-132
lib/src/facts/aix/filesystem_resolver.cc less more
0 #include <internal/facts/aix/filesystem_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2 #include <internal/util/aix/vmount.hpp>
3 #include <leatherman/file_util/file.hpp>
4 #include <leatherman/logging/logging.hpp>
5
6 #include <boost/algorithm/string.hpp>
7
8 #include <map>
9 #include <string>
10 #include <vector>
11
12 #include <lvm.h>
13 #include <odmi.h>
14 #include <sys/cfgodm.h>
15 #include <sys/statfs.h>
16
17 using namespace std;
18 using namespace facter::util::aix;
19 namespace fu = leatherman::file_util;
20
21 namespace facter { namespace facts { namespace aix {
22 filesystem_resolver::data filesystem_resolver::collect_data(collection& facts)
23 {
24 data result;
25 collect_filesystem_data(result);
26 collect_mountpoint_data(result);
27 collect_partition_data(result);
28 return result;
29 }
30
31 void filesystem_resolver::collect_filesystem_data(data& result)
32 {
33 fu::each_line("/etc/vfs", [&](const string& line) {
34 auto first_char = line.find_first_not_of(" \t");
35 if (first_char != string::npos && // skip completely blank lines
36 line[first_char] != '#' && // skip comment lines
37 line[first_char] != '%') { // skip defaultvfs line
38 vector<string> tokens;
39 boost::split(tokens, line, boost::is_space(), boost::token_compress_on);
40 _filesystems.emplace(make_pair(stoul(tokens[1]), tokens[0]));
41 result.filesystems.emplace(move(tokens[0]));
42 }
43 return true;
44 });
45 }
46
47 void filesystem_resolver::collect_mountpoint_data(data& result)
48 {
49 for (const auto& mount : mountctl()) {
50 mountpoint m;
51 // AIX Version 5.3 does not define the MNT_AHAFS variable.
52 // Anything above aix 5.3 will have _AIXVERSION_610 defined.
53 #ifndef _AIXVERSION_610
54 if (mount.vmt_gfstype == MNT_PROCFS) {
55 continue;
56 }
57 #else
58 if (mount.vmt_gfstype == MNT_PROCFS || mount.vmt_gfstype == MNT_AHAFS) {
59 continue;
60 }
61 #endif
62 m.filesystem = _filesystems[mount.vmt_gfstype];
63 m.device = reinterpret_cast<char*>(vmt2dataptr(&mount, VMT_OBJECT));
64 m.name = reinterpret_cast<char*>(vmt2dataptr(&mount, VMT_STUB));
65
66 string opts = reinterpret_cast<char*>(vmt2dataptr(&mount, VMT_ARGS));
67 boost::split(m.options, opts, boost::is_any_of(","));
68
69 struct statfs64 fs;
70 if (0 != statfs64(const_cast<char*>(m.name.c_str()), &fs)) {
71 LOG_WARNING("Could not get fs data for {1}: {2}", m.name, errno);
72 } else {
73 m.size = fs.f_bsize*fs.f_blocks;
74 m.available = fs.f_bsize*fs.f_bfree;
75 }
76
77 _mounts[m.device] = m.name;
78 result.mountpoints.emplace_back(move(m));
79 }
80 }
81
82 void filesystem_resolver::collect_partition_data(data& result)
83 {
84 auto cu_dv = odm_class<CuDv>::open("CuDv");
85 auto cu_at = odm_class<CuAt>::open("CuAt");
86 for (auto& dv : cu_dv.query("PdDvLn=logical_volume/lvsubclass/lvtype")) {
87 partition p;
88 p.name = string("/dev/") + dv.name;
89
90 string query = (boost::format("name=%1%") % dv.name).str();
91 for (auto& at : cu_at.query(query)) {
92 if (0 == strcmp(at.attribute, "label")) {
93 p.label = at.value;
94 } else if (0 == strcmp(at.attribute, "lvserial_id")) {
95 struct lv_id id;
96 // AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD.EEEE format
97 // First four chunks are hexadecimal 32-bit integers.
98 // After the dot is a decimal integer
99 // Volume groups from the 90s only have 64-bit IDs,
100 // rather than the full 128.
101
102 auto vgid = string(at.value);
103 auto length = vgid.find_first_of('.');
104 id.vg_id.word1 = stoul(vgid.substr(0, 8), nullptr, 16);
105 id.vg_id.word2 = stoul(vgid.substr(8, 8), nullptr, 16);
106 if (length == 32) {
107 id.vg_id.word3 = stoul(vgid.substr(16, 8), nullptr, 16);
108 id.vg_id.word4 = stoul(vgid.substr(24, 8), nullptr, 16);
109 }
110 id.minor_num = stoul(vgid.substr(length+1, string::npos), nullptr, 10);
111
112 struct querylv* lv;
113 if (0 != lvm_querylv(&id, &lv, nullptr)) {
114 LOG_WARNING("Could not get info for partition '{1}' from the LVM subsystem", p.name);
115 } else if (!lv) {
116 LOG_WARNING("querylv returned success but we got a null LV. WTF?");
117 } else {
118 // Size is calculated as "currentsize * 2^ppsize".
119 p.size = lv->currentsize;
120 p.size <<= lv->ppsize;
121 }
122 } else if (0 == strcmp(at.attribute, "type")) {
123 p.filesystem = at.value;
124 }
125 }
126
127 p.mount = _mounts[p.name];
128 result.partitions.emplace_back(move(p));
129 }
130 }
131 }}}
+0
-27
lib/src/facts/aix/kernel_resolver.cc less more
0 #include <internal/facts/aix/kernel_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/execution/execution.hpp>
4 #include <leatherman/util/regex.hpp>
5 #include <leatherman/file_util/file.hpp>
6 #include <boost/regex.hpp>
7
8 using namespace leatherman::execution;
9 using namespace std;
10
11 namespace facter { namespace facts { namespace aix {
12
13 kernel_resolver::data kernel_resolver::collect_data(collection& facts)
14 {
15 data result;
16
17 auto exec = execute("/usr/bin/oslevel", {"-s"});
18
19 result.name = "AIX";
20 result.release = exec.output;
21 result.version = exec.output.substr(0, 4);
22
23 return result;
24 }
25
26 }}} // namespace facter::facts::aix
+0
-50
lib/src/facts/aix/load_average_resolver.cc less more
0 #include <internal/facts/aix/load_average_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <leatherman/locale/locale.hpp>
3
4 #include <sys/inttypes.h>
5 #include <sys/kinfo.h>
6
7 // Mark string for translation (alias for leatherman::locale::format)
8 using leatherman::locale::_;
9
10 using namespace std;
11
12 /**
13 * This system call lets us query the kernel directly for system
14 * information. We use it to get our current load averages.
15 *
16 * @param info the info we're retrieving from the kernel.
17 * @param buf the buffer that we'll store the information in
18 * @param buf_size a pointer to the variable containing the size of the buffer in bytes
19 * @param arg no idea what this param. represents. we will usually set this to 0.
20 * @return 0 if we successfully retrieve the information, else a negative value
21 */
22 extern "C" int getkerninfo(int info, char* buf, int* buf_size, int32long64_t arg);
23
24 // Converts the given integer average into a load average.
25 static double to_load_avg(double average) {
26 // 65536 is the load average scale on AIX machines.
27 return average / 65536;
28 }
29
30 namespace facter { namespace facts { namespace aix {
31
32 boost::optional<tuple<double, double, double> > load_average_resolver::get_load_averages()
33 {
34 // This approach was adapted from screen-4.6.2's loadav.c file. See
35 // https://www.mail-archive.com/opensuse-commit@opensuse.org/msg122486.html
36 array<long long, 3> averages;
37 int buf_size = averages.size() * sizeof(long long);
38 int rc = getkerninfo(KINFO_GET_AVENRUN, reinterpret_cast<char*>(averages.data()), &buf_size, 0);
39 if (rc < 0) {
40 LOG_DEBUG(_("failed to retrieve the load averages"));
41 return boost::none;
42 }
43
44 return make_tuple(
45 to_load_avg(averages[0]),
46 to_load_avg(averages[1]),
47 to_load_avg(averages[2]));
48 }
49 }}} // namespace facter::facts::aix
+0
-63
lib/src/facts/aix/memory_resolver.cc less more
0 #include <internal/facts/aix/memory_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2
3 #include <leatherman/logging/logging.hpp>
4 #include <sys/vminfo.h>
5 #include <sys/cfgodm.h>
6 #include <sys/limits.h>
7 #include <system_error>
8
9 using namespace std;
10 using namespace facter::util::aix;
11
12 // This routine is useful to encapsulate knowledge of the PAGE_SIZE
13 // in one place and to also handle implicit conversions of numeric
14 // values to uint64_t, which is what we use to represent bytes. Otherwise,
15 // we risk accidentally capturing an overflowed value in our computed
16 // memory facts.
17 static uint64_t pages_to_bytes(uint64_t num_pages) {
18 return num_pages * PAGE_SIZE;
19 }
20
21 namespace facter { namespace facts { namespace aix {
22 memory_resolver::data memory_resolver::collect_data(collection& facts)
23 {
24 data result;
25
26 vminfo info;
27 auto res = vmgetinfo(&info, VMINFO, sizeof(info));
28 if (res < 0) {
29 throw system_error(errno, system_category());
30 }
31 result.mem_total = pages_to_bytes(info.memsizepgs);
32 result.mem_free = pages_to_bytes(info.numfrb);
33
34 auto cu_at_query = odm_class<CuAt>::open("CuAt").query("value=paging and attribute=type");
35 for (auto& cu_at : cu_at_query) {
36 string device = string("/dev/") + cu_at.name;
37 pginfo info;
38 auto res = swapqry(const_cast<char*>(device.c_str()), &info);
39 if (res < 0) {
40 // it's really hard to tell from the ODM if a device
41 // is a disk, just by its name. So we'll always try to
42 // swapqry the things that have an attribute we
43 // expect, but ignore any errno values that look like
44 // "this just wasn't a good device to query"
45 // ENXIO: No such device address.
46 if (errno != ENODEV &&
47 errno != ENOENT &&
48 errno != ENOTBLK &&
49 errno != ENXIO) {
50 throw system_error(errno, system_category(), device);
51 } else {
52 LOG_DEBUG("cannot use device {1}: error is {2}", device, errno);
53 }
54 }
55
56 result.swap_total += pages_to_bytes(info.size);
57 result.swap_free += pages_to_bytes(info.free);
58 }
59
60 return result;
61 }
62 }}} // namespace facter::facts::aix
+0
-250
lib/src/facts/aix/networking_resolver.cc less more
0 #include <internal/facts/aix/networking_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/locale/locale.hpp>
4 #include <boost/algorithm/string.hpp>
5
6 #include <unordered_map>
7
8 #include <inttypes.h>
9 #include <net/if.h>
10 #include <net/if_dl.h>
11 #include <net/route.h>
12 #include <netinet/in.h>
13 #include <sys/kinfo.h>
14
15 // Mark string for translation (alias for leatherman::locale::format)
16 using leatherman::locale::_;
17
18 // This usage is recommended in several mailing lists, and is used at
19 // least in Samba to query mac addresses. I saw some references to old
20 // IBM publications also recommending its use, but couldn't actually
21 // dig those pubs up.
22 //
23 // The structs it *returns* are in headers, though (specifically
24 // mentioning usage of this function).
25 //
26 // This is all leftovers from ancient versions of the BSD networking
27 // stack that every OS in the world has stolen/borrowed. It is pretty
28 // gross in a lot of ways, but is what we have to work with on AIX.
29 //
30 // There does not appear to be a different or documented way to get
31 // this information, outside of shelling out to tools which provide
32 // human- rather than machine-readable output.
33 extern "C" int getkerninfo(int, char*, int*, int32long64_t);
34
35 template <typename T>
36 static std::vector<T> getkerninfo(int query) {
37 for (;;) {
38 auto ksize = getkerninfo(query, nullptr, nullptr, 0);
39 if (ksize == 0) {
40 throw std::runtime_error(_("getkerninfo call was unsuccessful"));
41 }
42 auto alloc_size = ksize;
43 auto count = alloc_size/sizeof(T);
44 std::vector<T> result(count);
45 if (getkerninfo(query, reinterpret_cast<char*>(result.data()), &ksize, 0) == -1) {
46 throw std::runtime_error(_("getkerninfo call was unsuccessful"));
47 }
48
49 // getkerninfo updates the size variable to match our actual
50 // buffer size. If we need more space we loop to
51 // reallocate. Otherwise we make sure the vector is trimmed to
52 // the proper size and return the contents.
53 if (ksize <= alloc_size) {
54 result.resize(ksize/sizeof(T));
55 return result;
56 }
57 }
58 }
59
60 namespace facter { namespace facts { namespace aix {
61 networking_resolver::data networking_resolver::collect_data(collection& facts) {
62 auto data = posix::networking_resolver::collect_data(facts);
63
64 // Query the kernel for the list of network interfaces and
65 // their associated addresses
66 data.interfaces = get_interfaces();
67
68 // query the network device descriptors from the kernel. This
69 // gives us physical information, such as mtu.
70 auto mtus = get_mtus();
71
72 for (auto& iface : data.interfaces) {
73 auto mtu_iter = mtus.find(iface.name);
74 if (mtu_iter != mtus.end()) {
75 iface.mtu = stoll(mtu_iter->second);
76 }
77 }
78
79 data.primary_interface = get_primary_interface();
80
81 return data;
82 }
83
84 networking_resolver::mtu_map networking_resolver::get_mtus() const {
85 mtu_map result;
86 leatherman::execution::each_line("/usr/bin/netstat", {"-in"}, [&](std::string line) {
87 std::vector<std::string> fields;
88 boost::trim(line);
89 boost::split(fields, line, boost::is_space(), boost::token_compress_on);
90 if (boost::starts_with(fields[2], "link")) {
91 result[fields[0]] = fields[1];
92 }
93 return true;
94 });
95 return result;
96 }
97
98 std::vector<networking_resolver::interface> networking_resolver::get_interfaces() const {
99 auto buffer = getkerninfo<char>(KINFO_RT_IFLIST);
100
101 // interfaces are identified by 16-bit IDs. these may or may
102 // not be sequential, so we use a map as a sparse array
103 std::map<u_short, interface> ifaces;
104
105 decltype(buffer)::size_type cursor = 0;
106 while (cursor < buffer.size()) {
107 if_msghdr* hdr = reinterpret_cast<if_msghdr*>(buffer.data() + cursor);
108
109 switch (hdr->ifm_type) {
110 case RTM_IFINFO: {
111 sockaddr_dl* link_addr = reinterpret_cast<sockaddr_dl*>(hdr+1); // sockaddr immediately follows the header
112
113 // Name is not zero-terminated, we must pass the length to the string constructor.
114 ifaces[hdr->ifm_index].name = std::string(link_addr->sdl_data, link_addr->sdl_nlen);
115
116 // The mac address is stored in binary immediately following the name length
117 ifaces[hdr->ifm_index].macaddress = macaddress_to_string(reinterpret_cast<uint8_t*>(link_addr->sdl_data+link_addr->sdl_nlen));
118 break;
119 }
120 case RTM_NEWADDR: {
121 // This is gross. Immediately following the header is
122 // a number of addresses which may or may not
123 // individually be present based on a bitfield. They
124 // are stored in a specific order, at least.
125 // Additionally, each address struct could be cut off
126 // or padded - we need to check the length of each one
127 // to know where the next one starts. PLUS we don't
128 // know whether we're looking at IPV4 or IPV6 until we
129 // find an address that actually specifies its
130 // protocol (the first one might not).
131
132 // sockaddr_storage is guaranteed to be big enough for the memcpy below.
133 std::array<sockaddr_storage, RTAX_MAX> addrs;
134 memset(addrs.data(), 0, RTAX_MAX*sizeof(sockaddr_storage));
135
136 // This represents our position walking the list of address objects
137 int addr_cursor = cursor + sizeof(if_msghdr);
138
139 #define FACT_READ_ADDR(a) if (hdr->ifm_addrs & RTA_##a) { \
140 sockaddr* sa = reinterpret_cast<sockaddr*>(buffer.data()+addr_cursor); \
141 memcpy(&(addrs[ RTAX_##a ]), sa, sa->sa_len); \
142 addr_cursor += RT_ROUNDUP(sa); \
143 }
144 FACT_READ_ADDR(DST);
145 FACT_READ_ADDR(GATEWAY);
146 FACT_READ_ADDR(NETMASK);
147 FACT_READ_ADDR(GENMASK);
148 FACT_READ_ADDR(IFP);
149 FACT_READ_ADDR(IFA);
150 FACT_READ_ADDR(AUTHOR);
151 FACT_READ_ADDR(BRD);
152
153 // WOO addresses read. Now we try to figure out if
154 // we're IPv4 or IPv6. We skip any other families, and
155 // warn if we get a mixed set of families.
156
157 int family = AF_UNSPEC;
158 for (const auto& addr : addrs) {
159 if (family != AF_UNSPEC &&
160 addr.ss_family != AF_UNSPEC &&
161 family != addr.ss_family) {
162 family = AF_MAX;
163 break;
164 }
165 family = addr.ss_family;
166 }
167
168 binding addr_binding;
169 sockaddr* netmask = reinterpret_cast<sockaddr*>(&addrs[RTAX_NETMASK]);
170 sockaddr* address = reinterpret_cast<sockaddr*>(&addrs[RTAX_IFA]);
171 if (netmask->sa_len) {
172 netmask->sa_family = family; // AIX likes to return the netmask with AF_UNSPEC family.
173 addr_binding.netmask = address_to_string(netmask);
174 }
175 if (address->sa_len) {
176 addr_binding.address = address_to_string(address);
177 }
178 if (address->sa_len && netmask->sa_len) {
179 addr_binding.network = address_to_string(address, netmask);
180 }
181
182 if (family == AF_MAX) {
183 LOG_WARNING("got mixed address families for interface {1}, can't map them to a single binding.", ifaces[hdr->ifm_index].name);
184 } else if (family == AF_INET) {
185 LOG_INFO("got ipv4 addresses for interface {1}", ifaces[hdr->ifm_index].name);
186 ifaces[hdr->ifm_index].ipv4_bindings.push_back(addr_binding);
187 } else if (family == AF_INET6) {
188 LOG_INFO("got ipv6 addresses for interface {1}", ifaces[hdr->ifm_index].name);
189 ifaces[hdr->ifm_index].ipv6_bindings.push_back(addr_binding);
190 } else if (family != AF_UNSPEC) {
191 LOG_INFO("skipping unknown address family {1} for interface {2}", family, ifaces[hdr->ifm_index].name);
192 } else {
193 LOG_INFO("somehow didn't get an address family for interface {1}", ifaces[hdr->ifm_index].name);
194 }
195 break;
196 }
197 default: {
198 LOG_INFO("got an unknown RT_IFLIST message: {1}", hdr->ifm_type);
199 break;
200 }
201 }
202
203 cursor += hdr->ifm_msglen;
204 }
205
206 // Now that we're done processing the data we don't care about
207 // the kernel's iface IDs anymore.
208 std::vector<interface> result;
209 for (auto& iface : ifaces) {
210 result.push_back(iface.second);
211 }
212 return result;
213 }
214
215 std::string networking_resolver::get_primary_interface() const
216 {
217 std::string value;
218 leatherman::execution::each_line("netstat", { "-rn"}, [&value](std::string& line) {
219 boost::trim(line);
220 if (boost::starts_with(line, "default")) {
221 std::vector<std::string> fields;
222 boost::split(fields, line, boost::is_space(), boost::token_compress_on);
223 value = fields.size() < 6 ? "" : fields[5];
224 return false;
225 }
226 return true;
227 });
228 return value;
229 }
230
231
232 bool networking_resolver::is_link_address(const sockaddr* addr) const
233 {
234 // We explicitly populate the MAC address; we don't need address_to_string to support link layer addresses
235 return false;
236 }
237
238 uint8_t const* networking_resolver::get_link_address_bytes(const sockaddr * addr) const
239 {
240 return nullptr;
241 }
242
243 uint8_t networking_resolver::get_link_address_length(const sockaddr * addr) const
244 {
245 return 0;
246 }
247
248
249 }}} // namespace facter::facts::aix
+0
-51
lib/src/facts/aix/nim_resolver.cc less more
0 #include <internal/facts/aix/nim_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <leatherman/logging/logging.hpp>
6 #include <leatherman/execution/execution.hpp>
7 #include <leatherman/util/regex.hpp>
8 #include <boost/regex.hpp>
9
10 #include <sys/cfgodm.h>
11
12 using namespace std;
13 using namespace facter::util::aix;
14 using namespace leatherman::util;
15 using namespace leatherman::execution;
16 using namespace facter::facts;
17
18 namespace facter { namespace facts { namespace aix {
19
20 nim_resolver::nim_resolver() :
21 resolver(
22 "AIX NIM type",
23 {
24 fact::nim_type,
25 })
26 {
27 }
28
29 string nim_resolver::read_niminfo() {
30 auto exec = execute("cat", {"/etc/niminfo"});
31 if (!exec.success) {
32 LOG_DEBUG("Could not read `/etc/niminfo`");
33 return {};
34 }
35
36 boost::smatch type;
37 boost::regex expr{"NIM_CONFIGURATION=(master|standalone)"};
38 boost::regex_search(exec.output, type, expr);
39
40 return type[1];
41 }
42
43 void nim_resolver::resolve(collection& facts) {
44 string type = read_niminfo();
45 if (type.empty()) {
46 return;
47 }
48 facts.add(fact::nim_type, make_value<string_value>(type, true));
49 }
50 }}}; // namespace facter::facts::aix
+0
-99
lib/src/facts/aix/operating_system_resolver.cc less more
0 #include <internal/facts/aix/operating_system_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/os.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <facter/facts/scalar_value.hpp>
8 #include <leatherman/logging/logging.hpp>
9
10 #include <boost/algorithm/string.hpp>
11 #include <odmi.h>
12 #include <sys/cfgodm.h>
13
14 using namespace std;
15 using namespace facter::util::aix;
16
17 // This routine's meant to be a general utility function that replicates the behavior of
18 // lsattr -El <object> -a <field>. Although it's only used to get the modelname of the
19 // sys0 device, we still would like to have it here in case we ever need to separate it
20 // out to an AIX utils file to query other attributes. Part of what lsattr does is check
21 // PdAt if we don't have an entry for the object's attribute in CuAt, even though it is very
22 // unlikely that sys0.modelname will not have a CuAt entry. That is why we have the extra code
23 // in here.
24 static string getattr(string object, string field)
25 {
26 // High-level logic here is:
27 // * Check if there's an entry for our object's field attribute in CuAt (the device-specific
28 // attribute entry).
29 //
30 // * Else, check for the field attribute's default value in PdAt. We do this by first
31 // figuring out the PdDv type from the CuDv entry for the object, then use our PdDv type
32 // to query the field's default value in PdAt.
33 string query = (boost::format("name = %1% AND attribute = %2%") % object % field).str();
34 auto cuat_query = odm_class<CuAt>::open("CuAt").query(query);
35
36 // This is a more verbose way of saying that we only expect our query to have one element
37 auto cuat_ref = cuat_query.begin();
38 if (cuat_ref != cuat_query.end()) {
39 auto value = string(cuat_ref->value);
40 if (value.empty()) {
41 LOG_DEBUG("Could not get a value from the ODM for {1}'s '{2}' attribute.", object, field);
42 }
43 return value;
44 }
45
46 // Get the PdDv type from the CuDv entry
47 query = (boost::format("name = %1%") % object).str();
48 auto cudv_query = odm_class<CuDv>::open("CuDv").query(query);
49 auto cudv_ref = cudv_query.begin();
50 if (cudv_ref == cudv_query.end()) {
51 LOG_DEBUG("Could not get a value from the ODM for {1}'s '{2}' attribute: There is no CuDv entry for {1}.", object, field);
52 return "";
53 }
54 auto pddv_type = cudv_ref->PdDvLn_Lvalue;
55
56 query = (boost::format("uniquetype = %1% AND attribute = %2%") % pddv_type % field).str();
57 auto pdat_query = odm_class<PdAt>::open("PdAt").query(query);
58 auto pdat_ref = pdat_query.begin();
59 if (pdat_ref != pdat_query.end()) {
60 auto value = string(pdat_ref->deflt);
61 if (value.empty()) {
62 LOG_DEBUG("Could not get a value from the ODM for {1}'s '{2}' attribute.", object, field);
63 }
64 return value;
65 }
66
67 LOG_DEBUG("Could not get a value from the ODM for {1}'s '{2}' attribute: There is no PdAt entry for {1} with {2}.", object, field);
68 return "";
69 }
70
71 namespace facter { namespace facts { namespace aix {
72
73 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
74 {
75 // Default to the base implementation
76 auto result = posix::operating_system_resolver::collect_data(facts);
77
78 // on AIX, major version is hyphen-delimited. The base
79 // resolver can't figure this out for us.
80 vector<string> tokens;
81 boost::split(tokens, result.release, boost::is_any_of("-"));
82 result.major = tokens[0];
83
84 // Get the hardware
85 result.hardware = getattr("sys0", "modelname");
86
87 // Now get the architecture. We use processor.models[0] for this information.
88 auto processors = facts.get<map_value>(fact::processors);
89 auto models = processors ? processors->get<array_value>("models") : nullptr;
90 if (! models || models->empty()) {
91 LOG_DEBUG("Could not get a value for the OS architecture. Your machine does not have any processors!");
92 } else {
93 result.architecture = models->get<string_value>(0)->value();
94 }
95
96 return result;
97 }
98 }}}
+0
-106
lib/src/facts/aix/processor_resolver.cc less more
0 #include <facter/util/string.hpp>
1 #include <internal/facts/aix/processor_resolver.hpp>
2 #include <internal/util/aix/odm.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <leatherman/util/scope_exit.hpp>
5
6 #include <cstdint>
7 #include <stdexcept>
8 #include <odmi.h>
9 #include <sys/cfgodm.h>
10
11 using namespace std;
12 using namespace facter::util::aix;
13 using facter::util::maybe_stoi;
14
15 struct physical_processor
16 {
17 string type;
18 long long frequency;
19 int smt_threads;
20 bool smt_enabled;
21 };
22
23 namespace facter { namespace facts { namespace aix {
24
25 processor_resolver::data processor_resolver::collect_data(collection& facts)
26 {
27 auto result = posix::processor_resolver::collect_data(facts);
28
29 // On AIX, we query the object data manager (odm) for
30 // processor information. This is a semi-hierarchical
31 // datastore of all the information about the system. For
32 // processors, we need to go through three links:
33 //
34 // 1. We query for all "predefined devices" with the
35 // "processor" class. I don't know if it's actually possible
36 // to get more than one result here - on the Puppet LPARs
37 // there is only one.
38 //
39 // 2. For each predefined device, we query the "custom
40 // devices". These represent the actual processors in the
41 // machine.
42 //
43 // 3. For each custom device, we query its attributes. These
44 // are things like frequency, type, and SMT information.
45
46 vector<string> processor_types;
47 auto pd_dv_query = odm_class<PdDv>::open("PdDv").query("class=processor");
48 for (auto& pd_dv : pd_dv_query) {
49 LOG_DEBUG("got a processor type: {1}", pd_dv.uniquetype);
50 processor_types.push_back(pd_dv.uniquetype);
51 }
52
53 vector<string> processor_names;
54 for (string& type : processor_types) {
55 string query = (boost::format("PdDvLn=%1%") % type).str();
56 auto cu_dv_query = odm_class<CuDv>::open("CuDv").query(query);
57 for (auto& cu_dv : cu_dv_query) {
58 LOG_DEBUG("got a processor: {1}", cu_dv.name);
59 processor_names.push_back(cu_dv.name);
60 }
61 }
62
63 for (string& name : processor_names) {
64 string query = (boost::format("name=%1%") % name).str();
65 physical_processor proc;
66 auto cu_at_query = odm_class<CuAt>::open("CuAt").query(query);
67 for (auto& cu_at : cu_at_query) {
68 LOG_DEBUG("got attribute {1}={2} for processor {3}", cu_at.attribute, cu_at.value, name);
69 if (cu_at.attribute == string("frequency")) {
70 proc.frequency = stoll(cu_at.value);
71 } else if (cu_at.attribute == string("type")) {
72 proc.type = cu_at.value;
73 } else if (cu_at.attribute == string("smt_threads")) {
74 auto smt_threads = maybe_stoi(cu_at.value);
75 if (smt_threads) {
76 proc.smt_threads = smt_threads.get();
77 }
78 } else if (cu_at.attribute == string("smt_enabled")) {
79 proc.smt_enabled = (cu_at.value == string("true"));
80 } else {
81 LOG_INFO("don't know what to do with processor attribute {1}", cu_at.attribute)
82 }
83 }
84
85 if (result.speed == 0) {
86 result.speed = proc.frequency;
87 } else if (result.speed != proc.frequency) {
88 LOG_WARNING("mismatched processor frequencies found; facter will only report one of them");
89 }
90
91 if (proc.smt_enabled) {
92 result.logical_count += proc.smt_threads;
93 vector<string> types(proc.smt_threads, proc.type);
94 result.models.insert(result.models.begin(),
95 make_move_iterator(types.begin()),
96 make_move_iterator(types.end()));
97 } else {
98 result.logical_count += 1;
99 result.models.push_back(move(proc.type));
100 }
101 }
102
103 return result;
104 }
105 }}} // namespace facter::facts::aix
+0
-43
lib/src/facts/aix/serial_number_resolver.cc less more
0 #include <internal/facts/aix/serial_number_resolver.hpp>
1 #include <internal/util/aix/odm.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <leatherman/logging/logging.hpp>
6 #include <leatherman/util/regex.hpp>
7 #include <boost/regex.hpp>
8
9 #include <sys/cfgodm.h>
10
11 using namespace std;
12 using namespace facter::util::aix;
13 using namespace leatherman::util;
14
15 namespace facter { namespace facts { namespace aix {
16
17 serial_number_resolver::serial_number_resolver() :
18 resolver(
19 "AIX serial number",
20 {
21 fact::serial_number,
22 })
23 {
24 }
25
26 void serial_number_resolver::resolve(collection& facts) {
27 auto cu_at_query = odm_class<CuAt>::open("CuAt").query("name=sys0 and attribute=systemid");
28 auto result = *cu_at_query.begin();
29
30 // the ODM returns a string of the form "IBM,XXSERIAL#". We
31 // need to strip the "IBM,XX" from the start ofthis, and keep
32 // only the 7 actual serial number bytes.
33 const auto regex = boost::regex("^IBM,\\d\\d(.+)");
34 string serial;
35 if (re_search(string(result.value), regex, &serial)) {
36 facts.add(fact::serial_number, make_value<string_value>(move(serial), true));
37 } else {
38 LOG_WARNING("Could not retrieve serial number: sys0 systemid did not match the expected format");
39 }
40 }
41
42 }}} // namespace facter::facts::aix
+0
-111
lib/src/facts/array_value.cc less more
0 #include <facter/facts/array_value.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <rapidjson/document.h>
4 #include <yaml-cpp/yaml.h>
5
6 using namespace std;
7 using namespace rapidjson;
8 using namespace YAML;
9
10 namespace facter { namespace facts {
11
12 array_value::array_value(array_value&& other)
13 {
14 *this = std::move(other);
15 }
16
17 array_value& array_value::operator=(array_value&& other)
18 {
19 value::operator=(static_cast<value&&>(other));
20 if (this != &other) {
21 _elements = std::move(other._elements);
22 }
23 return *this;
24 }
25
26 void array_value::add(unique_ptr<value> value)
27 {
28 if (!value) {
29 LOG_DEBUG("null value cannot be added to array.");
30 return;
31 }
32
33 _elements.emplace_back(move(value));
34 }
35
36 bool array_value::empty() const
37 {
38 return _elements.empty();
39 }
40
41 size_t array_value::size() const
42 {
43 return _elements.size();
44 }
45
46 void array_value::each(function<bool(value const*)> func) const
47 {
48 for (auto const& element : _elements) {
49 if (!func(element.get())) {
50 break;
51 }
52 }
53 }
54
55 void array_value::to_json(json_allocator& allocator, json_value& value) const
56 {
57 value.SetArray();
58 value.Reserve(_elements.size(), allocator);
59
60 for (auto const& element : _elements) {
61 json_value child;
62 element->to_json(allocator, child);
63 value.PushBack(child, allocator);
64 }
65 }
66
67 value const* array_value::operator[](size_t i) const
68 {
69 if (i >= _elements.size()) {
70 return nullptr;
71 }
72 return _elements[i].get();
73 }
74
75 ostream& array_value::write(ostream& os, bool quoted, unsigned int level) const
76 {
77 if (_elements.empty()) {
78 os << "[]";
79 return os;
80 }
81
82 // Write out the elements in the array
83 os << "[\n";
84 bool first = true;
85 for (auto const& element : _elements) {
86 if (first) {
87 first = false;
88 } else {
89 os << ",\n";
90 }
91 fill_n(ostream_iterator<char>(os), level * 2, ' ');
92 element->write(os, true /* always quote strings in an array */, level + 1);
93 }
94 os << "\n";
95 fill_n(ostream_iterator<char>(os), (level > 0 ? (level - 1) : 0) * 2, ' ');
96 os << "]";
97 return os;
98 }
99
100 Emitter& array_value::write(Emitter& emitter) const
101 {
102 emitter << BeginSeq;
103 for (auto const& element : _elements) {
104 element->write(emitter);
105 }
106 emitter << EndSeq;
107 return emitter;
108 }
109
110 }} // namespace facter::facts
+0
-27
lib/src/facts/bsd/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/posix/kernel_resolver.hpp>
2 #include <internal/facts/posix/operating_system_resolver.hpp>
3 #include <internal/facts/bsd/uptime_resolver.hpp>
4 #include <internal/facts/bsd/filesystem_resolver.hpp>
5 #include <internal/facts/ssh_resolver.hpp>
6 #include <internal/facts/posix/identity_resolver.hpp>
7 #include <internal/facts/posix/timezone_resolver.hpp>
8 #include <internal/facts/glib/load_average_resolver.hpp>
9
10 using namespace std;
11
12 namespace facter { namespace facts {
13
14 void collection::add_platform_facts()
15 {
16 add(make_shared<posix::kernel_resolver>());
17 add(make_shared<posix::operating_system_resolver>());
18 add(make_shared<bsd::uptime_resolver>());
19 add(make_shared<bsd::filesystem_resolver>());
20 add(make_shared<ssh_resolver>());
21 add(make_shared<posix::identity_resolver>());
22 add(make_shared<posix::timezone_resolver>());
23 add(make_shared<glib::load_average_resolver>());
24 }
25
26 }} // namespace facter::facts
+0
-92
lib/src/facts/bsd/filesystem_resolver.cc less more
0 #include <internal/facts/bsd/filesystem_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <facter/util/string.hpp>
3 #include <sys/mount.h>
4 #include <tuple>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace facter::util;
9
10 namespace facter { namespace facts { namespace bsd {
11
12 filesystem_resolver::data filesystem_resolver::collect_data(collection& facts)
13 {
14 data result;
15
16 // First get the count of file systems
17 int count = getfsstat(nullptr, 0, MNT_NOWAIT);
18 if (count == -1) {
19 LOG_ERROR("getfsstat failed: {1} ({2}): file system facts are unavailable.", strerror(errno), errno);
20 return result;
21 }
22
23 // Get the actual data
24 vector<struct statfs> filesystems(count);
25 count = getfsstat(filesystems.data(), filesystems.size() * sizeof(struct statfs), MNT_NOWAIT);
26 if (count == -1) {
27 LOG_ERROR("getfsstat failed: {1} ({2}): file system facts are unavailable.", strerror(errno), errno);
28 return result;
29 }
30
31 result.mountpoints.reserve(count);
32
33 // Populate an entry for each mounted file system
34 for (auto& fs : filesystems) {
35 mountpoint point;
36 point.name = fs.f_mntonname;
37 point.device = fs.f_mntfromname;
38 point.filesystem = fs.f_fstypename;
39 point.size = (static_cast<uint64_t>(fs.f_bsize)
40 * static_cast<uint64_t>(fs.f_blocks));
41 point.available = (static_cast<uint64_t>(fs.f_bsize)
42 * static_cast<uint64_t>(fs.f_bfree));
43 point.options = to_options(fs);
44 result.mountpoints.emplace_back(move(point));
45
46 result.filesystems.insert(fs.f_fstypename);
47 }
48 return result;
49 }
50
51 vector<string> filesystem_resolver::to_options(struct statfs const& fs)
52 {
53 static vector<tuple<unsigned int, string>> const flags = {
54 make_tuple<unsigned int, string>(MNT_RDONLY, "readonly"),
55 make_tuple<unsigned int, string>(MNT_SYNCHRONOUS, "noasync"),
56 make_tuple<unsigned int, string>(MNT_NOEXEC, "noexec"),
57 make_tuple<unsigned int, string>(MNT_NOSUID, "nosuid"),
58 #ifndef __OpenBSD__
59 make_tuple<unsigned int, string>(MNT_UNION, "union"),
60 #endif
61 make_tuple<unsigned int, string>(MNT_ASYNC, "async"),
62 make_tuple<unsigned int, string>(MNT_EXPORTED, "exported"),
63 make_tuple<unsigned int, string>(MNT_LOCAL, "local"),
64 make_tuple<unsigned int, string>(MNT_QUOTA, "quota"),
65 make_tuple<unsigned int, string>(MNT_ROOTFS, "root"),
66 make_tuple<unsigned int, string>(MNT_NOATIME, "noatime"),
67 #if !defined(__FreeBSD__)
68 make_tuple<unsigned int, string>(MNT_NODEV, "nodev"),
69 #endif
70 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
71 // the following constants aren't defined on FreeBSD 10/OpenBSD
72 make_tuple<unsigned int, string>(MNT_DONTBROWSE, "nobrowse"),
73 make_tuple<unsigned int, string>(MNT_AUTOMOUNTED, "automounted"),
74 make_tuple<unsigned int, string>(MNT_JOURNALED, "journaled"),
75 make_tuple<unsigned int, string>(MNT_DEFWRITE, "deferwrites"),
76 #endif
77 #ifdef __OpenBSD__
78 make_tuple<unsigned int, string>(MNT_WXALLOWED, "wxallowed"),
79 #endif
80 };
81
82 vector<string> options;
83 for (auto const& flag : flags) {
84 if (fs.f_flags & get<0>(flag)) {
85 options.push_back(get<1>(flag));
86 }
87 }
88 return options;
89 }
90
91 }}} // namespace facter::facts::bsd
+0
-273
lib/src/facts/bsd/networking_resolver.cc less more
0 #include <facter/util/string.hpp>
1 #include <internal/facts/bsd/networking_resolver.hpp>
2 #include <internal/util/bsd/scoped_ifaddrs.hpp>
3 #include <leatherman/execution/execution.hpp>
4 #include <leatherman/file_util/file.hpp>
5 #include <leatherman/file_util/directory.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <leatherman/util/regex.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <boost/filesystem.hpp>
10 #include <netinet/in.h>
11 #include <unordered_map>
12
13 using namespace std;
14 using namespace facter::util::bsd;
15 using namespace leatherman::execution;
16 using namespace leatherman::util;
17 using facter::util::maybe_stoi;
18
19 namespace lth_file = leatherman::file_util;
20
21 namespace facter { namespace facts { namespace bsd {
22
23 networking_resolver::data networking_resolver::collect_data(collection& facts)
24 {
25 auto data = posix::networking_resolver::collect_data(facts);
26
27 // Scope the head ifaddrs ptr
28 scoped_ifaddrs addrs;
29 if (!addrs) {
30 LOG_WARNING("getifaddrs failed: {1} ({2}): interface information is unavailable.", strerror(errno), errno);
31 return data;
32 }
33
34 // Map an interface to entries describing that interface
35 multimap<string, ifaddrs const*> interface_map;
36 for (ifaddrs* ptr = addrs; ptr; ptr = ptr->ifa_next) {
37 // We only support IPv4, IPv6, and link interfaces
38 if (!ptr->ifa_addr || !ptr->ifa_name ||
39 (ptr->ifa_addr->sa_family != AF_INET &&
40 ptr->ifa_addr->sa_family != AF_INET6 &&
41 !is_link_address(ptr->ifa_addr))) {
42 continue;
43 }
44
45 interface_map.insert({ ptr->ifa_name, ptr });
46 }
47
48 data.primary_interface = get_primary_interface();
49
50 // Start by getting the DHCP servers
51 auto dhcp_servers = find_dhcp_servers();
52
53 // Walk the interfaces
54 decltype(interface_map.begin()) it = interface_map.begin();
55 while (it != interface_map.end()) {
56 string const& name = it->first;
57
58 interface iface;
59 iface.name = name;
60
61 // Walk the addresses of this interface and populate the data
62 for (; it != interface_map.end() && it->first == name; ++it) {
63 populate_binding(iface, it->second);
64 populate_mtu(iface, it->second);
65 }
66
67 // Populate the interface's DHCP server value
68 auto dhcp_server_it = dhcp_servers.find(name);
69 if (dhcp_server_it == dhcp_servers.end()) {
70 iface.dhcp_server = find_dhcp_server(name);
71 } else {
72 iface.dhcp_server = dhcp_server_it->second;
73 }
74
75 data.interfaces.emplace_back(move(iface));
76 }
77 return data;
78 }
79
80 void networking_resolver::populate_binding(interface& iface, ifaddrs const* addr) const
81 {
82 // If the address is a link address, populate the MAC
83 if (is_link_address(addr->ifa_addr)) {
84 iface.macaddress = address_to_string(addr->ifa_addr);
85 return;
86 }
87
88 // Populate the correct bindings list
89 vector<binding>* bindings = nullptr;
90 if (addr->ifa_addr->sa_family == AF_INET) {
91 bindings = &iface.ipv4_bindings;
92 } else if (addr->ifa_addr->sa_family == AF_INET6) {
93 bindings = &iface.ipv6_bindings;
94 }
95
96 if (!bindings) {
97 return;
98 }
99
100 binding b;
101 b.address = address_to_string(addr->ifa_addr);
102 if (addr->ifa_netmask) {
103 b.netmask = address_to_string(addr->ifa_netmask);
104 b.network = address_to_string(addr->ifa_addr, addr->ifa_netmask);
105 }
106 bindings->emplace_back(std::move(b));
107 }
108
109 void networking_resolver::populate_mtu(interface& iface, ifaddrs const* addr) const
110 {
111 // The MTU exists on link addresses
112 if (!is_link_address(addr->ifa_addr) || !addr->ifa_data) {
113 return;
114 }
115
116 iface.mtu = get_link_mtu(addr->ifa_name, addr->ifa_data);
117 }
118
119 string networking_resolver::get_primary_interface() const
120 {
121 string interface;
122 each_line("route", { "-n", "get", "default" }, [&interface](string& line){
123 boost::trim(line);
124 if (boost::starts_with(line, "interface: ")) {
125 interface = line.substr(11);
126 boost::trim(interface);
127 return false;
128 }
129 return true;
130 });
131 LOG_DEBUG("got primary interface: \"{1}\"", interface);
132 return interface;
133 }
134
135 void networking_resolver::find_dhclient_dhcp_servers(std::map<std::string, std::string>& servers) const
136 {
137 static vector<string> const dhclient_search_directories = {
138 "/var/lib/dhclient",
139 "/var/lib/dhcp",
140 "/var/lib/dhcp3",
141 "/var/lib/NetworkManager",
142 "/var/db"
143 };
144
145 for (auto const& dir : dhclient_search_directories) {
146 LOG_DEBUG("searching \"{1}\" for dhclient lease files.", dir);
147 lth_file::each_file(dir, [&](string const& path) {
148 LOG_DEBUG("reading \"{1}\" for dhclient lease information.", path);
149
150 // Each lease entry should have the interface declaration before the options
151 // We respect the last lease for an interface in the file
152 string interface;
153 lth_file::each_line(path, [&](string& line) {
154 boost::trim(line);
155 if (boost::starts_with(line, "interface ")) {
156 interface = line.substr(10);
157 trim_if(interface, boost::is_any_of("\";"));
158 } else if (!interface.empty() && boost::starts_with(line, "option dhcp-server-identifier ")) {
159 string server = line.substr(30);
160 trim_if(server, boost::is_any_of("\";"));
161 servers.emplace(make_pair(move(interface), move(server)));
162 }
163 return true;
164 });
165 return true;
166 }, "^dhclient.*lease.*$");
167 }
168 }
169 void networking_resolver::find_nm_internal_dhcp_servers(std::map<std::string, std::string>& servers) const
170 {
171 static vector <string> const nm_search_directories = {
172 "/var/lib/NetworkManager"
173 };
174
175 for (auto const& dir : nm_search_directories) {
176 LOG_DEBUG("searching \"{1}\" for NetworkManager internal lease files", dir)
177 lth_file::each_file(dir, [&](string const& path) {
178 LOG_DEBUG("reading \"{1}\" for NetworkManager lease information.", path);
179
180 vector<string> parts;
181
182 boost::split(parts, path, boost::is_any_of("-"));
183 auto filename = parts.back();
184
185 boost::split(parts, filename, boost::is_any_of("."));
186
187 string interface = parts[0];
188
189 lth_file::each_line(path, [&](string &line) {
190 if (boost::starts_with(line, "SERVER_ADDRESS")) {
191 string server = line.substr(15);
192 servers.emplace(make_pair(move(interface), move(server)));
193 }
194 return true;
195 });
196 return true;
197 }, "^internal.*lease.*$");
198 }
199 }
200
201 void networking_resolver::find_networkd_dhcp_servers(std::map<std::string, std::string>& servers) const
202 {
203 // Files in this lease directory are not part of systemd's public API,
204 // and they include warnings against parsing them, but they seem to be
205 // the only way to get this information for now.
206 // Each file is named after the interface's index number.
207 static const string networkd_lease_directory = "/run/systemd/netif/leases/";
208
209 if (!boost::filesystem::is_directory(networkd_lease_directory)) return;
210
211 static boost::regex ip_link_re("^(\\d+):\\s+([^:]+)");
212 unordered_map<int, string> iface_index_names;
213 string key, value;
214
215 // Gather a map of interface indices and their interface names from `ip link show` --
216 // Only the index is known in the lease files.
217 each_line("ip", {"link", "show"}, [&](string line) {
218 if (re_search(line, ip_link_re, &key, &value)) {
219 iface_index_names.insert(make_pair(stoi(key), value));
220 }
221 return true;
222 });
223
224 LOG_DEBUG("searching \"{1}\" for systemd-networkd DHCP lease files", networkd_lease_directory);
225
226 lth_file::each_file(networkd_lease_directory, [&](string const& path) {
227 LOG_DEBUG("searching \"{1}\" for systemd-networkd DHCP lease information", path);
228 string server_address;
229
230 static boost::regex server_address_re("^SERVER_ADDRESS=(.*)$");
231 lth_file::each_line(path, [&](string& line) {
232 boost::trim(line);
233 if (re_search(line, server_address_re, &server_address)) {
234 boost::filesystem::path p {path};
235 auto iface_index = maybe_stoi(p.filename().string());
236 if (!iface_index) return true;
237 servers.emplace(make_pair(iface_index_names[iface_index.get()], server_address));
238 }
239 return true;
240 });
241 return true;
242 });
243 }
244
245 map<string, string> networking_resolver::find_dhcp_servers() const
246 {
247 map<string, string> servers;
248
249 find_networkd_dhcp_servers(servers);
250 if (servers.empty()) find_dhclient_dhcp_servers(servers);
251 if (servers.empty()) find_nm_internal_dhcp_servers(servers);
252
253 return servers;
254 }
255
256 string networking_resolver::find_dhcp_server(string const& interface) const
257 {
258 // Use dhcpcd if it's present to get the interface's DHCP lease information
259 // This assumes we've already searched for the interface with dhclient
260 string value;
261 each_line("dhcpcd", { "-U", interface }, [&value](string& line) {
262 if (boost::starts_with(line, "dhcp_server_identifier=")) {
263 value = line.substr(23);
264 boost::trim(value);
265 return false;
266 }
267 return true;
268 });
269 return value;
270 }
271
272 }}} // namespace facter::facts::bsd
+0
-23
lib/src/facts/bsd/uptime_resolver.cc less more
0 #include <internal/facts/bsd/uptime_resolver.hpp>
1 #include <ctime>
2 #include <sys/sysctl.h>
3
4 using namespace std;
5
6 namespace facter { namespace facts { namespace bsd {
7
8 int64_t uptime_resolver::get_uptime()
9 {
10 // this approach adapted from: http://stackoverflow.com/a/11676260/1004272
11 timeval boottime;
12 size_t len = sizeof(boottime);
13 int mib[2] = { CTL_KERN, KERN_BOOTTIME };
14 if (sysctl(mib, 2, &boottime, &len, NULL, 0) == 0) {
15 time_t bsec = boottime.tv_sec;
16 time_t now = time(NULL);
17 return now - bsec;
18 }
19 return posix::uptime_resolver::get_uptime();
20 }
21
22 }}} // namespace facter::facts::bsd
+0
-152
lib/src/facts/cache.cc less more
0 #include <internal/facts/cache.hpp>
1 #include <internal/facts/external/json_resolver.hpp>
2
3 #include <rapidjson/document.h>
4 #include <rapidjson/prettywriter.h>
5 #include <leatherman/logging/logging.hpp>
6 #include <leatherman/file_util/directory.hpp>
7 #include <leatherman/file_util/file.hpp>
8 #include <boost/nowide/fstream.hpp>
9 #include <boost/system/error_code.hpp>
10 #include <time.h>
11
12 using namespace std;
13 using namespace rapidjson;
14 using namespace facter::facts::external;
15 namespace boost_file = boost::filesystem;
16
17 namespace facter { namespace facts { namespace cache {
18
19 void clean_cache(unordered_map<string, int64_t> const& facts_to_cache, string cache_location) {
20 boost_file::path cache_dir = boost_file::path(cache_location);
21 if (!boost_file::is_directory(cache_dir)) {
22 return;
23 }
24 for (boost_file::directory_iterator itr(cache_dir);
25 itr != boost_file::directory_iterator();
26 ++itr) {
27 boost_file::path cache_file = itr->path();
28 if (!facts_to_cache.count(cache_file.filename().string())) {
29 boost::system::error_code ec;
30 boost_file::remove(cache_file, ec);
31 if (!ec) {
32 LOG_DEBUG("Deleting unused cache file {1}", cache_file.string());
33 } else {
34 continue;
35 }
36 }
37 }
38 }
39
40 bool cache_is_valid(boost_file::path const& cache_file, int64_t ttl) {
41 time_t last_mod = boost_file::last_write_time(cache_file);
42 time_t now;
43 double lifetime_seconds = difftime(time(&now), last_mod);
44 return static_cast<int64_t>(lifetime_seconds) < ttl;
45 }
46
47 void load_facts_from_cache(boost_file::path const& cache_file, shared_ptr<base_resolver> res, collection& facts) {
48 string cache_file_path = cache_file.string();
49 if (leatherman::file_util::file_readable(cache_file_path)) {
50 try {
51 json_resolver json_res(cache_file_path);
52 json_res.resolve(facts);
53 } catch (external_fact_exception& ex) {
54 LOG_DEBUG("cache file for {1} facts contained invalid JSON, refreshing", res->name());
55 refresh_cache(res, cache_file, facts);
56 return;
57 }
58 } else {
59 LOG_DEBUG("cache file for {1} facts was missing, refreshing", res->name());
60 refresh_cache(res, cache_file, facts);
61 return;
62 }
63 }
64
65 void refresh_cache(shared_ptr<base_resolver> res, boost_file::path const& cache_file, collection& facts) {
66 res->resolve(facts);
67 boost_file::remove(cache_file);
68 write_json_cache_file(facts, cache_file.string(), res->names());
69 }
70
71 void use_cache(collection& facts, shared_ptr<base_resolver> res, int64_t ttl) {
72 boost_file::path cache_dir = boost_file::path(fact_cache_location());
73 if (!boost_file::is_directory(cache_dir)) {
74 boost_file::create_directories(cache_dir);
75 }
76 boost_file::path cache_file = cache_dir / res->name();
77 if (leatherman::file_util::file_readable(cache_file.string()) && cache_is_valid(cache_file, ttl)) {
78 LOG_DEBUG("loading cached values for {1} facts", res->name());
79 load_facts_from_cache(cache_file, res, facts);
80 } else {
81 LOG_DEBUG("caching values for {1} facts", res->name());
82 refresh_cache(res, cache_file, facts);
83 }
84 }
85
86 void write_json_cache_file(const collection& facts, boost_file::path const& file_path, vector<string> const& fact_names)
87 {
88 json_document document;
89 document.SetObject();
90
91 auto builder = ([&](string const& key, value const* val) {
92 json_value value;
93 if (val) {
94 val->to_json(document.GetAllocator(), value);
95 } else {
96 value.SetString("", 0);
97 }
98 document.AddMember(StringRef(key.c_str(), key.size()), value, document.GetAllocator());
99 });
100
101 for (auto const& name : fact_names) {
102 auto fact_value = facts.get_resolved(name);
103 if (fact_value) {
104 builder(name, fact_value);
105 }
106 }
107
108 string file_path_string = file_path.string();
109 boost::nowide::ofstream stream(file_path_string);
110 stream_adapter adapter(stream);
111 PrettyWriter<stream_adapter> writer(adapter);
112 writer.SetIndent(' ', 2);
113 document.Accept(writer);
114 }
115
116 boost_file::path custom_fact_cache_file_location() {
117 boost_file::path cache_dir = boost_file::path(facter::facts::cache::fact_cache_location());
118 if (!boost_file::is_directory(cache_dir))
119 boost_file::create_directories(cache_dir);
120 boost_file::path custom_fact_cache_file_location = cache_dir / cached_custom_facts;
121
122 return custom_fact_cache_file_location;
123 }
124
125 bool load_cached_custom_facts(collection& collection, int64_t ttl)
126 {
127 boost_file::path cache_file = custom_fact_cache_file_location();
128 if (leatherman::file_util::file_readable(cache_file.string()) && cache::cache_is_valid(cache_file, ttl)) {
129 try {
130 LOG_DEBUG("Loading cached custom facts from file \"{1}\"", cache_file.string());
131 facts::external::json_resolver json_res(cache_file.string());
132 json_res.resolve(collection);
133 return true;
134 } catch (exception& ex) {
135 LOG_DEBUG("Custom facts cache file contained invalid JSON, refreshing");
136 return false;
137 }
138 } else {
139 LOG_DEBUG("Custom facts cache file expired/missing. Refreshing");
140 boost_file::remove(cache_file);
141 }
142 return false;
143 }
144
145 void write_cached_custom_facts(const collection& facts, const std::vector<std::string>& cached_custom_facts_list)
146 {
147 boost_file::path cache_file = custom_fact_cache_file_location();
148 LOG_DEBUG("Saving cached custom facts to {1}", cache_file);
149 write_json_cache_file(facts, cache_file, cached_custom_facts_list);
150 }
151 }}} // namespace facter::facts::cache
+0
-704
lib/src/facts/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <facter/facts/external_resolvers_factory.hpp>
2 #include <facter/facts/resolver.hpp>
3 #include <facter/facts/value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <facter/ruby/ruby.hpp>
8 #include <facter/util/string.hpp>
9 #include <facter/version.h>
10 #include <internal/facts/resolvers/hypervisors_resolver.hpp>
11 #include <internal/facts/resolvers/ruby_resolver.hpp>
12 #include <internal/facts/resolvers/path_resolver.hpp>
13 #include <internal/facts/resolvers/ec2_resolver.hpp>
14 #include <internal/facts/resolvers/gce_resolver.hpp>
15 #include <internal/facts/resolvers/augeas_resolver.hpp>
16 #include <internal/ruby/ruby_value.hpp>
17 #include <internal/facts/cache.hpp>
18 #include <leatherman/dynamic_library/dynamic_library.hpp>
19 #include <leatherman/file_util/directory.hpp>
20 #include <leatherman/util/environment.hpp>
21 #include <leatherman/logging/logging.hpp>
22 #include <boost/filesystem.hpp>
23 #include <boost/algorithm/string.hpp>
24 #include <rapidjson/document.h>
25 #include <rapidjson/prettywriter.h>
26 #include <yaml-cpp/yaml.h>
27 #include <algorithm>
28
29 using namespace std;
30 using namespace facter::util;
31 using namespace rapidjson;
32 using namespace YAML;
33 using namespace boost::filesystem;
34 using namespace leatherman::util;
35 using namespace leatherman::file_util;
36 using facter::util::maybe_stoi;
37
38 namespace facter { namespace facts {
39
40 collection::collection(set<string> const& blocklist, unordered_map<string, int64_t> const& ttls, bool ignore_cache) :
41 _blocklist(blocklist), _ttls(ttls), _ignore_cache(ignore_cache)
42 {
43 // This needs to be defined here since we use incomplete types in the header
44 }
45
46 collection::~collection()
47 {
48 // This needs to be defined here since we use incomplete types in the header
49 }
50
51 collection::collection(collection&& other)
52 {
53 *this = std::move(other);
54 }
55
56 collection& collection::operator=(collection&& other)
57 {
58 if (this != &other) {
59 _facts = std::move(other._facts);
60 _resolvers = std::move(other._resolvers);
61 _resolver_map = std::move(other._resolver_map);
62 _pattern_resolvers = std::move(other._pattern_resolvers);
63 _blocklist = std::move(other._blocklist);
64 _ttls = std::move(other._ttls);
65 }
66 return *this;
67 }
68
69 void collection::add_default_facts(bool include_ruby_facts)
70 {
71 add_common_facts(include_ruby_facts);
72 add_platform_facts();
73 }
74
75 void collection::add(shared_ptr<resolver> const& res)
76 {
77 if (!res) {
78 return;
79 }
80
81 for (auto const& name : res->names()) {
82 _resolver_map.insert({ name, res });
83 }
84
85 if (res->has_patterns()) {
86 _pattern_resolvers.push_back(res);
87 }
88
89 _resolvers.push_back(res);
90 }
91
92 void collection::add(string name, unique_ptr<value> value)
93 {
94 // Ensure the fact is resolved before replacing it
95 auto old_value = get_value(name);
96
97 if (LOG_IS_DEBUG_ENABLED()) {
98 if (old_value) {
99 ostringstream old_value_ss;
100 old_value->write(old_value_ss);
101 if (!value) {
102 LOG_DEBUG("fact \"{1}\" resolved to null and the existing value of {2} will be removed.", name, old_value_ss.str());
103 } else {
104 ostringstream new_value_ss;
105 value->write(new_value_ss);
106 if (old_value->weight() > value->weight()) {
107 LOG_DEBUG("new value for fact \"{1}\" ignored, because it's a lower weight", name);
108 } else {
109 LOG_DEBUG("fact \"{1}\" has changed from {2} to {3}.", name, old_value_ss.str(), new_value_ss.str());
110 }
111 }
112 } else {
113 if (!value) {
114 LOG_DEBUG("fact \"{1}\" resolved to null and will not be added.", name);
115 } else {
116 ostringstream new_value_ss;
117 value->write(new_value_ss);
118 LOG_DEBUG("fact \"{1}\" has resolved to {2}.", name, new_value_ss.str());
119 }
120 }
121 }
122
123 if (!value) {
124 if (old_value) {
125 remove(name);
126 }
127 return;
128 }
129
130 // keep existing value if it has a larger weight value
131 if (old_value && old_value->weight() > value->weight())
132 return;
133
134 _facts[move(name)] = move(value);
135 }
136
137 void collection::add_custom(string name, unique_ptr<value> value, size_t weight)
138 {
139 if (value)
140 value->weight(weight);
141 add(move(name), move(value));
142 }
143
144 void collection::add_external(string name, unique_ptr<value> value)
145 {
146 if (value)
147 value->weight(external_fact_weight);
148 add(move(name), move(value));
149 }
150
151 void collection::get_external_facts_files_from_dir(external_files_list& files,
152 string const& dir, bool warn)
153 {
154 // If dir is relative, make it an absolute path before passing to can_resolve.
155 boost::system::error_code ec;
156 path search_dir = absolute(dir);
157
158 if (!is_directory(search_dir, ec)) {
159 // Warn the user if not using the default search directories
160 string msg = ec ? ec.message() : "not a directory";
161 if (warn) {
162 LOG_WARNING("skipping external facts for \"{1}\": {2}", dir, msg);
163 } else {
164 LOG_DEBUG("skipping external facts for \"{1}\": {2}", dir, msg);
165 }
166 return;
167 }
168
169 LOG_DEBUG("searching {1} for external facts.", search_dir);
170 external_resolvers_factory erf;
171 each_file(search_dir.string(), [&](string const& path) {
172 try {
173 auto resolver = erf.get_resolver(path);
174 files.push_back(make_pair(path, resolver));
175 } catch (external::external_fact_no_resolver& e) {
176 LOG_DEBUG("skipping file \"{1}\": {2}", path, e.what());
177 }
178 return true;
179 });
180 }
181
182 map<string, vector<string>> collection::get_external_facts_groups(vector<string> const& directories)
183 {
184 map<string, vector<string>> external_facts_groups;
185 for (auto const& it : get_external_facts_files(directories)) {
186 external_facts_groups[it.second->name()] = {};
187 }
188 return external_facts_groups;
189 }
190 collection::external_files_list collection::get_external_facts_files(vector<string> const& directories)
191 {
192 external_files_list external_facts_files;
193 // Build a list of pairs of files and the resolver that can resolve it
194 // Start with default Facter search directories, then user-specified directories.
195 for (auto const& dir : get_external_fact_directories()) {
196 get_external_facts_files_from_dir(external_facts_files, dir, false);
197 }
198 for (auto const& dir : directories) {
199 get_external_facts_files_from_dir(external_facts_files, dir, true);
200 }
201 return external_facts_files;
202 }
203
204 void collection::add_external_facts(vector<string> const& directories)
205 {
206 external_files_list external_facts_files = get_external_facts_files(directories);
207 if (external_facts_files.empty()) {
208 LOG_DEBUG("no external facts were found.");
209 } else {
210 map<string, string> known_external_facts_cache_groups;
211 for (auto const& kvp : external_facts_files) {
212 // Check if the resolver should be cached
213 auto resolver_ttl = _ttls.find(kvp.second->name());
214 if (!_ignore_cache && resolver_ttl != _ttls.end()) {
215 auto resolver = kvp.second;
216 auto it = known_external_facts_cache_groups.find(resolver->name());
217
218 if ( it != known_external_facts_cache_groups.end() ) {
219 LOG_ERROR(
220 "Caching is enabled for group \"{1}\" while there "
221 "are at least two external facts files with "
222 "the same filename. To fix this either remove "
223 "\"{1}\" from cached "
224 "groups or rename one of the "
225 "files:\n\"{2}\"\n\"{3}\" ",
226 resolver->name(), kvp.first, it->second);
227 break;
228 }
229 known_external_facts_cache_groups.insert(make_pair(resolver->name(), kvp.first));
230 cache::use_cache(*this, resolver, (*resolver_ttl).second);
231 continue;
232 }
233 try {
234 kvp.second->resolve(*this);
235 }
236 catch (external::external_fact_exception& ex) {
237 LOG_ERROR(
238 "error while processing \"{1}\" for external facts: {2}",
239 kvp.first, ex.what());
240 }
241 }
242 }
243 }
244
245 void collection::add_environment_facts(function<void(string const& name)> callback)
246 {
247 environment::each([&](string& name, string& value) {
248 // If the variable starts with "FACTER_", the remainder of the variable is the fact name
249 if (!boost::istarts_with(name, "FACTER_")) {
250 return true;
251 }
252
253 auto fact_name = name.substr(7);
254 boost::to_lower(fact_name);
255 LOG_DEBUG("setting fact \"{1}\" based on the value of environment variable \"{2}\".", fact_name, name);
256
257 // Add the value based on the environment variable
258 auto fact_value = make_value<string_value>(move(value));
259 fact_value->weight(external_fact_weight);
260 add(fact_name, move(fact_value));
261 if (callback) {
262 callback(fact_name);
263 }
264 return true;
265 });
266 }
267
268 void collection::remove(shared_ptr<resolver> const& res)
269 {
270 if (!res) {
271 return;
272 }
273
274 // Remove all name associations
275 for (auto const& name : res->names()) {
276 auto range = _resolver_map.equal_range(name);
277 auto it = range.first;
278 while (it != range.second) {
279 if (it->second != res) {
280 ++it;
281 continue;
282 }
283 it = _resolver_map.erase(it);
284 }
285 }
286
287 _pattern_resolvers.remove(res);
288 _resolvers.remove(res);
289 }
290
291 void collection::remove(string const& name)
292 {
293 // Ensure the fact is in the collection
294 // This will properly resolve the fact prior to removing it
295 if (!get_value(name)) {
296 return;
297 }
298
299 _facts.erase(name);
300 }
301
302 void collection::clear()
303 {
304 _facts.clear();
305 _resolvers.clear();
306 _resolver_map.clear();
307 _pattern_resolvers.clear();
308 }
309
310 bool collection::empty()
311 {
312 return _facts.empty() && _resolvers.empty();
313 }
314
315 map<string, vector<string>> collection::get_fact_groups() {
316 map<string, vector<string>> fact_groups;
317 for (auto res : _resolvers) {
318 fact_groups.emplace(res->name(), res->names());
319 }
320 return fact_groups;
321 }
322
323 map<string, vector<string>> collection::get_blockable_fact_groups() {
324 map<string, vector<string>> blockgroups;
325 for (auto res : _resolvers) {
326 if (res->is_blockable()) {
327 blockgroups.emplace(res->name(), res->names());
328 }
329 }
330 return blockgroups;
331 }
332
333 size_t collection::size()
334 {
335 resolve_facts();
336 return _facts.size();
337 }
338
339 const std::unordered_map<std::string, int64_t>& collection::get_ttls() {
340 return _ttls;
341 }
342
343 value const* collection::operator[](string const& name)
344 {
345 return get_value(name);
346 }
347
348 void collection::each(function<bool(string const&, value const*)> func)
349 {
350 resolve_facts();
351
352 // We intentionally are using find_if with no return value as a "map until" construct.
353 // cppcheck-suppress ignoredReturnValue
354 find_if(begin(_facts), end(_facts), [&func](map<string, unique_ptr<value>>::value_type const& it) {
355 return !func(it.first, it.second.get());
356 });
357 }
358
359 ostream& collection::write(ostream& stream, format fmt, set<string> const& queries)
360 {
361 return write(stream, fmt, queries, false, false);
362 }
363
364 ostream& collection::write(ostream& stream, format fmt, set<string> const& queries, bool show_legacy, bool strict_errors)
365 {
366 if (queries.empty()) {
367 // Resolve all facts
368 resolve_facts();
369 }
370
371 if (fmt == format::hash) {
372 write_hash(stream, queries, show_legacy, strict_errors);
373 } else if (fmt == format::json) {
374 write_json(stream, queries, show_legacy, strict_errors);
375 } else if (fmt == format::yaml) {
376 write_yaml(stream, queries, show_legacy, strict_errors);
377 }
378 return stream;
379 }
380
381 bool collection::try_block(shared_ptr<resolver> const& res) {
382 if (_blocklist.count(res->name())) {
383 if (res->is_blockable()) {
384 LOG_DEBUG("blocking collection of {1} facts.", res->name());
385 return true;
386 } else {
387 LOG_DEBUG("{1} resolver cannot be blocked.", res->name());
388 }
389 }
390 return false;
391 }
392
393 void collection::resolve(shared_ptr<resolver> const& res) {
394 remove(res);
395
396 // Check if the resolver has been blocked
397 if (try_block(res)) {
398 return;
399 }
400
401 // Check if the resolver should be cached
402 auto resolver_ttl = _ttls.find(res->name());
403 if (!_ignore_cache && resolver_ttl != _ttls.end()) {
404 cache::use_cache(*this, res, (*resolver_ttl).second);
405 return;
406 }
407
408 // Resolve normally
409 LOG_DEBUG("resolving {1} facts.", res->name());
410 try {
411 res->resolve(*this);
412 } catch (std::runtime_error &e) {
413 LOG_WARNING("exception resolving {1} facts, some facts will not be available: {2}", res->name(), e.what());
414 }
415 }
416
417 void collection::resolve_facts()
418 {
419 // Delete any unused cache files
420 if (!_ignore_cache) {
421 cache::clean_cache(_ttls);
422 }
423 // Remove the front of the resolvers list and resolve until no resolvers are left
424 while (!_resolvers.empty()) {
425 auto resolver = _resolvers.front();
426 resolve(resolver);
427 }
428 }
429
430 void collection::resolve_fact(string const& name)
431 {
432 // Resolve every resolver mapped to this name first
433 auto range = _resolver_map.equal_range(name);
434 auto it = range.first;
435 while (it != range.second) {
436 auto resolver = (it++)->second;
437 resolve(resolver);
438 }
439
440 // Resolve every resolver that matches the given name
441 auto pattern_it = _pattern_resolvers.begin();
442 while (pattern_it != _pattern_resolvers.end()) {
443 if (!(*pattern_it)->is_match(name)) {
444 ++pattern_it;
445 continue;
446 }
447 auto resolver = *(pattern_it++);
448 resolve(resolver);
449 }
450 }
451
452 value const* collection::get_value(string const& name)
453 {
454 resolve_fact(name);
455
456 // Lookup the fact
457 auto it = _facts.find(name);
458 return it == _facts.end() ? nullptr : it->second.get();
459 }
460
461 value const* collection::query_value(string const& query, bool strict_errors)
462 {
463 // First attempt to lookup a fact with the exact name of the query
464 value const* current = get_value(query);
465 if (current) {
466 return current;
467 }
468
469 bool in_quotes = false;
470 vector<string> segments;
471 string segment;
472 for (auto const& c : query) {
473 if (c == '"') {
474 in_quotes = !in_quotes;
475 continue;
476 }
477 if (in_quotes || c != '.') {
478 segment += c;
479 continue;
480 }
481 segments.emplace_back(move(segment));
482 segment.clear();
483 }
484 if (!segment.empty()) {
485 segments.emplace_back(move(segment));
486 }
487
488 auto segment_end = end(segments);
489 for (auto segment = begin(segments); segment != segment_end; ++segment) {
490 auto rb_val = dynamic_cast<ruby::ruby_value const *>(current);
491 if (rb_val) {
492 current = facter::ruby::lookup(current, segment, segment_end);
493 if (!current) {
494 LOG_DEBUG("cannot lookup an element with \"{1}\" from Ruby fact", *segment);
495 }
496 // Once we hit Ruby there's no going back, so whatever we get from Ruby is the value.
497 return current;
498 } else {
499 current = lookup(current, *segment, strict_errors);
500 }
501 if (!current) {
502 // Break out early if there's no value for this segment
503 return nullptr;
504 }
505 }
506
507 return current;
508 }
509
510 value const* collection::lookup(value const* value, string const& name, bool strict_errors)
511 {
512 if (!value) {
513 value = get_value(name);
514 if (!value) {
515 string message = "fact \"{1}\" does not exist.";
516 if (strict_errors) {
517 LOG_ERROR(message, name);
518 } else {
519 LOG_DEBUG(message, name);
520 }
521 }
522 return value;
523 }
524
525 auto map = dynamic_cast<map_value const*>(value);
526 if (map) {
527 value = (*map)[name];
528 if (!value) {
529 LOG_DEBUG("cannot lookup a hash element with \"{1}\": element does not exist.", name);
530 }
531 return value;
532 }
533
534 auto array = dynamic_cast<array_value const*>(value);
535 if (!array) {
536 return nullptr;
537 }
538
539 auto maybe_index = maybe_stoi(name);;
540 if (!maybe_index) {
541 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected an integral value.", name);
542 return nullptr;
543 }
544
545 int index = maybe_index.get();
546 if (index < 0) {
547 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected a non-negative value.", name);
548 return nullptr;
549 }
550
551 if (array->empty()) {
552 LOG_DEBUG("cannot lookup an array element with \"{1}\": the array is empty.", name);
553 return nullptr;
554 }
555
556 if (static_cast<size_t>(index) >= array->size()) {
557 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected an integral value between 0 and {2} (inclusive).", name, array->size() - 1);
558 return nullptr;
559 }
560
561 return (*array)[index];
562 }
563
564 void collection::write_hash(ostream& stream, set<string> const& queries, bool show_legacy, bool strict_errors)
565 {
566 // If there's only one query, print the result without the name
567 if (queries.size() == 1u) {
568 auto value = query_value(*queries.begin(), strict_errors);
569 if (value) {
570 value->write(stream, false);
571 }
572 return;
573 }
574
575 bool first = true;
576 auto writer = ([&](string const& key, value const* val) {
577 // Ignore facts with hidden values
578 if (!show_legacy && queries.empty() && val && val->hidden()) {
579 return;
580 }
581 if (first) {
582 first = false;
583 } else {
584 stream << '\n';
585 }
586 stream << key << " => ";
587 if (val) {
588 val->write(stream, false);
589 }
590 });
591
592 if (!queries.empty()) {
593 // Print queried facts
594 vector<pair<string, value const*>> facts;
595 for (auto const& query : queries) {
596 facts.push_back(make_pair(query, this->query_value(query, strict_errors)));
597 }
598
599 for (auto const& kvp : facts) {
600 writer(kvp.first, kvp.second);
601 }
602 } else {
603 // Print all facts in the map
604 for (auto const& kvp : _facts) {
605 writer(kvp.first, kvp.second.get());
606 }
607 }
608 }
609
610 void collection::write_json(ostream& stream, set<string> const& queries, bool show_legacy, bool strict_errors)
611 {
612 json_document document;
613 document.SetObject();
614
615 auto builder = ([&](string const& key, value const* val) {
616 // Ignore facts with hidden values
617 if (!show_legacy && queries.empty() && val && val->hidden()) {
618 return;
619 }
620 json_value value;
621 if (val) {
622 val->to_json(document.GetAllocator(), value);
623 } else {
624 value.SetString("", 0);
625 }
626 document.AddMember(StringRef(key.c_str(), key.size()), value, document.GetAllocator());
627 });
628
629 if (!queries.empty()) {
630 for (auto const& query : queries) {
631 builder(query, this->query_value(query, strict_errors));
632 }
633 } else {
634 for (auto const& kvp : _facts) {
635 builder(kvp.first, kvp.second.get());
636 }
637 }
638
639 stream_adapter adapter(stream);
640 PrettyWriter<stream_adapter> writer(adapter);
641 writer.SetIndent(' ', 2);
642 document.Accept(writer);
643 }
644
645 void collection::write_yaml(ostream& stream, set<string> const& queries, bool show_legacy, bool strict_errors)
646 {
647 Emitter emitter(stream);
648 emitter << BeginMap;
649
650 auto writer = ([&](string const& key, value const* val) {
651 // Ignore facts with hidden values
652 if (!show_legacy && queries.empty() && val && val->hidden()) {
653 return;
654 }
655 emitter << Key;
656 if (needs_quotation(key)) {
657 emitter << DoubleQuoted;
658 }
659 emitter << key << YAML::Value;
660 if (val) {
661 val->write(emitter);
662 } else {
663 emitter << DoubleQuoted << "";
664 }
665 });
666
667 if (!queries.empty()) {
668 vector<pair<string, value const*>> facts;
669 for (auto const& query : queries) {
670 facts.push_back(make_pair(query, this->query_value(query, strict_errors)));
671 }
672
673 for (auto const& kvp : facts) {
674 writer(kvp.first, kvp.second);
675 }
676 } else {
677 for (auto const& kvp : _facts) {
678 writer(kvp.first, kvp.second.get());
679 }
680 }
681 emitter << EndMap;
682 }
683
684 void collection::add_common_facts(bool include_ruby_facts)
685 {
686 add("facterversion", make_value<string_value>(LIBFACTER_VERSION));
687 #ifdef AIO_AGENT_VERSION
688 add("aio_agent_version", make_value<string_value>(AIO_AGENT_VERSION));
689 #endif
690
691 if (include_ruby_facts) {
692 add(make_shared<resolvers::ruby_resolver>());
693 }
694 add(make_shared<resolvers::path_resolver>());
695 add(make_shared<resolvers::ec2_resolver>());
696 add(make_shared<resolvers::gce_resolver>());
697 add(make_shared<resolvers::augeas_resolver>());
698 #ifdef USE_WHEREAMI
699 add(make_shared<resolvers::hypervisors_resolver>());
700 #endif
701 }
702
703 }} // namespace facter::facts
+0
-82
lib/src/facts/external/execution_resolver.cc less more
0 #include <internal/facts/external/execution_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/array_value.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <internal/util/yaml.hpp>
6
7 #include <leatherman/execution/execution.hpp>
8 #include <leatherman/logging/logging.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <yaml-cpp/yaml.h>
11
12 using namespace std;
13 using namespace leatherman::execution;
14 using namespace facter::facts;
15 using namespace facter::facts::external;
16 using namespace facter::util::yaml;
17
18 namespace facter { namespace facts { namespace external {
19
20 void execution_resolver::resolve(collection& facts)
21 {
22 LOG_DEBUG("resolving facts from executable file \"{1}\".", _path);
23
24 try
25 {
26 bool resolved = false;
27 auto result = execute(_path, 0,
28 {
29 execution_options::trim_output,
30 execution_options::merge_environment,
31 execution_options::throw_on_failure,
32 execution_options::convert_newlines
33 });
34
35 try {
36 auto node = YAML::Load(result.output);
37 for (auto const& kvp : node) {
38 add_value(kvp.first.as<string>(), kvp.second, facts, _names);
39
40 // If YAML doesn't correctly parse, it will
41 // sometimes just return an empty node instead of
42 // erroring. Only claiming we've resolved if we
43 // add at least one child value from the YAML
44 // allows us to still pass on to the keyval
45 // interpretation in those cases.
46 resolved = true;
47 }
48 } catch (YAML::Exception& ex) {
49 LOG_DEBUG("Could not parse executable fact output as YAML or JSON ({1})", ex.msg);
50 }
51
52 if (!resolved) {
53 std::vector<string> lines;
54 boost::split(lines, result.output, boost::is_any_of("\n"));
55 for (const auto& line : lines) {
56 auto pos = line.find('=');
57 if (pos == string::npos) {
58 LOG_DEBUG("ignoring line in output: {1}", line);
59 continue;
60 }
61 // Add as a string fact
62 string fact = line.substr(0, pos);
63 boost::to_lower(fact);
64 _names.push_back(fact);
65 facts.add_external(move(fact), make_value<string_value>(line.substr(pos+1)));
66 }
67 }
68
69 // Log a warning if there is error output from the command
70 if (!result.error.empty()) {
71 LOG_WARNING("external fact file \"{1}\" had output on stderr: {2}", _path, result.error);
72 }
73 }
74 catch (execution_exception& ex) {
75 throw external_fact_exception(ex.what());
76 }
77
78 LOG_DEBUG("completed resolving facts from executable file \"{1}\".", _path);
79 }
80
81 }}} // namespace facter::facts::external
+0
-217
lib/src/facts/external/json_resolver.cc less more
0 #include <internal/facts/external/json_resolver.hpp>
1 #include <internal/util/scoped_file.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <leatherman/locale/locale.hpp>
8 #include <rapidjson/reader.h>
9 #include <rapidjson/filereadstream.h>
10 #include <rapidjson/error/en.h>
11 #include <boost/algorithm/string.hpp>
12 #include <stack>
13 #include <tuple>
14
15 // Mark string for translation (alias for leatherman::locale::format)
16 using leatherman::locale::_;
17
18 using namespace std;
19 using namespace facter::facts;
20 using namespace facter::util;
21 using namespace rapidjson;
22
23 namespace facter { namespace facts { namespace external {
24
25 // Helper event handler for parsing JSON data
26 struct json_event_handler
27 {
28 explicit json_event_handler(collection& facts, vector<string>& names) :
29 _initialized(false),
30 _facts(facts),
31 _names(names)
32 {
33 }
34
35 bool Null()
36 {
37 check_initialized();
38
39 // Ignore this fact as values cannot be null
40 _key.clear();
41 return true;
42 }
43
44 bool Bool(bool b)
45 {
46 add_value(make_value<boolean_value>(b));
47 return true;
48 }
49
50 bool Int(int i)
51 {
52 Int64(static_cast<uint64_t>(i));
53 return true;
54 }
55
56 bool Uint(unsigned int i)
57 {
58 Int64(static_cast<uint64_t>(i));
59 return true;
60 }
61
62 bool Int64(int64_t i)
63 {
64 add_value(make_value<integer_value>(i));
65 return true;
66 }
67
68 bool Uint64(uint64_t i)
69 {
70 Int64(static_cast<uint64_t>(i));
71 return true;
72 }
73
74 bool Double(double d)
75 {
76 add_value(make_value<double_value>(d));
77 return true;
78 }
79
80 bool String(char const* str, SizeType length, bool copy)
81 {
82 add_value(make_value<string_value>(string(str, length)));
83 return true;
84 }
85
86 bool Key(const char* str, SizeType length, bool copy)
87 {
88 check_initialized();
89 _key = string(str, length);
90 return true;
91 }
92
93 bool StartObject()
94 {
95 if (!_initialized) {
96 _initialized = true;
97 return true;
98 }
99
100 // Push a map onto the stack
101 _stack.emplace(make_tuple(move(_key), make_value<map_value>()));
102 return true;
103 }
104
105 bool EndObject(SizeType count)
106 {
107 // Check to see if the stack is empty since we don't push for the top-level object
108 if (_stack.empty()) {
109 return true;
110 }
111
112 // Pop the data off the stack
113 auto top = move(_stack.top());
114 _stack.pop();
115
116 // Restore the key and add the value
117 _key = move(get<0>(top));
118 add_value(move(get<1>(top)));
119 return true;
120 }
121
122 bool StartArray()
123 {
124 check_initialized();
125
126 // Push an array onto the stack
127 _stack.emplace(make_tuple(move(_key), make_value<array_value>()));
128 return true;
129 }
130
131 bool EndArray(SizeType count)
132 {
133 // Pop the data off the stack
134 auto top = move(_stack.top());
135 _stack.pop();
136
137 // Restore the key and add the value
138 _key = move(get<0>(top));
139 add_value(move(get<1>(top)));
140 return true;
141 }
142
143 private:
144 template <typename T> void add_value(unique_ptr<T>&& val)
145 {
146 check_initialized();
147
148 // If the stack is empty, just add it as a top-level fact
149 if (_stack.empty()) {
150 if (_key.empty()) {
151 throw external::external_fact_exception(_("expected non-empty key in object."));
152 }
153 boost::to_lower(_key);
154 _names.push_back(_key);
155 _facts.add_external(move(_key), move(val));
156 return;
157 }
158
159 // If there's an array or map on the stack, add the value as an element
160 auto& top = _stack.top();
161 auto& current = get<1>(top);
162 auto array = dynamic_cast<array_value*>(current.get());
163 if (array) {
164 array->add(move(val));
165 return;
166 }
167 auto map = dynamic_cast<map_value*>(current.get());
168 if (map) {
169 if (_key.empty()) {
170 throw external::external_fact_exception(_("expected non-empty key in object."));
171 }
172 map->add(move(_key), move(val));
173 }
174 }
175
176 void check_initialized() const
177 {
178 if (!_initialized) {
179 throw external::external_fact_exception(_("expected document to contain an object."));
180 }
181 }
182
183 bool _initialized;
184 collection& _facts;
185 vector<std::string>& _names;
186 string _key;
187 stack<tuple<string, unique_ptr<value>>> _stack;
188 };
189
190 void json_resolver::resolve(collection& facts)
191 {
192 LOG_DEBUG("resolving facts from JSON file \"{1}\".", _path);
193
194 // Open the file
195 // We used a scoped_file here because rapidjson expects a FILE*
196 scoped_file file(_path, "r");
197 if (file == nullptr) {
198 throw external_fact_exception(_("file could not be opened."));
199 }
200
201 // Use the existing FileStream class
202 char buffer[4096];
203 FileReadStream stream(file, buffer, sizeof(buffer));
204
205 // Parse the file and report any errors
206 Reader reader;
207 json_event_handler handler(facts, _names);
208 auto result = reader.Parse(stream, handler);
209 if (!result) {
210 throw external_fact_exception(GetParseError_En(result.Code()));
211 }
212
213 LOG_DEBUG("completed resolving facts from JSON file \"{1}\".", _path);
214 }
215
216 }}} // namespace facter::facts::external
+0
-32
lib/src/facts/external/resolver.cc less more
0 #include <facter/facts/external/resolver.hpp>
1 #include <boost/filesystem.hpp>
2
3 using namespace std;
4
5 namespace facter { namespace facts { namespace external {
6
7 external_fact_exception::external_fact_exception(string const& message) :
8 runtime_error(message)
9 {
10 }
11
12 external_fact_no_resolver::external_fact_no_resolver(std::string const& message) :
13 runtime_error(message)
14 {
15 }
16
17 resolver::resolver(std::string const &path):_path(path) {
18 boost::filesystem::path p(path);
19 _name =p.filename().string();
20 }
21
22 string const& resolver::name() const
23 {
24 return _name;
25 }
26
27 vector<string> const& resolver::names() const
28 {
29 return _names;
30 }
31 }}} // namespace facter::facts::external
+0
-41
lib/src/facts/external/text_resolver.cc less more
0 #include <internal/facts/external/text_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <leatherman/file_util/file.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <leatherman/locale/locale.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 // Mark string for translation (alias for leatherman::locale::format)
9 using leatherman::locale::_;
10
11 using namespace std;
12
13 namespace lth_file = leatherman::file_util;
14
15 namespace facter { namespace facts { namespace external {
16
17 void text_resolver::resolve(collection& facts)
18 {
19 LOG_DEBUG("resolving facts from text file \"{1}\".", _path);
20
21 if (!lth_file::each_line(_path, [&facts, this](string& line) {
22 auto pos = line.find('=');
23 if (pos == string::npos) {
24 LOG_DEBUG("ignoring line in output: {1}", line);
25 return true;
26 }
27 // Add as a string fact
28 string fact = line.substr(0, pos);
29 boost::to_lower(fact);
30 _names.push_back(fact);
31 facts.add_external(move(fact), make_value<string_value>(line.substr(pos+1)));
32 return true;
33 })) {
34 throw external_fact_exception(_("file could not be opened."));
35 }
36
37 LOG_DEBUG("completed resolving facts from text file \"{1}\".", _path);
38 }
39
40 }}} // namespace facter::facts::external
+0
-107
lib/src/facts/external/windows/powershell_resolver.cc less more
0 #include <internal/facts/external/windows/powershell_resolver.hpp>
1 #include <internal/util/yaml.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/fact.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <leatherman/util/strings.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <boost/filesystem.hpp>
10
11 using namespace std;
12 using namespace boost::filesystem;
13 using namespace facter::util::yaml;
14 using namespace leatherman::execution;
15
16 namespace facter { namespace facts { namespace external {
17
18 void powershell_resolver::resolve(collection& facts)
19 {
20 LOG_DEBUG("resolving facts from powershell script \"{1}\".", _path);
21
22 try
23 {
24 bool resolved = false;
25 string pwrshell = "powershell";
26
27 // When facter is a 32-bit process running on 64-bit windows (such as in a 32-bit puppet installation that
28 // includes native facter), PATH-lookp finds the 32-bit powershell and leads to problems. For example, if
29 // using powershell to read values from the registry, it will read the 32-bit view of the registry. Also 32
30 // and 64-bit versions have different modules available (since PSModulePath is in system32). Use the
31 // system32 fact to find the correct powershell executable.
32 auto system32 = facts.get<string_value>(fact::windows_system32);
33 if (system32) {
34 auto pathNative = path(system32->value()) / "WindowsPowerShell" / "v1.0" / "powershell.exe";
35 auto pwrshellNative = which(pathNative.string());
36 if (!pwrshellNative.empty()) {
37 pwrshell = move(pwrshellNative);
38 }
39 }
40
41 auto const pwrshell_opts = vector<string> {
42 "-NoProfile",
43 "-NonInteractive",
44 "-NoLogo",
45 "-ExecutionPolicy",
46 "Bypass",
47 "-File",
48 _path
49 };
50
51 auto result = execute(pwrshell, pwrshell_opts, 0, {
52 execution_options::trim_output,
53 execution_options::merge_environment,
54 execution_options::throw_on_failure
55 });
56
57 try {
58 auto node = YAML::Load(result.output);
59 for (auto const& kvp : node) {
60 string key = kvp.first.as<string>();
61 boost::to_lower(key);
62 add_value(key, kvp.second, facts, _names);
63
64 // If YAML doesn't correctly parse, it will
65 // sometimes just return an empty node instead of
66 // erroring. Only claiming we've resolved if we
67 // add at least one child value from the YAML
68 // allows us to still pass on to the key=val
69 // interpretation in those cases.
70 resolved = true;
71 }
72 } catch (YAML::Exception& ex) {
73 LOG_DEBUG("Could not parse executable fact output as YAML or JSON ({1})", ex.msg);
74 }
75
76 if (!resolved) {
77 // YAML/JSON parse not successful; Look for key=value pairs
78 leatherman::util::each_line(result.output, [&facts, this](string const& line) {
79 auto pos = line.find('=');
80 if (pos == string::npos) {
81 LOG_DEBUG("ignoring line in output: {1}", line);
82 return true;
83 }
84 // Add as a string fact
85 string fact = line.substr(0, pos);
86 boost::to_lower(fact);
87 string value = line.substr(pos + 1);
88 _names.push_back(fact);
89 facts.add_external(move(fact), make_value<string_value>(move(value)));
90 return true;
91 });
92 }
93
94 // Log a warning if the script had any error output
95 if (!result.error.empty()) {
96 LOG_WARNING("external fact file \"{1}\" had output on stderr: {2}", _path, result.error);
97 }
98 }
99 catch (execution_exception& ex) {
100 throw external_fact_exception(ex.what());
101 }
102
103 LOG_DEBUG("completed resolving facts from powershell script \"{1}\".", _path);
104 }
105
106 }}} // namespace facter::facts::external
+0
-40
lib/src/facts/external/yaml_resolver.cc less more
0 #include <internal/facts/external/yaml_resolver.hpp>
1 #include <internal/util/yaml.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/locale/locale.hpp>
4 #include <boost/algorithm/string.hpp>
5 #include <boost/nowide/fstream.hpp>
6 #include <yaml-cpp/yaml.h>
7 #include <yaml-cpp/eventhandler.h>
8
9 // Mark string for translation (alias for leatherman::locale::format)
10 using leatherman::locale::_;
11
12 using namespace std;
13 using namespace YAML;
14 using namespace facter::util::yaml;
15
16 namespace facter { namespace facts { namespace external {
17
18 void yaml_resolver::resolve(collection& facts)
19 {
20 LOG_DEBUG("resolving facts from YAML file \"{1}\".", _path);
21
22 boost::nowide::ifstream stream(_path.c_str());
23 if (!stream) {
24 throw external_fact_exception(_("file could not be opened."));
25 }
26
27 try {
28 Node node = YAML::Load(stream);
29 for (auto const& kvp : node) {
30 add_value(kvp.first.as<string>(), kvp.second, facts, _names);
31 }
32 } catch (Exception& ex) {
33 throw external_fact_exception(ex.msg);
34 }
35
36 LOG_DEBUG("completed resolving facts from YAML file \"{1}\".", _path);
37 }
38
39 }}} // namespace facter::facts::external
+0
-47
lib/src/facts/external_resolvers_factory.cc less more
0 #include <facter/facts/external_resolvers_factory.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <boost/algorithm/string.hpp>
3
4 #include <internal/facts/external/json_resolver.hpp>
5 #include <internal/facts/external/text_resolver.hpp>
6 #include <internal/facts/external/yaml_resolver.hpp>
7 #include <internal/facts/external/execution_resolver.hpp>
8
9
10 using namespace std;
11 namespace facter { namespace facts {
12
13 bool external_resolvers_factory::text_resolver_can_resolve(string const &path) {
14 return boost::iends_with(path, ".txt");
15 }
16
17 bool external_resolvers_factory::json_resolver_can_resolve(string const &path) {
18 return boost::iends_with(path, ".json");
19 }
20
21 bool external_resolvers_factory::yaml_resolver_can_resolve(string const &path) {
22 return boost::iends_with(path, ".yaml");
23 }
24
25 bool external_resolvers_factory::execution_resolver_can_resolve(string const &path) {
26 return !leatherman::execution::which(path, {}).empty();
27 }
28
29 shared_ptr<external::resolver> external_resolvers_factory::get_common_resolver(const string& path)
30 {
31 if (text_resolver_can_resolve(path)) {
32 return make_shared<external::text_resolver>(path);
33 }
34 if (json_resolver_can_resolve(path)) {
35 return make_shared<external::json_resolver>(path);
36 }
37 if (yaml_resolver_can_resolve(path)) {
38 return make_shared<external::yaml_resolver>(path);
39 }
40 if (execution_resolver_can_resolve(path)) {
41 return make_shared<external::execution_resolver>(path);
42 }
43 return NULL;
44 }
45
46 }} // namespace facter::facts
+0
-43
lib/src/facts/freebsd/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/freebsd/filesystem_resolver.hpp>
2 #include <internal/facts/bsd/uptime_resolver.hpp>
3 #include <internal/facts/glib/load_average_resolver.hpp>
4 #include <internal/facts/freebsd/processor_resolver.hpp>
5 #include <internal/facts/freebsd/dmi_resolver.hpp>
6 #include <internal/facts/posix/identity_resolver.hpp>
7 #include <internal/facts/posix/kernel_resolver.hpp>
8 #include <internal/facts/ssh_resolver.hpp>
9 #include <internal/facts/posix/timezone_resolver.hpp>
10 #include <internal/facts/freebsd/operating_system_resolver.hpp>
11 #include <internal/facts/freebsd/networking_resolver.hpp>
12 #include <internal/facts/freebsd/zfs_resolver.hpp>
13 #include <internal/facts/freebsd/zpool_resolver.hpp>
14 #include <internal/facts/freebsd/virtualization_resolver.hpp>
15 #include <internal/facts/freebsd/memory_resolver.hpp>
16 #include <internal/facts/freebsd/disk_resolver.hpp>
17
18 using namespace std;
19
20 namespace facter { namespace facts {
21
22 void collection::add_platform_facts()
23 {
24 add(make_shared<posix::kernel_resolver>());
25 add(make_shared<freebsd::operating_system_resolver>());
26 add(make_shared<freebsd::networking_resolver>());
27 add(make_shared<bsd::uptime_resolver>());
28 add(make_shared<freebsd::filesystem_resolver>());
29 add(make_shared<ssh_resolver>());
30 add(make_shared<posix::identity_resolver>());
31 add(make_shared<posix::timezone_resolver>());
32 add(make_shared<glib::load_average_resolver>());
33 add(make_shared<freebsd::processor_resolver>());
34 add(make_shared<freebsd::dmi_resolver>());
35 add(make_shared<freebsd::zfs_resolver>());
36 add(make_shared<freebsd::zpool_resolver>());
37 add(make_shared<freebsd::virtualization_resolver>());
38 add(make_shared<freebsd::memory_resolver>());
39 add(make_shared<freebsd::disk_resolver>());
40 }
41
42 }} // namespace facter::facts
+0
-35
lib/src/facts/freebsd/disk_resolver.cc less more
0 #include <internal/facts/freebsd/disk_resolver.hpp>
1 #include <internal/util/freebsd/geom.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 #include <libgeom.h>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 disk_resolver::data disk_resolver::collect_data(collection& facts)
11 {
12 data result;
13
14 try {
15 facter::util::freebsd::geom_class disks("DISK");
16
17 for (auto& geom : disks.geoms) {
18 for (auto& provider : geom.providers) {
19 disk d;
20 d.name = provider.name();
21 d.size = provider.mediasize();
22 d.model = provider.config("descr");
23 d.serial_number = provider.config("ident");
24 result.disks.push_back(move(d));
25 }
26 }
27 } catch (util::freebsd::geom_exception const &e) {
28 LOG_ERROR(e.what());
29 }
30
31 return result;
32 }
33
34 }}} // namespace facter::facts::freebsd
+0
-43
lib/src/facts/freebsd/dmi_resolver.cc less more
0 #include <internal/facts/freebsd/dmi_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2
3 #include <kenv.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace freebsd {
8
9 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
10 {
11 data result;
12 result.bios_vendor = kenv_lookup("smbios.bios.vendor");
13 result.bios_version = kenv_lookup("smbios.bios.version");
14 result.bios_release_date = kenv_lookup("smbios.bios.reldate");
15 result.uuid = kenv_lookup("smbios.system.uuid");
16 result.serial_number = kenv_lookup("smbios.system.serial");
17 result.product_name = kenv_lookup("smbios.system.product");
18 if (result.product_name.length() == 0) {
19 result.product_name = result.bios_vendor;
20 }
21 result.manufacturer = kenv_lookup("smbios.system.maker");
22 // Fix for Proxmox VMs
23 if (result.manufacturer == "QEMU") {
24 result.product_name = "KVM";
25 }
26
27 return result;
28 }
29
30 string dmi_resolver::kenv_lookup(const char* file)
31 {
32 char buffer[100] = {};
33
34 LOG_DEBUG("kenv lookup for {1}", file);
35 if (kenv(KENV_GET, file, buffer, sizeof(buffer) - 1) == -1) {
36 LOG_INFO("kenv lookup for {1} failed: {2} ({3})", file, strerror(errno), errno);
37 return "";
38 }
39 return buffer;
40 }
41
42 } } } // namespace facter::facts::freebsd
+0
-37
lib/src/facts/freebsd/filesystem_resolver.cc less more
0 #include <internal/facts/freebsd/filesystem_resolver.hpp>
1 #include <internal/util/freebsd/geom.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 #include <libgeom.h>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace freebsd {
9
10 filesystem_resolver::data filesystem_resolver::collect_data(collection& facts)
11 {
12 data result = bsd::filesystem_resolver::collect_data(facts);
13
14 try {
15 facter::util::freebsd::geom_class disks("PART");
16
17 for (auto& geom : disks.geoms) {
18 for (auto& provider : geom.providers) {
19 partition p;
20 p.name = provider.name();
21 p.size = provider.mediasize();
22 if (geom.config("scheme") == "GPT") {
23 p.partition_label = provider.config("label");
24 p.partition_uuid = provider.config("rawuuid");
25 }
26 result.partitions.push_back(move(p));
27 }
28 }
29 } catch (util::freebsd::geom_exception const& e) {
30 LOG_ERROR(e.what());
31 }
32
33 return result;
34 }
35
36 }}} // namespace facter::facts::freebsd
+0
-76
lib/src/facts/freebsd/memory_resolver.cc less more
0 #include <internal/facts/freebsd/memory_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <sys/types.h>
4 #include <sys/param.h>
5 #include <sys/sysctl.h>
6 #include <vm/vm_param.h>
7 #include <unistd.h>
8
9 using namespace std;
10 using namespace leatherman::execution;
11
12 namespace facter { namespace facts { namespace freebsd {
13
14 memory_resolver::data memory_resolver::collect_data(collection& facts)
15 {
16 data result;
17
18 size_t size;
19 int pagesize = getpagesize();
20
21 // Memory usage
22
23 unsigned long physmem;
24 size = sizeof(physmem);
25 if (0 == sysctlbyname("hw.physmem", &physmem, &size, NULL, 0)) {
26 result.mem_total = physmem;
27 }
28
29 unsigned int inactive_count = 0;
30 size = sizeof(inactive_count);
31 sysctlbyname("vm.stats.vm.v_inactive_count", &inactive_count, &size, NULL, 0);
32
33 unsigned int cache_count = 0;
34 size = sizeof(cache_count);
35 sysctlbyname("vm.stats.vm.v_cache_count", &cache_count, &size, NULL, 0);
36
37 unsigned int free_count = 0;
38 size = sizeof(free_count);
39 sysctlbyname("vm.stats.vm.v_free_count", &free_count, &size, NULL, 0);
40
41 long mem_free_page_count = inactive_count + cache_count + free_count;
42 result.mem_free = mem_free_page_count * pagesize;
43
44 // Swap usage
45
46 struct xswdev xsw;
47 size = sizeof(xsw);
48
49 int mib[16];
50 size_t mibsize;
51 mibsize = sizeof mib / sizeof mib[0];
52 if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) {
53 LOG_DEBUG("sysctlnametomib() failed");
54 } else {
55 for (int n = 0; ; ++n) {
56 mib[mibsize] = n;
57 if (-1 == sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0))
58 break;
59
60 if (xsw.xsw_version != XSWDEV_VERSION) {
61 LOG_DEBUG("xswdev version mismatch");
62 } else {
63 result.swap_total += xsw.xsw_nblks;
64 result.swap_free += xsw.xsw_nblks - xsw.xsw_used;
65 }
66 }
67
68 result.swap_free *= pagesize;
69 result.swap_total *= pagesize;
70 }
71
72 return result;
73 }
74
75 }}} // namespace facter::facts::freebsd
+0
-64
lib/src/facts/freebsd/networking_resolver.cc less more
0 #include <internal/facts/freebsd/networking_resolver.hpp>
1 #include <internal/util/bsd/scoped_ifaddrs.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <boost/algorithm/string.hpp>
5 #include <sys/sockio.h>
6 #include <sys/ioctl.h>
7 #include <net/if.h>
8 #include <net/if_dl.h>
9 #include <netinet/in.h>
10
11 using namespace std;
12 using namespace facter::util;
13 using namespace facter::util::bsd;
14 using namespace leatherman::execution;
15
16 namespace facter { namespace facts { namespace freebsd {
17
18 bool networking_resolver::is_link_address(sockaddr const* addr) const
19 {
20 return addr && addr->sa_family == AF_LINK;
21 }
22
23 uint8_t const* networking_resolver::get_link_address_bytes(sockaddr const* addr) const
24 {
25 if (!is_link_address(addr)) {
26 return nullptr;
27 }
28 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
29 if (link_addr->sdl_alen != 6 && link_addr->sdl_alen != 20) {
30 return nullptr;
31 }
32 return reinterpret_cast<uint8_t const*>(LLADDR(link_addr));
33 }
34
35 uint8_t networking_resolver::get_link_address_length(sockaddr const* addr) const
36 {
37 if (!is_link_address(addr)) {
38 return 0;
39 }
40 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
41 return link_addr->sdl_alen;
42 }
43
44 boost::optional<uint64_t> networking_resolver::get_link_mtu(string const& interface, void* data) const
45 {
46 ifreq ifr;
47 memset(&ifr, 0, sizeof(ifr));
48 strncpy(ifr.ifr_name, interface.c_str(), sizeof(ifr.ifr_name));
49 int s = socket(AF_INET, SOCK_DGRAM, 0);
50 if (s < 0) {
51 LOG_WARNING("socket failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
52 return boost::none;
53 }
54
55 if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
56 LOG_WARNING("ioctl failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
57 return boost::none;
58 }
59
60 return ifr.ifr_mtu;
61 }
62
63 }}} // namespace facter::facts::freebsd
+0
-29
lib/src/facts/freebsd/operating_system_resolver.cc less more
0 #include <leatherman/execution/execution.hpp>
1 #include <leatherman/util/regex.hpp>
2
3 #include <internal/facts/freebsd/operating_system_resolver.hpp>
4
5 using namespace std;
6 using namespace leatherman::execution;
7 using namespace leatherman::util;
8
9 namespace facter { namespace facts { namespace freebsd {
10
11 void operating_system_resolver::collect_release_data(collection& facts, data& result)
12 {
13 auto exec = execute("freebsd-version");
14 if (exec.success) {
15 result.release = exec.output;
16
17 string major, minor, branch;
18 re_search(exec.output, boost::regex("(\\d+)\\.(\\d+)-(.*)"), &major, &minor, &branch);
19 result.major = move(major);
20 result.minor = move(minor);
21 result.freebsd.branch = move(branch);
22
23 string patchlevel;
24 re_search(result.freebsd.branch, boost::regex("RELEASE-p(\\d+)"), &patchlevel);
25 result.freebsd.patchlevel = move(patchlevel);
26 }
27 }
28 } } } // namespace facter::facts::freebsd
+0
-63
lib/src/facts/freebsd/processor_resolver.cc less more
0 #include <internal/facts/freebsd/processor_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/types.h>
3 #include <sys/sysctl.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace freebsd {
8
9 processor_resolver::data processor_resolver::collect_data(collection& facts)
10 {
11 auto result = posix::processor_resolver::collect_data(facts);
12 size_t len;
13 int mib[2];
14 mib[0] = CTL_HW;
15
16 // Get the logical count of processors
17 len = sizeof(result.logical_count);
18 mib[1] = HW_NCPU;
19
20 if (sysctl(mib, 2, &result.logical_count, &len, nullptr, 0) == -1) {
21 LOG_DEBUG("sysctl hw.ncpu failed: {1} ({2}): logical processor count is unavailable.", strerror(errno), errno);
22 }
23
24 // For each logical processor, collect the model name
25 if (result.logical_count > 0) {
26 // Note: we're using the same description string for all logical processors:
27 // using different CPUs is not even likely to work.
28 vector<char> buffer(256);
29
30 while (true) {
31 size_t size = buffer.size();
32 mib[1] = HW_MODEL;
33 if (sysctl(mib, 2, buffer.data(), &size, nullptr, 0) == 0) {
34 buffer.resize(size + 1);
35 result.models.resize(result.logical_count, buffer.data());
36 break;
37 }
38 if (errno != ENOMEM) {
39 LOG_DEBUG("sysctl hw.model failed: {1} ({2}): processor models are unavailable.", strerror(errno), errno);
40 break;
41 }
42 buffer.resize(buffer.size() * 2);
43 }
44 }
45
46 // Set the speed
47 len = sizeof(result.speed);
48
49 int cmd[2];
50 size_t two = 2;
51 sysctlnametomib("hw.clockrate", cmd, &two);
52
53 if (sysctl(cmd, 2, &result.speed, &len, nullptr, 0) == -1) {
54 LOG_DEBUG("sysctl hw.cpuspeed failed: {1} ({2}): processor speed is unavailable.", strerror(errno), errno);
55 }
56 // Scale the speed to something resolve() can correctly map
57 result.speed *= 1000 * 1000;
58
59 return result;
60 }
61
62 }}} // namespace facter::facts::freebsd
+0
-41
lib/src/facts/freebsd/virtualization_resolver.cc less more
0 #include <internal/facts/freebsd/virtualization_resolver.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/vm.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 #include <sys/types.h>
9 #include <sys/sysctl.h>
10
11 using namespace std;
12 using namespace facter::facts;
13 using namespace leatherman::execution;
14
15 namespace facter { namespace facts { namespace freebsd {
16
17 string virtualization_resolver::get_hypervisor(collection& facts)
18 {
19 string value = get_jail_vm();
20
21 if (value.empty()) {
22 value = get_fact_vm(facts);
23 }
24
25 return value;
26 }
27
28 string virtualization_resolver::get_jail_vm()
29 {
30 int jailed;
31 size_t size = sizeof(jailed);
32 if (sysctlbyname("security.jail.jailed", &jailed, &size, NULL, 0) == 0) {
33 if (jailed)
34 return vm::jail;
35 }
36
37 return {};
38 }
39
40 } } } // namespace facter::facts::freebsd
+0
-12
lib/src/facts/freebsd/zfs_resolver.cc less more
0 #include <internal/facts/freebsd/zfs_resolver.hpp>
1
2 using namespace std;
3
4 namespace facter { namespace facts { namespace freebsd {
5
6 string zfs_resolver::zfs_command()
7 {
8 return "/sbin/zfs";
9 }
10
11 }}} // namespace facter::facts::freebsd
+0
-12
lib/src/facts/freebsd/zpool_resolver.cc less more
0 #include <internal/facts/freebsd/zpool_resolver.hpp>
1
2 using namespace std;
3
4 namespace facter { namespace facts { namespace freebsd {
5
6 string zpool_resolver::zpool_command()
7 {
8 return "/sbin/zpool";
9 }
10
11 }}} // namespace facter::facts::freebsd
+0
-22
lib/src/facts/glib/load_average_resolver.cc less more
0 #include <internal/facts/glib/load_average_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <cstdlib>
3
4 #ifdef __sun
5 #include <sys/loadavg.h>
6 #endif
7
8 using namespace std;
9
10 namespace facter { namespace facts { namespace glib {
11
12 boost::optional<tuple<double, double, double> > load_average_resolver::get_load_averages()
13 {
14 array<double, 3> averages;
15 if (getloadavg(averages.data(), averages.size()) == -1) {
16 LOG_DEBUG("failed to retrieve load averages: {1} ({2}).", strerror(errno), errno);
17 return boost::none;
18 }
19 return make_tuple(averages[0], averages[1], averages[2]);
20 }
21 }}} // namespace facter::facts::glib
+0
-44
lib/src/facts/linux/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/linux/kernel_resolver.hpp>
2 #include <internal/facts/posix/identity_resolver.hpp>
3 #include <internal/facts/linux/operating_system_resolver.hpp>
4 #include <internal/facts/linux/networking_resolver.hpp>
5 #include <internal/facts/linux/disk_resolver.hpp>
6 #include <internal/facts/linux/dmi_resolver.hpp>
7 #include <internal/facts/linux/processor_resolver.hpp>
8 #include <internal/facts/linux/uptime_resolver.hpp>
9 #include <internal/facts/linux/virtualization_resolver.hpp>
10 #include <internal/facts/ssh_resolver.hpp>
11 #include <internal/facts/posix/timezone_resolver.hpp>
12 #include <internal/facts/linux/filesystem_resolver.hpp>
13 #include <internal/facts/linux/memory_resolver.hpp>
14 #include <internal/facts/glib/load_average_resolver.hpp>
15 #include <internal/facts/posix/xen_resolver.hpp>
16 #include <internal/facts/linux/fips_resolver.hpp>
17
18
19 using namespace std;
20
21 namespace facter { namespace facts {
22
23 void collection::add_platform_facts()
24 {
25 add(make_shared<linux::kernel_resolver>());
26 add(make_shared<linux::operating_system_resolver>());
27 add(make_shared<linux::networking_resolver>());
28 add(make_shared<linux::disk_resolver>());
29 add(make_shared<linux::dmi_resolver>());
30 add(make_shared<linux::processor_resolver>());
31 add(make_shared<linux::uptime_resolver>());
32 add(make_shared<ssh_resolver>());
33 add(make_shared<linux::virtualization_resolver>());
34 add(make_shared<posix::identity_resolver>());
35 add(make_shared<posix::timezone_resolver>());
36 add(make_shared<linux::filesystem_resolver>());
37 add(make_shared<linux::memory_resolver>());
38 add(make_shared<glib::load_average_resolver>());
39 add(make_shared<posix::xen_resolver>());
40 add(make_shared<linux::fips_resolver>());
41 }
42
43 }} // namespace facter::facts
+0
-107
lib/src/facts/linux/disk_resolver.cc less more
0 #include <internal/facts/linux/disk_resolver.hpp>
1 #include <leatherman/file_util/file.hpp>
2 #include <leatherman/file_util/directory.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <boost/lexical_cast.hpp>
5 #include <boost/filesystem.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 #ifdef USE_UDEV
9 #include <libudev.h>
10 #endif // USE_UDEV
11
12 using namespace std;
13 using namespace boost::filesystem;
14 using boost::lexical_cast;
15 using boost::bad_lexical_cast;
16
17 namespace lth_file = leatherman::file_util;
18
19 namespace facter { namespace facts { namespace linux {
20
21 disk_resolver::data disk_resolver::collect_data(collection& facts)
22 {
23 static string root_directory = "/sys/block";
24
25 // The size of the block devices is in 512 byte blocks
26 const int block_size = 512;
27
28 data result;
29
30 boost::system::error_code ec;
31 if (!is_directory(root_directory, ec)) {
32 LOG_DEBUG("{1}: {2}: disk facts are unavailable.", root_directory, ec.message());
33 return result;
34 }
35
36 #ifdef USE_UDEV
37 struct udev *udev;
38 udev = udev_new();
39 #endif // USE_UDEV
40
41 lth_file::each_subdirectory(root_directory, [&](string const& dir) {
42 path device_directory(dir);
43
44 disk d;
45 d.name = device_directory.filename().string();
46
47 // Check for the device subdirectory's existence
48 path device_subdirectory = device_directory / "device";
49 boost::system::error_code ec;
50 if (!is_directory(device_subdirectory, ec)) {
51 return true;
52 }
53
54 string size_file_path = (device_directory / "size").string();
55 string vendor_file_path = (device_subdirectory / "vendor").string();
56 string model_file_path = (device_subdirectory / "model").string();
57
58 // Read the size of the block device
59 // The size is in 512 byte blocks
60 if (is_regular_file(size_file_path, ec)) {
61 try {
62 string blocks = lth_file::read(size_file_path);
63 boost::trim(blocks);
64 d.size = lexical_cast<uint64_t>(blocks) * block_size;
65 } catch (bad_lexical_cast& ex) {
66 LOG_DEBUG("size of disk {1} is invalid: size information is unavailable.", d.name);
67 }
68 }
69
70 // Read the vendor fact
71 if (is_regular_file(vendor_file_path, ec)) {
72 d.vendor = lth_file::read(vendor_file_path);
73 boost::trim(d.vendor);
74 }
75
76 // Read the model fact
77 if (is_regular_file(model_file_path, ec)) {
78 d.model = lth_file::read(model_file_path);
79 boost::trim(d.model);
80 }
81
82 #ifdef USE_UDEV
83 auto dev = udev_device_new_from_subsystem_sysname(udev, "block", d.name.c_str());
84 if (dev) {
85 const char *serial;
86 serial = udev_device_get_property_value(dev, "ID_SCSI_SERIAL");
87 if (!serial)
88 serial = udev_device_get_property_value(dev, "ID_SERIAL_SHORT");
89
90 if (serial)
91 d.serial_number = serial;
92 }
93 #endif // USE_UDEV
94
95 result.disks.emplace_back(move(d));
96 return true;
97 });
98
99 #ifdef USE_UDEV
100 udev_unref(udev);
101 #endif // USE_UDEV
102
103 return result;
104 }
105
106 }}} // namespace facter::facts::linux
+0
-208
lib/src/facts/linux/dmi_resolver.cc less more
0 #include <internal/facts/linux/dmi_resolver.hpp>
1 #include <internal/util/agent.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <leatherman/file_util/file.hpp>
8 #include <leatherman/execution/execution.hpp>
9 #include <boost/filesystem.hpp>
10 #include <boost/algorithm/string.hpp>
11
12 using namespace std;
13 using namespace boost::filesystem;
14 namespace bs = boost::system;
15 namespace lth_file = leatherman::file_util;
16 using namespace facter::util;
17 using namespace leatherman::util;
18
19 namespace facter { namespace facts { namespace linux {
20
21 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
22 {
23 data result;
24
25 // Check that /sys/class/dmi exists (requires kernel 2.6.23+)
26 bs::error_code ec;
27 if (exists("/sys/class/dmi/", ec)) {
28 result.bios_vendor = read("/sys/class/dmi/id/bios_vendor");
29 result.bios_version = read("/sys/class/dmi/id/bios_version");
30 result.bios_release_date = read("/sys/class/dmi/id/bios_date");
31 result.board_asset_tag = read("/sys/class/dmi/id/board_asset_tag");
32 result.board_manufacturer = read("/sys/class/dmi/id/board_vendor");
33 result.board_product_name = read("/sys/class/dmi/id/board_name");
34 result.board_serial_number = read("/sys/class/dmi/id/board_serial");
35 result.chassis_asset_tag = read("/sys/class/dmi/id/chassis_asset_tag");
36 result.manufacturer = read("/sys/class/dmi/id/sys_vendor");
37 result.product_name = read("/sys/class/dmi/id/product_name");
38 result.serial_number = read("/sys/class/dmi/id/product_serial");
39 result.uuid = read("/sys/class/dmi/id/product_uuid");
40 result.chassis_type = to_chassis_description(read("/sys/class/dmi/id/chassis_type"));
41 } else {
42 // dmidecode does not work on power machines, so if we're on one, then there's no need
43 // to proceed any further
44 auto isa = facts.get<string_value>(fact::hardware_isa);
45 if (isa && boost::starts_with(isa->value(), "ppc64")) {
46 return result;
47 }
48
49 LOG_DEBUG("/sys/class/dmi cannot be accessed: using dmidecode to query DMI information.");
50
51 int dmi_type = -1;
52 string dmidecode = agent::which("dmidecode");
53 leatherman::execution::each_line(dmidecode, [&](string& line) {
54 parse_dmidecode_output(result, line, dmi_type);
55 return true;
56 });
57 }
58 return result;
59 }
60
61 void dmi_resolver::parse_dmidecode_output(data& result, string& line, int& dmi_type)
62 {
63 static const boost::regex dmi_section_pattern("^Handle 0x.{4}, DMI type (\\d{1,3})");
64
65 // Stores the relevant sections; this is in order based on DMI type ID
66 // Ensure there's a trailing semicolon on each entry and keep in sync with the switch statement below
67 static const vector<vector<string>> sections = {
68 { // BIOS (0)
69 "vendor:",
70 "version:",
71 "release date:",
72 },
73 { // System (1)
74 "manufacturer:",
75 "product:",
76 "product name:",
77 "serial number:",
78 "uuid:",
79 },
80 { // Base Board (2)
81 "manufacturer:",
82 "product:",
83 "product name:",
84 "serial number:",
85 "asset tag:",
86 },
87 { // Chassis (3)
88 "type:",
89 "chassis type:",
90 "asset tag:",
91 }
92 };
93
94 // Check for a section header
95 if (re_search(line, dmi_section_pattern, &dmi_type)) {
96 return;
97 }
98
99 // Check that we're in a relevant section
100 if (dmi_type < 0 || static_cast<size_t>(dmi_type) >= sections.size()) {
101 return;
102 }
103
104 // Trim leading whitespace
105 boost::trim_left(line);
106
107 // Find a matching header
108 auto const& headers = sections[dmi_type];
109 auto it = find_if(headers.begin(), headers.end(), [&](string const& header) {
110 return boost::istarts_with(line, header);
111 });
112 if (it == headers.end()) {
113 return;
114 }
115
116 // Get the value and trim it
117 string value = line.substr(it->size());
118 boost::trim(value);
119
120 // Calculate the index into the header vector
121 size_t index = it - headers.begin();
122
123 // Assign to the appropriate member
124 string* member = nullptr;
125 switch (dmi_type) {
126 case 0: { // BIOS information
127 if (index == 0) {
128 member = &result.bios_vendor;
129 } else if (index == 1) {
130 member = &result.bios_version;
131 } else if (index == 2) {
132 member = &result.bios_release_date;
133 }
134 break;
135 }
136
137 case 1: { // System information
138 if (index == 0) {
139 member = &result.manufacturer;
140 } else if (index == 1 || index == 2) {
141 member = &result.product_name;
142 } else if (index == 3) {
143 member = &result.serial_number;
144 } else if (index == 4) {
145 member = &result.uuid;
146 }
147 break;
148 }
149
150 case 2: { // Base board information
151 if (index == 0) {
152 member = &result.board_manufacturer;
153 } else if (index == 1 || index == 2) {
154 member = &result.board_product_name;
155 } else if (index == 3) {
156 member = &result.board_serial_number;
157 } else if (index == 4) {
158 member = &result.board_asset_tag;
159 }
160 break;
161 }
162
163 case 3: { // Chassis information
164 if (index == 0 || index == 1) {
165 member = &result.chassis_type;
166 } else if (index == 2) {
167 member = &result.chassis_asset_tag;
168 }
169 break;
170 }
171
172 default:
173 break;
174 }
175
176 if (member) {
177 *member = std::move(value);
178 }
179 }
180
181 string dmi_resolver::read(std::string const& path)
182 {
183 bs::error_code ec;
184 if (!is_regular_file(path, ec)) {
185 LOG_DEBUG("{1}: {2}.", path, ec.message());
186 return {};
187 }
188
189 string value;
190 if (!lth_file::read(path, value)) {
191 LOG_DEBUG("{1}: file could not be read.", path);
192 return {};
193 }
194
195 boost::trim(value);
196
197 // Replace any non-printable ASCII characters with '.'
198 // This mimics the behavior of dmidecode
199 for (auto& c : value) {
200 if (c < 32 || c == 127) {
201 c = '.';
202 }
203 }
204 return value;
205 }
206
207 }}} // namespace facter::facts::linux
+0
-301
lib/src/facts/linux/filesystem_resolver.cc less more
0 #include <internal/facts/linux/filesystem_resolver.hpp>
1 #include <internal/util/scoped_file.hpp>
2 #include <leatherman/file_util/file.hpp>
3 #include <leatherman/file_util/directory.hpp>
4 #include <leatherman/util/regex.hpp>
5 #include <leatherman/logging/logging.hpp>
6 #include <boost/algorithm/string.hpp>
7 #include <boost/filesystem.hpp>
8 #include <boost/lexical_cast.hpp>
9 #include <mntent.h>
10 #include <sys/vfs.h>
11 #include <set>
12 #include <map>
13
14 #ifdef USE_BLKID
15 #include <blkid/blkid.h>
16 #endif // USE_BLKID
17
18 using namespace std;
19 using namespace facter::facts;
20 using namespace facter::util;
21 using namespace boost::filesystem;
22 namespace sys = boost::system;
23 namespace lth_file = leatherman::file_util;
24 using namespace leatherman::util;
25
26 using boost::lexical_cast;
27 using boost::bad_lexical_cast;
28
29 namespace facter { namespace facts { namespace linux {
30
31 string filesystem_resolver::safe_convert(char const* value)
32 {
33 string result;
34
35 if (value) {
36 while (*value) {
37 unsigned char c = static_cast<unsigned char>(*value);
38 if (c >= 128) {
39 result += "M-";
40 c -= 128;
41 }
42 if (c < 32 || c == 0xf7) {
43 result += '^';
44 c ^= 0x40;
45 } else if (c == '"' || c == '\\') {
46 result += '\\';
47 }
48 result += static_cast<char>(c);
49 ++value;
50 }
51 }
52 return result;
53 }
54
55 filesystem_resolver::data filesystem_resolver::collect_data(collection& facts)
56 {
57 data result;
58 collect_mountpoint_data(result);
59 collect_filesystem_data(result);
60 collect_partition_data(result);
61 return result;
62 }
63
64 void filesystem_resolver::collect_mountpoint_data(data& result)
65 {
66 // Populate the mountpoint data
67 scoped_file file(setmntent("/etc/mtab", "r"));
68 if (!static_cast<FILE *>(file)) {
69 LOG_ERROR("setmntent failed: {1} ({2}): mountpoints are unavailable.", strerror(errno), errno);
70 return;
71 }
72 string root_device;
73 mntent entry;
74 char buffer[4096];
75 map<string, mountpoint> mountpoints_map;
76 while (mntent *ptr = getmntent_r(file, &entry, buffer, sizeof(buffer))) {
77 string device = ptr->mnt_fsname;
78 string mtype = ptr->mnt_type;
79
80 // Skip over any non-tmpfs mount under /proc or /sys
81 if (mtype != "tmpfs" && (boost::starts_with(ptr->mnt_dir, "/proc") || boost::starts_with(ptr->mnt_dir, "/sys"))) {
82 continue;
83 }
84
85 // Skip over automounts
86 if (mtype == "autofs") {
87 continue;
88 }
89
90 // If the "root" device, lookup the actual device from the kernel options
91 // This is done because not all systems symlink /dev/root
92 if (device == "/dev/root") {
93 if (root_device.empty()) {
94 boost::regex root_pattern("root=([^\\s]+)");
95 lth_file::each_line("/proc/cmdline", [&](string& line) {
96 if (re_search(line, root_pattern, &root_device)) {
97 return false;
98 }
99 return true;
100 });
101 }
102 if (!root_device.empty()) {
103 device = root_device;
104 }
105 }
106
107 mountpoint point;
108 point.name = ptr->mnt_dir;
109 point.device = std::move(device);
110 point.filesystem = ptr->mnt_type;
111 boost::split(point.options, ptr->mnt_opts, boost::is_any_of(","), boost::token_compress_on);
112
113 struct statfs stats;
114 if (statfs(ptr->mnt_dir, &stats) != -1) {
115 point.size = (static_cast<uint64_t>(stats.f_frsize)
116 * static_cast<uint64_t>(stats.f_blocks));
117 point.available = (static_cast<uint64_t>(stats.f_frsize)
118 * static_cast<uint64_t>(stats.f_bavail));
119 point.free = (static_cast<uint64_t>(stats.f_frsize)
120 * static_cast<uint64_t>(stats.f_bfree));
121 }
122
123 auto iterator = mountpoints_map.find(point.name);
124 if (iterator != mountpoints_map.end()){
125 if (boost::starts_with(point.device, "/dev/") || point.filesystem == "tmpfs")
126 iterator->second = point;
127 } else {
128 mountpoints_map[point.name] = point;
129 }
130 }
131
132 result.mountpoints.reserve(mountpoints_map.size());
133 for_each(mountpoints_map.begin(), mountpoints_map.end(),
134 [&](const map<string, mountpoint>::value_type& p)
135 { result.mountpoints.emplace_back(p.second); });
136 }
137
138 void filesystem_resolver::collect_filesystem_data(data& result)
139 {
140 // Populate the partition data
141 lth_file::each_line("/proc/filesystems", [&](string &line) {
142 boost::trim(line);
143
144 // Ignore lines without devices or fuseblk
145 if (boost::starts_with(line, "nodev") || line == "fuseblk") {
146 return true;
147 }
148
149 result.filesystems.emplace(move(line));
150 return true;
151 });
152 }
153
154 void filesystem_resolver::collect_partition_data(data& result)
155 {
156 // Populate a map of device -> mountpoint
157 map<string, string> mountpoints;
158 for (auto const& point : result.mountpoints) {
159 mountpoints.insert(make_pair(point.device, point.name));
160 }
161
162 void* cache = nullptr;
163
164 #ifdef USE_BLKID
165 blkid_cache actual = nullptr;
166 if (blkid_get_cache(&actual, "/dev/null") == 0) {
167 // Do a probe since we're not using a cache file
168 if (blkid_probe_all(actual) != 0) {
169 LOG_DEBUG("blkid_probe_all failed: partition attributes are not available.");
170 blkid_put_cache(actual);
171 actual = nullptr;
172 }
173 cache = actual;
174 } else {
175 LOG_DEBUG("blkid_get_cache failed: partition attributes are not available.");
176 }
177 #else
178 LOG_DEBUG("facter was built without libblkid support: partition attributes are not available.");
179 #endif // USE_BLKID
180
181 lth_file::each_subdirectory("/sys/block", [&](string const& subdirectory) {
182 path block_device_path(subdirectory);
183 auto block_device_filename = block_device_path.filename().string();
184
185 // For devices, look up partition subdirectories
186 sys::error_code ec;
187 if (is_directory(block_device_path / "device", ec)) {
188 lth_file::each_subdirectory(subdirectory, [&](string const& subdirectory) {
189 path partition_path(subdirectory);
190 auto partition_name = partition_path.filename().string();
191
192 // Ignore any subdirectory that does not start with the device file name
193 if (!boost::starts_with(partition_name, block_device_filename)) {
194 return true;
195 }
196
197 partition part;
198 part.name = "/dev/" + partition_name;
199 populate_partition_attributes(part, subdirectory, cache, mountpoints);
200 result.partitions.emplace_back(std::move(part));
201 return true;
202 });
203 } else if (is_directory(block_device_path / "dm", ec)) {
204 // For mapped devices, lookup the mapping name
205 partition part;
206 string mapping_name = lth_file::read((block_device_path / "dm" / "name").string());
207 boost::trim(mapping_name);
208 if (mapping_name.empty()) {
209 mapping_name = "/dev/" + block_device_filename;
210 } else {
211 mapping_name = "/dev/mapper/" + mapping_name;
212 }
213 part.name = std::move(mapping_name);
214
215 populate_partition_attributes(part, block_device_path.string(), cache, mountpoints);
216 result.partitions.emplace_back(std::move(part));
217 } else if (is_directory(block_device_path / "loop")) {
218 // Lookup the backing file
219 partition part;
220 part.name = "/dev/" + block_device_filename;
221 part.backing_file = lth_file::read((block_device_path / "loop" / "backing_file").string());
222 boost::trim(part.backing_file);
223
224 populate_partition_attributes(part, block_device_path.string(), cache, mountpoints);
225 result.partitions.emplace_back(std::move(part));
226 }
227 return true;
228 });
229
230 #ifdef USE_BLKID
231 // Cleanup the blkid cache if there is one
232 if (cache) {
233 blkid_put_cache(static_cast<blkid_cache>(cache));
234 cache = nullptr;
235 }
236 #endif // USE_BLKID
237 }
238
239 void filesystem_resolver::populate_partition_attributes(partition& part, string const& device_directory, void* cache, map<string, string> const& mountpoints)
240 {
241 #ifdef USE_BLKID
242 if (cache) {
243 auto device = blkid_get_dev(static_cast<blkid_cache>(cache), part.name.c_str(), 0);
244 if (!device) {
245 LOG_DEBUG("blkid_get_dev failed: partition attributes are unavailable for '{1}'.", part.name);
246 } else {
247 // Populate the attributes
248 auto it = blkid_tag_iterate_begin(device);
249 if (it) {
250 const char* name;
251 const char* value;
252 while (blkid_tag_next(it, &name, &value) == 0) {
253 string* ptr = nullptr;
254 string attribute = name;
255 boost::to_lower(attribute);
256 if (attribute == "type") {
257 ptr = &part.filesystem;
258 } else if (attribute == "label") {
259 ptr = &part.label;
260 } else if (attribute == "partlabel") {
261 ptr = &part.partition_label;
262 } else if (attribute == "uuid") {
263 ptr = &part.uuid;
264 } else if (attribute == "partuuid") {
265 ptr = &part.partition_uuid;
266 }
267 if (!ptr) {
268 continue;
269 }
270 (*ptr) = safe_convert(value);
271 }
272 blkid_tag_iterate_end(it);
273 }
274 }
275 }
276 #endif // USE_BLKID
277
278 // Lookup the mountpoint
279 auto it = mountpoints.find(part.name);
280 if (it != mountpoints.end()) {
281 part.mount = it->second;
282 }
283
284 // The size of a block, in bytes
285 const int block_size = 512;
286
287 // Read the size
288 string blocks = lth_file::read(device_directory + "/size");
289 boost::trim(blocks);
290 if (!blocks.empty()) {
291 try {
292 part.size = lexical_cast<uint64_t>(blocks) * block_size;
293 } catch (bad_lexical_cast& ex) {
294 LOG_DEBUG("cannot determine size of partition '{1}': '{2}' is not an integral value.", part.name, blocks);
295 }
296 }
297 }
298
299 }}} // namespace facter::facts::linux
300
+0
-31
lib/src/facts/linux/fips_resolver.cc less more
0 #include <internal/facts/linux/fips_resolver.hpp>
1 #include <leatherman/file_util/file.hpp>
2 #include <boost/algorithm/string.hpp>
3 #include <boost/lexical_cast.hpp>
4
5 using namespace std;
6
7 using boost::lexical_cast;
8 using boost::bad_lexical_cast;
9
10 namespace lth_file = leatherman::file_util;
11
12 namespace facter { namespace facts { namespace linux {
13
14 fips_resolver::data fips_resolver::collect_data(collection& facts)
15 {
16 data result;
17
18 // Set a safe default
19 result.is_fips_mode_enabled = false;
20
21 lth_file::each_line("/proc/sys/crypto/fips_enabled", [&](string& line) {
22 boost::trim(line);
23 result.is_fips_mode_enabled = line != "0";
24
25 return true;
26 });
27 return result;
28 }
29
30 }}} // namespace facter::facts::linux
+0
-20
lib/src/facts/linux/kernel_resolver.cc less more
0 #include <internal/facts/linux/kernel_resolver.hpp>
1 #include <leatherman/util/regex.hpp>
2 #include <boost/regex.hpp>
3 #include <tuple>
4
5 using namespace std;
6 using namespace leatherman::util;
7
8 namespace facter { namespace facts { namespace linux {
9
10 tuple<string, string> kernel_resolver::parse_version(string const& version) const
11 {
12 string major, minor;
13 if (re_search(version, boost::regex("(\\d+\\.\\d+)(.*)"), &major, &minor)) {
14 return make_tuple(major, minor);
15 }
16 return make_tuple(move(version), string());
17 }
18
19 }}} // namespace facter::facts::resolvers
+0
-50
lib/src/facts/linux/memory_resolver.cc less more
0 #include <internal/facts/linux/memory_resolver.hpp>
1 #include <leatherman/file_util/file.hpp>
2 #include <boost/algorithm/string.hpp>
3 #include <boost/lexical_cast.hpp>
4
5 using namespace std;
6
7 using boost::lexical_cast;
8 using boost::bad_lexical_cast;
9
10 namespace lth_file = leatherman::file_util;
11
12 namespace facter { namespace facts { namespace linux {
13
14 memory_resolver::data memory_resolver::collect_data(collection& facts)
15 {
16 data result;
17 lth_file::each_line("/proc/meminfo", [&](string& line) {
18 uint64_t* variable = nullptr;
19 if (boost::starts_with(line, "MemTotal:")) {
20 variable = &result.mem_total;
21 } else if (boost::starts_with(line, "MemFree:") ||
22 boost::starts_with(line, "Buffers:") ||
23 boost::starts_with(line, "Cached:")) {
24 variable = &result.mem_free;
25 } else if (boost::starts_with(line, "SwapTotal:")) {
26 variable = &result.swap_total;
27 } else if (boost::starts_with(line, "SwapFree:")) {
28 variable = &result.swap_free;
29 }
30 if (!variable) {
31 return true;
32 }
33
34 vector<boost::iterator_range<string::iterator>> parts;
35 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
36 if (parts.size() < 2) {
37 return true;
38 }
39
40 try {
41 *variable += lexical_cast<uint64_t>(parts[1]) * 1024;
42 } catch (bad_lexical_cast&) {
43 }
44 return true;
45 });
46 return result;
47 }
48
49 }}} // namespace facter::facts::linux
+0
-283
lib/src/facts/linux/networking_resolver.cc less more
0 #include <internal/facts/linux/networking_resolver.hpp>
1 #include <internal/util/posix/scoped_descriptor.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/file_util/file.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <algorithm>
7 #include <cstring>
8 #include <unordered_set>
9 #include <netpacket/packet.h>
10 #include <net/if.h>
11 #include <sys/ioctl.h>
12
13 using namespace std;
14 using namespace facter::util::posix;
15
16 namespace lth_file = leatherman::file_util;
17 namespace lth_exe = leatherman::execution;
18
19 namespace facter { namespace facts { namespace linux {
20
21 networking_resolver::data networking_resolver::collect_data(collection& facts)
22 {
23 read_routing_table();
24 data result = bsd::networking_resolver::collect_data(facts);
25 populate_from_routing_table(result);
26
27 // On linux, the macaddress of bonded interfaces is reported
28 // as the address of the bonding master. We want to report the
29 // original HW address, so we dig it out of /proc
30 for (auto& interface : result.interfaces) {
31 // For each interface we check if we're part of a bond,
32 // and update the `macaddress` fact if we are
33 auto bond_master = get_bond_master(interface.name);
34 if (!bond_master.empty()) {
35 bool in_our_block = false;
36 lth_file::each_line("/proc/net/bonding/"+bond_master, [&](string& line) {
37 // /proc/net/bonding files are organized into chunks for each slave
38 // interface. We want to grab the mac address for the block we're in.
39 if (line == "Slave Interface: " + interface.name) {
40 in_our_block = true;
41 } else if (line.find("Slave Interface") != string::npos) {
42 in_our_block = false;
43 }
44
45 // If we're in the block for our iface, we can grab the HW address
46 if (in_our_block && line.find("Permanent HW addr: ") != string::npos) {
47 auto split = line.find(':') + 2;
48 interface.macaddress = line.substr(split, string::npos);
49 return false;
50 }
51 return true;
52 });
53 }
54 }
55 return result;
56 }
57
58 bool networking_resolver::is_link_address(sockaddr const* addr) const
59 {
60 return addr && addr->sa_family == AF_PACKET;
61 }
62
63 uint8_t const* networking_resolver::get_link_address_bytes(sockaddr const* addr) const
64 {
65 if (!is_link_address(addr)) {
66 return nullptr;
67 }
68 sockaddr_ll const* link_addr = reinterpret_cast<sockaddr_ll const*>(addr);
69 if (link_addr->sll_halen != 6 && link_addr->sll_halen != 20) {
70 return nullptr;
71 }
72 return reinterpret_cast<uint8_t const*>(link_addr->sll_addr);
73 }
74
75 uint8_t networking_resolver::get_link_address_length(sockaddr const* addr) const
76 {
77 if (!is_link_address(addr)) {
78 return 0;
79 }
80 sockaddr_ll const* link_addr = reinterpret_cast<sockaddr_ll const*>(addr);
81 return link_addr->sll_halen;
82 }
83
84 boost::optional<uint64_t> networking_resolver::get_link_mtu(string const& interface, void* data) const
85 {
86 // Unfortunately in Linux, the data points at interface statistics
87 // Nothing useful for us, so we need to use ioctl to query the MTU
88 ifreq req;
89 memset(&req, 0, sizeof(req));
90 strncpy(req.ifr_name, interface.c_str(), sizeof(req.ifr_name) - 1);
91
92 scoped_descriptor sock(socket(AF_INET, SOCK_DGRAM, 0));
93 if (static_cast<int>(sock) < 0) {
94 LOG_WARNING("socket failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
95 return boost::none;
96 }
97
98 if (ioctl(sock, SIOCGIFMTU, &req) == -1) {
99 LOG_WARNING("ioctl failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
100 return boost::none;
101 }
102 return req.ifr_mtu;
103 }
104
105 string networking_resolver::get_primary_interface() const
106 {
107 // If we have a list of routes, then we'll determine the
108 // primary interface from that later on when we are processing
109 // them.
110 if (routes4.size()) {
111 return {};
112 }
113
114 // Read /proc/net/route to determine the primary interface
115 // We consider the primary interface to be the one that has 0.0.0.0 as the
116 // routing destination.
117 string interface;
118 lth_file::each_line("/proc/net/route", [&interface](string& line) {
119 vector<boost::iterator_range<string::iterator>> parts;
120 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
121 if (parts.size() > 7 && parts[1] == boost::as_literal("00000000")
122 && parts[7] == boost::as_literal("00000000")) {
123 interface.assign(parts[0].begin(), parts[0].end());
124 return false;
125 }
126 return true;
127 });
128 return interface;
129 }
130
131 void networking_resolver::read_routing_table()
132 {
133 auto ip_command = lth_exe::which("ip");
134 if (ip_command.empty()) {
135 LOG_DEBUG("Could not find the 'ip' command. Network bindings will not be populated from routing table");
136 return;
137 }
138
139 unordered_set<string> known_route_types {
140 "anycast",
141 "unicast",
142 "broadcast",
143 "local",
144 "nat",
145 "unreachable",
146 "prohibit",
147 "blackhole",
148 "throw"
149 };
150
151 auto parse_route_line = [&known_route_types](string& line, int family, std::vector<route>& routes) {
152 vector<boost::iterator_range<string::iterator>> parts;
153 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
154
155 // skip links that are linkdown
156 if (std::find_if(parts.cbegin(), parts.cend(), [](const boost::iterator_range<string::iterator>& range) {
157 return std::string(range.begin(), range.end()) == "linkdown";
158 }) != parts.cend()) {
159 return true;
160 }
161
162 // FACT-1282
163 std::string route_type(parts[0].begin(), parts[0].end());
164 if (known_route_types.find(route_type) != known_route_types.end()) {
165 parts.erase(parts.begin());
166 }
167
168 route r;
169 r.destination.assign(parts[0].begin(), parts[0].end());
170
171 // Check if we queried for the IPV6 routing tables. If yes, then check if our
172 // destination address is missing a ':'. If yes, then IPV6 is disabled since
173 // IPV6 addresses have a ':' in them. Our ip command has mistakenly outputted IPV4
174 // information. This is bogus data that we want to flush.
175 //
176 // See FACT-1475 for more details.
177 if (family == AF_INET6 && r.destination.find(':') == string::npos) {
178 routes = {};
179 return false;
180 }
181
182 // Iterate over key/value pairs and add the ones we care
183 // about to our routes entries
184 for (size_t i = 1; i < parts.size(); i += 2) {
185 std::string key(parts[i].begin(), parts[i].end());
186 if (key == "dev") {
187 r.interface.assign(parts[i+1].begin(), parts[i+1].end());
188 }
189 if (key == "src") {
190 r.source.assign(parts[i+1].begin(), parts[i+1].end());
191 }
192 }
193 routes.push_back(r);
194 return true;
195 };
196
197 lth_exe::each_line(ip_command, { "route", "show" }, [this, &parse_route_line](string& line) {
198 return parse_route_line(line, AF_INET, this->routes4);
199 });
200 lth_exe::each_line(ip_command, { "-6", "route", "show" }, [this, &parse_route_line](string& line) {
201 return parse_route_line(line, AF_INET6, this->routes6);
202 });
203 }
204
205 void networking_resolver::populate_from_routing_table(networking_resolver::data& result) const
206 {
207 for (const auto& r : routes4) {
208 if (r.destination == "default" && result.primary_interface.empty()) {
209 result.primary_interface = r.interface;
210 }
211 associate_src_with_iface(r, result, [](interface& iface) -> vector<binding>& {
212 return iface.ipv4_bindings;
213 });
214 }
215
216 for (const auto& r : routes6) {
217 associate_src_with_iface(r, result, [](interface& iface) -> vector<binding>& {
218 return iface.ipv6_bindings;
219 });
220 }
221 }
222
223 template<typename F>
224 void networking_resolver::associate_src_with_iface(const networking_resolver::route& r, networking_resolver::data& result, F get_bindings) const {
225 if (!r.source.empty()) {
226 auto iface = find_if(result.interfaces.begin(), result.interfaces.end(), [&](const interface& iface) {
227 return iface.name == r.interface;
228 });
229 if (iface != result.interfaces.end()) {
230 auto& bindings = get_bindings(*iface);
231 auto existing_binding = find_if(bindings.begin(), bindings.end(), [&](const binding& b) {
232 return b.address == r.source;
233 });
234 if (existing_binding == bindings.end()) {
235 binding b = { r.source, "", "" };
236 bindings.emplace_back(move(b));
237 }
238 }
239 }
240 }
241
242 string networking_resolver::get_bond_master(const std::string& name) const {
243 static bool have_logged_about_bonding = false;
244 auto ip_command = lth_exe::which("ip");
245 if (ip_command.empty()) {
246 if (!have_logged_about_bonding) {
247 LOG_DEBUG("Could not find the 'ip' command. Physical macaddress for bonded interfaces will be incorrect.");
248 have_logged_about_bonding = true;
249 }
250 return {};
251 }
252
253 string bonding_master;
254
255 lth_exe::each_line(ip_command, {"link", "show", name}, [&bonding_master](string& line) {
256 if (line.find("SLAVE") != string::npos) {
257 vector<boost::iterator_range<string::iterator>> parts;
258 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
259
260 // We have to use find_if here since a boost::iterator_range doesn't compare properly to a string.
261 auto master = find_if(parts.begin(), parts.end(), [](boost::iterator_range<string::iterator>& part){
262 string p {part.begin(), part.end()};
263 return p == "master";
264 });
265
266 // the actual master interface is in the output as
267 // "master <iface>". Once we've found the master
268 // string above, we get the next token and return that
269 // as our interface device.
270 if (master != parts.end()) {
271 auto master_iface = master + 1;
272 if (master_iface != parts.end()) {
273 bonding_master.assign(master_iface->begin(), master_iface->end());
274 return false;
275 }
276 }
277 }
278 return true;
279 });
280 return bonding_master;
281 }
282 }}} // namespace facter::facts::linux
+0
-178
lib/src/facts/linux/operating_system_resolver.cc less more
0 #include <internal/facts/linux/operating_system_resolver.hpp>
1 #include <internal/facts/linux/release_file.hpp>
2 #include <internal/facts/linux/os_linux.hpp>
3 #include <internal/facts/linux/os_cisco.hpp>
4 #include <internal/facts/linux/os_osrelease.hpp>
5 #include <facter/facts/os.hpp>
6 #include <facter/facts/scalar_value.hpp>
7 #include <facter/facts/map_value.hpp>
8 #include <facter/facts/collection.hpp>
9 #include <leatherman/execution/execution.hpp>
10 #include <leatherman/file_util/file.hpp>
11 #include <leatherman/util/regex.hpp>
12 #include <boost/filesystem.hpp>
13 #include <boost/algorithm/string.hpp>
14 #include <map>
15 #include <vector>
16 #include <tuple>
17 #include <memory>
18
19 using namespace std;
20 using namespace leatherman::execution;
21 using namespace boost::filesystem;
22 using namespace leatherman::util;
23
24 namespace lth_file = leatherman::file_util;
25
26 namespace facter { namespace facts { namespace linux {
27
28 static unique_ptr<os_linux> get_os()
29 {
30 auto release_info = os_linux::key_value_file(release_file::os, {"ID", "CISCO_RELEASE_INFO"});
31 auto const& id = release_info["ID"];
32 if (id == "coreos" || id == "cumulus-linux" || id == "opensuse" ||
33 id == "opensuse-leap" || id== "sled" || id == "sles" || id == "ubuntu") {
34 return unique_ptr<os_linux>(new os_osrelease());
35 } else {
36 auto const& cisco = release_info["CISCO_RELEASE_INFO"];
37 boost::system::error_code ec;
38 if (!cisco.empty() && is_regular_file(cisco, ec)) {
39 return unique_ptr<os_linux>(new os_cisco(cisco));
40 }
41 }
42 return unique_ptr<os_linux>(new os_linux());
43 }
44
45 static string get_selinux_mountpoint()
46 {
47 static boost::regex regexp("\\S+ (\\S+) selinuxfs");
48 string mountpoint;
49 lth_file::each_line("/proc/self/mounts", [&](string& line) {
50 if (re_search(line, regexp, &mountpoint)) {
51 return false;
52 }
53 return true;
54 });
55 return mountpoint;
56 }
57
58 operating_system_resolver::selinux_data operating_system_resolver::collect_selinux_data()
59 {
60 static string SELINUX_CONFIG_FILE("/etc/selinux/config");
61
62 selinux_data result;
63 result.supported = true;
64
65 string mountpoint = get_selinux_mountpoint();
66 result.enabled = !mountpoint.empty() && exists(SELINUX_CONFIG_FILE);
67 if (!result.enabled) {
68 return result;
69 }
70
71 // Get the policy version
72 result.policy_version = lth_file::read(mountpoint + "/policyvers");
73
74 // Check for enforcement
75 string enforce = lth_file::read(mountpoint + "/enforce");
76 if (!enforce.empty()) {
77 if (enforce == "1") {
78 result.enforced = true;
79 result.current_mode = "enforcing";
80 } else {
81 result.current_mode = "permissive";
82 }
83 }
84
85 // Parse the SELinux config for mode and policy
86 static boost::regex mode_regex("(?m)^SELINUX=(\\w+)$");
87 static boost::regex policy_regex("(?m)^SELINUXTYPE=(\\w+)$");
88 lth_file::each_line(SELINUX_CONFIG_FILE, [&](string& line) {
89 if (re_search(line, mode_regex, &result.config_mode)) {
90 return true;
91 }
92 if (re_search(line, policy_regex, &result.config_policy)) {
93 return true;
94 }
95 return true;
96 });
97 return result;
98 }
99
100 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
101 {
102 // Default to the base implementation
103 data result = posix::operating_system_resolver::collect_data(facts);
104
105 // Populate distro info
106 each_line("lsb_release", {"-a"}, [&](string& line) {
107 string* variable = nullptr;
108 size_t offset = 0;
109 if (boost::starts_with(line, "LSB Version:")) {
110 variable = &result.specification_version;
111 offset = 12;
112 } else if (boost::starts_with(line, "Distributor ID:")) {
113 variable = &result.distro.id;
114 offset = 15;
115 } else if (boost::starts_with(line, "Description:")) {
116 variable = &result.distro.description;
117 offset = 12;
118 } else if (boost::starts_with(line, "Codename:")) {
119 variable = &result.distro.codename;
120 offset = 9;
121 } else if (boost::starts_with(line, "Release:")) {
122 variable = &result.distro.release;
123 offset = 8;
124 }
125 if (!variable) {
126 return true;
127 }
128 *variable = line.substr(offset);
129 boost::trim(*variable);
130 return true;
131 });
132
133 auto implementation = get_os();
134 auto name = implementation->get_name(result.distro.id);
135 if (!name.empty()) {
136 result.name = move(name);
137 }
138
139 auto family = implementation->get_family(result.name);
140 if (!family.empty()) {
141 result.family = move(family);
142 }
143
144 auto release = implementation->get_release(result.name, result.distro.release);
145 if (!release.empty()) {
146 if (result.name == os::debian) { // on debian 10 final iso, versions from lsb_release and /etc/debian_version are different
147 result.distro.release = release;
148 }
149 result.release = move(release);
150 tie(result.major, result.minor) = implementation->parse_release(result.name, result.release);
151 }
152
153 // Convert the architecture value depending on distro
154 // For certain distros, use "amd64" for "x86_64"
155 // For certain distros, use "x86" for "i386"
156 if (result.architecture == "x86_64" && (
157 result.name == os::debian ||
158 result.name == os::linux_mint ||
159 result.name == os::gentoo ||
160 result.name == os::kfreebsd ||
161 result.name == os::ubuntu)) {
162 result.architecture = "amd64";
163 } else if (re_search(result.architecture, boost::regex("i[3456]86|pentium"))) {
164 // For 32-bit, use "x86" for Gentoo and "i386" for everyone else
165 if (result.name == os::gentoo) {
166 result.architecture = "x86";
167 } else {
168 result.architecture = "i386";
169 }
170 }
171
172 result.selinux = collect_selinux_data();
173
174 return result;
175 }
176
177 }}} // namespace facter::facts::linux
+0
-402
lib/src/facts/linux/os_linux.cc less more
0 #include <internal/facts/linux/os_linux.hpp>
1 #include <internal/facts/resolvers/operating_system_resolver.hpp>
2 #include <facter/facts/os.hpp>
3 #include <facter/facts/os_family.hpp>
4 #include <leatherman/execution/execution.hpp>
5 #include <leatherman/file_util/file.hpp>
6 #include <leatherman/util/regex.hpp>
7 #include <boost/filesystem.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <vector>
10
11 using namespace std;
12 using namespace leatherman::execution;
13 using namespace boost::filesystem;
14 using namespace leatherman::util;
15
16 namespace bs = boost::system;
17 namespace lth_file = leatherman::file_util;
18
19 namespace facter { namespace facts { namespace linux {
20
21 // Return contents of the os-release file
22 // http://www.freedesktop.org/software/systemd/man/os-release.html
23 map<string, string> os_linux::key_value_file(string file, set<string> const& items)
24 {
25 map<string, string> values;
26 bs::error_code ec;
27 if (!items.empty() && is_regular_file(file, ec)) {
28 string key, value;
29 lth_file::each_line(file, [&](string& line) {
30 if (re_search(line, boost::regex("(?m)^(\\w+)=[\"']?(.+?)[\"']?$"), &key, &value)) {
31 if (items.count(key)) {
32 values.insert(make_pair(key, value));
33 }
34 }
35 return items.size() != values.size();
36 });
37 }
38 return values;
39 }
40
41 os_linux::os_linux(std::set<std::string> items, std::string file) :
42 _release_info(key_value_file(file, items)) {}
43
44 static string check_debian_linux(string const& distro_id)
45 {
46 // Check for Debian variants
47 bs::error_code ec;
48 if (is_regular_file(release_file::huawei, ec)) {
49 return os::huawei;
50 }
51
52 if (is_regular_file(release_file::devuan, ec)) {
53 return os::devuan;
54 }
55
56 if (is_regular_file(release_file::debian, ec)) {
57 if (distro_id == os::ubuntu || distro_id == os::linux_mint) {
58 return distro_id;
59 }
60 return os::debian;
61 }
62 return {};
63 }
64
65 static string check_oracle_linux()
66 {
67 bs::error_code ec;
68 if (is_regular_file(release_file::oracle_enterprise_linux, ec)) {
69 if (is_regular_file(release_file::oracle_vm_linux, ec)) {
70 return os::oracle_vm_linux;
71 }
72 return os::oracle_enterprise_linux;
73 }
74 return {};
75 }
76
77 static string check_redhat_linux()
78 {
79 bs::error_code ec;
80 if (is_regular_file(release_file::redhat, ec)) {
81 static vector<tuple<boost::regex, string>> const regexs {
82 make_tuple(boost::regex("(?i)centos"), string(os::centos)),
83 make_tuple(boost::regex("(?i)scientific linux CERN"), string(os::scientific_cern)),
84 make_tuple(boost::regex("(?i)scientific linux release"), string(os::scientific)),
85 make_tuple(boost::regex("(?im)^cloudlinux"), string(os::cloud_linux)),
86 make_tuple(boost::regex("(?im)^virtuozzo linux"), string(os::virtuozzo_linux)),
87 make_tuple(boost::regex("(?im)^virtuozzolinux"), string(os::virtuozzo_linux)),
88 make_tuple(boost::regex("(?i)Ascendos"), string(os::ascendos)),
89 make_tuple(boost::regex("(?im)^XenServer"), string(os::xen_server)),
90 make_tuple(boost::regex("(?im)^XCP-ng"), string(os::xcp_ng)),
91 make_tuple(boost::regex("XCP"), string(os::zen_cloud_platform)),
92 make_tuple(boost::regex("(?im)^Parallels Server Bare Metal"), string(os::psbm)),
93 make_tuple(boost::regex("(?m)^Fedora release"), string(os::fedora)),
94 };
95
96 string contents = lth_file::read(release_file::redhat);
97 boost::trim(contents);
98 for (auto const& regex : regexs) {
99 if (re_search(contents, get<0>(regex))) {
100 return get<1>(regex);
101 }
102 }
103 return os::redhat;
104 }
105 return {};
106 }
107
108 static string check_photon_linux()
109 {
110 string contents = lth_file::read(release_file::lsb);
111 boost::trim(contents);
112 if (re_search(contents, boost::regex("VMware Photon"))) {
113 return string(os::photon_os);
114 }
115 return {};
116 }
117
118 static string check_suse_linux()
119 {
120 bs::error_code ec;
121 if (is_regular_file(release_file::suse, ec)) {
122 static vector<tuple<boost::regex, string>> const regexs {
123 make_tuple(boost::regex("(?im)^SUSE LINUX Enterprise Server"), string(os::suse_enterprise_server)),
124 make_tuple(boost::regex("(?im)^SUSE LINUX Enterprise Desktop"), string(os::suse_enterprise_desktop)),
125 make_tuple(boost::regex("(?im)^openSUSE"), string(os::open_suse)),
126 };
127
128 string contents = lth_file::read(release_file::suse);
129 boost::trim(contents);
130 for (auto const& regex : regexs) {
131 if (re_search(contents, get<0>(regex))) {
132 return get<1>(regex);
133 }
134 }
135 return os::suse;
136 }
137 return {};
138 }
139
140 static string check_other_linux()
141 {
142 static vector<tuple<string, string>> const files {
143 make_tuple(string(release_file::arista_eos), string(os::arista_eos)),
144 make_tuple(string(release_file::gentoo), string(os::gentoo)),
145 make_tuple(string(release_file::mageia), string(os::mageia)),
146 make_tuple(string(release_file::mandriva), string(os::mandriva)),
147 make_tuple(string(release_file::mandrake), string(os::mandrake)),
148 make_tuple(string(release_file::meego), string(os::meego)),
149 make_tuple(string(release_file::archlinux), string(os::archlinux)),
150 make_tuple(string(release_file::manjarolinux), string(os::manjarolinux)),
151 make_tuple(string(release_file::oracle_linux), string(os::oracle_linux)),
152 make_tuple(string(release_file::openwrt), string(os::openwrt)),
153 make_tuple(string(release_file::alpine), string(os::alpine)),
154 make_tuple(string(release_file::vmware_esx), string(os::vmware_esx)),
155 make_tuple(string(release_file::slackware), string(os::slackware)),
156 };
157
158 for (auto const& file : files) {
159 bs::error_code ec;
160 if (is_regular_file(get<0>(file), ec)) {
161 return get<1>(file);
162 }
163 }
164 return {};
165 }
166
167 static string check_amazon()
168 {
169 bs::error_code ec;
170 if (is_regular_file(release_file::amazon, ec)) {
171 return os::amazon;
172 }
173 return {};
174 }
175
176 string os_linux::get_name(string const& distro_id) const
177 {
178 // Check for Debian first; this happens after AristaEOS in Facter 2.x
179 // but that platform is not a Debian so shouldn't matter.
180 auto value = check_debian_linux(distro_id);
181 if (!value.empty()) {
182 return value;
183 }
184
185 // Check for specialized distributions next
186 value = check_other_linux();
187 if (!value.empty()) {
188 return value;
189 }
190
191 // Check for Oracle Enterprise Linux next
192 value = check_oracle_linux();
193 if (!value.empty()) {
194 return value;
195 }
196
197 // Check for RedHat next
198 value = check_redhat_linux();
199 if (!value.empty()) {
200 return value;
201 }
202
203 // Check for SuSE next
204 value = check_suse_linux();
205 if (!value.empty()) {
206 return value;
207 }
208
209 value = check_photon_linux();
210 if (!value.empty()) {
211 return value;
212 }
213 // This should happen after everything else because it's a relatively broad match
214 return check_amazon();
215 }
216
217 string os_linux::get_family(string const& name) const
218 {
219 static map<string, string> const systems = {
220 { string(os::redhat), string(os_family::redhat) },
221 { string(os::fedora), string(os_family::redhat) },
222 { string(os::centos), string(os_family::redhat) },
223 { string(os::scientific), string(os_family::redhat) },
224 { string(os::scientific_cern), string(os_family::redhat) },
225 { string(os::ascendos), string(os_family::redhat) },
226 { string(os::cloud_linux), string(os_family::redhat) },
227 { string(os::psbm), string(os_family::redhat) },
228 { string(os::oracle_linux), string(os_family::redhat) },
229 { string(os::oracle_vm_linux), string(os_family::redhat) },
230 { string(os::oracle_enterprise_linux), string(os_family::redhat) },
231 { string(os::amazon), string(os_family::redhat) },
232 { string(os::xen_server), string(os_family::redhat) },
233 { string(os::xcp_ng), string(os_family::redhat) },
234 { string(os::virtuozzo_linux), string(os_family::redhat) },
235 { string(os::photon_os), string(os_family::redhat) },
236 { string(os::huawei), string(os_family::debian) },
237 { string(os::linux_mint), string(os_family::debian) },
238 { string(os::ubuntu), string(os_family::debian) },
239 { string(os::debian), string(os_family::debian) },
240 { string(os::devuan), string(os_family::debian) },
241 { string(os::suse_enterprise_server), string(os_family::suse) },
242 { string(os::suse_enterprise_desktop), string(os_family::suse) },
243 { string(os::open_suse), string(os_family::suse) },
244 { string(os::suse), string(os_family::suse) },
245 { string(os::gentoo), string(os_family::gentoo) },
246 { string(os::archlinux), string(os_family::archlinux) },
247 { string(os::manjarolinux), string(os_family::archlinux) },
248 { string(os::mandrake), string(os_family::mandrake) },
249 { string(os::mandriva), string(os_family::mandrake) },
250 { string(os::mageia), string(os_family::mandrake) },
251 };
252 auto const& it = systems.find(name);
253 if (it != systems.end()) {
254 return it->second;
255 }
256 return {};
257 }
258
259 string os_linux::get_release(string const& name, string const& distro_release) const
260 {
261 // Map of release files that contain a "release X.X.X" on the first line
262 static map<string, string> const release_files = {
263 { string(os::amazon), string(release_file::amazon) },
264 { string(os::centos), string(release_file::redhat) },
265 { string(os::redhat), string(release_file::redhat) },
266 { string(os::scientific), string(release_file::redhat) },
267 { string(os::scientific_cern), string(release_file::redhat) },
268 { string(os::ascendos), string(release_file::redhat) },
269 { string(os::cloud_linux), string(release_file::redhat) },
270 { string(os::psbm), string(release_file::redhat) },
271 { string(os::xen_server), string(release_file::redhat) },
272 { string(os::xcp_ng), string(release_file::redhat) },
273 { string(os::virtuozzo_linux), string(release_file::redhat) },
274 { string(os::fedora), string(release_file::fedora) },
275 { string(os::meego), string(release_file::meego) },
276 { string(os::oracle_linux), string(release_file::oracle_linux) },
277 { string(os::oracle_enterprise_linux), string(release_file::oracle_enterprise_linux) },
278 { string(os::oracle_vm_linux), string(release_file::oracle_vm_linux) },
279 { string(os::arista_eos), string(release_file::arista_eos) },
280 { string(os::gentoo), string(release_file::gentoo) },
281 };
282
283 string value;
284 auto it = release_files.find(name);
285 if (it != release_files.end()) {
286 string contents;
287 if (lth_file::each_line(it->second, [&](string& line) {
288 // We only need the first line
289 contents = move(line);
290 return false;
291 })) {
292 if (boost::ends_with(contents, "(Rawhide)")) {
293 value = "Rawhide";
294 } else if (contents.find("release") != string::npos) {
295 re_search(contents, boost::regex("release (\\d[\\d.]*)"), &value);
296 } else {
297 re_search(contents, boost::regex("Amazon Linux (\\d+)"), &value);
298 }
299 }
300 }
301
302 // Debian uses the entire contents of the release file as the version
303 if (value.empty() && name == os::debian) {
304 value = lth_file::read(release_file::debian);
305 boost::trim_right(value);
306 }
307
308 // Devuan uses the entire contents of the release file as the version
309 if (value.empty() && name == os::devuan) {
310 value = lth_file::read(release_file::devuan);
311 boost::trim_right(value);
312 }
313
314 // Alpine uses the entire contents of the release file as the version
315 if (value.empty() && name == os::alpine) {
316 value = lth_file::read(release_file::alpine);
317 boost::trim_right(value);
318 }
319
320 // HuaweiOS uses the entire contents of the release file as the version
321 if (value.empty() && name == os::huawei) {
322 value = lth_file::read(release_file::huawei);
323 boost::trim_right(value);
324 }
325
326 // Check for SuSE related distros, read the release file
327 if (value.empty() && (
328 name == os::suse ||
329 name == os::suse_enterprise_server ||
330 name == os::suse_enterprise_desktop ||
331 name == os::open_suse)) {
332 string contents = lth_file::read(release_file::suse);
333 string major;
334 string minor;
335 if (re_search(contents, boost::regex("(?m)^VERSION\\s*=\\s*(\\d+)\\.?(\\d+)?"), &major, &minor)) {
336 // Check that we have a minor version; if not, use the patch level
337 if (minor.empty()) {
338 if (!re_search(contents, boost::regex("(?m)^PATCHLEVEL\\s*=\\s*(\\d+)"), &minor)) {
339 minor = "0";
340 }
341 }
342 value = major + "." + minor;
343 } else {
344 value = "unknown";
345 }
346 }
347 if (value.empty() && name == os::photon_os) {
348 string major, minor;
349 string contents = lth_file::read(release_file::lsb);
350 string pattern = "DISTRIB_RELEASE=\"(\\d+)\\.(\\d+)( ([a-zA-Z]+\\d+))?\"";
351 if (re_search(contents, boost::regex(pattern), &major, &minor)) {
352 value = major + "." + minor;
353 }
354 }
355
356 // Read version files of particular operating systems
357 if (value.empty()) {
358 const char* file = nullptr;
359 boost::regex pattern;
360 if (name == os::ubuntu) {
361 file = release_file::lsb;
362 pattern = "(?m)^DISTRIB_RELEASE=(\\d+\\.\\d+)(?:\\.\\d+)*";
363 } else if (name == os::slackware) {
364 file = release_file::slackware;
365 pattern = "Slackware ([0-9.]+)";
366 } else if (name == os::mageia) {
367 file = release_file::mageia;
368 pattern = "Mageia release ([0-9.]+)";
369 } else if (name == os::linux_mint) {
370 file = release_file::linux_mint_info;
371 pattern = "(?m)^RELEASE=(\\d+)";
372 } else if (name == os::openwrt) {
373 file = release_file::openwrt_version;
374 pattern = "(?m)^(\\d+\\.\\d+.*)";
375 } else if (name == os::arista_eos) {
376 file = release_file::arista_eos;
377 pattern = "Arista Networks EOS (\\d+\\.\\d+\\.\\d+[A-M]?)";
378 }
379 if (file) {
380 string contents = lth_file::read(file);
381 re_search(contents, pattern, &value);
382 }
383 }
384
385 // For VMware ESX, execute the vmware tool
386 if (value.empty() && name == os::vmware_esx) {
387 auto exec = execute("vmware", { "-v" });
388 if (exec.success) {
389 re_search(exec.output, boost::regex("VMware ESX .*?(\\d.*)"), &value);
390 }
391 }
392
393 return value;
394 }
395
396 tuple<string, string> os_linux::parse_release(string const& name, string const& release) const
397 {
398 return facter::facts::resolvers::operating_system_resolver::parse_distro(name, release);
399 }
400
401 }}} // namespace facter::facts::linux
+0
-203
lib/src/facts/linux/processor_resolver.cc less more
0 #include <internal/facts/linux/processor_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/os.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/util/string.hpp>
6 #include <leatherman/file_util/file.hpp>
7 #include <leatherman/file_util/directory.hpp>
8 #include <leatherman/util/regex.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <boost/filesystem.hpp>
11 #include <unordered_set>
12
13 using namespace std;
14 using namespace boost::filesystem;
15 using facter::util::maybe_stoi;
16
17 namespace lth_file = leatherman::file_util;
18 namespace lth_util = leatherman::util;
19
20 namespace facter { namespace facts { namespace linux {
21 static bool split_line(string const& line, string& key, string& value)
22 {
23 // Split the line on colon
24 auto pos = line.find(":");
25 if (pos == string::npos) {
26 return false;
27 }
28 key = line.substr(0, pos);
29 boost::trim(key);
30 value = line.substr(pos + 1);
31 boost::trim(value);
32
33 return true;
34 }
35
36 processor_resolver::ArchitectureType processor_resolver::architecture_type(data const& data, std::string const& root)
37 {
38 if (!data.isa.empty()) {
39 return (boost::starts_with(data.isa, "ppc64")) ? ArchitectureType::POWER : ArchitectureType::X86;
40 }
41
42 // use /proc/cpuinfo
43 unordered_set<string> to_be_seen;
44 bool seen_all = false;
45 lth_file::each_line(root + "/proc/cpuinfo", [&](string& line) {
46 // if we already know that we're on a Power machine, we can just skip
47 // the remaining lines
48 if (seen_all) {
49 return false;
50 }
51
52 string key, value;
53 if (!split_line(line, key, value)) {
54 return true;
55 }
56
57 if (key == "processor") {
58 to_be_seen = unordered_set<string>{{"cpu", "clock", "revision"}};
59 } else if (find(to_be_seen.begin(), to_be_seen.end(), key) != to_be_seen.end()) {
60 to_be_seen.erase(key);
61 seen_all = to_be_seen.empty();
62 }
63 return true;
64 });
65
66 return seen_all ? ArchitectureType::POWER : ArchitectureType::X86;
67 }
68
69 // returns true if the first cpu is a valid, physical cpu to use when computing the processor
70 // speed
71 bool processor_resolver::compute_cpu_counts(data& data, std::string const& root, std::function<bool(std::string const&)> is_valid_id) {
72 unordered_set<string> cpus;
73 bool cpu0_valid = false;
74
75 lth_file::each_subdirectory(root + "/sys/devices/system/cpu", [&](string const& cpu_directory) {
76 string physical_id_path = (path(cpu_directory) / "/topology/physical_package_id").string();
77 if (lth_file::file_readable(physical_id_path)) {
78 bool at_cpu0 = data.logical_count == 0;
79 data.logical_count++;
80 string id = lth_file::read(physical_id_path);
81 boost::trim(id);
82 if ((is_valid_id(id) && cpus.emplace(move(id)).second)) {
83 // Haven't seen this processor before
84 ++data.physical_count;
85 if (at_cpu0) {
86 cpu0_valid = true;
87 }
88 }
89 }
90
91 return true;
92 }, "^cpu\\d+$");
93
94 return cpu0_valid;
95 }
96
97 void processor_resolver::maybe_add_speed(data& data, std::string const& speed)
98 {
99 auto maybe_speed = maybe_stoi(speed);
100 if (maybe_speed && maybe_speed.get() > 0) {
101 data.speed = maybe_speed.get() * static_cast<int64_t>(1000);
102 }
103 }
104
105 bool processor_resolver::add_x86_cpu_data(data& data, std::string const& root)
106 {
107 bool cpu0_valid = compute_cpu_counts(data, root, [](string const& id) {
108 // any id is fine for x86 cpus
109 return true;
110 });
111 bool have_counts = data.logical_count > 0;
112
113 unordered_set<string> cpus;
114 string id;
115 lth_file::each_line(root + "/proc/cpuinfo", [&](string& line) {
116 string key, value;
117 if (!split_line(line, key, value)) {
118 return true;
119 }
120
121 if (key == "processor") {
122 // Start of a logical processor
123 id = move(value);
124 if (!have_counts) {
125 ++data.logical_count;
126 }
127 } else if (!id.empty() && key == "model name") {
128 // Add the model for this logical processor
129 data.models.emplace_back(move(value));
130 } else if (!have_counts && key == "physical id" && cpus.emplace(move(value)).second) {
131 // Couldn't determine physical count from sysfs, but CPU topology is present, so use it
132 ++data.physical_count;
133 }
134 return true;
135 });
136
137 return cpu0_valid;
138 }
139
140 bool processor_resolver::add_power_cpu_data(data& data, std::string const& root)
141 {
142 bool cpu0_valid = compute_cpu_counts(data, root, [](string const& id) {
143 // only non-negative ids are allowed for power cpus
144 auto maybe_id = maybe_stoi(id);
145 return !maybe_id || maybe_id.get() >= 0;
146 });
147
148 // per ticket specs, logical count is computed below
149 data.logical_count = 0;
150
151 unordered_set<string> cpus;
152 string id;
153 lth_file::each_line(root + "/proc/cpuinfo", [&](string& line) {
154 string key, value;
155 if (!split_line(line, key, value)) {
156 return true;
157 }
158
159 if (key == "processor") {
160 // Start of a logical processor
161 id = move(value);
162 ++data.logical_count;
163 } else if (!id.empty() && key == "cpu") {
164 // Add the model for this logical processor
165 data.models.emplace_back(move(value));
166 } else if (key == "clock" && data.speed == 0) {
167 // Parse out the processor speed (in MHz)
168 string speed;
169 if (lth_util::re_search(value, boost::regex("^(\\d+).*MHz"), &speed)) {
170 maybe_add_speed(data, speed);
171 }
172 }
173 return true;
174 });
175
176 return cpu0_valid;
177 }
178
179 void processor_resolver::add_cpu_data(data& data, std::string const& root)
180 {
181 bool cpu0_valid = (architecture_type(data, root) == ArchitectureType::X86) ?
182 add_x86_cpu_data(data, root)
183 : add_power_cpu_data(data, root);
184
185 if (data.speed != 0 || !cpu0_valid) {
186 return;
187 }
188
189 // Read in the max speed from the first cpu
190 // The speed is in kHz
191 string speed = lth_file::read(root + "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
192 maybe_add_speed(data, speed);
193 }
194
195 processor_resolver::data processor_resolver::collect_data(collection& facts)
196 {
197 auto result = posix::processor_resolver::collect_data(facts);
198 add_cpu_data(result);
199 return result;
200 }
201
202 }}} // namespace facter::facts::linux
+0
-15
lib/src/facts/linux/uptime_resolver.cc less more
0 #include <internal/facts/linux/uptime_resolver.hpp>
1 #include <sys/sysinfo.h>
2
3 namespace facter { namespace facts { namespace linux {
4
5 int64_t uptime_resolver::get_uptime()
6 {
7 struct sysinfo info;
8 if (sysinfo(&info) == 0) {
9 return info.uptime;
10 }
11 return posix::uptime_resolver::get_uptime();
12 }
13
14 }}} // namespace facter::facts::linux
+0
-300
lib/src/facts/linux/virtualization_resolver.cc less more
0 #include <internal/facts/linux/virtualization_resolver.hpp>
1 #include <internal/util/agent.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/collection.hpp>
4 #include <facter/facts/fact.hpp>
5 #include <facter/facts/vm.hpp>
6 #include <leatherman/file_util/file.hpp>
7 #include <leatherman/file_util/directory.hpp>
8 #include <leatherman/util/regex.hpp>
9 #include <leatherman/logging/logging.hpp>
10 #include <boost/filesystem.hpp>
11 #include <boost/algorithm/string.hpp>
12
13 using namespace std;
14 using namespace facter::facts;
15 using namespace facter::util;
16 using namespace leatherman::util;
17 using namespace leatherman::execution;
18 using namespace boost::filesystem;
19
20 namespace bs = boost::system;
21 namespace lth_file = leatherman::file_util;
22
23 namespace facter { namespace facts { namespace linux {
24
25 string virtualization_resolver::get_cloud_provider(collection& facts)
26 {
27 std::string provider = get_azure();
28 return provider;
29 }
30
31 string virtualization_resolver::get_azure()
32 {
33 std::string provider;
34 static vector<string> const dhclient_search_directories = {
35 "/var/lib/dhcp",
36 "/var/lib/NetworkManager"
37 };
38
39 for (auto const& dir : dhclient_search_directories) {
40 LOG_DEBUG("searching \"{1}\" for dhclient lease files.", dir);
41 lth_file::each_file(dir, [&](string const& leases_file) {
42 LOG_DEBUG("reading \"{1}\" for dhclient lease azure information.", leases_file);
43 provider = get_azure_from_leases_file(leases_file);
44 return provider.empty();
45 }, "^dhclient.*lease.*$");
46 if (!provider.empty())
47 break;
48 }
49 return provider;
50 }
51
52 // Search for DHCP option 245. This is an accepted method of determining
53 // whether a machine is running inside Azure. Source:
54 // https://social.msdn.microsoft.com/Forums/azure/en-US/f7fbbee6-370a-41c2-a384-d14ab2a0ac12/what-is-the-correct-method-in-linux-on-azure-to-test-if-you-are-an-azure-vm-?forum=WAVirtualMachinesforWindows
55 string virtualization_resolver::get_azure_from_leases_file(string leases_file)
56 {
57 string provider;
58 lth_file::each_line(leases_file, [&](string& line) {
59 if (line.find("option 245") != std::string::npos || line.find("option unknown-245") != std::string::npos) {
60 LOG_DEBUG("found azure option in \"{1}\" lease file.", leases_file);
61 provider = "azure";
62 return false;
63 }
64 return true;
65 });
66 return provider;
67 }
68
69 string virtualization_resolver::get_hypervisor(collection& facts)
70 {
71 // First check for Docker/LXC
72 string value = get_cgroup_vm();
73
74 // Next check for Google Compute Engine
75 if (value.empty()) {
76 value = get_gce_vm(facts);
77 }
78
79 // Next check based on the virt-what command
80 if (value.empty()) {
81 value = get_what_vm();
82 }
83
84 // Next check the vmware tool output
85 if (value.empty()) {
86 value = get_vmware_vm();
87 }
88
89 // Next check for OpenVZ
90 if (value.empty()) {
91 value = get_openvz_vm();
92 }
93
94 // Next check for VServer
95 if (value.empty()) {
96 value = get_vserver_vm();
97 }
98
99 // Next check for Xen
100 if (value.empty()) {
101 value = get_xen_vm();
102 }
103
104 // Next check other facts for the VM
105 if (value.empty()) {
106 value = get_fact_vm(facts);
107 }
108
109 // Lastly, resort to lspci to look for hardware related to certain VMs
110 if (value.empty()) {
111 value = get_lspci_vm();
112 }
113
114 return value;
115 }
116
117 string virtualization_resolver::get_cgroup_vm()
118 {
119 string value;
120 lth_file::each_line("/proc/1/cgroup", [&](string& line) {
121 vector<boost::iterator_range<string::iterator>> parts;
122 boost::split(parts, line, boost::is_any_of(":"), boost::token_compress_on);
123 if (parts.size() < 3) {
124 return true;
125 }
126 if (boost::contains(parts[2], boost::as_literal("/docker"))) {
127 value = vm::docker;
128 return false;
129 }
130 if (boost::contains(parts[2], boost::as_literal("/lxc"))) {
131 value = vm::lxc;
132 return false;
133 }
134 return true;
135 });
136 return value;
137 }
138
139 string virtualization_resolver::get_gce_vm(collection& facts)
140 {
141 auto vendor = facts.get<string_value>(fact::bios_vendor);
142 if (vendor && vendor->value().find("Google") != string::npos) {
143 return vm::gce;
144 }
145 return {};
146 }
147
148 string virtualization_resolver::get_what_vm()
149 {
150 string virt_what = agent::which("virt-what");
151 string value;
152 each_line(virt_what, [&](string& line) {
153 // Some versions of virt-what dump error/warning messages to stdout
154 if (boost::starts_with(line, "virt-what:")) {
155 return true;
156 }
157 // Take the first line that isn't an error/warning
158 // unless it's "xen", in which case we expect a second
159 // line with more useful information
160 if (line == "xen") {
161 return true;
162 }
163 value = move(line);
164 return false;
165 });
166
167 // Do some normalization of virt-what's output
168 if (!value.empty()) {
169 boost::to_lower(value);
170 if (value == "linux_vserver") {
171 return get_vserver_vm();
172 }
173 if (value == "xen-hvm") {
174 return vm::xen_hardware;
175 }
176 if (value == "xen-dom0") {
177 return vm::xen_privileged;
178 }
179 if (value == "xen-domu") {
180 return vm::xen_unprivileged;
181 }
182 if (value == "ibm_systemz") {
183 return vm::zlinux;
184 }
185 }
186 return value;
187 }
188
189 string virtualization_resolver::get_vserver_vm()
190 {
191 string value;
192 lth_file::each_line("/proc/self/status", [&](string& line) {
193 vector<boost::iterator_range<string::iterator>> parts;
194 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
195 if (parts.size() != 2) {
196 return true;
197 }
198 if (parts[0] == boost::as_literal("s_context:") || parts[0] == boost::as_literal("VxID:")) {
199 if (parts[1] == boost::as_literal("0")) {
200 value = vm::vserver_host;
201 } else {
202 value = vm::vserver;
203 }
204 return false;
205 }
206 return true;
207 });
208 return value;
209 }
210
211 string virtualization_resolver::get_vmware_vm()
212 {
213 auto exec = execute("vmware", { "-v" });
214 if (!exec.success) {
215 return {};
216 }
217 vector<string> parts;
218 boost::split(parts, exec.output, boost::is_space(), boost::token_compress_on);
219 if (parts.size() < 2) {
220 return {};
221 }
222 boost::to_lower(parts[0]);
223 boost::to_lower(parts[1]);
224 return parts[0] + '_' + parts[1];
225 }
226
227 string virtualization_resolver::get_openvz_vm()
228 {
229 // Detect if it's a OpenVZ without being CloudLinux
230 bs::error_code ec;
231 if (!is_directory("/proc/vz", ec) ||
232 is_regular_file("/proc/lve/list", ec) ||
233 boost::filesystem::is_empty("/proc/vz", ec)) {
234 return {};
235 }
236 string value;
237 lth_file::each_line("/proc/self/status", [&](string& line) {
238 vector<boost::iterator_range<string::iterator>> parts;
239 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
240 if (parts.size() != 2) {
241 return true;
242 }
243 if (parts[0] == boost::as_literal("envID:")) {
244 if (parts[1] == boost::as_literal("0")) {
245 value = vm::openvz_hn;
246 } else {
247 value = vm::openvz_ve;
248 }
249 return false;
250 }
251 return true;
252 });
253 return value;
254 }
255
256 string virtualization_resolver::get_xen_vm()
257 {
258 // Check for a required Xen file
259 bs::error_code ec;
260 if (exists("/dev/xen/evtchn", ec) && !ec) {
261 return vm::xen_privileged;
262 }
263 ec.clear();
264 if (exists("/proc/xen", ec) && !ec) {
265 return vm::xen_unprivileged;
266 }
267 ec.clear();
268 if (exists("/dev/xvda1", ec) && !ec) {
269 return vm::xen_unprivileged;
270 }
271 return {};
272 }
273
274 string virtualization_resolver::get_lspci_vm()
275 {
276 static vector<tuple<boost::regex, string>> vms = {
277 make_tuple(boost::regex("VM[wW]are"), string(vm::vmware)),
278 make_tuple(boost::regex("VirtualBox"), string(vm::virtualbox)),
279 make_tuple(boost::regex("1ab8:|[Pp]arallels"), string(vm::parallels)),
280 make_tuple(boost::regex("XenSource"), string(vm::xen_hardware)),
281 make_tuple(boost::regex("Microsoft Corporation Hyper-V"), string(vm::hyperv)),
282 make_tuple(boost::regex("Class 8007: Google, Inc"), string(vm::gce)),
283 make_tuple(boost::regex("virtio", boost::regex::icase), string(vm::kvm)),
284 };
285
286 string value;
287 each_line("lspci", [&](string& line) {
288 for (auto const& vm : vms) {
289 if (re_search(line, get<0>(vm))) {
290 value = get<1>(vm);
291 return false;
292 }
293 }
294 return true;
295 });
296 return value;
297 }
298
299 }}} // namespace facter::facts::linux
+0
-119
lib/src/facts/map_value.cc less more
0 #include <facter/facts/map_value.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <facter/util/string.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <rapidjson/document.h>
5 #include <yaml-cpp/yaml.h>
6
7 using namespace std;
8 using namespace facter::util;
9 using namespace rapidjson;
10 using namespace YAML;
11
12 namespace facter { namespace facts {
13
14 map_value::map_value(map_value&& other)
15 {
16 *this = std::move(other);
17 }
18
19 map_value& map_value::operator=(map_value&& other)
20 {
21 value::operator=(static_cast<value&&>(other));
22 if (this != &other) {
23 _elements = std::move(other._elements);
24 }
25 return *this;
26 }
27
28 void map_value::add(string name, unique_ptr<value> value)
29 {
30 if (!value) {
31 LOG_DEBUG("null value cannot be added to map.");
32 return;
33 }
34
35 _elements.emplace(move(name), move(value));
36 }
37
38 bool map_value::empty() const
39 {
40 return _elements.empty();
41 }
42
43 size_t map_value::size() const
44 {
45 return _elements.size();
46 }
47
48 void map_value::each(function<bool(string const&, value const*)> func) const
49 {
50 for (auto const& kvp : _elements) {
51 if (!func(kvp.first, kvp.second.get())) {
52 break;
53 }
54 }
55 }
56
57 value const* map_value::operator[](string const& name) const
58 {
59 auto it = _elements.find(name);
60 if (it == _elements.end()) {
61 return nullptr;
62 }
63 return it->second.get();
64 }
65
66 void map_value::to_json(json_allocator& allocator, json_value& value) const
67 {
68 value.SetObject();
69
70 for (auto const& kvp : _elements) {
71 json_value child;
72 kvp.second->to_json(allocator, child);
73 value.AddMember(rapidjson::StringRef(kvp.first.c_str(), kvp.first.size()), std::move(child), allocator);
74 }
75 }
76
77 ostream& map_value::write(ostream& os, bool quoted, unsigned int level) const
78 {
79 if (_elements.empty()) {
80 os << "{}";
81 return os;
82 }
83
84 // Write out the elements in the map
85 os << "{\n";
86 bool first = true;
87 for (auto const& kvp : _elements) {
88 if (first) {
89 first = false;
90 } else {
91 os << ",\n";
92 }
93 fill_n(ostream_iterator<char>(os), level * 2, ' ');
94 os << kvp.first << " => ";
95 kvp.second->write(os, true /* always quote strings in a map */, level + 1);
96 }
97 os << "\n";
98 fill_n(ostream_iterator<char>(os), (level > 0 ? (level - 1) : 0) * 2, ' ');
99 os << "}";
100 return os;
101 }
102
103 Emitter& map_value::write(Emitter& emitter) const
104 {
105 emitter << BeginMap;
106 for (auto const& kvp : _elements) {
107 emitter << Key;
108 if (needs_quotation(kvp.first)) {
109 emitter << DoubleQuoted;
110 }
111 emitter << kvp.first << YAML::Value;
112 kvp.second->write(emitter);
113 }
114 emitter << EndMap;
115 return emitter;
116 }
117
118 }} // namespace facter::facts
+0
-37
lib/src/facts/openbsd/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/bsd/filesystem_resolver.hpp>
2 #include <internal/facts/bsd/uptime_resolver.hpp>
3 #include <internal/facts/glib/load_average_resolver.hpp>
4 #include <internal/facts/openbsd/dmi_resolver.hpp>
5 #include <internal/facts/openbsd/memory_resolver.hpp>
6 #include <internal/facts/openbsd/networking_resolver.hpp>
7 #include <internal/facts/openbsd/virtualization_resolver.hpp>
8 #include <internal/facts/openbsd/processor_resolver.hpp>
9 #include <internal/facts/posix/identity_resolver.hpp>
10 #include <internal/facts/posix/kernel_resolver.hpp>
11 #include <internal/facts/ssh_resolver.hpp>
12 #include <internal/facts/posix/timezone_resolver.hpp>
13 #include <internal/facts/posix/operating_system_resolver.hpp>
14
15 using namespace std;
16
17 namespace facter { namespace facts {
18
19 void collection::add_platform_facts()
20 {
21 add(make_shared<posix::kernel_resolver>());
22 add(make_shared<posix::operating_system_resolver>());
23 add(make_shared<bsd::uptime_resolver>());
24 add(make_shared<bsd::filesystem_resolver>());
25 add(make_shared<ssh_resolver>());
26 add(make_shared<posix::identity_resolver>());
27 add(make_shared<posix::timezone_resolver>());
28 add(make_shared<glib::load_average_resolver>());
29 add(make_shared<openbsd::networking_resolver>());
30 add(make_shared<openbsd::dmi_resolver>());
31 add(make_shared<openbsd::memory_resolver>());
32 add(make_shared<openbsd::virtualization_resolver>());
33 add(make_shared<openbsd::processor_resolver>());
34 }
35
36 }} // namespace facter::facts
+0
-47
lib/src/facts/openbsd/dmi_resolver.cc less more
0 #include <internal/facts/openbsd/dmi_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2
3 #include <sys/sysctl.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace openbsd {
8
9 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
10 {
11 data result;
12 result.bios_vendor = sysctl_lookup(HW_VENDOR);
13 result.uuid = sysctl_lookup(HW_UUID);
14 result.serial_number = sysctl_lookup(HW_SERIALNO);
15 // OpenBSD running as virtual machine within
16 // OpenBSD vmm don't return HW_PRODUCT. For that
17 // case use the HW_VENDOR, to please the
18 // virtualization_resolver
19 result.product_name = sysctl_lookup(HW_PRODUCT);
20 if (result.product_name.length() == 0) {
21 result.product_name = result.bios_vendor;
22 }
23 result.bios_version = sysctl_lookup(HW_VERSION);
24
25 return result;
26 }
27
28 string dmi_resolver::sysctl_lookup(int mib_2)
29 {
30 int mib[2];
31 size_t len;
32 char value[BUFSIZ];
33
34 mib[0] = CTL_HW;
35 mib[1] = mib_2;
36 len = sizeof(value) - 1;
37
38 if (sysctl(mib, 2, &value, &len, nullptr, 0) == -1) {
39 LOG_DEBUG("sysctl_lookup failed: {1} ({2}).", strerror(errno), errno);
40 return "";
41 }
42
43 return value;
44 }
45
46 } } } // namespace facter::facts::openbsd
+0
-76
lib/src/facts/openbsd/memory_resolver.cc less more
0 #include <internal/facts/openbsd/memory_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <sys/types.h>
4 #include <sys/param.h>
5 #include <sys/mount.h>
6 #include <sys/sysctl.h>
7 #include <sys/swap.h>
8 #include <unistd.h>
9
10 using namespace std;
11 using namespace leatherman::execution;
12
13 namespace facter { namespace facts { namespace openbsd {
14
15 memory_resolver::data memory_resolver::collect_data(collection& facts)
16 {
17 data result;
18
19 // Get the system page size
20 int pagesize_mib[] = { CTL_HW, HW_PAGESIZE };
21 int page_size = 0;
22 size_t len = sizeof(page_size);
23 if (sysctl(pagesize_mib, 2, &page_size, &len, nullptr, 0) == -1) {
24 LOG_DEBUG("sysctl failed: {1} ({2}): system page size is unknown.", strerror(errno), errno);
25 } else {
26 int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
27 struct uvmexp uvmexp;
28 len = sizeof(uvmexp);
29 if (sysctl(uvmexp_mib, 2, &uvmexp, &len, nullptr, 0) == -1) {
30 LOG_DEBUG("sysctl uvmexp failed: {1} ({2}): free memory is not available.", strerror(errno), errno);
31 }
32
33 // Should we account for the buffer cache?
34 result.mem_total = static_cast<u_int64_t>(uvmexp.npages) << uvmexp.pageshift;
35 result.mem_free = static_cast<u_int64_t>(uvmexp.free) << uvmexp.pageshift;
36 }
37
38 // NB: swapctl(2) for SWAP_NSWAP cannot fail
39 int nswaps = swapctl(SWAP_NSWAP, 0, 0);
40 vector<struct swapent> swapdev(nswaps);
41
42 if (swapctl(SWAP_STATS, swapdev.data(), nswaps) == -1) {
43 LOG_DEBUG("swapctl: SWAP_STATS failed: {1} ({2})", strerror(errno), errno);
44 return result;
45 }
46
47 uint64_t swap_used = 0;
48 for (auto &&swap : swapdev) {
49 if (swap.se_flags & SWF_ENABLE) {
50 result.swap_total += swap.se_nblks / (1024 / DEV_BSIZE);
51 swap_used += swap.se_inuse / (1024 / DEV_BSIZE);
52 }
53 }
54
55 result.swap_free = result.swap_total - swap_used;
56
57 // Adjust for expected scale
58 result.swap_total *= 1024;
59 result.swap_free *= 1024;
60
61 // 0 is for CTL_SWPENC_NAMES' "enable", see uvm_swap_encrypt.h
62 int swap_encrypted_mib[] = { CTL_VM, VM_SWAPENCRYPT, 0 };
63 int encrypted;
64 len = sizeof(encrypted);
65
66 if (sysctl(swap_encrypted_mib, 3, &encrypted, &len, nullptr, 0) == -1) {
67 LOG_DEBUG("sysctl failed: {1} ({2}): encrypted swap fact not available.", strerror(errno), errno);
68 }
69
70 result.swap_encryption = encrypted ? encryption_status::encrypted : encryption_status::not_encrypted;
71
72 return result;
73 }
74
75 }}} // namespace facter::facts::openbsd
+0
-64
lib/src/facts/openbsd/networking_resolver.cc less more
0 #include <internal/facts/openbsd/networking_resolver.hpp>
1 #include <internal/util/bsd/scoped_ifaddrs.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <boost/algorithm/string.hpp>
5 #include <sys/sockio.h>
6 #include <sys/ioctl.h>
7 #include <net/if.h>
8 #include <net/if_dl.h>
9 #include <netinet/in.h>
10
11 using namespace std;
12 using namespace facter::util;
13 using namespace facter::util::bsd;
14 using namespace leatherman::execution;
15
16 namespace facter { namespace facts { namespace openbsd {
17
18 bool networking_resolver::is_link_address(sockaddr const* addr) const
19 {
20 return addr && addr->sa_family == AF_LINK;
21 }
22
23 uint8_t const* networking_resolver::get_link_address_bytes(sockaddr const* addr) const
24 {
25 if (!is_link_address(addr)) {
26 return nullptr;
27 }
28 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
29 if (link_addr->sdl_alen != 6 && link_addr->sdl_alen != 20) {
30 return nullptr;
31 }
32 return reinterpret_cast<uint8_t const*>(LLADDR(link_addr));
33 }
34
35 uint8_t networking_resolver::get_link_address_length(sockaddr const* addr) const
36 {
37 if (!is_link_address(addr)) {
38 return 0;
39 }
40 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
41 return link_addr->sdl_alen;
42 }
43
44 boost::optional<uint64_t> networking_resolver::get_link_mtu(string const& interface, void* data) const
45 {
46 ifreq ifr;
47 memset(&ifr, 0, sizeof(ifr));
48 strncpy(ifr.ifr_name, interface.c_str(), sizeof(ifr.ifr_name));
49 int s = socket(AF_INET, SOCK_DGRAM, 0);
50 if (s < 0) {
51 LOG_WARNING("socket failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
52 return boost::none;
53 }
54
55 if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
56 LOG_WARNING("ioctl failed: {1} ({2}): interface MTU fact is unavailable for interface {3}.", strerror(errno), errno, interface);
57 return boost::none;
58 }
59
60 return ifr.ifr_mtu;
61 }
62
63 }}} // namespace facter::facts::openbsdbsd
+0
-59
lib/src/facts/openbsd/processor_resolver.cc less more
0 #include <internal/facts/openbsd/processor_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/types.h>
3 #include <sys/sysctl.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace openbsd {
8
9 processor_resolver::data processor_resolver::collect_data(collection& facts)
10 {
11 auto result = posix::processor_resolver::collect_data(facts);
12 size_t len;
13 int mib[2];
14 mib[0] = CTL_HW;
15
16 // Get the logical count of processors
17 len = sizeof(result.logical_count);
18 mib[1] = HW_NCPU;
19
20 if (sysctl(mib, 2, &result.logical_count, &len, nullptr, 0) == -1) {
21 LOG_DEBUG("sysctl hw.ncpu failed: {1} ({2}): logical processor count is unavailable.", strerror(errno), errno);
22 }
23
24 // For each logical processor, collect the model name
25 if (result.logical_count > 0) {
26 // Note: we're using the same description string for all logical processors:
27 // using different CPUs is not even likely to work.
28 vector<char> buffer(256);
29
30 while (true) {
31 size_t size = buffer.size();
32 mib[1] = HW_MODEL;
33 if (sysctl(mib, 2, buffer.data(), &size, nullptr, 0) == 0) {
34 buffer.resize(size + 1);
35 result.models.resize(result.logical_count, buffer.data());
36 break;
37 }
38 if (errno != ENOMEM) {
39 LOG_DEBUG("sysctl hw.model failed: {1} ({2}): processor models are unavailable.", strerror(errno), errno);
40 break;
41 }
42 buffer.resize(buffer.size() * 2);
43 }
44 }
45
46 // Set the speed
47 len = sizeof(result.speed);
48 mib[1] = HW_CPUSPEED;
49 if (sysctl(mib, 2, &result.speed, &len, nullptr, 0) == -1) {
50 LOG_DEBUG("sysctl hw.cpuspeed failed: {1} ({2}): processor speed is unavailable.", strerror(errno), errno);
51 }
52 // Scale the speed to something resolve() can correctly map
53 result.speed *= 1000 * 1000;
54
55 return result;
56 }
57
58 }}} // namespace facter::facts::openbsd
+0
-20
lib/src/facts/openbsd/virtualization_resolver.cc less more
0 #include <internal/facts/openbsd/virtualization_resolver.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/vm.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::execution;
11
12 namespace facter { namespace facts { namespace openbsd {
13
14 string virtualization_resolver::get_hypervisor(collection& facts)
15 {
16 return get_fact_vm(facts);
17 }
18
19 } } } // namespace facter::facts::openbsd
+0
-39
lib/src/facts/osx/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/posix/kernel_resolver.hpp>
2 #include <internal/facts/osx/operating_system_resolver.hpp>
3 #include <internal/facts/osx/networking_resolver.hpp>
4 #include <internal/facts/osx/processor_resolver.hpp>
5 #include <internal/facts/osx/dmi_resolver.hpp>
6 #include <internal/facts/osx/system_profiler_resolver.hpp>
7 #include <internal/facts/osx/virtualization_resolver.hpp>
8 #include <internal/facts/bsd/uptime_resolver.hpp>
9 #include <internal/facts/ssh_resolver.hpp>
10 #include <internal/facts/posix/identity_resolver.hpp>
11 #include <internal/facts/posix/timezone_resolver.hpp>
12 #include <internal/facts/bsd/filesystem_resolver.hpp>
13 #include <internal/facts/osx/memory_resolver.hpp>
14 #include <internal/facts/glib/load_average_resolver.hpp>
15
16 using namespace std;
17
18 namespace facter { namespace facts {
19
20 void collection::add_platform_facts()
21 {
22 add(make_shared<posix::kernel_resolver>());
23 add(make_shared<osx::operating_system_resolver>());
24 add(make_shared<bsd::uptime_resolver>());
25 add(make_shared<osx::networking_resolver>());
26 add(make_shared<osx::processor_resolver>());
27 add(make_shared<osx::dmi_resolver>());
28 add(make_shared<ssh_resolver>());
29 add(make_shared<osx::system_profiler_resolver>());
30 add(make_shared<osx::virtualization_resolver>());
31 add(make_shared<posix::identity_resolver>());
32 add(make_shared<posix::timezone_resolver>());
33 add(make_shared<bsd::filesystem_resolver>());
34 add(make_shared<osx::memory_resolver>());
35 add(make_shared<glib::load_average_resolver>());
36 }
37
38 }} // namespace facter::facts
+0
-32
lib/src/facts/osx/dmi_resolver.cc less more
0 #include <internal/facts/osx/dmi_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/sysctl.h>
3
4 using namespace std;
5
6 namespace facter { namespace facts { namespace osx {
7
8 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
9 {
10 data result;
11
12 int mib[] = { CTL_HW, HW_MODEL };
13 size_t length = 0;
14
15 // OSX only supports the product name
16 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), nullptr, &length, nullptr, 0) == -1) {
17 LOG_DEBUG("sysctl failed: {1} ({2}): DMI facts are unavailable.", strerror(errno), errno);
18 return result;
19 }
20
21 vector<char> model_name(length);
22 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), model_name.data(), &length, nullptr, 0) == -1) {
23 LOG_DEBUG("sysctl failed: {1} ({2}): DMI facts are unavailable.", strerror(errno), errno);
24 return result;
25 }
26
27 result.product_name = model_name.data();
28 return result;
29 }
30
31 }}} // namespace facter::facts::osx
+0
-60
lib/src/facts/osx/memory_resolver.cc less more
0 #include <internal/facts/osx/memory_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <mach/mach.h>
4 #include <sys/sysctl.h>
5
6 using namespace std;
7 using namespace leatherman::execution;
8
9 namespace facter { namespace facts { namespace osx {
10
11 memory_resolver::data memory_resolver::collect_data(collection& facts)
12 {
13 data result;
14
15 // Get the total memory size
16 int mib[] = { CTL_HW, HW_MEMSIZE };
17 size_t size = sizeof(result.mem_total);
18 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &result.mem_total, &size, nullptr, 0) == -1) {
19 LOG_DEBUG("sysctl failed: {1} ({2}): total memory size is not available.", strerror(errno), errno);
20 }
21
22 // Get the system page size
23 mib[0] = CTL_HW;
24 mib[1] = HW_PAGESIZE;
25 uint32_t page_size = 0;
26 size = sizeof(page_size);
27 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &page_size, &size, nullptr, 0) == -1) {
28 LOG_DEBUG("sysctl failed: {1} ({2}): system page size is unknown.", strerror(errno), errno);
29 } else {
30 // Get the VM stats for free memory
31 vm_statistics64 vm_stats;
32 mach_msg_type_number_t type = HOST_VM_INFO64_COUNT;
33 kern_return_t err = host_statistics64(mach_host_self(), HOST_VM_INFO64, reinterpret_cast<host_info64_t>(&vm_stats), &type);
34 if (err != KERN_SUCCESS) {
35 LOG_DEBUG("host_statistics64 failed: {1} ({2}): free memory size is not available.", mach_error_string(err), err);
36 } else {
37 // The free count already counts the free speculative pages
38 // To be consistent with what Activity Monitor displays for the "used" amount,
39 // subtract out those pages and only count the "completely free" pages.
40 result.mem_free = (vm_stats.free_count - vm_stats.speculative_count) * page_size;
41 }
42 }
43
44 // Get the swap usage statistics
45 mib[0] = CTL_VM;
46 mib[1] = VM_SWAPUSAGE;
47 xsw_usage swap_usage;
48 size = sizeof(swap_usage);
49 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &swap_usage, &size, nullptr, 0) == -1) {
50 LOG_DEBUG("sysctl failed: {1} ({2}): swap data is not available.", strerror(errno), errno);
51 } else {
52 result.swap_free = swap_usage.xsu_total - swap_usage.xsu_used;
53 result.swap_total = swap_usage.xsu_total;
54 result.swap_encryption = swap_usage.xsu_encrypted ? encryption_status::encrypted : encryption_status::not_encrypted;
55 }
56 return result;
57 }
58
59 }}} // namespace facter::facts::osx
+0
-80
lib/src/facts/osx/networking_resolver.cc less more
0 #include <internal/facts/osx/networking_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <leatherman/execution/execution.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <net/if_dl.h>
7 #include <net/if.h>
8
9 using namespace std;
10 using namespace leatherman::execution;
11
12 namespace facter { namespace facts { namespace osx {
13
14 bool networking_resolver::is_link_address(sockaddr const* addr) const
15 {
16 return addr && addr->sa_family == AF_LINK;
17 }
18
19 uint8_t const* networking_resolver::get_link_address_bytes(sockaddr const* addr) const
20 {
21 if (!is_link_address(addr)) {
22 return nullptr;
23 }
24 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
25 if (link_addr->sdl_alen != 6 && link_addr->sdl_alen != 20) {
26 return nullptr;
27 }
28 return reinterpret_cast<uint8_t const*>(LLADDR(link_addr));
29 }
30
31 uint8_t networking_resolver::get_link_address_length(sockaddr const* addr) const
32 {
33 if (!is_link_address(addr)) {
34 return 0;
35 }
36 sockaddr_dl const* link_addr = reinterpret_cast<sockaddr_dl const*>(addr);
37 return link_addr->sdl_alen;
38 }
39
40 boost::optional<uint64_t> networking_resolver::get_link_mtu(string const& interface, void* data) const
41 {
42 if (!data) {
43 return boost::none;
44 }
45 return reinterpret_cast<if_data const*>(data)->ifi_mtu;
46 }
47
48 string networking_resolver::get_primary_interface() const
49 {
50 string interface;
51 each_line("route", { "-n", "get", "default" }, [&interface](string& line){
52 boost::trim(line);
53 if (boost::starts_with(line, "interface: ")) {
54 interface = line.substr(11);
55 boost::trim(interface);
56 return false;
57 }
58 return true;
59 });
60 return interface;
61 }
62
63 map<string, string> networking_resolver::find_dhcp_servers() const
64 {
65 // We don't parse dhclient information on OSX
66 return map<string, string>();
67 }
68
69 string networking_resolver::find_dhcp_server(string const& interface) const
70 {
71 // Use ipconfig to get the server identifier
72 auto exec = execute("ipconfig", { "getoption", interface, "server_identifier" });
73 if (!exec.success) {
74 return {};
75 }
76 return exec.output;
77 }
78
79 }}} // namespace facter::facts::osx
+0
-49
lib/src/facts/osx/operating_system_resolver.cc less more
0 #include <internal/facts/osx/operating_system_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <boost/algorithm/string.hpp>
3 #include <boost/regex.hpp>
4 #include <string>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace leatherman::execution;
9
10 namespace facter { namespace facts { namespace osx {
11
12 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
13 {
14 // Default to the base implementation
15 data result = posix::operating_system_resolver::collect_data(facts);
16
17 each_line("/usr/bin/sw_vers", [&](string& line) {
18 // Split at the first ':'
19 auto pos = line.find(':');
20 if (pos == string::npos) {
21 return true;
22 }
23 string key = line.substr(0, pos);
24 boost::trim(key);
25 string value = line.substr(pos + 1);
26 boost::trim(value);
27
28 if (key == "ProductName") {
29 result.osx.product = move(value);
30 } else if (key == "BuildVersion") {
31 result.osx.build = move(value);
32 } else if (key == "ProductVersion") {
33 result.osx.version = move(value);
34 }
35
36 // Continue only if we haven't populated the data
37 return result.osx.product.empty() || result.osx.build.empty() || result.osx.version.empty();
38 });
39
40 // If osx.build is missing the patch version, add '.0'
41 if (boost::regex_match(result.osx.version, boost::regex("^\\d+\\.\\d+$"))) {
42 result.osx.version += ".0";
43 }
44
45 return result;
46 }
47
48 }}} // namespace facter::facts::osx
+0
-55
lib/src/facts/osx/processor_resolver.cc less more
0 #include <internal/facts/osx/processor_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/types.h>
3 #include <sys/sysctl.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace osx {
8
9 processor_resolver::data processor_resolver::collect_data(collection& facts)
10 {
11 auto result = posix::processor_resolver::collect_data(facts);
12
13 // Get the logical count of processors
14 size_t size = sizeof(result.logical_count);
15 if (sysctlbyname("hw.logicalcpu_max", &result.logical_count, &size, nullptr, 0) == -1) {
16 LOG_DEBUG("sysctlbyname failed: logical processor count is unavailable.", strerror(errno), errno);
17 }
18
19 // Get the physical count of processors
20 size = sizeof(result.physical_count);
21 if (sysctlbyname("hw.physicalcpu_max", &result.physical_count, &size, nullptr, 0) == -1) {
22 LOG_DEBUG("sysctlbyname failed: {1} ({2}): physical processor count is unavailable.", strerror(errno), errno);
23 }
24
25 // For each logical processor, collect the model name
26 if (result.logical_count > 0) {
27 // Note: we're using the same description string for all logical processors
28 vector<char> buffer(256);
29
30 while (true) {
31 size_t size = buffer.size();
32 if (sysctlbyname("machdep.cpu.brand_string", buffer.data(), &size, nullptr, 0) == 0) {
33 buffer.resize(size + 1);
34 result.models.resize(result.logical_count, buffer.data());
35 break;
36 }
37 if (errno != ENOMEM) {
38 LOG_DEBUG("sysctlbyname failed: {1} ({2}): processor models are unavailable.", strerror(errno), errno);
39 break;
40 }
41 buffer.resize(buffer.size() * 2);
42 }
43 }
44
45 // Set the speed
46 size = sizeof(result.speed);
47 if (sysctlbyname("hw.cpufrequency_max", &result.speed, &size, nullptr, 0) == -1) {
48 LOG_DEBUG("sysctlbyname failed: {1} ({2}): processor speed is unavailable.", strerror(errno), errno);
49 }
50
51 return result;
52 }
53
54 }}} // namespace facter::facts::osx
+0
-66
lib/src/facts/osx/system_profiler_resolver.cc less more
0 #include <internal/facts/osx/system_profiler_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <boost/algorithm/string.hpp>
3 #include <map>
4 #include <functional>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace leatherman::execution;
9
10 namespace facter { namespace facts { namespace osx {
11
12 system_profiler_resolver::data system_profiler_resolver::collect_data(collection& facts)
13 {
14 static map<string, function<string&(data&)>> data_map = {
15 { "Boot Mode", [](data& d) -> string& { return d.boot_mode; } },
16 { "Boot ROM Version", [](data& d) -> string& { return d.boot_rom_version; } },
17 { "Boot Volume", [](data& d) -> string& { return d.boot_volume; } },
18 { "Processor Name", [](data& d) -> string& { return d.processor_name; } },
19 { "Processor Speed", [](data& d) -> string& { return d.processor_speed; } },
20 { "Kernel Version", [](data& d) -> string& { return d.kernel_version; } },
21 { "L2 Cache (per Core)", [](data& d) -> string& { return d.l2_cache_per_core; } },
22 { "L3 Cache", [](data& d) -> string& { return d.l3_cache; } },
23 { "Computer Name", [](data& d) -> string& { return d.computer_name; } },
24 { "Model Identifier", [](data& d) -> string& { return d.model_identifier; } },
25 { "Model Name", [](data& d) -> string& { return d.model_name; } },
26 { "Total Number of Cores", [](data& d) -> string& { return d.cores; } },
27 { "System Version", [](data& d) -> string& { return d.system_version; } },
28 { "Number of Processors", [](data& d) -> string& { return d.processors; } },
29 { "Memory", [](data& d) -> string& { return d.memory; } },
30 { "Hardware UUID", [](data& d) -> string& { return d.hardware_uuid; } },
31 { "Secure Virtual Memory", [](data& d) -> string& { return d.secure_virtual_memory; } },
32 { "Serial Number (system)", [](data& d) -> string& { return d.serial_number; } },
33 { "SMC Version (system)", [](data& d) -> string& { return d.smc_version; } },
34 { "Time since boot", [](data& d) -> string& { return d.uptime; } },
35 { "User Name", [](data& d) -> string& { return d.username; } }
36 };
37
38 data result;
39 size_t count = 0;
40 each_line("/usr/sbin/system_profiler", { "SPSoftwareDataType", "SPHardwareDataType" }, [&](string& line) {
41 // Split at the first ':'
42 auto pos = line.find(':');
43 if (pos == string::npos) {
44 return true;
45 }
46 string key = line.substr(0, pos);
47 boost::trim(key);
48 string value = line.substr(pos + 1);
49 boost::trim(value);
50
51 // Lookup the data based on the "key"
52 auto it = data_map.find(key);
53 if (it == data_map.end()) {
54 return true;
55 }
56 it->second(result) = move(value);
57
58 // Continue only if we haven't collected all the data
59 return ++count < data_map.size();
60 });
61
62 return result;
63 }
64
65 }}} // namespace facter::facts::osx
+0
-46
lib/src/facts/osx/virtualization_resolver.cc less more
0 #include <internal/facts/osx/virtualization_resolver.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/vm.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::execution;
11
12 namespace facter { namespace facts { namespace osx {
13
14 string virtualization_resolver::get_hypervisor(collection& facts)
15 {
16 // Check for VMWare
17 auto machine_model = facts.get<string_value>(fact::sp_machine_model);
18 if (machine_model) {
19 if (boost::starts_with(machine_model->value(), "VMware")) {
20 return vm::vmware;
21 }
22 }
23
24 // Check for VirtualBox
25 auto boot_rom_version = facts.get<string_value>(fact::sp_boot_rom_version);
26 if (boot_rom_version) {
27 if (boot_rom_version->value() == "VirtualBox") {
28 return vm::virtualbox;
29 }
30 }
31
32 // Check for Parallels
33 string value;
34 each_line("/usr/sbin/system_profiler", { "SPEthernetDataType" }, [&](string& line) {
35 boost::trim(line);
36 if (line == "Subsystem Vendor ID: 0x1ab8") {
37 value = vm::parallels;
38 return false;
39 }
40 return true;
41 });
42 return value;
43 }
44
45 }}} // namespace facter::facts::osx
+0
-9
lib/src/facts/posix/cache.cc less more
0 #include <internal/facts/cache.hpp>
1
2 namespace facter { namespace facts { namespace cache {
3
4 std::string fact_cache_location() {
5 return "/opt/puppetlabs/facter/cache/cached_facts/";
6 }
7
8 }}} // namespace facter::facts::cache
+0
-32
lib/src/facts/posix/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <leatherman/util/environment.hpp>
2 #include <unistd.h>
3 #include <vector>
4 #include <string>
5 #include <cstdlib>
6 #include <memory>
7
8 using namespace std;
9 using namespace leatherman::util;
10 using namespace facter::facts::external;
11
12 namespace facter { namespace facts {
13
14 vector<string> collection::get_external_fact_directories() const
15 {
16 vector<string> directories;
17 if (getuid()) {
18 string home;
19 if (environment::get("HOME", home)) {
20 directories.emplace_back(home + "/.puppetlabs/opt/facter/facts.d");
21 directories.emplace_back(home + "/.facter/facts.d");
22 }
23 } else {
24 directories.emplace_back("/opt/puppetlabs/facter/facts.d");
25 directories.emplace_back("/etc/facter/facts.d");
26 directories.emplace_back("/etc/puppetlabs/facter/facts.d");
27 }
28 return directories;
29 }
30
31 }} // namespace facter::facts
+0
-12
lib/src/facts/posix/external_resolvers_factory.cc less more
0 #include <leatherman/locale/locale.hpp>
1 #include <facter/facts/external_resolvers_factory.hpp>
2
3 using namespace std;
4 namespace facter { namespace facts {
5 shared_ptr<external::resolver> external_resolvers_factory::get_resolver(const string& path) {
6 auto resolver = get_common_resolver(path);
7 if (resolver)
8 return resolver;
9 throw external::external_fact_no_resolver(leatherman::locale::_("No resolver for external facts file {1}", path));
10 }
11 }} // facter::facts
+0
-79
lib/src/facts/posix/identity_resolver.cc less more
0 #include <internal/facts/posix/identity_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <pwd.h>
5 #include <grp.h>
6
7 using namespace std;
8
9 namespace facter { namespace facts { namespace posix {
10
11 identity_resolver::data identity_resolver::collect_data(collection& facts)
12 {
13 data result;
14
15 vector<char> buffer;
16 long buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX);
17
18 if (buffer_size == -1) {
19 buffer.resize(1024);
20 } else {
21 buffer.resize(buffer_size);
22 }
23
24 uid_t uid = geteuid();
25 struct passwd pwd;
26 struct passwd *pwd_ptr;
27 int err = -1;
28
29 do {
30 err = getpwuid_r(uid, &pwd, buffer.data(), buffer.size(), &pwd_ptr);
31 if (err == ERANGE) {
32 buffer.resize(buffer.size() + 1024);
33 }
34 } while (err == EINTR || err == ERANGE);
35
36
37 if (err != 0) {
38 LOG_WARNING("getpwuid_r failed: {1} ({2})", strerror(err), err);
39 } else if (pwd_ptr == NULL) {
40 LOG_WARNING("effective uid {1} does not have a passwd entry.", uid);
41 } else {
42 result.user_id = static_cast<int64_t>(uid);
43 result.user_name = pwd.pw_name;
44 result.privileged = (uid == 0);
45 }
46
47 buffer_size = sysconf(_SC_GETGR_R_SIZE_MAX);
48
49 if (buffer_size == -1) {
50 buffer.resize(1024);
51 } else {
52 buffer.resize(buffer_size);
53 }
54
55 gid_t gid = getegid();
56 struct group grp;
57 struct group *grp_ptr;
58
59 do {
60 err = getgrgid_r(gid, &grp, buffer.data(), buffer.size(), &grp_ptr);
61 if (err == ERANGE) {
62 buffer.resize(buffer.size() + 1024);
63 }
64 } while (err == EINTR || err == ERANGE);
65
66 if (err != 0) {
67 LOG_WARNING("getgrgid_r failed: {1} ({2})", strerror(err), err);
68 } else if (grp_ptr == NULL) {
69 LOG_WARNING("effective gid {1} does not have a group entry.", gid);
70 } else {
71 result.group_id = static_cast<int64_t>(gid);
72 result.group_name = grp.gr_name;
73 }
74
75 return result;
76 }
77
78 }}} // facter::facts::posix
+0
-25
lib/src/facts/posix/kernel_resolver.cc less more
0 #include <internal/facts/posix/kernel_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <sys/utsname.h>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace posix {
8
9 kernel_resolver::data kernel_resolver::collect_data(collection& facts)
10 {
11 data result;
12 struct utsname name;
13 if (uname(&name) == -1) {
14 LOG_WARNING("uname failed: {1} ({2}): kernel facts are unavailable.", strerror(errno), errno);
15 return result;
16 }
17
18 result.name = name.sysname;
19 result.release = name.release;
20 result.version = result.release.substr(0, result.release.find('-'));
21 return result;
22 }
23
24 }}} // namespace facter::facts::posix
+0
-137
lib/src/facts/posix/networking_resolver.cc less more
0 #include <internal/facts/posix/networking_resolver.hpp>
1 #include <internal/util/posix/scoped_addrinfo.hpp>
2 #include <leatherman/file_util/file.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <boost/algorithm/string.hpp>
5 #include <unistd.h>
6 #include <limits.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9
10 using namespace std;
11 using namespace facter::util::posix;
12
13 namespace lth_file = leatherman::file_util;
14
15 namespace facter { namespace facts { namespace posix {
16
17 string networking_resolver::address_to_string(sockaddr const* addr, sockaddr const* mask) const
18 {
19 if (!addr) {
20 return {};
21 }
22
23 // Check for IPv4 and IPv6
24 if (addr->sa_family == AF_INET) {
25 in_addr ip = reinterpret_cast<sockaddr_in const*>(addr)->sin_addr;
26
27 // Apply an IPv4 mask
28 if (mask && mask->sa_family == addr->sa_family) {
29 ip.s_addr &= reinterpret_cast<sockaddr_in const*>(mask)->sin_addr.s_addr;
30 }
31
32 char buffer[INET_ADDRSTRLEN] = {};
33 inet_ntop(AF_INET, &ip, buffer, sizeof(buffer));
34 return buffer;
35 } else if (addr->sa_family == AF_INET6) {
36 in6_addr ip = reinterpret_cast<sockaddr_in6 const*>(addr)->sin6_addr;
37
38 // Apply an IPv6 mask
39 if (mask && mask->sa_family == addr->sa_family) {
40 auto mask_ptr = reinterpret_cast<sockaddr_in6 const*>(mask);
41 for (size_t i = 0; i < 16; ++i) {
42 ip.s6_addr[i] &= mask_ptr->sin6_addr.s6_addr[i];
43 }
44 }
45
46 char buffer[INET6_ADDRSTRLEN] = {};
47 inet_ntop(AF_INET6, &ip, buffer, sizeof(buffer));
48 return buffer;
49 } else if (is_link_address(addr)) {
50 auto link_addr = get_link_address_bytes(addr);
51 uint8_t link_addr_len = get_link_address_length(addr);
52 if (link_addr) {
53 return macaddress_to_string(reinterpret_cast<uint8_t const*>(link_addr), link_addr_len);
54 }
55 }
56
57 return {};
58 }
59
60 networking_resolver::data networking_resolver::collect_data(collection& facts)
61 {
62 data result;
63
64 // Get the maximum size of the host name
65 int size = sysconf(_SC_HOST_NAME_MAX);
66 if (size <= 0) {
67 size = 1024;
68 }
69 // Get the hostname (+1 to ensure a null is returned on platforms where maximum truncation may occur)
70 vector<char> name(size + 1);
71 if (gethostname(name.data(), size + 1) != 0) {
72 LOG_WARNING("gethostname failed: {1} ({2}): hostname is unavailable.", strerror(errno), errno);
73 } else {
74 // Check for fully-qualified hostname
75 auto it = find(name.begin(), name.end(), '.');
76 if (it != name.end()) {
77 LOG_DEBUG("using the FQDN returned by gethostname: {1}.", name.data());
78 result.hostname.assign(name.begin(), it);
79 if (++it != name.end()) {
80 // Use the remainder of the string, up to the first null character
81 result.domain = &*it;
82 }
83 } else {
84 // Not fully qualified; just set hostname
85 result.hostname = name.data();
86 }
87 }
88
89 // If the hostname was not already fully qualified, attempt to resolve it
90 if (result.domain.empty() && !result.hostname.empty()) {
91 // Retrieve the FQDN by resolving the hostname
92 scoped_addrinfo info(result.hostname);
93 if (info.result() != 0 && info.result() != EAI_NONAME) {
94 LOG_WARNING("getaddrinfo failed: {1} ({2}): hostname may not be externally resolvable.", gai_strerror(info.result()), info.result());
95 } else if (!info || info.result() == EAI_NONAME || !static_cast<addrinfo*>(info)->ai_canonname || result.hostname == static_cast<addrinfo*>(info)->ai_canonname) {
96 LOG_DEBUG("hostname \"{1}\" could not be resolved: hostname may not be externally resolvable.", result.hostname);
97 } else {
98 result.fqdn = static_cast<addrinfo*>(info)->ai_canonname;
99 }
100
101 // Set the domain name if the FQDN is prefixed with the hostname
102 if (boost::starts_with(result.fqdn, result.hostname + ".")) {
103 result.domain = result.fqdn.substr(result.hostname.size() + 1);
104 }
105 }
106
107 // If no domain, look it up based on resolv.conf
108 if (result.domain.empty()) {
109 string search;
110 lth_file::each_line("/etc/resolv.conf", [&](string& line) {
111 vector<boost::iterator_range<string::iterator>> parts;
112 boost::split(parts, line, boost::is_space(), boost::token_compress_on);
113 if (parts.size() < 2) {
114 return true;
115 }
116 if (parts[0] == boost::as_literal("domain")) {
117 // Treat the first domain entry as the domain
118 result.domain.assign(parts[1].begin(), parts[1].end());
119 return false;
120 }
121 if (search.empty() && parts[0] == boost::as_literal("search")) {
122 // Found a "search" entry, but keep looking for other domain entries
123 // We use the first search domain as the domain.
124 search.assign(parts[1].begin(), parts[1].end());
125 return true;
126 }
127 return true;
128 });
129 if (result.domain.empty()) {
130 result.domain = move(search);
131 }
132 }
133 return result;
134 }
135
136 }}} // namespace facter::facts::posix
+0
-29
lib/src/facts/posix/operatingsystem_resolver.cc less more
0 #include <internal/facts/posix/operating_system_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <sys/utsname.h>
4
5 using namespace std;
6 using namespace leatherman::execution;
7
8 namespace facter { namespace facts { namespace posix {
9
10 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
11 {
12 // Default to the base implementation
13 data result = resolvers::operating_system_resolver::collect_data(facts);
14
15 struct utsname name;
16 memset(&name, 0, sizeof(name));
17 if (uname(&name) == -1) {
18 LOG_DEBUG("uname failed: {1} ({2}): OS hardware is unavailable.", strerror(errno), errno);
19 } else {
20 result.hardware = name.machine;
21 }
22
23 // By default, the architecture is the same as the hardware model
24 result.architecture = result.hardware;
25 return result;
26 }
27
28 }}} // namespace facter::facts::posix
+0
-22
lib/src/facts/posix/processor_resolver.cc less more
0 #include <internal/facts/posix/processor_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 using namespace std;
5 using namespace leatherman::execution;
6
7 namespace facter { namespace facts { namespace posix {
8
9 processor_resolver::data processor_resolver::collect_data(collection& facts)
10 {
11 data result;
12
13 // Unfortunately there's no corresponding member in utsname for "processor", so we need to spawn
14 auto exec = execute("uname", { "-p" });
15 if (exec.success) {
16 result.isa = exec.output;
17 }
18 return result;
19 }
20
21 }}} // namespace facter::facts::posix
+0
-38
lib/src/facts/posix/ssh_resolver.cc less more
0 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1 #include <internal/facts/ssh_resolver.hpp>
2
3 using namespace std;
4 using namespace boost::filesystem;
5
6 namespace bs = boost::system;
7
8 namespace facter { namespace facts {
9
10 path ssh_resolver::retrieve_key_file(string const& filename)
11 {
12 path key_file;
13
14 static vector<string> const search_directories = {
15 "/etc/ssh",
16 "/usr/local/etc/ssh",
17 "/etc",
18 "/usr/local/etc",
19 "/etc/opt/ssh"
20 };
21
22 for (auto const& directory : search_directories) {
23 key_file = directory;
24 key_file /= filename;
25
26 bs::error_code ec;
27 if (!is_regular_file(key_file, ec)) {
28 key_file.clear();
29 continue;
30 }
31 break;
32 }
33
34 return key_file;
35 }
36
37 }} // namespace facter::facts
+0
-26
lib/src/facts/posix/timezone_resolver.cc less more
0 #include <internal/facts/posix/timezone_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <time.h>
3
4 using namespace std;
5
6 namespace facter { namespace facts { namespace posix {
7
8 string timezone_resolver::get_timezone()
9 {
10 time_t since_epoch = time(NULL);
11 tm localtime;
12 char buffer[16];
13
14 if (!::localtime_r(&since_epoch, &localtime)) {
15 LOG_WARNING("localtime_r failed: timezone is unavailable.");
16 return {};
17 }
18 if (::strftime(buffer, sizeof(buffer), "%Z", &localtime) == 0) {
19 LOG_WARNING("strftime failed: timezone is unavailable.");
20 return {};
21 }
22 return buffer;
23 }
24
25 }}} // facter::facts::posix
+0
-78
lib/src/facts/posix/uptime_resolver.cc less more
0 #include <internal/facts/posix/uptime_resolver.hpp>
1 #ifdef HAVE_UTMPX_H
2 #include <internal/util/posix/utmpx_file.hpp>
3 #endif
4 #include <leatherman/util/regex.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <leatherman/locale/locale.hpp>
8
9 #include <ctime>
10
11 // Mark string for translation (alias for leatherman::locale::format)
12 using leatherman::locale::_;
13
14 using namespace std;
15 using namespace leatherman::util;
16 using namespace leatherman::execution;
17 #ifdef HAVE_UTMPX_H
18 using namespace facter::util::posix;
19 #endif
20
21 namespace facter { namespace facts { namespace posix {
22
23 int64_t uptime_resolver::parse_uptime(string const& output)
24 {
25 // This regex parsing is directly ported from facter:
26 // https://github.com/puppetlabs/facter/blob/2.0.1/lib/facter/util/uptime.rb#L42-L60
27
28 static boost::regex days_hours_mins_pattern("(\\d+) day(?:s|\\(s\\))?,?\\s+(\\d+):-?(\\d+)");
29 static boost::regex days_hours_pattern("(\\d+) day(?:s|\\(s\\))?,\\s+(\\d+) hr(?:s|\\(s\\))?,");
30 static boost::regex days_mins_pattern("(\\d+) day(?:s|\\(s\\))?,\\s+(\\d+) min(?:s|\\(s\\))?,");
31 static boost::regex days_pattern("(\\d+) day(?:s|\\(s\\))?,");
32 static boost::regex hours_mins_pattern("up\\s+(\\d+):-?(\\d+),");
33 static boost::regex hours_pattern("(\\d+) hr(?:s|\\(s\\))?,");
34 static boost::regex mins_pattern("(\\d+) min(?:s|\\(s\\))?,");
35
36 int days, hours, minutes;
37
38 if (re_search(output, days_hours_mins_pattern, &days, &hours, &minutes)) {
39 return 86400ll * days + 3600ll * hours + 60ll * minutes;
40 } else if (re_search(output, days_hours_pattern, &days, &hours)) {
41 return 86400ll * days + 3600ll * hours;
42 } else if (re_search(output, days_mins_pattern, &days, &minutes)) {
43 return 86400ll * days + 60ll * minutes;
44 } else if (re_search(output, days_pattern, &days)) {
45 return 86400ll * days;
46 } else if (re_search(output, hours_mins_pattern, &hours, &minutes)) {
47 return 3600ll * hours + 60ll * minutes;
48 } else if (re_search(output, hours_pattern, &hours)) {
49 return 3600ll * hours;
50 } else if (re_search(output, mins_pattern, &minutes)) {
51 return 60ll * minutes;
52 }
53 return -1;
54 }
55
56 int64_t uptime_resolver::get_uptime()
57 {
58 #ifdef HAVE_UTMPX_H
59 LOG_DEBUG(_("Attempting to calculate the uptime from the utmpx file"));
60 utmpx query;
61 query.ut_type = BOOT_TIME;
62 utmpx_file file;
63 auto ent = file.query(query);
64 if (ent) {
65 return time(NULL) - ent->ut_tv.tv_sec;
66 }
67 LOG_DEBUG(_("Could not calculate the uptime from the utmpx file"));
68 #endif
69
70 auto exec = execute("uptime");
71 if (!exec.success) {
72 return -1;
73 }
74 return parse_uptime(exec.output);
75 }
76
77 }}} // namespace facter::facts::posix
+0
-43
lib/src/facts/posix/xen_resolver.cc less more
0 #include <internal/facts/posix/xen_resolver.hpp>
1 #include <leatherman/execution/execution.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <boost/filesystem.hpp>
4
5 using namespace std;
6 using namespace facter::facts;
7 using namespace leatherman::execution;
8 using namespace boost::filesystem;
9 namespace bs = boost::system;
10
11 namespace facter { namespace facts { namespace posix {
12
13 string xen_resolver::xen_command()
14 {
15 constexpr char const* xen_toolstack = "/usr/lib/xen-common/bin/xen-toolstack";
16
17 bs::error_code ec;
18 if (exists(xen_toolstack, ec) && !ec) {
19 auto exec = execute(xen_toolstack);
20 if (exec.success) {
21 return exec.output;
22 } else {
23 LOG_DEBUG("failure executing {1}: {2}", xen_toolstack, exec.error);
24 return {};
25 }
26 } else {
27 LOG_TRACE("xen toolstack command not found: {1}", ec.message());
28
29 static vector<string> xen_commands{"/usr/sbin/xl", "/usr/sbin/xm"};
30 for (auto const& cmd : xen_commands) {
31 auto cmd_path = which(cmd);
32 if (!cmd_path.empty()) {
33 return cmd_path;
34 }
35 }
36
37 LOG_TRACE("no xen commands found");
38 return {};
39 }
40 }
41
42 }}} // namespace facter::facts::posix
+0
-105
lib/src/facts/resolver.cc less more
0 #include <facter/facts/resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <leatherman/util/regex.hpp>
3 #include <leatherman/util/environment.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <boost/locale/info.hpp>
6
7 using namespace std;
8 using namespace leatherman::util;
9
10 namespace facter { namespace facts {
11
12 invalid_name_pattern_exception::invalid_name_pattern_exception(string const& message) :
13 runtime_error(message)
14 {
15 }
16
17 resolver::resolver(string name, vector<string> names, vector<string> const& patterns) :
18 _name(move(name)),
19 _names(move(names))
20 {
21 for (auto const& pattern : patterns) {
22 try {
23 _regexes.push_back(boost::regex(pattern));
24 } catch (boost::regex_error const& ex) {
25 throw invalid_name_pattern_exception(ex.what());
26 }
27 }
28 }
29
30 resolver::~resolver()
31 {
32 // This needs to be defined here since we use incomplete types in the header
33 }
34
35 resolver::resolver(resolver&& other)
36 {
37 *this = std::move(other);
38 }
39
40 resolver& resolver::operator=(resolver&& other)
41 {
42 if (this != &other) {
43 _name = std::move(other._name);
44 _names = std::move(other._names);
45 _regexes = std::move(other._regexes);
46 _http_langs = std::move(other._http_langs);
47 }
48 return *this;
49 }
50
51 string const& resolver::name() const
52 {
53 return _name;
54 }
55
56 vector<string> const& resolver::names() const
57 {
58 return _names;
59 }
60
61 bool resolver::has_patterns() const
62 {
63 return _regexes.size() > 0;
64 }
65
66 bool resolver::is_match(string const& name) const
67 {
68 // Check to see if any of our regexes match
69 for (auto const& regex : _regexes) {
70 if (re_search(name, regex)) {
71 return true;
72 }
73 }
74 return false;
75 }
76
77 string const& resolver::http_langs()
78 {
79 #ifdef LEATHERMAN_USE_LOCALES
80 if (_http_langs.empty()) {
81 // build Accept-Language list for HTTP-based resolvers
82 const auto& loc = leatherman::locale::get_locale();
83 if (std::has_facet<boost::locale::info>(loc)) {
84 const auto& info = std::use_facet<boost::locale::info>(loc);
85 string lang = info.language();
86 // use country code when available; add fallback to base lang
87 if (!info.country().empty())
88 lang += "-" + info.country() + ", " + info.language();
89 // always include English (en) as a fallback
90 if (info.language() != "en")
91 lang += ", en";
92 std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower);
93 _http_langs = lang;
94 }
95 }
96 #endif
97 return _http_langs;
98 }
99
100 bool resolver::is_blockable() const
101 {
102 return false;
103 }
104 }} // namespace facter::facts
+0
-56
lib/src/facts/resolvers/augeas_resolver.cc less more
0 #include <internal/facts/resolvers/augeas_resolver.hpp>
1 #include <internal/util/agent.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include <leatherman/execution/execution.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <leatherman/util/regex.hpp>
9
10 using namespace std;
11 using namespace facter::util;
12 using namespace leatherman::util;
13 using namespace leatherman::execution;
14
15 namespace facter { namespace facts { namespace resolvers {
16
17 augeas_resolver::augeas_resolver() :
18 resolver(
19 "augeas",
20 {
21 fact::augeas,
22 fact::augeasversion,
23 })
24 {
25 }
26
27 string augeas_resolver::get_version()
28 {
29 string augparse = agent::which("augparse");
30 string value;
31 boost::regex regexp("^augparse (\\d+\\.\\d+\\.\\d+)");
32 // Version info goes on stderr.
33 each_line(augparse, {"--version"}, nullptr, [&](string& line) {
34 if (re_search(line, regexp, &value)) {
35 return false;
36 }
37 return true;
38 });
39 return value;
40 }
41
42 void augeas_resolver::resolve(collection& facts)
43 {
44 auto version = get_version();
45 if (version.empty()) {
46 return;
47 }
48
49 auto augeas = make_value<map_value>();
50 augeas->add("version", make_value<string_value>(version));
51 facts.add(fact::augeasversion, make_value<string_value>(move(version), true));
52 facts.add(fact::augeas, move(augeas));
53 }
54
55 }}} // namespace facter::facts::resolvers
+0
-72
lib/src/facts/resolvers/disk_resolver.cc less more
0 #include <internal/facts/resolvers/disk_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/util/string.hpp>
6
7 using namespace std;
8 using namespace facter::util;
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 disk_resolver::disk_resolver() :
13 resolver(
14 "disk",
15 {
16 fact::block_devices,
17 fact::disks
18 },
19 {
20 string("^") + fact::block_device + "_",
21 })
22 {
23 }
24
25 void disk_resolver::resolve(collection& facts)
26 {
27 auto data = collect_data(facts);
28
29 ostringstream names;
30 auto disks = make_value<map_value>();
31 for (auto& disk : data.disks) {
32 if (disk.name.empty()) {
33 continue;
34 }
35 auto value = make_value<map_value>();
36 if (!disk.vendor.empty()) {
37 facts.add(string(fact::block_device) + "_" + disk.name + "_vendor" , make_value<string_value>(disk.vendor, true));
38 value->add("vendor", make_value<string_value>(move(disk.vendor)));
39 }
40 if (!disk.model.empty()) {
41 facts.add(string(fact::block_device) + "_" + disk.name + "_model" , make_value<string_value>(disk.model, true));
42 value->add("model", make_value<string_value>(move(disk.model)));
43 }
44 if (!disk.product.empty()) {
45 value->add("product", make_value<string_value>(move(disk.product)));
46 }
47 facts.add(string(fact::block_device) + "_" + disk.name + "_size" , make_value<integer_value>(static_cast<int64_t>(disk.size), true));
48 value->add("size_bytes", make_value<integer_value>(disk.size));
49 value->add("size", make_value<string_value>(si_string(disk.size)));
50 if (!disk.serial_number.empty()) {
51 value->add("serial_number", make_value<string_value>(move(disk.serial_number)));
52 }
53
54 if (names.tellp() != 0) {
55 names << ',';
56 }
57 names << disk.name;
58
59 disks->add(move(disk.name), move(value));
60 }
61
62 if (names.tellp() > 0) {
63 facts.add(fact::block_devices, make_value<string_value>(names.str(), true));
64 }
65
66 if (!disks->empty()) {
67 facts.add(fact::disks, move(disks));
68 }
69 }
70
71 }}} // namespace facter::facts::resolvers
+0
-168
lib/src/facts/resolvers/dmi_resolver.cc less more
0 #include <internal/facts/resolvers/dmi_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 dmi_resolver::dmi_resolver() :
11 resolver(
12 "desktop management interface",
13 {
14 fact::dmi,
15 fact::bios_vendor,
16 fact::bios_version,
17 fact::bios_release_date,
18 fact::board_asset_tag,
19 fact::board_manufacturer,
20 fact::board_product_name,
21 fact::board_serial_number,
22 fact::chassis_asset_tag,
23 fact::manufacturer,
24 fact::product_name,
25 fact::serial_number,
26 fact::uuid,
27 fact::chassis_type,
28 })
29 {
30 }
31
32 string dmi_resolver::to_chassis_description(string const& type)
33 {
34 if (type.empty()) {
35 return {};
36 }
37
38 static map<string, string> const descriptions = {
39 { "1", "Other" },
40 // 2 is Unknown, which we'll output if it's not in the map anyway
41 { "3", "Desktop" },
42 { "4", "Low Profile Desktop" },
43 { "5", "Pizza Box" },
44 { "6", "Mini Tower" },
45 { "7", "Tower" },
46 { "8", "Portable" },
47 { "9", "Laptop" },
48 { "10", "Notebook" },
49 { "11", "Hand Held" },
50 { "12", "Docking Station" },
51 { "13", "All in One" },
52 { "14", "Sub Notebook" },
53 { "15", "Space-Saving" },
54 { "16", "Lunch Box" },
55 { "17", "Main System Chassis" },
56 { "18", "Expansion Chassis" },
57 { "19", "SubChassis" },
58 { "20", "Bus Expansion Chassis" },
59 { "21", "Peripheral Chassis" },
60 { "22", "Storage Chassis" },
61 { "23", "Rack Mount Chassis" },
62 { "24", "Sealed-Case PC" },
63 { "25", "Multi-system" },
64 { "26", "CompactPCI" },
65 { "27", "AdvancedTCA" },
66 { "28", "Blade" },
67 { "29", "Blade Enclosure" },
68 { "30", "Tablet" },
69 { "31", "Convertible" },
70 { "32", "Detachable" },
71 };
72
73 auto it = descriptions.find(type);
74 if (it != descriptions.end()) {
75 return it->second;
76 }
77 return "Unknown";
78 }
79
80 void dmi_resolver::resolve(collection& facts)
81 {
82 auto data = collect_data(facts);
83 auto dmi = make_value<map_value>();
84
85 auto bios = make_value<map_value>();
86 if (!data.bios_vendor.empty()) {
87 facts.add(fact::bios_vendor, make_value<string_value>(data.bios_vendor, true));
88 bios->add("vendor", make_value<string_value>(move(data.bios_vendor)));
89 }
90 if (!data.bios_version.empty()) {
91 facts.add(fact::bios_version, make_value<string_value>(data.bios_version, true));
92 bios->add("version", make_value<string_value>(move(data.bios_version)));
93 }
94 if (!data.bios_release_date.empty()) {
95 facts.add(fact::bios_release_date, make_value<string_value>(data.bios_release_date, true));
96 bios->add("release_date", make_value<string_value>(move(data.bios_release_date)));
97 }
98
99 auto board = make_value<map_value>();
100 if (!data.board_asset_tag.empty()) {
101 facts.add(fact::board_asset_tag, make_value<string_value>(data.board_asset_tag, true));
102 board->add("asset_tag", make_value<string_value>(move(data.board_asset_tag)));
103 }
104 if (!data.board_manufacturer.empty()) {
105 facts.add(fact::board_manufacturer, make_value<string_value>(data.board_manufacturer, true));
106 board->add("manufacturer", make_value<string_value>(move(data.board_manufacturer)));
107 }
108 if (!data.board_product_name.empty()) {
109 facts.add(fact::board_product_name, make_value<string_value>(data.board_product_name, true));
110 board->add("product", make_value<string_value>(move(data.board_product_name)));
111 }
112 if (!data.board_serial_number.empty()) {
113 facts.add(fact::board_serial_number, make_value<string_value>(data.board_serial_number, true));
114 board->add("serial_number", make_value<string_value>(move(data.board_serial_number)));
115 }
116
117 auto product = make_value<map_value>();
118 if (!data.product_name.empty()) {
119 facts.add(fact::product_name, make_value<string_value>(data.product_name, true));
120 product->add("name", make_value<string_value>(move(data.product_name)));
121 }
122 if (!data.serial_number.empty()) {
123 facts.add(fact::serial_number, make_value<string_value>(data.serial_number, true));
124 product->add("serial_number", make_value<string_value>(move(data.serial_number)));
125 }
126 if (!data.uuid.empty()) {
127 facts.add(fact::uuid, make_value<string_value>(data.uuid, true));
128 product->add("uuid", make_value<string_value>(move(data.uuid)));
129 }
130
131 auto chassis = make_value<map_value>();
132 if (!data.chassis_asset_tag.empty()) {
133 facts.add(fact::chassis_asset_tag, make_value<string_value>(data.chassis_asset_tag, true));
134 chassis->add("asset_tag", make_value<string_value>(move(data.chassis_asset_tag)));
135 }
136 if (!data.chassis_type.empty()) {
137 facts.add(fact::chassis_type, make_value<string_value>(data.chassis_type, true));
138 chassis->add("type", make_value<string_value>(move(data.chassis_type)));
139 }
140
141 if (!data.manufacturer.empty()) {
142 facts.add(fact::manufacturer, make_value<string_value>(data.manufacturer, true));
143 dmi->add("manufacturer", make_value<string_value>(move(data.manufacturer)));
144 }
145
146 if (!bios->empty()) {
147 dmi->add("bios", move(bios));
148 }
149
150 if (!board->empty()) {
151 dmi->add("board", move(board));
152 }
153
154 if (!product->empty()) {
155 dmi->add("product", move(product));
156 }
157
158 if (!chassis->empty()) {
159 dmi->add("chassis", move(chassis));
160 }
161
162 if (!dmi->empty()) {
163 facts.add(fact::dmi, move(dmi));
164 }
165 }
166
167 }}} // namespace facter::facts::resolvers
+0
-179
lib/src/facts/resolvers/ec2_resolver.cc less more
0 #include <internal/facts/resolvers/ec2_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/map_value.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/fact.hpp>
5 #include <facter/facts/vm.hpp>
6 #include <facter/util/string.hpp>
7 #include <leatherman/util/environment.hpp>
8 #include <leatherman/util/regex.hpp>
9 #include <leatherman/logging/logging.hpp>
10 #include <boost/algorithm/string.hpp>
11 #include <set>
12
13 #ifdef USE_CURL
14 #include <leatherman/curl/client.hpp>
15 namespace lth_curl = leatherman::curl;
16 #endif
17
18 using namespace std;
19 using namespace leatherman::util;
20
21 namespace facter { namespace facts { namespace resolvers {
22
23 ec2_resolver::ec2_resolver() :
24 resolver(
25 "EC2",
26 {
27 fact::ec2_metadata,
28 fact::ec2_userdata
29 })
30 {
31 }
32
33 #ifdef USE_CURL
34 static const char* EC2_METADATA_ROOT_URL = "http://169.254.169.254/latest/meta-data/";
35 static const char* EC2_USERDATA_ROOT_URL = "http://169.254.169.254/latest/user-data/";
36 static const unsigned int EC2_CONNECTION_TIMEOUT = 600;
37 #ifdef HAS_LTH_GET_INT
38 static const unsigned int EC2_SESSION_TIMEOUT = environment::get_int("EC2_SESSION_TIMEOUT", 5000);
39 #else
40 static const unsigned int EC2_SESSION_TIMEOUT = 5000;
41 #endif
42
43 static void query_metadata_value(lth_curl::client& cli, map_value& value, string const& url, string const& name, string const& http_langs)
44 {
45 lth_curl::request req(url + name);
46 req.connection_timeout(EC2_CONNECTION_TIMEOUT);
47 req.timeout(EC2_SESSION_TIMEOUT);
48 if (!http_langs.empty())
49 req.add_header("Accept-Language", http_langs);
50
51 auto response = cli.get(req);
52 if (response.status_code() != 200) {
53 LOG_DEBUG("request for {1} returned a status code of {2}.", req.url(), response.status_code());
54 return;
55 }
56
57 auto body = response.body();
58 boost::trim(body);
59
60 value.add(name, make_value<string_value>(move(body)));
61 }
62
63 static void query_metadata(lth_curl::client& cli, map_value& value, string const& url, string const& http_langs)
64 {
65 // Stores the metadata names to filter out
66 static set<string> filter = {
67 "security-credentials/"
68 };
69
70 lth_curl::request req(url);
71 req.connection_timeout(EC2_CONNECTION_TIMEOUT);
72 req.timeout(EC2_SESSION_TIMEOUT);
73 if (!http_langs.empty())
74 req.add_header("Accept-Language", http_langs);
75
76 auto response = cli.get(req);
77 if (response.status_code() != 200) {
78 LOG_DEBUG("request for {1} returned a status code of {2}.", req.url(), response.status_code());
79 return;
80 }
81 util::each_line(response.body(), [&](string& name) {
82 if (name.empty()) {
83 return true;
84 }
85
86 static boost::regex array_regex("^(\\d+)=.*$");
87
88 string index;
89 if (re_search(name, array_regex, &index)) {
90 name = index + "/";
91 }
92
93 // Check the filter for this name
94 if (filter.count(name) != 0) {
95 return true;
96 }
97
98 // If the name does not end with a '/', then it is a key name; request the value
99 if (name.back() != '/') {
100 query_metadata_value(cli, value, url, name, http_langs);
101 return true;
102 }
103
104 // Otherwise, this is a category; recurse down it
105 auto child = make_value<map_value>();
106 query_metadata(cli, *child, url + name, http_langs);
107 trim_right_if(name, boost::is_any_of("/"));
108 value.add(move(name), move(child));
109 return true;
110 });
111 }
112 #endif
113
114 void ec2_resolver::resolve(collection& facts)
115 {
116 #ifndef USE_CURL
117 LOG_INFO("EC2 facts are unavailable: facter was built without libcurl support.");
118 return;
119 #else
120 auto virtualization = facts.get<string_value>(fact::virtualization);
121 if (!virtualization || (virtualization->value() != vm::kvm && !boost::starts_with(virtualization->value(), "xen"))) {
122 LOG_DEBUG("EC2 facts are unavailable: not running under an EC2 instance.");
123 return;
124 }
125
126 LOG_DEBUG("querying EC2 instance metadata at {1}.", EC2_METADATA_ROOT_URL);
127
128 lth_curl::client cli;
129 auto metadata = make_value<map_value>();
130
131 try
132 {
133 query_metadata(cli, *metadata, EC2_METADATA_ROOT_URL, http_langs());
134
135 if (!metadata->empty()) {
136 facts.add(fact::ec2_metadata, move(metadata));
137 }
138 }
139 catch (lth_curl::http_request_exception& ex) {
140 if (ex.req().url() == EC2_METADATA_ROOT_URL) {
141 // The very first query failed; most likely not an EC2 instance
142 LOG_DEBUG("EC2 facts are unavailable: not running under an EC2 instance or EC2 is not responding in a timely manner.");
143 LOG_TRACE("EC2 metadata request failed: {1}", ex.what());
144 return;
145 }
146 LOG_ERROR("EC2 metadata request failed: {1}", ex.what());
147 }
148 catch (runtime_error& ex) {
149 LOG_ERROR("EC2 metadata request failed: {1}", ex.what());
150 }
151
152 LOG_DEBUG("querying EC2 instance user data at {1}.", EC2_USERDATA_ROOT_URL);
153
154 try {
155 lth_curl::request req(EC2_USERDATA_ROOT_URL);
156 req.connection_timeout(EC2_CONNECTION_TIMEOUT);
157 req.timeout(EC2_SESSION_TIMEOUT);
158 if (!http_langs().empty())
159 req.add_header("Accept-Language", http_langs());
160
161 auto response = cli.get(req);
162 if (response.status_code() != 200) {
163 LOG_DEBUG("request for {1} returned a status code of {2}.", req.url(), response.status_code());
164 return;
165 }
166
167 facts.add(fact::ec2_userdata, make_value<string_value>(response.body()));
168 } catch (runtime_error& ex) {
169 LOG_ERROR("EC2 user data request failed: {1}", ex.what());
170 }
171 #endif
172 }
173
174 bool ec2_resolver::is_blockable() const {
175 return true;
176 }
177
178 }}} // namespace facter::facts::resolvers
+0
-119
lib/src/facts/resolvers/filesystem_resolver.cc less more
0 #include <internal/facts/resolvers/filesystem_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/array_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <facter/util/string.hpp>
7 #include <boost/algorithm/string.hpp>
8
9 using namespace std;
10 using namespace facter::util;
11
12 namespace facter { namespace facts { namespace resolvers {
13
14 filesystem_resolver::filesystem_resolver() :
15 resolver(
16 "file system",
17 {
18 fact::mountpoints,
19 fact::filesystems,
20 fact::partitions
21 })
22 {
23 }
24
25 void filesystem_resolver::resolve(collection& facts)
26 {
27 auto data = collect_data(facts);
28
29 // Populate the mountpoints fact
30 if (!data.mountpoints.empty()) {
31 auto mountpoints = make_value<map_value>();
32 for (auto& mountpoint : data.mountpoints) {
33 if (mountpoint.name.empty()) {
34 continue;
35 }
36
37 uint64_t used = mountpoint.size - mountpoint.free;
38 uint64_t total = used + mountpoint.available;
39
40 auto value = make_value<map_value>();
41
42 if (!mountpoint.filesystem.empty()) {
43 value->add("filesystem", make_value<string_value>(move(mountpoint.filesystem)));
44 }
45 if (!mountpoint.device.empty()) {
46 value->add("device", make_value<string_value>(move(mountpoint.device)));
47 }
48 value->add("size_bytes", make_value<integer_value>(mountpoint.size));
49 value->add("size", make_value<string_value>(si_string(mountpoint.size)));
50 value->add("available_bytes", make_value<integer_value>(mountpoint.available));
51 value->add("available", make_value<string_value>(si_string(mountpoint.available)));
52 value->add("used_bytes", make_value<integer_value>(used));
53 value->add("used", make_value<string_value>(si_string(used)));
54 value->add("capacity", make_value<string_value>(percentage(used, total)));
55
56 if (!mountpoint.options.empty()) {
57 auto options = make_value<array_value>();
58 for (auto &option : mountpoint.options) {
59 options->add(make_value<string_value>(move(option)));
60 }
61 value->add("options", move(options));
62 }
63
64 mountpoints->add(move(mountpoint.name), move(value));
65 }
66 facts.add(fact::mountpoints, move(mountpoints));
67 }
68
69 // Populate the filesystems fact
70 if (!data.filesystems.empty()) {
71 facts.add(fact::filesystems, make_value<string_value>(boost::join(data.filesystems, ",")));
72 }
73
74 // Populate the partitions fact
75 if (!data.partitions.empty()) {
76 auto partitions = make_value<map_value>();
77 for (auto& partition : data.partitions) {
78 if (partition.name.empty()) {
79 continue;
80 }
81
82 auto value = make_value<map_value>();
83
84 if (!partition.filesystem.empty()) {
85 value->add("filesystem", make_value<string_value>(move(partition.filesystem)));
86 }
87 if (!partition.mount.empty()) {
88 value->add("mount", make_value<string_value>(move(partition.mount)));
89 }
90 if (!partition.label.empty()) {
91 value->add("label", make_value<string_value>(move(partition.label)));
92 }
93 if (!partition.partition_label.empty()) {
94 value->add("partlabel", make_value<string_value>(move(partition.partition_label)));
95 }
96 if (!partition.uuid.empty()) {
97 value->add("uuid", make_value<string_value>(move(partition.uuid)));
98 }
99 if (!partition.partition_uuid.empty()) {
100 value->add("partuuid", make_value<string_value>(move(partition.partition_uuid)));
101 }
102 if (!partition.backing_file.empty()) {
103 value->add("backing_file", make_value<string_value>(move(partition.backing_file)));
104 }
105 value->add("size_bytes", make_value<integer_value>(partition.size));
106 value->add("size", make_value<string_value>(si_string(partition.size)));
107
108 partitions->add(move(partition.name), move(value));
109 }
110 facts.add(fact::partitions, move(partitions));
111 }
112 }
113
114 bool filesystem_resolver::is_blockable() const {
115 return true;
116 }
117
118 }}} // namespace facter::facts::resolvers
+0
-23
lib/src/facts/resolvers/fips_resolver.cc less more
0 #include <internal/facts/resolvers/fips_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5
6 using namespace std;
7 using namespace facter::facts;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 fips_resolver::fips_resolver() :
12 resolver("fips", {fact::fips_enabled})
13 {
14 }
15
16 void fips_resolver::resolve(collection& facts)
17 {
18 auto data = collect_data(facts);
19 facts.add(fact::fips_enabled, make_value<boolean_value>(data.is_fips_mode_enabled));
20 }
21
22 }}} // namespace facter::facts::resolvers
+0
-277
lib/src/facts/resolvers/gce_resolver.cc less more
0 #include <internal/facts/resolvers/gce_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/array_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include <facter/facts/vm.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <leatherman/locale/locale.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <rapidjson/reader.h>
11 #include <rapidjson/error/en.h>
12 #include <stack>
13 #include <tuple>
14 #include <stdexcept>
15
16 // Mark string for translation (alias for leatherman::locale::format)
17 using leatherman::locale::_;
18
19 #ifdef USE_CURL
20 #include <leatherman/curl/client.hpp>
21 #include <leatherman/curl/request.hpp>
22 #include <leatherman/curl/response.hpp>
23 namespace lth_curl = leatherman::curl;
24 #endif
25
26 using namespace std;
27 using namespace rapidjson;
28
29 namespace facter { namespace facts { namespace resolvers {
30
31 #ifdef USE_CURL
32 static const unsigned int GCE_CONNECTION_TIMEOUT = 1000;
33 static const unsigned int GCE_SESSION_TIMEOUT = 5000;
34 #endif
35
36 // Helper event handler for parsing JSON data
37 struct gce_event_handler
38 {
39 explicit gce_event_handler(map_value& root) :
40 _initialized(false),
41 _root(root)
42 {
43 }
44
45 bool Null()
46 {
47 check_initialized();
48 _key.clear();
49 return true;
50 }
51
52 bool Bool(bool b)
53 {
54 add_value(make_value<boolean_value>(b));
55 return true;
56 }
57
58 bool Int(int i)
59 {
60 Uint64(static_cast<uint64_t>(i));
61 return true;
62 }
63
64 bool Uint(unsigned int i)
65 {
66 Uint64(static_cast<uint64_t>(i));
67 return true;
68 }
69
70 bool Int64(int64_t i)
71 {
72 Uint64(static_cast<uint64_t>(i));
73 return true;
74 }
75
76 bool Uint64(uint64_t i)
77 {
78 add_value(make_value<integer_value>(i));
79 return true;
80 }
81
82 bool Double(double d)
83 {
84 add_value(make_value<double_value>(d));
85 return true;
86 }
87
88 bool String(char const* s, SizeType len, bool copy)
89 {
90 string value(s, len);
91
92 // See https://cloud.google.com/compute/docs/metadata for information about these values
93 if (_key == "sshKeys") {
94 // The sshKeys attribute is a list of SSH keys delimited by newline characters
95 // Turn this value into an array for the fact
96
97 // Trim any whitespace off the string before splitting
98 boost::trim(value);
99
100 // Split at newlines and transform into an array value
101 vector<string> keys;
102 boost::split(keys, value, boost::is_any_of("\n"), boost::token_compress_on);
103
104 auto array = make_value<array_value>();
105 for (auto& key : keys) {
106 array->add(make_value<string_value>(move(key)));
107 }
108 add_value(move(array));
109 return true;
110 }
111 if (_key == "image" || _key == "machineType" || _key == "zone" || _key == "network") {
112 // These values are fully qualified, but we only want to display the last name
113 // Therefore, use only what comes after the last / character
114 auto pos = value.find_last_of('/');
115 if (pos != string::npos) {
116 value = value.substr(pos + 1);
117 }
118 }
119
120 add_value(make_value<string_value>(move(value)));
121 return true;
122 }
123
124 bool Key(const char* str, SizeType length, bool copy)
125 {
126 check_initialized();
127 _key = string(str, length);
128 return true;
129 }
130
131 bool StartObject()
132 {
133 if (!_initialized) {
134 _initialized = true;
135 return true;
136 }
137
138 // Push a map onto the stack
139 _stack.emplace(make_tuple(move(_key), make_value<map_value>()));
140 return true;
141 }
142
143 bool EndObject(SizeType count)
144 {
145 // Check to see if the stack is empty since we don't push for the top-level object
146 if (_stack.empty()) {
147 return true;
148 }
149
150 // Pop the data off the stack
151 auto top = move(_stack.top());
152 _stack.pop();
153
154 // Restore the key and add the value
155 _key = move(get<0>(top));
156 add_value(move(get<1>(top)));
157 return true;
158 }
159
160 bool StartArray()
161 {
162 check_initialized();
163
164 // Push an array onto the stack
165 _stack.emplace(make_tuple(move(_key), make_value<array_value>()));
166 return true;
167 }
168
169 bool EndArray(SizeType count)
170 {
171 // Pop the data off the stack
172 auto top = move(_stack.top());
173 _stack.pop();
174
175 // Restore the key and add the value
176 _key = move(get<0>(top));
177 add_value(move(get<1>(top)));
178 return true;
179 }
180
181 private:
182 template <typename T> void add_value(unique_ptr<T>&& val)
183 {
184 check_initialized();
185
186 value* current = nullptr;
187
188 if (_stack.empty()) {
189 current = &_root;
190 } else {
191 current = get<1>(_stack.top()).get();
192 }
193
194 auto map = dynamic_cast<map_value*>(current);
195 if (map) {
196 if (_key.empty()) {
197 throw external::external_fact_exception(_("expected non-empty key in object."));
198 }
199 map->add(move(_key), move(val));
200 return;
201 }
202 auto array = dynamic_cast<array_value*>(current);
203 if (array) {
204 array->add(move(val));
205 return;
206 }
207 }
208
209 void check_initialized() const
210 {
211 if (!_initialized) {
212 throw external::external_fact_exception(_("expected document to contain an object."));
213 }
214 }
215
216 bool _initialized;
217 map_value& _root;
218 string _key;
219 stack<tuple<string, unique_ptr<value>>> _stack;
220 };
221
222 gce_resolver::gce_resolver() :
223 resolver("GCE", { fact::gce })
224 {
225 }
226
227 void gce_resolver::resolve(collection& facts)
228 {
229 auto virtualization = facts.get<string_value>(fact::virtualization);
230 if (!virtualization || virtualization->value() != vm::gce) {
231 LOG_DEBUG("not running under a GCE instance.");
232 return;
233 }
234 #ifndef USE_CURL
235 LOG_INFO("GCE facts are unavailable: facter was built without libcurl support.");
236 return;
237 #else
238 LOG_DEBUG("querying GCE metadata.");
239
240 try
241 {
242 lth_curl::request req("http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=json");
243 req.add_header("Metadata-Flavor", "Google");
244 req.connection_timeout(GCE_CONNECTION_TIMEOUT);
245 req.timeout(GCE_SESSION_TIMEOUT);
246 if (!http_langs().empty())
247 req.add_header("Accept-Language", http_langs());
248
249 lth_curl::client cli;
250 auto response = cli.get(req);
251 if (response.status_code() != 200) {
252 LOG_DEBUG("request for {1} returned a status code of {2}.", req.url(), response.status_code());
253 return;
254 }
255
256 auto data = make_value<map_value>();
257
258 Reader reader;
259 StringStream ss(response.body().c_str());
260 gce_event_handler handler(*data);
261 auto result = reader.Parse(ss, handler);
262 if (!result) {
263 LOG_ERROR("failed to parse GCE metadata: {1}.", GetParseError_En(result.Code()));
264 return;
265 }
266
267 if (!data->empty()) {
268 facts.add(fact::gce, move(data));
269 }
270 } catch (runtime_error& ex) {
271 LOG_ERROR("GCE metadata request failed: {1}", ex.what());
272 }
273 #endif
274 }
275
276 }}} // namespace facter::facts::resolvers
+0
-76
lib/src/facts/resolvers/hypervisors_resolver.cc less more
0 #include <internal/facts/resolvers/hypervisors_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #ifdef USE_WHEREAMI
3 #include <whereami/whereami.hpp>
4 #endif
5
6 using namespace std;
7 using namespace facter::facts;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 using value_ptr = std::unique_ptr<value>;
12
13 /**
14 * Raw pointers must be extracted here because old versions of boost don't allow visitors to return move-only types.
15 */
16 struct metadata_value_visitor : public boost::static_visitor<value*>
17 {
18 value* operator()(int value) const
19 {
20 return make_value<integer_value>(value).release();
21 }
22
23 value* operator()(const std::string& value) const
24 {
25 return make_value<string_value>(value).release();
26 }
27
28 value* operator()(bool value) const
29 {
30 return make_value<boolean_value>(value).release();
31 }
32 };
33
34 void hypervisors_resolver_base::resolve(collection& facts)
35 {
36 auto data = collect_data(facts);
37 auto hypervisors = make_value<map_value>();
38
39 for (auto const& hypervisor_pair : data) {
40 auto hypervisor_metadata = make_value<map_value>();
41
42 for (auto const& metadata_pair : hypervisor_pair.second) {
43 value* the_value = boost::apply_visitor(metadata_value_visitor(), metadata_pair.second);
44 hypervisor_metadata->add(
45 metadata_pair.first,
46 unique_ptr<value>(the_value));
47 }
48
49 hypervisors->add(hypervisor_pair.first, move(hypervisor_metadata));
50 }
51
52 if (!hypervisors->empty()) {
53 facts.add(fact::hypervisors, move(hypervisors));
54 }
55 }
56
57 #ifdef USE_WHEREAMI
58 hypervisor_data hypervisors_resolver::collect_data(collection& facts)
59 {
60 hypervisor_data data;
61 auto results = whereami::hypervisors();
62
63 for (auto const& res : results) {
64 data.insert({res.name(), res.metadata()});
65 }
66
67 return data;
68 }
69 #endif
70
71 bool hypervisors_resolver_base::is_blockable() const {
72 return true;
73 }
74
75 }}} // namespace facter::facts::resolvers
+0
-50
lib/src/facts/resolvers/identity_resolver.cc less more
0 #include <internal/facts/resolvers/identity_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 identity_resolver::identity_resolver() :
11 resolver(
12 "id",
13 {
14 fact::id,
15 fact::gid,
16 fact::identity
17 })
18 {
19 }
20
21 void identity_resolver::resolve(collection &facts)
22 {
23 auto data = collect_data(facts);
24
25 auto identity = make_value<map_value>();
26 if (!data.user_name.empty()) {
27 facts.add(fact::id, make_value<string_value>(data.user_name, true));
28 identity->add("user", make_value<string_value>(move(data.user_name)));
29 }
30 if (data.user_id) {
31 identity->add("uid", make_value<integer_value>(*data.user_id));
32 }
33 if (!data.group_name.empty()) {
34 facts.add(fact::gid, make_value<string_value>(data.group_name, true));
35 identity->add("group", make_value<string_value>(move(data.group_name)));
36 }
37 if (data.group_id) {
38 identity->add("gid", make_value<integer_value>(*data.group_id));
39 }
40 if (data.privileged) {
41 identity->add("privileged", make_value<boolean_value>(*data.privileged));
42 }
43
44 if (!identity->empty()) {
45 facts.add(fact::identity, move(identity));
46 }
47 }
48
49 }}} // facter::facts::resolvers
+0
-62
lib/src/facts/resolvers/kernel_resolver.cc less more
0 #include <internal/facts/resolvers/kernel_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/map_value.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/fact.hpp>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 kernel_resolver::kernel_resolver() :
11 resolver(
12 "kernel",
13 {
14 fact::kernel,
15 fact::kernel_version,
16 fact::kernel_release,
17 fact::kernel_major_version
18 })
19 {
20 }
21
22 void kernel_resolver::resolve(collection& facts)
23 {
24 auto data = collect_data(facts);
25 if (!data.name.empty()) {
26 facts.add(fact::kernel, make_value<string_value>(move(data.name)));
27 }
28
29 if (!data.release.empty()) {
30 facts.add(fact::kernel_release, make_value<string_value>(move(data.release)));
31 }
32
33 if (!data.version.empty()) {
34 string major, minor;
35 tie(major, minor) = parse_version(data.version);
36
37 if (!major.empty()) {
38 facts.add(fact::kernel_major_version, make_value<string_value>(move(major)));
39 }
40 if (!minor.empty()) {
41 // TODO: for use in a structured fact; no point adding a new flat fact for it
42 }
43
44 facts.add(fact::kernel_version, make_value<string_value>(move(data.version)));
45 }
46 }
47
48 tuple<string, string> kernel_resolver::parse_version(string const& version) const
49 {
50 auto pos = version.find('.');
51 if (pos != string::npos) {
52 auto second = version.find('.', pos + 1);
53 if (second != string::npos) {
54 pos = second;
55 }
56 return make_tuple(version.substr(0, pos), version.substr(pos + 1));
57 }
58 return make_tuple(move(version), string());
59 }
60
61 }}} // namespace facter::facts::resolvers
+0
-60
lib/src/facts/resolvers/ldom_resolver.cc less more
0 #include <internal/facts/resolvers/ldom_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5
6 using namespace std;
7 using namespace facter::facts;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 ldom_resolver::ldom_resolver() :
12 resolver(
13 "ldom",
14 {
15 fact::ldom,
16 },
17 {
18 string("^ldom_"),
19 })
20
21 {
22 }
23
24 void ldom_resolver::resolve(collection& facts)
25 {
26 auto data = collect_data(facts);
27
28 if (!data.ldom.empty()) {
29 auto ldom = make_value<map_value>();
30
31 for (auto& sub_key : data.ldom) {
32 if (sub_key.values.size() == 0) {
33 continue;
34
35 } else if (sub_key.values.size() == 1) {
36 string key = sub_key.values.begin()->first;
37 string value = sub_key.values.begin()->second;
38
39 ldom->add(key, make_value<string_value>(value));
40 facts.add("ldom_" + key, make_value<string_value>(move(value), true));
41
42 } else {
43 // If we have multiple sub key values, insert a map into the structured fact to contain them.
44 auto sub_value = make_value<map_value>();
45
46 for (auto& kv : sub_key.values) {
47 sub_value->add(kv.first, make_value<string_value>(kv.second));
48 facts.add("ldom_" + sub_key.key + "_" + move(kv.first), make_value<string_value>(move(kv.second), true));
49 }
50
51 ldom->add(sub_key.key, move(sub_value));
52 }
53 }
54
55 facts.add(fact::ldom, move(ldom));
56 }
57 }
58
59 }}} // namespace facter::facts
+0
-36
lib/src/facts/resolvers/load_average_resolver.cc less more
0 #include <internal/facts/resolvers/load_average_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5
6 using namespace std;
7
8 namespace facter { namespace facts { namespace resolvers {
9
10 load_average_resolver::load_average_resolver() :
11 resolver(
12 "load_average",
13 {
14 fact::load_averages,
15 }
16 )
17 {
18 }
19
20 void load_average_resolver::resolve(collection& facts)
21 {
22 /* Get the load averages */
23 auto averages = get_load_averages();
24 if (!averages) {
25 return;
26 }
27
28 auto value = make_value<map_value>();
29 value->add("1m", make_value<double_value>(get<0>(*averages)));
30 value->add("5m", make_value<double_value>(get<1>(*averages)));
31 value->add("15m", make_value<double_value>(get<2>(*averages)));
32 facts.add(fact::load_averages, move(value));
33 }
34
35 }}} // namespace facter::facts::resolvers
+0
-88
lib/src/facts/resolvers/memory_resolver.cc less more
0 #include <internal/facts/resolvers/memory_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/util/string.hpp>
6
7 using namespace std;
8 using namespace facter::util;
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 memory_resolver::memory_resolver() :
13 resolver(
14 "memory",
15 {
16 fact::memory,
17 fact::memoryfree,
18 fact::memoryfree_mb,
19 fact::memorysize,
20 fact::memorysize_mb,
21 fact::swapfree,
22 fact::swapfree_mb,
23 fact::swapsize,
24 fact::swapsize_mb,
25 fact::swapencrypted
26 })
27 {
28 }
29
30 void memory_resolver::resolve(collection& facts)
31 {
32 data result = collect_data(facts);
33
34 auto value = make_value<map_value>();
35
36 if (result.mem_total > 0) {
37 uint64_t mem_used = result.mem_total - result.mem_free;
38
39 auto stats = make_value<map_value>();
40 stats->add("total", make_value<string_value>(si_string(result.mem_total)));
41 stats->add("total_bytes", make_value<integer_value>(result.mem_total));
42 stats->add("used", make_value<string_value>(si_string(mem_used)));
43 stats->add("used_bytes", make_value<integer_value>(mem_used));
44 stats->add("available", make_value<string_value>(si_string(result.mem_free)));
45 stats->add("available_bytes", make_value<integer_value>(result.mem_free));
46 stats->add("capacity", make_value<string_value>(percentage(mem_used, result.mem_total)));
47 value->add("system", move(stats));
48
49 // Add hidden facts
50 facts.add(fact::memoryfree, make_value<string_value>(si_string(result.mem_free), true));
51 facts.add(fact::memoryfree_mb, make_value<double_value>(result.mem_free / (1024.0 * 1024.0), true));
52 facts.add(fact::memorysize, make_value<string_value>(si_string(result.mem_total), true));
53 facts.add(fact::memorysize_mb, make_value<double_value>(result.mem_total / (1024.0 * 1024.0), true));
54 }
55
56 if (result.swap_total > 0) {
57 uint64_t swap_used = result.swap_total - result.swap_free;
58
59 auto stats = make_value<map_value>();
60 stats->add("total", make_value<string_value>(si_string(result.swap_total)));
61 stats->add("total_bytes", make_value<integer_value>(result.swap_total));
62 stats->add("used", make_value<string_value>(si_string(swap_used)));
63 stats->add("used_bytes", make_value<integer_value>(swap_used));
64 stats->add("available", make_value<string_value>(si_string(result.swap_free)));
65 stats->add("available_bytes", make_value<integer_value>(result.swap_free));
66 stats->add("capacity", make_value<string_value>(percentage(swap_used, result.swap_total)));
67 if (result.swap_encryption != encryption_status::unknown) {
68 stats->add("encrypted", make_value<boolean_value>(result.swap_encryption == encryption_status::encrypted));
69 }
70 value->add("swap", move(stats));
71
72 // Add hidden facts
73 facts.add(fact::swapfree, make_value<string_value>(si_string(result.swap_free), true));
74 facts.add(fact::swapfree_mb, make_value<double_value>(result.swap_free / (1024.0 * 1024.0), true));
75 facts.add(fact::swapsize, make_value<string_value>(si_string(result.swap_total), true));
76 facts.add(fact::swapsize_mb, make_value<double_value>(result.swap_total / (1024.0 * 1024.0), true));
77 if (result.swap_encryption != encryption_status::unknown) {
78 facts.add(fact::swapencrypted, make_value<boolean_value>(result.swap_encryption == encryption_status::encrypted, true));
79 }
80 }
81
82 if (!value->empty()) {
83 facts.add(fact::memory, move(value));
84 }
85 }
86
87 }}} // namespace facter::facts::resolvers
+0
-333
lib/src/facts/resolvers/networking_resolver.cc less more
0 #include <internal/facts/resolvers/networking_resolver.hpp>
1 #include <facter/facts/fact.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/array_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <boost/format.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <boost/asio/ip/address_v6.hpp>
10 #include <sstream>
11
12 using namespace std;
13
14 namespace facter { namespace facts { namespace resolvers {
15
16 networking_resolver::networking_resolver() :
17 resolver(
18 "networking",
19 {
20 fact::networking,
21 fact::hostname,
22 fact::ipaddress,
23 fact::ipaddress6,
24 fact::netmask,
25 fact::netmask6,
26 fact::network,
27 fact::network6,
28 fact::scope6,
29 fact::macaddress,
30 fact::interfaces,
31 fact::domain,
32 fact::fqdn,
33 fact::dhcp_servers,
34 },
35 {
36 string("^") + fact::ipaddress + "_",
37 string("^") + fact::ipaddress6 + "_",
38 string("^") + fact::mtu + "_",
39 string("^") + fact::netmask + "_",
40 string("^") + fact::netmask6 + "_",
41 string("^") + fact::network + "_",
42 string("^") + fact::network6 + "_",
43 string("^") + fact::scope6 + "_",
44 string("^") + fact::macaddress + "_",
45 })
46 {
47 }
48
49 void networking_resolver::resolve(collection& facts)
50 {
51 auto data = collect_data(facts);
52
53 // Some queries, such as /etc/resolv.conf, can return domains with a trailing dot (.).
54 // We want to strip the trailing dot, as it is not useful as a valid domain or fqdn.
55 boost::trim_right_if(data.domain, boost::is_any_of("."));
56
57 // If no FQDN, set it to the hostname + domain
58 if (!data.hostname.empty() && data.fqdn.empty()) {
59 data.fqdn = data.hostname + (data.domain.empty() ? "" : ".") + data.domain;
60 }
61
62 // If no primary interface was found, default to the first interface with a valid address
63 if (data.primary_interface.empty()) {
64 LOG_DEBUG("no primary interface found: using the first interface with an assigned address as the primary interface.");
65 auto primary = find_primary_interface(data.interfaces);
66 if (primary) {
67 data.primary_interface = primary->name;
68 }
69 }
70
71 auto networking = make_value<map_value>();
72
73 // Add the interface data
74 ostringstream interface_names;
75 auto dhcp_servers = make_value<map_value>(true);
76 auto interfaces = make_value<map_value>();
77 for (auto& interface : data.interfaces) {
78 bool primary = interface.name == data.primary_interface;
79 auto value = make_value<map_value>();
80
81 // Add the ipv4 bindings
82 add_bindings(interface, primary, true, facts, *networking, *value);
83
84 // Add the ipv6 bindings
85 add_bindings(interface, primary, false, facts, *networking, *value);
86
87 // Add the MAC address
88 if (!interface.macaddress.empty()) {
89 facts.add(string(fact::macaddress) + "_" + interface.name, make_value<string_value>(interface.macaddress, true));
90 if (primary) {
91 facts.add(fact::macaddress, make_value<string_value>(interface.macaddress, true));
92 networking->add("mac", make_value<string_value>(interface.macaddress));
93 }
94 value->add("mac", make_value<string_value>(move(interface.macaddress)));
95 }
96 // Add the DHCP server
97 if (!interface.dhcp_server.empty()) {
98 if (primary) {
99 dhcp_servers->add("system", make_value<string_value>(interface.dhcp_server));
100 networking->add("dhcp", make_value<string_value>(interface.dhcp_server));
101 }
102 dhcp_servers->add(string(interface.name), make_value<string_value>(interface.dhcp_server));
103 value->add("dhcp", make_value<string_value>(move(interface.dhcp_server)));
104 }
105 // Add the interface MTU
106 if (interface.mtu) {
107 facts.add(string(fact::mtu) + "_" + interface.name, make_value<integer_value>(*interface.mtu, true));
108 if (primary) {
109 networking->add("mtu", make_value<integer_value>(*interface.mtu));
110 }
111 value->add("mtu", make_value<integer_value>(*interface.mtu));
112 }
113
114 // Add the interface to the list of names
115 if (interface_names.tellp() != 0) {
116 interface_names << ",";
117 }
118 interface_names << interface.name;
119
120 interfaces->add(move(interface.name), move(value));
121 }
122
123 // Add top-level network data
124 if (!data.hostname.empty()) {
125 facts.add(fact::hostname, make_value<string_value>(data.hostname, true));
126 networking->add("hostname", make_value<string_value>(move(data.hostname)));
127 }
128 if (!data.domain.empty()) {
129 facts.add(fact::domain, make_value<string_value>(data.domain, true));
130 networking->add("domain", make_value<string_value>(move(data.domain)));
131 }
132 if (!data.fqdn.empty()) {
133 facts.add(fact::fqdn, make_value<string_value>(data.fqdn, true));
134 networking->add("fqdn", make_value<string_value>(move(data.fqdn)));
135 }
136 if (!data.primary_interface.empty()) {
137 networking->add("primary", make_value<string_value>(move(data.primary_interface)));
138 }
139
140 if (interface_names.tellp() != 0) {
141 facts.add(fact::interfaces, make_value<string_value>(interface_names.str(), true));
142 }
143
144 if (!dhcp_servers->empty()) {
145 facts.add(fact::dhcp_servers, move(dhcp_servers));
146 }
147
148 if (!interfaces->empty()) {
149 networking->add("interfaces", move(interfaces));
150 }
151 if (!networking->empty()) {
152 facts.add(fact::networking, move(networking));
153 }
154 }
155
156 string networking_resolver::get_scope(const string& address)
157 {
158 std::ostringstream ostream;
159 boost::asio::ip::address_v6 addr;
160 try {
161 addr = boost::asio::ip::address_v6::from_string(address);
162 } catch (boost::exception &e) {
163 return ostream.str();
164 }
165
166 if (addr.is_v4_compatible()) {
167 ostream << "compat,";
168 }
169
170 if (addr.is_link_local())
171 ostream << "link";
172 else if (addr.is_site_local())
173 ostream << "site";
174 else if (addr.is_loopback())
175 ostream << "host";
176 else
177 ostream << "global";
178
179 return ostream.str();
180 }
181
182 string networking_resolver::macaddress_to_string(uint8_t const* bytes, uint8_t byte_count)
183 {
184 if (!bytes || (byte_count != 6 && byte_count != 20)) {
185 return {};
186 }
187
188 // Ignore MAC address "0"
189 bool nonzero = false;
190 for (size_t i = 0; i < byte_count; ++i) {
191 if (bytes[i] != 0) {
192 nonzero = true;
193 break;
194 }
195 }
196 if (!nonzero) {
197 return {};
198 }
199
200 if (byte_count == 6) {
201 return (boost::format("%02x:%02x:%02x:%02x:%02x:%02x") %
202 static_cast<int>(bytes[0]) % static_cast<int>(bytes[1]) %
203 static_cast<int>(bytes[2]) % static_cast<int>(bytes[3]) %
204 static_cast<int>(bytes[4]) % static_cast<int>(bytes[5])).str();
205 } else if (byte_count == 20) {
206 return (boost::format("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x") %
207 static_cast<int>(bytes[0]) % static_cast<int>(bytes[1]) %
208 static_cast<int>(bytes[2]) % static_cast<int>(bytes[3]) %
209 static_cast<int>(bytes[4]) % static_cast<int>(bytes[5]) %
210 static_cast<int>(bytes[6]) % static_cast<int>(bytes[7]) %
211 static_cast<int>(bytes[8]) % static_cast<int>(bytes[9]) %
212 static_cast<int>(bytes[10]) % static_cast<int>(bytes[11]) %
213 static_cast<int>(bytes[12]) % static_cast<int>(bytes[13]) %
214 static_cast<int>(bytes[14]) % static_cast<int>(bytes[15]) %
215 static_cast<int>(bytes[16]) % static_cast<int>(bytes[17]) %
216 static_cast<int>(bytes[18]) % static_cast<int>(bytes[19])).str();
217 } else {
218 return {};
219 }
220 }
221
222 bool networking_resolver::ignored_ipv4_address(string const& addr)
223 {
224 // Excluding localhost and 169.254.x.x in Windows - this is the DHCP APIPA, meaning that if the node cannot
225 // get an ip address from the dhcp server, it auto-assigns a private ip address
226 return addr.empty() || boost::starts_with(addr, "127.") || boost::starts_with(addr, "169.254.");
227 }
228
229 bool networking_resolver::ignored_ipv6_address(string const& addr)
230 {
231 return addr.empty() || addr == "::1" || boost::starts_with(addr, "fe80");
232 }
233
234 networking_resolver::binding const* networking_resolver::find_default_binding(vector<binding> const& bindings, function<bool(string const&)> const& ignored)
235 {
236 for (auto& binding : bindings) {
237 if (!ignored(binding.address)) {
238 return &binding;
239 }
240 }
241 return bindings.empty() ? nullptr : &bindings.front();
242 }
243
244 void networking_resolver::add_bindings(interface& iface, bool primary, bool ipv4, collection& facts, map_value& networking, map_value& iface_value)
245 {
246 auto ip_fact = ipv4 ? fact::ipaddress : fact::ipaddress6;
247 auto ip_name = ipv4 ? "ip" : "ip6";
248 auto netmask_fact = ipv4 ? fact::netmask : fact::netmask6;
249 auto netmask_name = ipv4 ? "netmask" : "netmask6";
250 auto network_fact = ipv4 ? fact::network : fact::network6;
251 auto network_name = ipv4 ? "network" : "network6";
252 auto& bindings = ipv4 ? iface.ipv4_bindings : iface.ipv6_bindings;
253 auto bindings_name = ipv4 ? "bindings" : "bindings6";
254 auto ignored = ipv4 ? &ignored_ipv4_address : &ignored_ipv6_address;
255
256 // Add the default binding to the collection and interface
257 auto binding = find_default_binding(bindings, ignored);
258 if (binding) {
259 if (!binding->address.empty()) {
260 facts.add(string(ip_fact) + "_" + iface.name, make_value<string_value>(binding->address, true));
261 iface_value.add(ip_name, make_value<string_value>(binding->address));
262 if (!ipv4) {
263 facts.add(string(fact::scope6) + "_" + iface.name, make_value<string_value>(get_scope(binding->address), true));
264 iface_value.add("scope6", make_value<string_value>(get_scope(binding->address)));
265 }
266 if (primary) {
267 facts.add(ip_fact, make_value<string_value>(binding->address, true));
268 networking.add(ip_name, make_value<string_value>(binding->address));
269 if (!ipv4) {
270 facts.add(fact::scope6, make_value<string_value>(get_scope(binding->address), true));
271 networking.add("scope6", make_value<string_value>(get_scope(binding->address)));
272 }
273 }
274 }
275 if (!binding->netmask.empty()) {
276 facts.add(string(netmask_fact) + "_" + iface.name, make_value<string_value>(binding->netmask, true));
277 if (primary) {
278 facts.add(netmask_fact, make_value<string_value>(binding->netmask, true));
279 networking.add(netmask_name, make_value<string_value>(binding->netmask));
280 }
281 iface_value.add(netmask_name, make_value<string_value>(binding->netmask));
282 }
283 if (!binding->network.empty()) {
284 facts.add(string(network_fact) + "_" + iface.name, make_value<string_value>(binding->network, true));
285 if (primary) {
286 facts.add(network_fact, make_value<string_value>(binding->network, true));
287 networking.add(network_name, make_value<string_value>(binding->network));
288 }
289 iface_value.add(network_name, make_value<string_value>(binding->network));
290 }
291 }
292
293 // Set the bindings in the interface
294 if (!bindings.empty()) {
295 auto bindings_value = make_value<array_value>();
296 for (auto& binding : bindings) {
297 auto binding_value = make_value<map_value>();
298 if (!binding.address.empty()) {
299 binding_value->add("address", make_value<string_value>(move(binding.address)));
300 }
301 if (!binding.netmask.empty()) {
302 binding_value->add("netmask", make_value<string_value>(move(binding.netmask)));
303 }
304 if (!binding.network.empty()) {
305 binding_value->add("network", make_value<string_value>(move(binding.network)));
306 }
307 if (!binding_value->empty()) {
308 bindings_value->add(move(binding_value));
309 }
310 }
311 iface_value.add(bindings_name, move(bindings_value));
312 }
313 }
314
315 networking_resolver::interface const* networking_resolver::find_primary_interface(vector<interface> const& interfaces)
316 {
317 for (auto const& interface : interfaces) {
318 for (auto const& binding : interface.ipv4_bindings) {
319 if (!ignored_ipv4_address(binding.address)) {
320 return &interface;
321 }
322 }
323 for (auto const& binding : interface.ipv6_bindings) {
324 if (!ignored_ipv6_address(binding.address)) {
325 return &interface;
326 }
327 }
328 }
329 return nullptr;
330 }
331
332 }}} // namespace facter::facts::posix
+0
-309
lib/src/facts/resolvers/operating_system_resolver.cc less more
0 #include <internal/facts/resolvers/operating_system_resolver.hpp>
1 #include <internal/util/versions.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/collection.hpp>
5 #include <facter/facts/fact.hpp>
6 #include <facter/facts/os.hpp>
7 #include <leatherman/util/regex.hpp>
8
9 using namespace std;
10 using namespace facter::util;
11 using namespace leatherman::util;
12
13 namespace facter { namespace facts { namespace resolvers {
14
15 operating_system_resolver::operating_system_resolver() :
16 resolver(
17 "operating system",
18 {
19 fact::os,
20 fact::operating_system,
21 fact::os_family,
22 fact::operating_system_release,
23 fact::operating_system_major_release,
24 fact::hardware_model,
25 fact::architecture,
26 fact::lsb_dist_id,
27 fact::lsb_dist_release,
28 fact::lsb_dist_codename,
29 fact::lsb_dist_description,
30 fact::lsb_dist_major_release,
31 fact::lsb_dist_minor_release,
32 fact::lsb_release,
33 fact::macosx_buildversion,
34 fact::macosx_productname,
35 fact::macosx_productversion,
36 fact::macosx_productversion_major,
37 fact::macosx_productversion_minor,
38 fact::windows_edition_id,
39 fact::windows_installation_type,
40 fact::windows_product_name,
41 fact::windows_release_id,
42 fact::windows_system32,
43 fact::selinux,
44 fact::selinux_enforced,
45 fact::selinux_policyversion,
46 fact::selinux_current_mode,
47 fact::selinux_config_mode,
48 fact::selinux_config_policy,
49 })
50 {
51 }
52
53 void operating_system_resolver::resolve(collection& facts)
54 {
55 auto data = collect_data(facts);
56
57 auto os = make_value<map_value>();
58 if (!data.family.empty()) {
59 facts.add(fact::os_family, make_value<string_value>(data.family, true));
60 os->add("family", make_value<string_value>(move(data.family)));
61 }
62
63 if (!data.release.empty()) {
64 auto value = make_value<map_value>();
65
66 // When we have no major or minor, do a 'trivial' parse of
67 // the release to try to get SOMETHING out of it.
68 if (data.minor.empty() && data.major.empty()) {
69 std::tie(data.major, data.minor) = versions::major_minor(data.release);
70 }
71
72 if (!data.major.empty()) {
73 facts.add(fact::operating_system_major_release, make_value<string_value>(data.major, true));
74 value->add("major", make_value<string_value>(move(data.major)));
75 }
76 if (!data.minor.empty()) {
77 value->add("minor", make_value<string_value>(move(data.minor)));
78 }
79
80 facts.add(fact::operating_system_release, make_value<string_value>(data.release, true));
81 value->add("full", make_value<string_value>(move(data.release)));
82
83 // Populate FreeBSD-specific data
84 if (!data.freebsd.branch.empty()) {
85 value->add("branch", make_value<string_value>(move(data.freebsd.branch)));
86 }
87
88 if (!data.freebsd.patchlevel.empty()) {
89 value->add("patchlevel", make_value<string_value>(move(data.freebsd.patchlevel)));
90 }
91
92 os->add("release", move(value));
93 }
94
95 // Add the OS hardware and architecture facts
96 if (!data.hardware.empty()) {
97 facts.add(fact::hardware_model, make_value<string_value>(data.hardware, true));
98 os->add("hardware", make_value<string_value>(move(data.hardware)));
99 }
100 if (!data.architecture.empty()) {
101 facts.add(fact::architecture, make_value<string_value>(data.architecture, true));
102 os->add("architecture", make_value<string_value>(move(data.architecture)));
103 }
104
105 // Add distro facts
106 auto distro = make_value<map_value>();
107 if (!data.distro.id.empty()) {
108 facts.add(fact::lsb_dist_id, make_value<string_value>(data.distro.id, true));
109 distro->add("id", make_value<string_value>(move(data.distro.id)));
110 }
111 if (!data.distro.codename.empty()) {
112 facts.add(fact::lsb_dist_codename, make_value<string_value>(data.distro.codename, true));
113 distro->add("codename", make_value<string_value>(move(data.distro.codename)));
114 }
115 if (!data.distro.description.empty()) {
116 facts.add(fact::lsb_dist_description, make_value<string_value>(data.distro.description, true));
117 distro->add("description", make_value<string_value>(move(data.distro.description)));
118 }
119 if (!data.distro.release.empty()) {
120 auto value = make_value<map_value>();
121
122 string major, minor;
123 tie(major, minor) = parse_distro(data.name, data.distro.release);
124
125 if (major.empty()) {
126 major = data.distro.release;
127 }
128 facts.add(fact::lsb_dist_major_release, make_value<string_value>(major, true));
129 value->add("major", make_value<string_value>(move(major)));
130
131 if (!minor.empty()) {
132 facts.add(fact::lsb_dist_minor_release, make_value<string_value>(minor, true));
133 value->add("minor", make_value<string_value>(move(minor)));
134 }
135
136 facts.add(fact::lsb_dist_release, make_value<string_value>(data.distro.release, true));
137 value->add("full", make_value<string_value>(move(data.distro.release)));
138 distro->add("release", move(value));
139 }
140 if (!data.specification_version.empty()) {
141 facts.add(fact::lsb_release, make_value<string_value>(data.specification_version, true));
142 distro->add("specification", make_value<string_value>(move(data.specification_version)));
143 }
144
145 // Add the name last since the above release parsing is dependent on it
146 if (!data.name.empty()) {
147 facts.add(fact::operating_system, make_value<string_value>(data.name, true));
148 os->add("name", make_value<string_value>(move(data.name)));
149 }
150
151 if (!distro->empty()) {
152 os->add("distro", move(distro));
153 }
154
155 // Populate OSX-specific data
156 auto macosx = make_value<map_value>();
157 if (!data.osx.product.empty()) {
158 facts.add(fact::macosx_productname, make_value<string_value>(data.osx.product, true));
159 macosx->add("product", make_value<string_value>(move(data.osx.product)));
160 }
161 if (!data.osx.build.empty()) {
162 facts.add(fact::macosx_buildversion, make_value<string_value>(data.osx.build, true));
163 macosx->add("build", make_value<string_value>(move(data.osx.build)));
164 }
165
166 if (!data.osx.version.empty()) {
167 // Look for the last '.' for major/minor
168 auto version = make_value<map_value>();
169 auto pos = data.osx.version.rfind('.');
170 if (pos != string::npos) {
171 string major = data.osx.version.substr(0, pos);
172 string minor = data.osx.version.substr(pos + 1);
173
174 // If the major doesn't have a '.', treat the entire version as the major
175 // and use a minor of "0"
176 if (major.find('.') == string::npos) {
177 major = data.osx.version;
178 minor = "0";
179 }
180
181 if (!major.empty()) {
182 facts.add(fact::macosx_productversion_major, make_value<string_value>(major, true));
183 version->add("major", make_value<string_value>(move(major)));
184 }
185 if (!minor.empty()) {
186 facts.add(fact::macosx_productversion_minor, make_value<string_value>(minor, true));
187 version->add("minor", make_value<string_value>(move(minor)));
188 }
189 }
190 facts.add(fact::macosx_productversion, make_value<string_value>(data.osx.version, true));
191 version->add("full", make_value<string_value>(move(data.osx.version)));
192 macosx->add("version", move(version));
193 }
194
195 if (!macosx->empty()) {
196 os->add("macosx", move(macosx));
197 }
198
199 // Populate Windows-specific data
200 auto windows = make_value<map_value>();
201 if (!data.win.edition_id.empty()) {
202 facts.add(fact::windows_edition_id, make_value<string_value>(data.win.edition_id, true));
203 windows->add("edition_id", make_value<string_value>(move(data.win.edition_id)));
204 }
205
206 if (!data.win.installation_type.empty()) {
207 facts.add(fact::windows_installation_type, make_value<string_value>(data.win.installation_type, true));
208 windows->add("installation_type", make_value<string_value>(move(data.win.installation_type)));
209 }
210
211 if (!data.win.product_name.empty()) {
212 facts.add(fact::windows_product_name, make_value<string_value>(data.win.product_name, true));
213 windows->add("product_name", make_value<string_value>(move(data.win.product_name)));
214 }
215
216 if (!data.win.release_id.empty()) {
217 facts.add(fact::windows_release_id, make_value<string_value>(data.win.release_id, true));
218 windows->add("release_id", make_value<string_value>(move(data.win.release_id)));
219 }
220
221 if (!data.win.system32.empty()) {
222 facts.add(fact::windows_system32, make_value<string_value>(data.win.system32, true));
223 windows->add("system32", make_value<string_value>(move(data.win.system32)));
224 }
225
226 if (!windows->empty()) {
227 os->add("windows", move(windows));
228 }
229
230 if (data.selinux.supported) {
231 auto selinux = make_value<map_value>();
232 facts.add(fact::selinux, make_value<boolean_value>(data.selinux.enabled, true));
233 selinux->add("enabled", make_value<boolean_value>(data.selinux.enabled));
234 if (data.selinux.enabled) {
235 facts.add(fact::selinux_enforced, make_value<boolean_value>(data.selinux.enforced, true));
236 selinux->add("enforced", make_value<boolean_value>(data.selinux.enforced));
237 if (!data.selinux.current_mode.empty()) {
238 facts.add(fact::selinux_current_mode, make_value<string_value>(data.selinux.current_mode, true));
239 selinux->add("current_mode", make_value<string_value>(move(data.selinux.current_mode)));
240 }
241 if (!data.selinux.config_mode.empty()) {
242 facts.add(fact::selinux_config_mode, make_value<string_value>(data.selinux.config_mode, true));
243 selinux->add("config_mode", make_value<string_value>(move(data.selinux.config_mode)));
244 }
245 if (!data.selinux.config_policy.empty()) {
246 facts.add(fact::selinux_config_policy, make_value<string_value>(data.selinux.config_policy, true));
247 selinux->add("config_policy", make_value<string_value>(move(data.selinux.config_policy)));
248 }
249 if (!data.selinux.policy_version.empty()) {
250 facts.add(fact::selinux_policyversion, make_value<string_value>(data.selinux.policy_version, true));
251 selinux->add("policy_version", make_value<string_value>(move(data.selinux.policy_version)));
252 }
253 }
254 os->add("selinux", move(selinux));
255 }
256
257 if (!os->empty()) {
258 facts.add(fact::os, move(os));
259 }
260 }
261
262 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
263 {
264 data result;
265
266 collect_kernel_data(facts, result);
267 collect_release_data(facts, result);
268
269 return result;
270 }
271
272 void operating_system_resolver::collect_kernel_data(collection& facts, data& result)
273 {
274 auto kernel = facts.get<string_value>(fact::kernel);
275 if (kernel) {
276 result.name = kernel->value();
277 result.family = kernel->value();
278 }
279 }
280
281 void operating_system_resolver::collect_release_data(collection& facts, data& result)
282 {
283 auto release = facts.get<string_value>(fact::kernel_release);
284 if (release) {
285 result.release = release->value();
286 }
287 }
288
289 tuple<string, string> operating_system_resolver::parse_distro(string const& name, string const& release)
290 {
291 // This implementation couples all known formats for lsb_dist and the Linux release versions. If that
292 // coupling becomes a problem, we'll probably need to push parsing distro major/minor to inheriting resolvers,
293 // as we've done for parsing the release version.
294 if (name != os::ubuntu) {
295 auto pos = release.find('.');
296 if (pos != string::npos) {
297 auto second = release.find('.', pos + 1);
298 return make_tuple(release.substr(0, pos), release.substr(pos + 1, second - (pos + 1)));
299 }
300 return make_tuple(release, string());
301 }
302
303 string major, minor;
304 re_search(release, boost::regex("(\\d+\\.\\d*)\\.?(\\d*)"), &major, &minor);
305 return make_tuple(move(major), move(minor));
306 }
307
308 }}} // namespace facter::facts::resolvers
+0
-25
lib/src/facts/resolvers/path_resolver.cc less more
0 #include <internal/facts/resolvers/path_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <leatherman/util/environment.hpp>
5
6 using namespace std;
7 using namespace leatherman::util;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 path_resolver::path_resolver() :
12 resolver("path", {fact::path})
13 {
14 }
15
16 void path_resolver::resolve(collection& facts)
17 {
18 string path_val;
19 if (environment::get("PATH", path_val)) {
20 facts.add(fact::path, make_value<string_value>(move(path_val)));
21 }
22 }
23
24 }}} // namespace facter::facts::resolvers
+0
-70
lib/src/facts/resolvers/processor_resolver.cc less more
0 #include <internal/facts/resolvers/processor_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/util/string.hpp>
7
8 using namespace std;
9 using namespace facter::util;
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 processor_resolver::processor_resolver() :
14 resolver(
15 "processor",
16 {
17 fact::processors,
18 fact::processor_count,
19 fact::physical_processor_count,
20 fact::hardware_isa,
21 },
22 {
23 string("^") + fact::processor + "[0-9]+$",
24 })
25 {
26 }
27
28 void processor_resolver::resolve(collection& facts)
29 {
30 auto data = collect_data(facts);
31
32 auto cpus = make_value<map_value>();
33
34 if (!data.isa.empty()) {
35 facts.add(fact::hardware_isa, make_value<string_value>(data.isa, true));
36 cpus->add("isa", make_value<string_value>(move(data.isa)));
37 }
38
39 if (data.logical_count > 0) {
40 facts.add(fact::processor_count, make_value<integer_value>(data.logical_count, true));
41 cpus->add("count", make_value<integer_value>(data.logical_count));
42 }
43
44 if (data.physical_count > 0) {
45 facts.add(fact::physical_processor_count, make_value<integer_value>(data.physical_count, true));
46 cpus->add("physicalcount", make_value<integer_value>(data.physical_count));
47 }
48
49 if (data.speed > 0) {
50 cpus->add("speed", make_value<string_value>(frequency(data.speed)));
51 }
52
53 auto models = make_value<array_value>();
54 int processor = 0;
55 for (auto& model : data.models) {
56 facts.add(fact::processor + to_string(processor++), make_value<string_value>(model, true));
57 models->add(make_value<string_value>(move(model)));
58 }
59
60 if (!models->empty()) {
61 cpus->add("models", move(models));
62 }
63
64 if (!cpus->empty()) {
65 facts.add(fact::processors, move(cpus));
66 }
67 }
68
69 }}} // namespace facter::facts::resolvers
+0
-109
lib/src/facts/resolvers/ruby_resolver.cc less more
0 #include <internal/facts/resolvers/ruby_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <leatherman/ruby/api.hpp>
6 #include <leatherman/logging/logging.hpp>
7
8 using namespace std;
9 using namespace leatherman::ruby;
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 ruby_resolver::ruby_resolver() :
14 resolver(
15 "ruby",
16 {
17 fact::ruby,
18 fact::rubyplatform,
19 fact::rubysitedir,
20 fact::rubyversion
21 })
22 {
23 }
24
25 static void ruby_fact_rescue(api const& rb, function<VALUE()> cb, string const& label)
26 {
27 // Use rescue, because Ruby exceptions don't call destructors. The callback cb shouldn't
28 // have any object construction/destruction in it.
29 rb.rescue(cb, [&](VALUE ex) {
30 LOG_ERROR("error while resolving ruby {1} fact: {2}", label, rb.exception_to_string(ex));
31 return 0;
32 });
33 }
34
35 static string get_platform(api const& rb)
36 {
37 string platform;
38 ruby_fact_rescue(rb, [&]() {
39 auto val = rb.lookup({"RUBY_PLATFORM"});
40 platform = rb.to_string(val);
41 return 0;
42 }, "platform");
43 return platform;
44 }
45
46 static string get_sitedir(api const& rb)
47 {
48 string sitedir;
49 ruby_fact_rescue(rb, [&]() {
50 rb.rb_require("rbconfig");
51 auto config = rb.lookup({"RbConfig", "CONFIG"});
52 auto val = rb.rb_hash_lookup(config, rb.utf8_value("sitelibdir"));
53 sitedir = rb.to_string(val);
54 return 0;
55 }, "sitedir");
56 return sitedir;
57 }
58
59 static string get_version(api const& rb)
60 {
61 string version;
62 ruby_fact_rescue(rb, [&]() {
63 auto val = rb.lookup({"RUBY_VERSION"});
64 version = rb.to_string(val);
65 return 0;
66 }, "version");
67 return version;
68 }
69
70 static void add(collection& f, map_value& d, string s, string hidden, string nested)
71 {
72 if (!s.empty()) {
73 f.add(move(hidden), make_value<string_value>(s, true));
74 d.add(move(nested), make_value<string_value>(move(s)));
75 }
76 }
77
78 ruby_resolver::data ruby_resolver::collect_data(collection& facts)
79 {
80 data rb_data;
81
82 auto const& ruby = api::instance();
83 if (!ruby.initialized()) {
84 return rb_data;
85 }
86
87 rb_data.platform = get_platform(ruby);
88 rb_data.sitedir = get_sitedir(ruby);
89 rb_data.version = get_version(ruby);
90
91 return rb_data;
92 }
93
94 void ruby_resolver::resolve(collection& facts)
95 {
96 auto rb_data = collect_data(facts);
97
98 auto rb_map = make_value<map_value>();
99 add(facts, *rb_map, move(rb_data.platform), fact::rubyplatform, "platform");
100 add(facts, *rb_map, move(rb_data.sitedir), fact::rubysitedir, "sitedir");
101 add(facts, *rb_map, move(rb_data.version), fact::rubyversion, "version");
102
103 if (!rb_map->empty()) {
104 facts.add(fact::ruby, move(rb_map));
105 }
106 }
107
108 }}} // namespace facter::facts::resolvers
+0
-163
lib/src/facts/resolvers/ssh_resolver.cc less more
0 #include <internal/facts/resolvers/ssh_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4
5 #include <facter/util/string.hpp>
6 #include <leatherman/file_util/file.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <boost/format.hpp>
10
11 #ifdef USE_OPENSSL
12 #include <internal/util/scoped_bio.hpp>
13 #include <openssl/sha.h>
14 #include <openssl/evp.h>
15 using namespace facter::util;
16 #endif // USE_OPENSSL
17
18 using namespace std;
19 using namespace facter::util;
20 using namespace boost::filesystem;
21
22 namespace lth_file = leatherman::file_util;
23
24 namespace facter { namespace facts { namespace resolvers {
25
26 ssh_resolver::ssh_resolver() :
27 resolver(
28 "ssh",
29 {
30 fact::ssh,
31 fact::ssh_dsa_key,
32 fact::ssh_rsa_key,
33 fact::ssh_ecdsa_key,
34 fact::ssh_ed25519_key,
35 fact::sshfp_dsa,
36 fact::sshfp_rsa,
37 fact::sshfp_ecdsa,
38 fact::sshfp_ed25519,
39 })
40 {
41 }
42
43 ssh_resolver::data ssh_resolver::collect_data(collection& facts)
44 {
45 ssh_resolver::data result;
46 populate_key("ssh_host_rsa_key.pub", 1, result.rsa);
47 populate_key("ssh_host_dsa_key.pub", 2, result.dsa);
48 populate_key("ssh_host_ecdsa_key.pub", 3, result.ecdsa);
49 populate_key("ssh_host_ed25519_key.pub", 4, result.ed25519);
50 return result;
51 }
52
53 void ssh_resolver::resolve(collection& facts)
54 {
55 auto data = collect_data(facts);
56
57 auto ssh = make_value<map_value>();
58 add_key(facts, *ssh, data.dsa, "dsa", fact::ssh_dsa_key, fact::sshfp_dsa);
59 add_key(facts, *ssh, data.rsa, "rsa", fact::ssh_rsa_key, fact::sshfp_rsa);
60 add_key(facts, *ssh, data.ecdsa, "ecdsa", fact::ssh_ecdsa_key, fact::sshfp_ecdsa);
61 add_key(facts, *ssh, data.ed25519, "ed25519", fact::ssh_ed25519_key, fact::sshfp_ed25519);
62
63 if (!ssh->empty()) {
64 facts.add(fact::ssh, move(ssh));
65 }
66 }
67
68 void ssh_resolver::add_key(collection& facts, map_value& value, ssh_key& key, string const& name, string const& key_fact_name, string const& fingerprint_fact_name)
69 {
70 if (key.key.empty()) {
71 return;
72 }
73
74 auto key_value = make_value<map_value>();
75 auto fingerprint_value = make_value<map_value>();
76
77 facts.add(string(key_fact_name), make_value<string_value>(key.key, true));
78 key_value->add("key", make_value<string_value>(move(key.key)));
79 key_value->add("type", make_value<string_value>(move(key.type)));
80
81 string fingerprint;
82 if (!key.digest.sha1.empty()) {
83 fingerprint = key.digest.sha1;
84 fingerprint_value->add("sha1", make_value<string_value>(move(key.digest.sha1)));
85 }
86 if (!key.digest.sha256.empty()) {
87 if (!fingerprint.empty()) {
88 fingerprint += "\n";
89 }
90 fingerprint += key.digest.sha256;
91 fingerprint_value->add("sha256", make_value<string_value>(move(key.digest.sha256)));
92 }
93 if (!fingerprint.empty()) {
94 facts.add(string(fingerprint_fact_name), make_value<string_value>(move(fingerprint), true));
95 }
96 if (!fingerprint_value->empty()) {
97 key_value->add("fingerprints", move(fingerprint_value));
98 }
99
100 value.add(string(name), move(key_value));
101 }
102
103 void ssh_resolver::populate_key(std::string const& filename, int type, ssh_key& key)
104 {
105 path key_file = retrieve_key_file(filename);
106
107 // Log if we didn't find the file
108 if (key_file.empty()) {
109 LOG_DEBUG("{1} could not be located.", filename);
110 return;
111 }
112
113 // Read the file's contents
114 string contents = lth_file::read(key_file.string());
115 if (contents.empty()) {
116 LOG_DEBUG("{1} could not be read.", key_file);
117 return;
118 }
119
120 boost::trim(contents);
121 // The SSH public key file format is <algo> <key> <comment>
122 vector<boost::iterator_range<string::iterator>> parts;
123 boost::split(parts, contents, boost::is_any_of(" "), boost::token_compress_on);
124 if (parts.size() < 2) {
125 LOG_DEBUG("unexpected contents for {1}.", key_file);
126 return;
127 }
128
129 // Assign the key and its type
130 key.type.assign(parts[0].begin(), parts[0].end());
131 key.key.assign(parts[1].begin(), parts[1].end());
132
133 // Only fingerprint if we are using OpenSSL
134 #ifdef USE_OPENSSL
135 // Decode the key which is expected to be base64 encoded
136 vector<uint8_t> key_bytes(key.key.size());
137 scoped_bio b64((BIO_f_base64()));
138 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
139
140 // Despite the const_cast here, we're only reading from the string; BIO_new_mem_buf is not const-correct
141 scoped_bio mem(BIO_new_mem_buf(const_cast<char*>(key.key.c_str()), key.key.size()));
142 BIO* stream = BIO_push(b64, mem);
143 int length = BIO_read(stream, key_bytes.data(), key_bytes.size());
144 if (length < 1) {
145 LOG_DEBUG("failed to decode SSH key \"{1}\".", key.key);
146 return;
147 }
148
149 // Do a SHA1 and a SHA-256 hash for the fingerprints
150 uint8_t hash[SHA_DIGEST_LENGTH];
151 SHA1(key_bytes.data(), length, hash);
152 uint8_t hash256[SHA256_DIGEST_LENGTH];
153 SHA256(key_bytes.data(), length, hash256);
154
155 key.digest.sha1 = (boost::format("SSHFP %1% 1 %2%") % type % to_hex(hash, sizeof(hash))).str();
156 key.digest.sha256 = (boost::format("SSHFP %1% 2 %2%") % type % to_hex(hash256, sizeof(hash256))).str();
157 #else
158 LOG_INFO("facter was built without OpenSSL support: SSH fingerprint information is unavailable.");
159 #endif // USE_OPENSSL
160 }
161
162 }}} // namespace facter::facts::resolvers
+0
-137
lib/src/facts/resolvers/system_profiler_resolver.cc less more
0 #include <internal/facts/resolvers/system_profiler_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5
6 using namespace std;
7 using namespace facter::facts;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 system_profiler_resolver::system_profiler_resolver() :
12 resolver(
13 "system profiler",
14 {
15 fact::system_profiler,
16 fact::sp_boot_mode,
17 fact::sp_boot_rom_version,
18 fact::sp_boot_volume,
19 fact::sp_cpu_type,
20 fact::sp_current_processor_speed,
21 fact::sp_kernel_version,
22 fact::sp_l2_cache_core,
23 fact::sp_l3_cache,
24 fact::sp_local_host_name,
25 fact::sp_machine_model,
26 fact::sp_machine_name,
27 fact::sp_number_processors,
28 fact::sp_os_version,
29 fact::sp_packages,
30 fact::sp_physical_memory,
31 fact::sp_platform_uuid,
32 fact::sp_secure_vm,
33 fact::sp_serial_number,
34 fact::sp_smc_version_system,
35 fact::sp_uptime,
36 fact::sp_user_name,
37 })
38 {
39 }
40
41 void system_profiler_resolver::resolve(collection& facts)
42 {
43 auto data = collect_data(facts);
44
45 auto system_profiler = make_value<map_value>();
46 if (!data.boot_mode.empty()) {
47 facts.add(fact::sp_boot_mode, make_value<string_value>(data.boot_mode, true));
48 system_profiler->add("boot_mode", make_value<string_value>(move(data.boot_mode)));
49 }
50 if (!data.boot_rom_version.empty()) {
51 facts.add(fact::sp_boot_rom_version, make_value<string_value>(data.boot_rom_version, true));
52 system_profiler->add("boot_rom_version", make_value<string_value>(move(data.boot_rom_version)));
53 }
54 if (!data.boot_volume.empty()) {
55 facts.add(fact::sp_boot_volume, make_value<string_value>(data.boot_volume, true));
56 system_profiler->add("boot_volume", make_value<string_value>(move(data.boot_volume)));
57 }
58 if (!data.processor_name.empty()) {
59 facts.add(fact::sp_cpu_type, make_value<string_value>(data.processor_name, true));
60 system_profiler->add("processor_name", make_value<string_value>(move(data.processor_name)));
61 }
62 if (!data.processor_speed.empty()) {
63 facts.add(fact::sp_current_processor_speed, make_value<string_value>(data.processor_speed, true));
64 system_profiler->add("processor_speed", make_value<string_value>(move(data.processor_speed)));
65 }
66 if (!data.kernel_version.empty()) {
67 facts.add(fact::sp_kernel_version, make_value<string_value>(data.kernel_version, true));
68 system_profiler->add("kernel_version", make_value<string_value>(move(data.kernel_version)));
69 }
70 if (!data.l2_cache_per_core.empty()) {
71 facts.add(fact::sp_l2_cache_core, make_value<string_value>(data.l2_cache_per_core, true));
72 system_profiler->add("l2_cache_per_core", make_value<string_value>(move(data.l2_cache_per_core)));
73 }
74 if (!data.l3_cache.empty()) {
75 facts.add(fact::sp_l3_cache, make_value<string_value>(data.l3_cache, true));
76 system_profiler->add("l3_cache", make_value<string_value>(move(data.l3_cache)));
77 }
78 if (!data.computer_name.empty()) {
79 facts.add(fact::sp_local_host_name, make_value<string_value>(data.computer_name, true));
80 system_profiler->add("computer_name", make_value<string_value>(move(data.computer_name)));
81 }
82 if (!data.model_identifier.empty()) {
83 facts.add(fact::sp_machine_model, make_value<string_value>(data.model_identifier, true));
84 system_profiler->add("model_identifier", make_value<string_value>(move(data.model_identifier)));
85 }
86 if (!data.model_name.empty()) {
87 facts.add(fact::sp_machine_name, make_value<string_value>(data.model_name, true));
88 system_profiler->add("model_name", make_value<string_value>(move(data.model_name)));
89 }
90 if (!data.cores.empty()) {
91 facts.add(fact::sp_number_processors, make_value<string_value>(data.cores, true));
92 system_profiler->add("cores", make_value<string_value>(move(data.cores)));
93 }
94 if (!data.system_version.empty()) {
95 facts.add(fact::sp_os_version, make_value<string_value>(data.system_version, true));
96 system_profiler->add("system_version", make_value<string_value>(move(data.system_version)));
97 }
98 if (!data.processors.empty()) {
99 facts.add(fact::sp_packages, make_value<string_value>(data.processors, true));
100 system_profiler->add("processors", make_value<string_value>(move(data.processors)));
101 }
102 if (!data.memory.empty()) {
103 facts.add(fact::sp_physical_memory, make_value<string_value>(data.memory, true));
104 system_profiler->add("memory", make_value<string_value>(move(data.memory)));
105 }
106 if (!data.hardware_uuid.empty()) {
107 facts.add(fact::sp_platform_uuid, make_value<string_value>(data.hardware_uuid, true));
108 system_profiler->add("hardware_uuid", make_value<string_value>(move(data.hardware_uuid)));
109 }
110 if (!data.secure_virtual_memory.empty()) {
111 facts.add(fact::sp_secure_vm, make_value<string_value>(data.secure_virtual_memory, true));
112 system_profiler->add("secure_virtual_memory", make_value<string_value>(move(data.secure_virtual_memory)));
113 }
114 if (!data.serial_number.empty()) {
115 facts.add(fact::sp_serial_number, make_value<string_value>(data.serial_number, true));
116 system_profiler->add("serial_number", make_value<string_value>(move(data.serial_number)));
117 }
118 if (!data.smc_version.empty()) {
119 facts.add(fact::sp_smc_version_system, make_value<string_value>(data.smc_version, true));
120 system_profiler->add("smc_version", make_value<string_value>(move(data.smc_version)));
121 }
122 if (!data.uptime.empty()) {
123 facts.add(fact::sp_uptime, make_value<string_value>(data.uptime, true));
124 system_profiler->add("uptime", make_value<string_value>(move(data.uptime)));
125 }
126 if (!data.username.empty()) {
127 facts.add(fact::sp_user_name, make_value<string_value>(data.username, true));
128 system_profiler->add("username", make_value<string_value>(move(data.username)));
129 }
130
131 if (!system_profiler->empty()) {
132 facts.add(fact::system_profiler, move(system_profiler));
133 }
134 }
135
136 }}} // namespace facter::facts::resolvers
+0
-27
lib/src/facts/resolvers/timezone_resolver.cc less more
0 #include <internal/facts/resolvers/timezone_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4
5 namespace facter { namespace facts { namespace resolvers {
6
7 timezone_resolver::timezone_resolver() :
8 resolver(
9 "timezone",
10 {
11 fact::timezone,
12 })
13 {
14 }
15
16 void timezone_resolver::resolve(collection& facts)
17 {
18 auto timezone = get_timezone();
19 if (timezone.empty()) {
20 return;
21 }
22
23 facts.add(fact::timezone, make_value<string_value>(move(timezone)));
24 }
25
26 }}} // facter::facts::resolvers
+0
-63
lib/src/facts/resolvers/uptime_resolver.cc less more
0 #include <internal/facts/resolvers/uptime_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <boost/format.hpp>
6
7 using namespace std;
8
9 namespace facter { namespace facts { namespace resolvers {
10
11 uptime_resolver::uptime_resolver() :
12 resolver(
13 "uptime",
14 {
15 fact::system_uptime,
16 fact::uptime,
17 fact::uptime_days,
18 fact::uptime_hours,
19 fact::uptime_seconds
20 })
21 {
22 }
23
24 void uptime_resolver::resolve(collection& facts)
25 {
26 auto seconds = get_uptime();
27 if (seconds < 0) {
28 return;
29 }
30
31 auto minutes = (seconds / 60) % 60;
32 auto hours = seconds / (60 * 60);
33 auto days = seconds / (60 * 60 * 24);
34 string uptime;
35
36 switch (days) {
37 case 0:
38 uptime = (boost::format("%d:%02d hours") % hours % minutes).str();
39 break;
40 case 1:
41 uptime = "1 day";
42 break;
43 default:
44 uptime = (boost::format("%d days") % days).str();
45 break;
46 }
47
48 // Add hidden facts
49 facts.add(fact::uptime_seconds, make_value<integer_value>(seconds, true));
50 facts.add(fact::uptime_hours, make_value<integer_value>(hours, true));
51 facts.add(fact::uptime_days, make_value<integer_value>(days, true));
52 facts.add(fact::uptime, make_value<string_value>(uptime, true));
53
54 auto value = make_value<map_value>();
55 value->add("seconds", make_value<integer_value>(seconds));
56 value->add("hours", make_value<integer_value>(hours));
57 value->add("days", make_value<integer_value>(days));
58 value->add("uptime", make_value<string_value>(move(uptime)));
59 facts.add(fact::system_uptime, move(value));
60 }
61
62 }}} // namespace facter::facts::resolvers
+0
-119
lib/src/facts/resolvers/virtualization_resolver.cc less more
0 #include <internal/facts/resolvers/virtualization_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/map_value.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/vm.hpp>
6 #include <set>
7
8 using namespace std;
9 using namespace facter::facts;
10
11 namespace facter { namespace facts { namespace resolvers {
12
13 virtualization_resolver::virtualization_resolver() :
14 resolver(
15 "virtualization",
16 {
17 fact::virtualization,
18 fact::is_virtual,
19 fact::cloud
20 })
21 {
22 }
23
24 void virtualization_resolver::resolve(collection& facts)
25 {
26 auto data = collect_data(facts);
27
28 facts.add(fact::is_virtual, make_value<boolean_value>(data.is_virtual));
29 facts.add(fact::virtualization, make_value<string_value>(data.hypervisor));
30 if (!data.cloud.provider.empty()) {
31 auto cloud = make_value<map_value>();
32 cloud->add("provider", make_value<string_value>(data.cloud.provider));
33 facts.add(fact::cloud, move(cloud));
34 }
35 }
36
37 data virtualization_resolver::collect_data(collection& facts)
38 {
39 data data;
40 auto hypervisor = get_hypervisor(facts);
41
42 if (hypervisor.empty()) {
43 hypervisor = "physical";
44 }
45
46 auto cloud_provider = get_cloud_provider(facts);
47 data.is_virtual = is_virtual(hypervisor);
48 data.hypervisor = hypervisor;
49 data.cloud.provider = cloud_provider;
50 return data;
51 }
52
53 string virtualization_resolver::get_cloud_provider(collection& facts)
54 {
55 // Default implementation for other resolvers.
56 return "";
57 }
58
59 bool virtualization_resolver::is_virtual(string const& hypervisor)
60 {
61 // Set of hypervisor values we consider to not be virtual
62 static set<string> hypervisors = {
63 "physical",
64 string(vm::xen_privileged),
65 string(vm::vmware_server),
66 string(vm::vmware_workstation),
67 string(vm::openvz_hn),
68 string(vm::vserver_host),
69 };
70 return hypervisors.count(hypervisor) == 0;
71 }
72
73 string virtualization_resolver::get_fact_vm(collection& facts)
74 {
75 // First, attempt to match on the SMBIOS reported product name
76 static vector<tuple<string, string>> product_names = {
77 make_tuple("VMware", string(vm::vmware)),
78 make_tuple("VirtualBox", string(vm::virtualbox)),
79 make_tuple("Parallels", string(vm::parallels)),
80 make_tuple("KVM", string(vm::kvm)),
81 make_tuple("Virtual Machine", string(vm::hyperv)),
82 make_tuple("RHEV Hypervisor", string(vm::redhat_ev)),
83 make_tuple("oVirt Node", string(vm::ovirt)),
84 make_tuple("HVM domU", string(vm::xen_hardware)),
85 make_tuple("Bochs", string(vm::bochs)),
86 make_tuple("OpenBSD", string(vm::vmm)),
87 make_tuple("BHYVE", string(vm::bhyve)),
88 };
89
90 auto product_name = facts.get<string_value>(fact::product_name);
91
92 if (product_name) {
93 for (auto const& vm : product_names) {
94 if (product_name->value().find(get<0>(vm)) != string::npos) {
95 return get<1>(vm);
96 }
97 }
98 }
99
100 // Next, try the reported BIOS vendor
101 static vector<tuple<string, string>> vendor_names = {
102 make_tuple("Amazon EC2", string(vm::kvm)),
103 };
104
105 auto vendor_name = facts.get<string_value>(fact::bios_vendor);
106
107 if (vendor_name) {
108 for (auto const& vm : vendor_names) {
109 if (vendor_name->value().find(get<0>(vm)) != string::npos) {
110 return get<1>(vm);
111 }
112 }
113 }
114
115 return {};
116 }
117
118 }}} // namespace facter::facts::resolvers
+0
-80
lib/src/facts/resolvers/xen_resolver.cc less more
0 #include <internal/facts/resolvers/xen_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/vm.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <leatherman/execution/execution.hpp>
8 #include <leatherman/util/regex.hpp>
9 #include <leatherman/logging/logging.hpp>
10 #include <boost/algorithm/string.hpp>
11
12 using namespace std;
13 using namespace facter::facts;
14 using namespace leatherman::execution;
15 using namespace leatherman::util;
16
17 namespace facter { namespace facts { namespace resolvers {
18
19 xen_resolver::xen_resolver() :
20 resolver(
21 "Xen",
22 {
23 fact::xen,
24 fact::xendomains
25 })
26 {
27 }
28
29 void xen_resolver::resolve(collection& facts)
30 {
31 // Confine to fact virtual == xen0
32 auto virt = facts.get<string_value>(fact::virtualization);
33 if (!virt || virt->value() != vm::xen_privileged) {
34 return;
35 }
36
37 auto data = collect_data(facts);
38
39 if (!data.domains.empty()) {
40 auto xendomains = boost::algorithm::join(data.domains, ",");
41 facts.add(fact::xendomains, make_value<string_value>(move(xendomains), true));
42 }
43
44 auto domains = make_value<array_value>();
45 for (auto& domain : data.domains) {
46 domains->add(make_value<string_value>(move(domain)));
47 }
48
49 auto xen = make_value<map_value>();
50 if (!domains->empty()) {
51 xen->add("domains", move(domains));
52 }
53
54 if (!xen->empty()) {
55 facts.add(fact::xen, move(xen));
56 }
57 }
58
59 xen_resolver::data xen_resolver::collect_data(collection& facts)
60 {
61 data result;
62
63 auto command = xen_command();
64 if (!command.empty()) {
65 static boost::regex domain_header("^(Name|Domain-0)");
66 static boost::regex domain_entry("^([^\\s]*)\\s");
67 each_line(command, {"list"}, [&](string& line) {
68 string domain;
69 if (!boost::regex_match(line, domain_header) && re_search(line, domain_entry, &domain)) {
70 result.domains.emplace_back(move(domain));
71 }
72 return true;
73 });
74 }
75
76 return result;
77 }
78
79 }}} // namespace facter::facts::resolvers
+0
-63
lib/src/facts/resolvers/zfs_resolver.cc less more
0 #include <internal/facts/resolvers/zfs_resolver.hpp>
1 #include <facter/facts/fact.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <leatherman/execution/execution.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::execution;
11 using namespace leatherman::util;
12
13 namespace facter { namespace facts { namespace resolvers {
14
15 zfs_resolver::zfs_resolver() :
16 resolver(
17 "ZFS",
18 {
19 fact::zfs_version,
20 fact::zfs_versionnumbers
21 })
22 {
23 }
24
25 void zfs_resolver::resolve(collection& facts)
26 {
27 auto data = collect_data(facts);
28
29 if (!data.version.empty()) {
30 facts.add(fact::zfs_version, make_value<string_value>(move(data.version)));
31 }
32 if (!data.versions.empty()) {
33 facts.add(fact::zfs_versionnumbers, make_value<string_value>(boost::join(data.versions, ",")));
34 }
35 }
36
37 zfs_resolver::data zfs_resolver::collect_data(collection& facts)
38 {
39 data result;
40
41 // Get the ZFS version
42 static boost::regex zfs_version("currently running ZFS filesystem version (\\d+)[.]");
43 each_line(zfs_command(), {"upgrade"}, [&] (string& line) {
44 if (re_search(line, zfs_version, &result.version)) {
45 return false;
46 }
47 return true;
48 });
49
50 // Get the ZFS versions
51 static boost::regex zfs_supported_version("^\\s*(\\d+)[ ]");
52 each_line(zfs_command(), {"upgrade", "-v"}, [&] (string& line) {
53 string version;
54 if (re_search(line, zfs_supported_version, &version)) {
55 result.versions.emplace_back(move(version));
56 }
57 return true;
58 });
59 return result;
60 }
61
62 }}} // namespace facter::facts::resolvers
+0
-91
lib/src/facts/resolvers/zone_resolver.cc less more
0 #include <internal/facts/resolvers/zone_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/facts/array_value.hpp>
5 #include <facter/facts/map_value.hpp>
6
7 using namespace std;
8 using namespace facter::facts;
9
10 namespace facter { namespace facts { namespace resolvers {
11
12 zone_resolver::zone_resolver() :
13 resolver(
14 "Solaris zone",
15 {
16 fact::zones,
17 fact::zonename,
18 fact::solaris_zones,
19 },
20 {
21 string("^zone_.+_") + fact::zone_id + "$",
22 string("^zone_.+_") + fact::zone_name + "$",
23 string("^zone_.+_") + fact::zone_status + "$",
24 string("^zone_.+_") + fact::zone_path + "$",
25 string("^zone_.+_") + fact::zone_uuid + "$",
26 string("^zone_.+_") + fact::zone_brand + "$",
27 string("^zone_.+_") + fact::zone_iptype + "$"
28 }
29 )
30 {
31 }
32
33 void zone_resolver::resolve(collection& facts)
34 {
35 auto data = collect_data(facts);
36
37 auto zones = make_value<map_value>();
38 for (auto& zone : data.zones) {
39 auto value = make_value<map_value>();
40
41 if (!zone.id.empty()) {
42 facts.add(string("zone_") + zone.name + "_" + fact::zone_id, make_value<string_value>(zone.id, true));
43 value->add("id", make_value<string_value>(move(zone.id)));
44 }
45 if (!zone.name.empty()) {
46 facts.add(string("zone_") + zone.name + "_" + fact::zone_name, make_value<string_value>(zone.name, true));
47 }
48 if (!zone.status.empty()) {
49 facts.add(string("zone_") + zone.name + "_" + fact::zone_status, make_value<string_value>(zone.status, true));
50 value->add("status", make_value<string_value>(move(zone.status)));
51 }
52 if (!zone.path.empty()) {
53 facts.add(string("zone_") + zone.name + "_" + fact::zone_path, make_value<string_value>(zone.path, true));
54 value->add("path", make_value<string_value>(move(zone.path)));
55 }
56 if (!zone.uuid.empty()) {
57 facts.add(string("zone_") + zone.name + "_" + fact::zone_uuid, make_value<string_value>(zone.uuid, true));
58 value->add("uuid", make_value<string_value>(move(zone.uuid)));
59 }
60 if (!zone.brand.empty()) {
61 facts.add(string("zone_") + zone.name + "_" + fact::zone_brand, make_value<string_value>(zone.brand, true));
62 value->add("brand", make_value<string_value>(move(zone.brand)));
63 }
64 if (!zone.ip_type.empty()) {
65 facts.add(string("zone_") + zone.name + "_" + fact::zone_iptype, make_value<string_value>(zone.ip_type, true));
66 value->add("ip_type", make_value<string_value>(move(zone.ip_type)));
67 }
68
69 zones->add(move(zone.name), move(value));
70 }
71
72 facts.add(fact::zones, make_value<integer_value>(zones->size(), true));
73
74 if (zones->size() > 0) {
75 auto solaris_zones = make_value<map_value>();
76
77 if (!data.current_zone_name.empty()) {
78 solaris_zones->add("current", make_value<string_value>(data.current_zone_name));
79 }
80
81 solaris_zones->add("zones", move(zones));
82 facts.add(fact::solaris_zones, move(solaris_zones));
83 }
84
85 if (!data.current_zone_name.empty()) {
86 facts.add(fact::zonename, make_value<string_value>(move(data.current_zone_name), true));
87 }
88 }
89
90 }}} // namespace facter::facts::resolvers
+0
-93
lib/src/facts/resolvers/zpool_resolver.cc less more
0 #include <internal/facts/resolvers/zpool_resolver.hpp>
1 #include <facter/facts/fact.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <leatherman/execution/execution.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::execution;
11 using namespace leatherman::util;
12
13 namespace facter { namespace facts { namespace resolvers {
14
15 zpool_resolver::zpool_resolver() :
16 resolver(
17 "ZFS storage pool",
18 {
19 fact::zpool_version,
20 fact::zpool_featureflags,
21 fact::zpool_versionnumbers,
22 })
23 {
24 }
25
26 void zpool_resolver::resolve(collection& facts)
27 {
28 auto data = collect_data(facts);
29
30 if (!data.version.empty()) {
31 facts.add(fact::zpool_version, make_value<string_value>(move(data.version)));
32 }
33 if (!data.feature_flags.empty()) {
34 facts.add(fact::zpool_featureflags, make_value<string_value>(boost::join(data.feature_flags, ",")));
35 }
36 if (!data.versions.empty()) {
37 facts.add(fact::zpool_versionnumbers, make_value<string_value>(boost::join(data.versions, ",")));
38 }
39 }
40
41 zpool_resolver::data zpool_resolver::collect_data(collection& facts)
42 {
43 data result;
44
45 enum { UNKNOWN, FEATURES, VERSIONS } state = UNKNOWN;
46
47 // Get the zpool version and features
48 static boost::regex zpool_version("^This system is currently running ZFS pool version (\\d+)\\.$");
49 static boost::regex zpool_feature_flags("^This system supports ZFS pool feature flags\\.$");
50
51 static boost::regex zpool_supported_feature_header("^The following features are supported:$");
52 static boost::regex zpool_supported_versions_header("^The following versions are supported:$");
53 static boost::regex zpool_supported_legacy_versions_header("^The following legacy versions are also supported:$");
54
55 static boost::regex zpool_supported_feature("^([[:alnum:]_]+)(\\s+\\(read-only compatible\\))?$");
56 static boost::regex zpool_supported_version("^\\s*(\\d+)[ ]");
57
58 string feature;
59 each_line(zpool_command(), {"upgrade", "-v"}, [&] (string& line) {
60 switch (state) {
61 case UNKNOWN:
62 if (re_search(line, zpool_version, &result.version)) {
63 } else if (re_search(line, zpool_feature_flags)) {
64 result.version = "5000";
65 } else if (re_search(line, zpool_supported_feature_header)) {
66 state = FEATURES;
67 } else if (re_search(line, zpool_supported_versions_header)) {
68 state = VERSIONS;
69 }
70 break;
71
72 case FEATURES:
73 if (re_search(line, zpool_supported_feature, &feature)) {
74 result.feature_flags.emplace_back(move(feature));
75 } else if (re_search(line, zpool_supported_legacy_versions_header)) {
76 state = VERSIONS;
77 }
78 break;
79
80 case VERSIONS:
81 string feature;
82 if (re_search(line, zpool_supported_version, &feature)) {
83 result.versions.emplace_back(move(feature));
84 }
85 break;
86 }
87 return true;
88 });
89 return result;
90 }
91
92 }}} // namespace facter::facts::resolvers
+0
-73
lib/src/facts/scalar_value.cc less more
0 #include <facter/facts/scalar_value.hpp>
1 #include <facter/util/string.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <iomanip>
5
6 using namespace std;
7 using namespace facter::util;
8 using namespace rapidjson;
9 using namespace YAML;
10
11 namespace facter { namespace facts {
12
13 template <>
14 void scalar_value<string>::to_json(json_allocator& allocator, json_value& value) const
15 {
16 value.SetString(_value.c_str(), _value.size());
17 }
18
19 template <>
20 void scalar_value<int64_t>::to_json(json_allocator& allocator, json_value& value) const
21 {
22 value.SetInt64(_value);
23 }
24
25 template <>
26 void scalar_value<bool>::to_json(json_allocator& allocator, json_value& value) const
27 {
28 value.SetBool(_value);
29 }
30
31 template <>
32 void scalar_value<double>::to_json(json_allocator& allocator, json_value& value) const
33 {
34 value.SetDouble(_value);
35 }
36
37 template <>
38 Emitter& scalar_value<string>::write(Emitter& emitter) const
39 {
40 if (needs_quotation(_value)) {
41 emitter << DoubleQuoted;
42 }
43 emitter << _value;
44 return emitter;
45 }
46
47 template <>
48 ostream& scalar_value<bool>::write(ostream& os, bool quoted, unsigned int level) const
49 {
50 os << boolalpha << _value << noboolalpha;
51 return os;
52 }
53
54 template <>
55 ostream& scalar_value<string>::write(ostream& os, bool quoted, unsigned int level) const
56 {
57 if (quoted) {
58 os << '"';
59 }
60 os << _value;
61 if (quoted) {
62 os << '"';
63 }
64 return os;
65 }
66
67 template struct scalar_value<string>;
68 template struct scalar_value<int64_t>;
69 template struct scalar_value<bool>;
70 template struct scalar_value<double>;
71
72 }} // namespace facter::facts
+0
-51
lib/src/facts/solaris/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/ssh_resolver.hpp>
2 #include <internal/facts/solaris/kernel_resolver.hpp>
3 #include <internal/facts/posix/identity_resolver.hpp>
4 #include <internal/facts/solaris/operating_system_resolver.hpp>
5 #include <internal/facts/solaris/networking_resolver.hpp>
6 #include <internal/facts/solaris/processor_resolver.hpp>
7 #include <internal/facts/posix/uptime_resolver.hpp>
8 #include <internal/facts/posix/timezone_resolver.hpp>
9 #include <internal/facts/solaris/filesystem_resolver.hpp>
10 #include <internal/facts/solaris/disk_resolver.hpp>
11 #include <internal/facts/solaris/dmi_resolver.hpp>
12 #include <internal/facts/solaris/virtualization_resolver.hpp>
13 #include <internal/facts/solaris/memory_resolver.hpp>
14 #include <internal/facts/solaris/zpool_resolver.hpp>
15 #include <internal/facts/solaris/zfs_resolver.hpp>
16 #include <internal/facts/solaris/zone_resolver.hpp>
17 #include <internal/facts/solaris/ldom_resolver.hpp>
18 #include <internal/facts/glib/load_average_resolver.hpp>
19 #include <internal/facts/posix/xen_resolver.hpp>
20
21 using namespace std;
22
23 namespace facter { namespace facts {
24
25 void collection::add_platform_facts()
26 {
27 add(make_shared<ssh_resolver>());
28 add(make_shared<solaris::kernel_resolver>());
29 add(make_shared<solaris::operating_system_resolver>());
30 add(make_shared<solaris::networking_resolver>());
31 add(make_shared<solaris::processor_resolver>());
32 add(make_shared<posix::uptime_resolver>());
33 add(make_shared<posix::identity_resolver>());
34 add(make_shared<posix::timezone_resolver>());
35 add(make_shared<solaris::filesystem_resolver>());
36 add(make_shared<solaris::dmi_resolver>());
37 add(make_shared<solaris::disk_resolver>());
38 add(make_shared<solaris::virtualization_resolver>());
39 add(make_shared<solaris::memory_resolver>());
40 add(make_shared<glib::load_average_resolver>());
41 add(make_shared<posix::xen_resolver>());
42
43 // solaris specific
44 add(make_shared<solaris::zpool_resolver>());
45 add(make_shared<solaris::zfs_resolver>());
46 add(make_shared<solaris::zone_resolver>());
47 add(make_shared<solaris::ldom_resolver>());
48 }
49
50 }} // namespace facter::facts
+0
-35
lib/src/facts/solaris/disk_resolver.cc less more
0 #include <internal/facts/solaris/disk_resolver.hpp>
1 #include <internal/util/solaris/k_stat.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <boost/algorithm/string.hpp>
4
5 using namespace std;
6 using namespace facter::util::solaris;
7
8 namespace facter { namespace facts { namespace solaris {
9
10 disk_resolver::data disk_resolver::collect_data(collection& facts)
11 {
12 try {
13 data result;
14 k_stat ks;
15 auto ke = ks["sderr"];
16 for (auto& kv : ke) {
17 disk d;
18 string name = kv.name();
19 d.name = name.substr(0, name.find(','));
20 d.product = kv.value<string>("Product");
21 boost::trim(d.product);
22 d.vendor = kv.value<string>("Vendor");
23 boost::trim(d.vendor);
24 d.size = static_cast<int64_t>(kv.value<uint64_t>("Size"));
25 result.disks.emplace_back(move(d));
26 }
27 return result;
28 } catch (kstat_exception& ex) {
29 LOG_DEBUG("disk information is unavailable: {1}.", ex.what());
30 return {};
31 }
32 }
33
34 }}} // namespace facter::facts::solaris
+0
-86
lib/src/facts/solaris/dmi_resolver.cc less more
0 #include <internal/facts/solaris/dmi_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <leatherman/util/regex.hpp>
7 #include <boost/algorithm/string.hpp>
8
9 using namespace std;
10 using namespace leatherman::util;
11 using namespace leatherman::execution;
12
13 namespace facter { namespace facts { namespace solaris {
14
15 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
16 {
17 data result;
18
19 auto isa = facts.get<string_value>(fact::hardware_isa);
20 if (isa && isa->value() == "i386") {
21 static boost::regex bios_vendor_re("Vendor: (.+)");
22 static boost::regex bios_version_re("Version String: (.+)");
23 static boost::regex bios_release_re("Release Date: (.+)");
24 each_line("/usr/sbin/smbios", {"-t", "SMB_TYPE_BIOS"}, [&](string& line) {
25 if (result.bios_vendor.empty()) {
26 re_search(line, bios_vendor_re, &result.bios_vendor);
27 }
28 if (result.bios_version.empty()) {
29 re_search(line, bios_version_re, &result.bios_version);
30 }
31 if (result.bios_release_date.empty()) {
32 re_search(line, bios_release_re, &result.bios_release_date);
33 }
34 return result.bios_release_date.empty() || result.bios_vendor.empty() || result.bios_version.empty();
35 });
36
37 static boost::regex manufacturer_re("Manufacturer: (.+)");
38 static boost::regex uuid_re("UUID: (.+)");
39 static boost::regex serial_re("Serial Number: (.+)");
40 static boost::regex product_re("Product: (.+)");
41 each_line("/usr/sbin/smbios", {"-t", "SMB_TYPE_SYSTEM"}, [&](string& line) {
42 if (result.manufacturer.empty()) {
43 re_search(line, manufacturer_re, &result.manufacturer);
44 }
45 if (result.product_name.empty()) {
46 re_search(line, product_re, &result.product_name);
47 }
48 if (result.uuid.empty()) {
49 re_search(line, uuid_re, &result.uuid);
50 }
51 if (result.serial_number.empty()) {
52 re_search(line, serial_re, &result.serial_number);
53 }
54 return result.manufacturer.empty() || result.product_name.empty() || result.uuid.empty() || result.serial_number.empty();
55 });
56
57 static boost::regex chassis_type_re("(?:Chassis )?Type: (.+)");
58 static boost::regex chassis_asset_tag_re("Asset Tag: (.+)");
59 each_line("/usr/sbin/smbios", {"-t", "SMB_TYPE_CHASSIS"}, [&](string& line) {
60 if (result.chassis_type.empty()) {
61 re_search(line, chassis_type_re, &result.chassis_type);
62 }
63 if (result.chassis_asset_tag.empty()) {
64 re_search(line, chassis_asset_tag_re, &result.chassis_asset_tag);
65 }
66 return result.chassis_type.empty() || result.chassis_asset_tag.empty();
67 });
68 } else if (isa && isa->value() == "sparc") {
69 static boost::regex product_name_manufacturer_re("^System Configuration:\\s+(.+?)\\s+sun\\d+\\S+\\s+(.+)");
70 each_line("/usr/sbin/prtdiag", [&](string& line) {
71 re_search(line, product_name_manufacturer_re, &result.manufacturer, &result.product_name);
72 return result.manufacturer.empty() || result.product_name.empty();
73 });
74 // Manufacturer appears to have two spaces before and after it, but we don't want to rely on that formatting.
75 boost::trim(result.manufacturer);
76
77 auto exec = execute("/usr/sbin/sneep");
78 if (exec.success) {
79 result.serial_number = exec.output;
80 }
81 }
82 return result;
83 }
84
85 }}} // namespace facter::facts::solaris
+0
-92
lib/src/facts/solaris/filesystem_resolver.cc less more
0 #include <internal/facts/solaris/filesystem_resolver.hpp>
1 #include <internal/util/solaris/k_stat.hpp>
2 #include <internal/util/scoped_file.hpp>
3 #include <leatherman/file_util/file.hpp>
4 #include <facter/util/string.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <leatherman/execution/execution.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <boost/algorithm/string.hpp>
9 #include <boost/filesystem.hpp>
10 #include <sys/mnttab.h>
11 #include <fcntl.h>
12 #include <sys/vfs.h>
13 #include <sys/statvfs.h>
14 #include <unordered_set>
15
16 using namespace std;
17 using namespace facter::facts;
18 using namespace facter::util;
19 using namespace facter::util::solaris;
20 using namespace leatherman::util;
21 using namespace boost::filesystem;
22
23 namespace facter { namespace facts { namespace solaris {
24
25 filesystem_resolver::data filesystem_resolver::collect_data(collection& facts)
26 {
27 data result;
28 collect_mountpoint_data(result);
29 collect_filesystem_data(result);
30 return result;
31 }
32
33 void filesystem_resolver::collect_mountpoint_data(data& result)
34 {
35 scoped_file file(fopen("/etc/mnttab", "r"));
36 if (!static_cast<FILE*>(file)) {
37 LOG_ERROR("fopen of /etc/mnttab failed: {1} ({2}): mountpoint data is unavailable.", strerror(errno), errno);
38 return;
39 }
40
41 mnttab entry;
42 unordered_set<string> auto_home_paths;
43 vector<mountpoint> mountpoints;
44 while (getmntent(file, &entry) == 0) {
45 mountpoint point;
46
47 struct statvfs64 stats;
48 if (statvfs64(entry.mnt_mountp, &stats) != -1) {
49 point.size = stats.f_frsize * stats.f_blocks;
50 point.available = stats.f_frsize * stats.f_bfree;
51 }
52
53 if (entry.mnt_special == string("auto_home")) {
54 auto_home_paths.emplace(entry.mnt_mountp);
55 continue;
56 } else if (entry.mnt_fstype == string("autofs")) {
57 continue;
58 }
59
60 point.name = entry.mnt_mountp;
61 point.device = entry.mnt_special;
62 point.filesystem = entry.mnt_fstype;
63 boost::split(point.options, entry.mnt_mntopts, boost::is_any_of(","), boost::token_compress_on);
64
65 mountpoints.emplace_back(move(point));
66 }
67
68 for (auto& point : mountpoints) {
69 auto mount_parent = point.name.substr(0, point.name.find_last_of('/'));
70
71 // Only add entries that are not mounted from an auto_home setup
72 if (auto_home_paths.count(mount_parent) == 0) {
73 result.mountpoints.emplace_back(move(point));
74 }
75 }
76 }
77
78 void filesystem_resolver::collect_filesystem_data(data& result)
79 {
80 // Build a list of mounted filesystems
81 static boost::regex fs_re("^fs/.*/(.*)$");
82 leatherman::execution::each_line("/usr/sbin/sysdef", [&](string& line) {
83 string fs;
84 if (re_search(line, fs_re, &fs)) {
85 result.filesystems.insert(move(fs));
86 }
87 return true;
88 });
89 }
90
91 }}} // namespace facter::facts::solaris
+0
-24
lib/src/facts/solaris/kernel_resolver.cc less more
0 #include <internal/facts/solaris/kernel_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <sys/utsname.h>
3
4 using namespace std;
5
6 namespace facter { namespace facts { namespace solaris {
7
8 kernel_resolver::data kernel_resolver::collect_data(collection& facts)
9 {
10 data result;
11 struct utsname name;
12 if (uname(&name) == -1) {
13 LOG_WARNING("uname failed: {1} ({2}): kernel facts are unavailable.", strerror(errno), errno);
14 return result;
15 }
16
17 result.name = name.sysname;
18 result.release = name.release;
19 result.version = name.version;
20 return result;
21 }
22
23 }}} // namespace facter::facts::solaris
+0
-87
lib/src/facts/solaris/ldom_resolver.cc less more
0 #include <internal/facts/solaris/ldom_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <leatherman/execution/execution.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <boost/algorithm/string.hpp>
8 #include <string>
9 #include <vector>
10
11 using namespace std;
12 using namespace facter::facts;
13 using namespace leatherman::execution;
14 using namespace leatherman::util;
15
16 namespace facter { namespace facts { namespace solaris {
17
18 ldom_resolver::data ldom_resolver::collect_data(collection& facts)
19 {
20 /*
21 Convert virtinfo parseable output format to array of arrays.
22 DOMAINROLE|impl=LDoms|control=true|io=true|service=true|root=true
23 DOMAINNAME|name=primary
24 DOMAINUUID|uuid=8e0d6ec5-cd55-e57f-ae9f-b4cc050999a4
25 DOMAINCONTROL|name=san-t2k-6
26 DOMAINCHASSIS|serialno=0704RB0280
27
28 For keys containing multiple value such as domain role:
29 ldom_{key}_{subkey} = value
30 Otherwise the fact will simply be:
31 ldom_{key} = value
32 */
33 data result;
34
35 auto isa = facts.get<string_value>(fact::hardware_isa);
36 if (isa && isa->value() != "sparc") {
37 return result;
38 }
39
40 each_line("/usr/sbin/virtinfo", { "-a", "-p" }, [&] (string& line) {
41 if (!re_search(line, boost::regex("^DOMAIN"))) {
42 return true;
43 }
44
45 vector<string> items;
46 boost::split(items, line, boost::is_any_of("|"));
47
48 // The first element is the key, i.e, "domainrole." Subsequent entries are values.
49 if (items.empty()) {
50 return true;
51 } else if (items.size() == 2) {
52 ldom_info ldom_data;
53 string key = items[0];
54 string value = items[1].substr(items[1].find("=") + 1);
55
56 transform(key.begin(), key.end(), key.begin(), ::tolower);
57
58 ldom_data.key = key;
59 ldom_data.values.insert({ key, value });
60 result.ldom.emplace_back(ldom_data);
61 } else {
62 // When there are multiple values to a line, we insert them all in a single sub-map.
63 ldom_info ldom_data;
64 string base_key = items[0]; // Base key is used as top level sub-key in the structured fact
65 transform(base_key.begin(), base_key.end(), base_key.begin(), ::tolower);
66 ldom_data.key = base_key;
67
68 items.erase(items.begin());
69
70 for (string val : items) {
71 auto pos = val.find("=");
72 string sub_key = val.substr(0, pos);
73 string value = val.substr(pos + 1);
74
75 ldom_data.values.insert({ sub_key, value });
76 }
77
78 result.ldom.emplace_back(ldom_data);
79 }
80 return true;
81 });
82
83 return result;
84 }
85
86 }}} // namespace facter::facts::solaris
+0
-91
lib/src/facts/solaris/memory_resolver.cc less more
0 #include <internal/facts/solaris/memory_resolver.hpp>
1 #include <internal/util/solaris/k_stat.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <leatherman/util/regex.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <sys/sysinfo.h>
7 #include <sys/sysconfig.h>
8 #include <sys/stat.h>
9 #include <sys/swap.h>
10
11 using namespace std;
12 using namespace facter::util::solaris;
13 using namespace leatherman::execution;
14 using namespace leatherman::util;
15
16 namespace facter { namespace facts { namespace solaris {
17
18 memory_resolver::data memory_resolver::collect_data(collection& facts)
19 {
20 data result;
21
22 const uint64_t page_size = sysconf(_SC_PAGESIZE);
23 const auto max_dev_size = PATH_MAX;
24
25 try {
26 k_stat ks;
27 auto ke = ks[make_pair("unix", "system_pages")][0];
28 result.mem_total = ke.value<ulong_t>("physmem") * page_size;
29 result.mem_free = ke.value<ulong_t>("pagesfree") * page_size;
30 } catch (kstat_exception &ex) {
31 LOG_DEBUG("failed to read memory facts from kstat api: {1}.", ex.what());
32
33 uint64_t physmem, pagesfree;
34
35 static boost::regex physmem_rx("^\\s*physmem\\s+(.+)$");
36 static boost::regex pagesfree_rx("^\\s*pagesfree\\s+(\\d+)$");
37
38 each_line("/usr/bin/kstat", {"-m", "unix", "-n", "system_pages"}, [&] (string& line) {
39 if (re_search(line, physmem_rx, &physmem)) {
40 result.mem_total = physmem * page_size;
41 } else if (re_search(line, pagesfree_rx, &pagesfree)) {
42 result.mem_free = pagesfree * page_size;
43 }
44 return result.mem_total == 0 || result.mem_free == 0;
45 });
46 }
47
48 // Swap requires a little more effort. See
49 // https://community.oracle.com/thread/1951228?start=0&tstart=0
50 // http://www.brendangregg.com/K9Toolkit/swapinfo
51 int num = 0;
52 if ((num = swapctl(SC_GETNSWP, 0)) == -1) {
53 LOG_DEBUG("swapctl failed: {1} ({2}): swap information is unavailable", strerror(errno), errno);
54 return result;
55 }
56 if (num == 0) {
57 // no swap devices configured
58 return result;
59 }
60
61 // swap devices can be added online. So add one extra.
62 num++;
63
64 vector<char> buffer(num * sizeof(swapent_t) + sizeof(swaptbl_t));
65 vector<vector<char>> str_table(num);
66
67 swaptbl_t* swaps = reinterpret_cast<swaptbl_t*>(buffer.data());
68 swaps->swt_n = num;
69
70 for (int i = 0; i < num; i++) {
71 str_table[i].resize(max_dev_size);
72 swaps->swt_ent[i].ste_path = str_table[i].data();
73 }
74
75 if (swapctl(SC_LIST, swaps) == -1) {
76 LOG_DEBUG("swapctl with SC_LIST failed: {1} ({2}): swap information is unavailable", strerror(errno), errno);
77 return result;
78 }
79
80 for (int i = 0; i < num; i++) {
81 result.swap_free += swaps->swt_ent[i].ste_free;
82 result.swap_total += swaps->swt_ent[i].ste_pages;
83 }
84
85 result.swap_free *= page_size;
86 result.swap_total *= page_size;
87
88 return result;
89 }
90 }}} // namespace facter::facts::solaris
+0
-188
lib/src/facts/solaris/networking_resolver.cc less more
0 #include <internal/facts/solaris/networking_resolver.hpp>
1 #include <internal/util/posix/scoped_descriptor.hpp>
2 #include <facter/util/string.hpp>
3 #include <leatherman/execution/execution.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <sys/sockio.h>
7 #include <net/if_arp.h>
8
9 using namespace std;
10 using namespace facter::util::posix;
11 using namespace facter::util;
12 using namespace leatherman::execution;
13
14 namespace facter { namespace facts { namespace solaris {
15
16 networking_resolver::data networking_resolver::collect_data(collection& facts)
17 {
18 auto data = posix::networking_resolver::collect_data(facts);
19
20 scoped_descriptor ctl(socket(AF_INET, SOCK_DGRAM, 0));
21 if (static_cast<int>(ctl) == -1) {
22 LOG_DEBUG("socket failed {1} ({2}): interface information is unavailable.", strerror(errno), errno);
23 return data;
24 }
25
26 // (patterned on bsd impl)
27 lifnum ifnr{AF_UNSPEC, 0, 0};
28 if (ioctl(ctl, SIOCGLIFNUM, &ifnr) == -1) {
29 LOG_DEBUG("ioctl with SIOCGLIFNUM failed: {1} ({2}): interface information is unavailable.", strerror(errno), errno);
30 return data;
31 }
32
33 vector<lifreq> buffer(ifnr.lifn_count);
34 lifconf lifc = {AF_UNSPEC, 0, static_cast<int>(buffer.size() * sizeof(lifreq)), reinterpret_cast<caddr_t>(buffer.data())};
35 if (ioctl(ctl, SIOCGLIFCONF, &lifc) == -1) {
36 LOG_DEBUG("ioctl with SIOCGLIFCONF failed: {1} ({2}): interface information is unavailable.", strerror(errno), errno);
37 return data;
38 }
39
40 // put them in a multimap so that similar address can be
41 // grouped together.
42 multimap<string, const lifreq*> interface_map;
43 for (lifreq const& lreq : buffer) {
44 interface_map.insert({lreq.lifr_name, &lreq});
45 }
46
47 data.primary_interface = get_primary_interface();
48
49 // Walk the interfaces
50 decltype(interface_map.begin()) it = interface_map.begin();
51 while (it != interface_map.end()) {
52 string const& name = it->first;
53
54 interface iface;
55 iface.name = name;
56
57 // Populate the MAC address and MTU once per interface
58 populate_macaddress(iface, it->second);
59 populate_mtu(iface, it->second);
60
61 // Walk the addresses for this interface
62 do {
63 populate_binding(iface, it->second);
64 ++it;
65 } while (it != interface_map.end() && it->first == name);
66
67 // Find the DCHP server for the interface
68 iface.dhcp_server = find_dhcp_server(name);
69
70 data.interfaces.emplace_back(move(iface));
71 }
72 return data;
73 }
74
75 void networking_resolver::populate_binding(interface& iface, lifreq const* addr) const
76 {
77 // Populate the correct bindings list
78 vector<binding>* bindings = nullptr;
79 if (addr->lifr_addr.ss_family == AF_INET) {
80 bindings = &iface.ipv4_bindings;
81 } else if (addr->lifr_addr.ss_family == AF_INET) {
82 bindings = &iface.ipv6_bindings;
83 }
84
85 if (!bindings) {
86 return;
87 }
88
89 // Create a binding
90 binding b;
91 b.address = address_to_string(reinterpret_cast<sockaddr const*>(&addr->lifr_addr));
92
93 // Get the netmask
94 scoped_descriptor ctl(socket(addr->lifr_addr.ss_family, SOCK_DGRAM, 0));
95 if (static_cast<int>(ctl) == -1) {
96 LOG_DEBUG("socket failed: {1} ({2}): netmask and network for interface {3} are unavailable.", strerror(errno), errno, addr->lifr_name);
97 } else {
98 lifreq netmask_addr = *addr;
99 if (ioctl(ctl, SIOCGLIFNETMASK, &netmask_addr) == -1) {
100 LOG_DEBUG("ioctl with SIOCGLIFNETMASK failed: {1} ({2}): netmask and network for interface {3} are unavailable.", strerror(errno), errno, addr->lifr_name);
101 } else {
102 b.netmask = address_to_string(reinterpret_cast<sockaddr const*>(&netmask_addr.lifr_addr));
103 b.network = address_to_string(reinterpret_cast<sockaddr const*>(&addr->lifr_addr), reinterpret_cast<sockaddr const*>(&netmask_addr.lifr_addr));
104 }
105 }
106
107 bindings->emplace_back(std::move(b));
108 }
109
110 void networking_resolver::populate_macaddress(interface& iface, lifreq const* addr) const
111 {
112 scoped_descriptor ctl(socket(addr->lifr_addr.ss_family, SOCK_DGRAM, 0));
113 if (static_cast<int>(ctl) == -1) {
114 LOG_DEBUG("socket failed: {1} ({2}): link level address for interface {3} is unavailable.", strerror(errno), errno, addr->lifr_name);
115 return;
116 }
117
118 arpreq arp;
119 sockaddr_in* arp_addr = reinterpret_cast<sockaddr_in*>(&arp.arp_pa);
120 arp_addr->sin_addr.s_addr = reinterpret_cast<sockaddr_in const*>(&addr->lifr_addr)->sin_addr.s_addr;
121 if (ioctl(ctl, SIOCGARP, &arp) == -1) {
122 LOG_DEBUG("ioctl with SIOCGARP failed: {1} ({2}): link level address for {3} is unavailable.", strerror(errno), errno, addr->lifr_name);
123 return;
124 }
125
126 iface.macaddress = macaddress_to_string(reinterpret_cast<uint8_t const*>(arp.arp_ha.sa_data));
127 }
128
129 void networking_resolver::populate_mtu(interface& iface, lifreq const* addr) const
130 {
131 scoped_descriptor ctl(socket(addr->lifr_addr.ss_family, SOCK_DGRAM, 0));
132 if (static_cast<int>(ctl) == -1) {
133 LOG_DEBUG("socket failed: {1} ({2}): MTU for interface {3} is unavailable.", strerror(errno), errno, addr->lifr_name);
134 return;
135 }
136
137 lifreq mtu = *addr;
138 if (ioctl(ctl, SIOCGLIFMTU, &mtu) == -1) {
139 LOG_DEBUG("ioctl with SIOCGLIFMTU failed: {1} ({2}): MTU for interface {3} is unavailable.", strerror(errno), errno, addr->lifr_name);
140 return;
141 }
142
143 iface.mtu = mtu.lifr_metric;
144 }
145
146 string networking_resolver::get_primary_interface() const
147 {
148 string interface;
149 each_line("route", { "-n", "get", "default" }, [&interface](string& line){
150 boost::trim(line);
151 if (boost::starts_with(line, "interface: ")) {
152 interface = line.substr(11);
153 boost::trim(interface);
154 return false;
155 }
156 return true;
157 });
158 LOG_DEBUG("got primary interface: \"{1}\"", interface);
159 return interface;
160 }
161
162 bool networking_resolver::is_link_address(const sockaddr* addr) const
163 {
164 // We explicitly populate the MAC address; we don't need address_to_string to support link layer addresses
165 return false;
166 }
167
168 uint8_t const* networking_resolver::get_link_address_bytes(const sockaddr * addr) const
169 {
170 return nullptr;
171 }
172
173 uint8_t networking_resolver::get_link_address_length(const sockaddr * addr) const
174 {
175 return 0;
176 }
177
178 string networking_resolver::find_dhcp_server(string const& interface) const
179 {
180 auto exec = execute("dhcpinfo", { "-i", interface, "ServerID" });
181 if (!exec.success) {
182 return {};
183 }
184 return exec.output;
185 }
186
187 }}} // namespace facter::facts::solaris
+0
-83
lib/src/facts/solaris/operating_system_resolver.cc less more
0 #include <internal/facts/solaris/operating_system_resolver.hpp>
1 #include <facter/facts/os.hpp>
2 #include <facter/facts/os_family.hpp>
3 #include <leatherman/file_util/file.hpp>
4 #include <leatherman/util/regex.hpp>
5
6 using namespace std;
7 using namespace leatherman::util;
8
9 namespace lth_file = leatherman::file_util;
10
11 namespace facter { namespace facts { namespace solaris {
12
13 static string get_family(string const& name)
14 {
15 if (!name.empty()) {
16 static map<string, string> const systems = {
17 { string(os::sunos), string(os_family::solaris) },
18 { string(os::solaris), string(os_family::solaris) },
19 { string(os::nexenta), string(os_family::solaris) },
20 { string(os::omni), string(os_family::solaris) },
21 { string(os::open_indiana), string(os_family::solaris) },
22 { string(os::smart), string(os_family::solaris) },
23 };
24 auto const& it = systems.find(name);
25 if (it != systems.end()) {
26 return it->second;
27 }
28 }
29 return {};
30 }
31
32 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
33 {
34 // Default to the base implementation
35 auto result = posix::operating_system_resolver::collect_data(facts);
36 if (result.name == os::sunos) {
37 result.name = os::solaris;
38 }
39
40 auto family = get_family(result.name);
41 if (!family.empty()) {
42 result.family = move(family);
43 }
44
45 /*
46 Oracle Solaris 10 1/13 s10x_u11wos_24a X86
47 Oracle Solaris 10 9/10 s10s_u9wos_14a SPARC
48 Oracle Solaris 11 11/11 X86
49 Oracle Solaris 11.2 X86
50
51 There are a few places (operatingsystemmajrelease,...) where s10 and s11 differ
52 for similar versioning. For e.g it shifts from `10_u11` to `11 11/11` these needs
53 to be resolved further using the `pkg info kernel` command (TODO).
54 */
55
56 static boost::regex regexp_s10("Solaris \\d+ \\d+/\\d+ s(\\d+)[sx]?_u(\\d+)wos_");
57 static boost::regex regexp_s11("Solaris (\\d+)[.](\\d+)");
58 static boost::regex regexp_s11b("Solaris (\\d+) ");
59 lth_file::each_line("/etc/release", [&](string& line) {
60 string major, minor;
61 if (re_search(line, regexp_s10, &major, &minor)) {
62 result.release = major + "_u" + minor;
63 result.major = move(major);
64 result.minor = move(minor);
65 return false;
66 } else if (re_search(line, regexp_s11, &major, &minor)) {
67 result.release = major + "." + minor;
68 result.major = move(major);
69 result.minor = move(minor);
70 return false;
71 } else if (re_search(line, regexp_s11b, &major)) {
72 result.release = major + ".0";
73 result.major = move(major);
74 result.minor = "0";
75 return false;
76 }
77 return true;
78 });
79 return result;
80 }
81
82 }}} // namespace facter::facts::solaris
+0
-105
lib/src/facts/solaris/processor_resolver.cc less more
0 #include <internal/facts/solaris/processor_resolver.hpp>
1 #include <internal/util/solaris/k_stat.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/execution/execution.hpp>
4 #include <leatherman/util/regex.hpp>
5 #include <unordered_set>
6 #include <sys/processor.h>
7
8 using namespace std;
9 using namespace facter::util::solaris;
10 using namespace leatherman::util;
11 using namespace leatherman::execution;
12
13 /*
14 * https://blogs.oracle.com/mandalika/entry/solaris_show_me_the_cpu
15 * What we want to do is to count the distinct number of chip_id (#nproc),
16 * then the distinct number of core_id (#ncores) and the number of instances
17 * of hardware threads (given by valid procid).
18 *
19 * Our info comes from the following structure
20 *
21 $ kstat -m cpu_info
22 module: cpu_info instance: 0
23 name: cpu_info0 class: misc
24 brand Intel(r) Core(tm) i7-4850HQ CPU @ 2.30GHz
25 cache_id 0
26 chip_id 0
27 clock_MHz 2300
28 clog_id 0
29 core_id 0
30 cpu_type i386
31 crtime 6.654772184
32 current_clock_Hz 2294715939
33 current_cstate 0
34 family 6
35 fpu_type i387 compatible
36 implementation x86 (chipid 0x0 GenuineIntel family 6 model 70 step 1 clock 2300 MHz)
37 model 70
38 ncore_per_chip 1
39 ncpu_per_chip 1
40 pg_id -1
41 pkg_core_id 0
42 snaptime 22631.883297199
43 state on-line
44 state_begin 1409334365
45 stepping 1
46 supported_frequencies_Hz 2294715939
47 supported_max_cstates 1
48 vendor_id GenuineIntel
49 */
50
51 namespace facter { namespace facts { namespace solaris {
52
53 processor_resolver::data processor_resolver::collect_data(collection& facts)
54 {
55 auto result = posix::processor_resolver::collect_data(facts);
56
57 try {
58 unordered_set<int> chips;
59 k_stat kc;
60 auto kv = kc["cpu_info"];
61 for (auto const& ke : kv) {
62 try {
63 ++result.logical_count;
64 result.models.emplace_back(ke.value<string>("brand"));
65 chips.insert(ke.value<int32_t>("chip_id"));
66
67 // Get the speed of the first processor
68 if (result.speed == 0) {
69 result.speed = static_cast<int64_t>(ke.value<uint64_t>("current_clock_Hz"));
70 }
71 } catch (kstat_exception& ex) {
72 LOG_DEBUG("failed to read processor data entry: {1}.", ex.what());
73 }
74 }
75 result.physical_count = chips.size();
76 } catch (kstat_exception& ex) {
77 LOG_DEBUG("failed to read processor data from kstat api: {1}.", ex.what());
78
79 unordered_set<int> chips;
80 string brand;
81 int32_t chip_id;
82 int64_t current_clock_hz;
83
84 static boost::regex brand_rx("^\\s*brand\\s+(.+)$");
85 static boost::regex chip_id_rx("^\\s*chip_id\\s+(\\d+)$");
86 static boost::regex current_clock_hz_rx("^\\s*current_clock_Hz\\s+(\\d+)$");
87
88 each_line("/usr/bin/kstat", {"cpu_info"}, [&] (string& line) {
89 if (re_search(line, brand_rx, &brand)) {
90 result.models.emplace_back(move(brand));
91 } else if (re_search(line, chip_id_rx, &chip_id)) {
92 ++result.logical_count;
93 chips.insert(chip_id);
94 } else if (result.speed == 0 && re_search(line, current_clock_hz_rx, &current_clock_hz)) {
95 result.speed = current_clock_hz;
96 }
97 return true;
98 });
99 result.physical_count = chips.size();
100 }
101
102 return result;
103 }
104 }}} // namespace facter::facts::solaris
+0
-38
lib/src/facts/solaris/virtualization_resolver.cc less more
0 #include <internal/facts/solaris/virtualization_resolver.hpp>
1 #include <facter/facts/vm.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/collection.hpp>
4 #include <facter/facts/fact.hpp>
5 #include <leatherman/execution/execution.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <boost/algorithm/string.hpp>
8 #include <map>
9
10 using namespace std;
11 using namespace facter::facts;
12 using namespace leatherman::execution;
13
14 namespace facter { namespace facts { namespace solaris {
15
16 string virtualization_resolver::get_hypervisor(collection& facts)
17 {
18 // If in an LDom, the LDom facts will resolve and we can use them to identify
19 // that we're in a virtual LDom environment. They should only resolve on SPARC.
20 auto ldom_domainrole_control = facts.get<string_value>("ldom_domainrole_control");
21 if (ldom_domainrole_control && ldom_domainrole_control->value() == "false") {
22 auto ldom_domainrole_impl = facts.get<string_value>("ldom_domainrole_impl");
23 if (ldom_domainrole_impl) {
24 return ldom_domainrole_impl->value();
25 }
26 }
27
28 // works for both x86 & sparc.
29 auto exec = execute("/usr/bin/zonename");
30 if (exec.success && exec.output != "global") {
31 return vm::zone;
32 }
33
34 // Look for hypervisor matches based on other facts
35 return get_fact_vm(facts);
36 }
37 }}} // namespace facter::facts::solaris
+0
-12
lib/src/facts/solaris/zfs_resolver.cc less more
0 #include <internal/facts/solaris/zfs_resolver.hpp>
1
2 using namespace std;
3
4 namespace facter { namespace facts { namespace solaris {
5
6 string zfs_resolver::zfs_command()
7 {
8 return "/sbin/zfs";
9 }
10
11 }}} // namespace facter::facts::solaris
+0
-33
lib/src/facts/solaris/zone_resolver.cc less more
0 #include <internal/facts/solaris/zone_resolver.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/util/regex.hpp>
4 #include <boost/algorithm/string.hpp>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace leatherman::util;
9 using namespace leatherman::execution;
10
11 namespace facter { namespace facts { namespace solaris {
12
13 zone_resolver::data zone_resolver::collect_data(collection& facts)
14 {
15 data result;
16 auto exec = execute("/bin/zonename");
17 if (exec.success) {
18 result.current_zone_name = exec.output;
19 }
20
21 static boost::regex zone_pattern("(\\d+|-):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)");
22
23 each_line("/usr/sbin/zoneadm", {"list", "-cp"}, [&](string& line) {
24 zone z;
25 if (re_search(line, zone_pattern, &z.id, &z.name, &z.status, &z.path, &z.uuid, &z.brand, &z.ip_type)) {
26 result.zones.emplace_back(move(z));
27 }
28 return true;
29 });
30 return result;
31 }
32 }}} // namespace facter::facts::solaris
+0
-12
lib/src/facts/solaris/zpool_resolver.cc less more
0 #include <internal/facts/solaris/zpool_resolver.hpp>
1
2 using namespace std;
3
4 namespace facter { namespace facts { namespace solaris {
5
6 string zpool_resolver::zpool_command()
7 {
8 return "/sbin/zpool";
9 }
10
11 }}} // namespace facter::facts::solaris
+0
-11
lib/src/facts/windows/cache.cc less more
0 #include <internal/facts/cache.hpp>
1 #include <leatherman/windows/file_util.hpp>
2
3 namespace facter { namespace facts { namespace cache {
4
5 std::string fact_cache_location() {
6 return leatherman::windows::file_util::get_programdata_dir() +
7 "\\PuppetLabs\\facter\\cache\\cached_facts\\";
8 }
9
10 }}} // namespace facter::facts::cache
+0
-79
lib/src/facts/windows/collection.cc less more
0 #include <facter/facts/collection.hpp>
1 #include <internal/facts/windows/dmi_resolver.hpp>
2 #include <internal/facts/windows/fips_resolver.hpp>
3 #include <internal/facts/windows/identity_resolver.hpp>
4 #include <internal/facts/windows/kernel_resolver.hpp>
5 #include <internal/facts/windows/memory_resolver.hpp>
6 #include <internal/facts/windows/networking_resolver.hpp>
7 #include <internal/facts/ssh_resolver.hpp>
8 #include <internal/facts/windows/operating_system_resolver.hpp>
9 #include <internal/facts/windows/processor_resolver.hpp>
10 #include <internal/facts/windows/timezone_resolver.hpp>
11 #include <internal/facts/windows/uptime_resolver.hpp>
12 #include <internal/facts/windows/virtualization_resolver.hpp>
13 #include <leatherman/util/environment.hpp>
14 #include <leatherman/windows/file_util.hpp>
15 #include <leatherman/windows/system_error.hpp>
16 #include <leatherman/windows/user.hpp>
17 #include <leatherman/windows/windows.hpp>
18 #include <leatherman/logging/logging.hpp>
19 #include <boost/filesystem.hpp>
20 #include <Shlobj.h>
21
22 using namespace std;
23 using namespace leatherman::windows;
24 using namespace leatherman::util;
25 using namespace facter::facts::external;
26 using namespace boost::filesystem;
27
28 namespace facter { namespace facts {
29
30 vector<string> collection::get_external_fact_directories() const
31 {
32 if (user::is_admin()) {
33 // Get the common data path
34 try {
35 path p = file_util::get_programdata_dir() / "PuppetLabs" / "facter" / "facts.d";
36 return {p.string()};
37 } catch (file_util::unknown_folder_exception &e) {
38 LOG_WARNING("external facts unavailable, %1%", e.what());
39 }
40 } else {
41 auto home = user::home_dir();
42 if (!home.empty()) {
43 path p1 = path(home) / ".puppetlabs" / "opt" / "facter" / "facts.d";
44 path p2 = path(home) / ".facter" / "facts.d";
45 return {p1.string(), p2.string()};
46 }
47
48 LOG_DEBUG("HOME environment variable not set, external facts unavailable");
49 }
50
51 return {};
52 }
53
54 void collection::add_platform_facts()
55 {
56 #ifdef HAS_LTH_GET_DWORD
57 add(make_shared<windows::fips_resolver>());
58 #endif
59 add(make_shared<ssh_resolver>());
60 add(make_shared<windows::identity_resolver>());
61 add(make_shared<windows::kernel_resolver>());
62 add(make_shared<windows::memory_resolver>());
63 add(make_shared<windows::networking_resolver>());
64 add(make_shared<windows::timezone_resolver>());
65 add(make_shared<windows::uptime_resolver>());
66
67 try {
68 shared_ptr<wmi> shared_wmi = make_shared<wmi>();
69 add(make_shared<windows::dmi_resolver>(shared_wmi));
70 add(make_shared<windows::operating_system_resolver>(shared_wmi));
71 add(make_shared<windows::processor_resolver>(shared_wmi));
72 add(make_shared<windows::virtualization_resolver>(shared_wmi));
73 } catch (wmi_exception &e) {
74 LOG_ERROR("failed adding platform facts that require WMI: {1}", e.what());
75 }
76 }
77
78 }} // namespace facter::facts
+0
-39
lib/src/facts/windows/dmi_resolver.cc less more
0 #include <internal/facts/windows/dmi_resolver.hpp>
1 #include <leatherman/windows/wmi.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 using namespace std;
5 using namespace leatherman::windows;
6
7 namespace facter { namespace facts { namespace windows {
8
9 dmi_resolver::dmi_resolver(shared_ptr<wmi> wmi_conn) :
10 resolvers::dmi_resolver(),
11 _wmi(move(wmi_conn))
12 {
13 }
14
15 dmi_resolver::data dmi_resolver::collect_data(collection& facts)
16 {
17 data result;
18
19 auto vals = _wmi->query(wmi::computersystemproduct, {wmi::name, wmi::uuid});
20 if (vals.empty()) {
21 LOG_DEBUG("WMI query returned no results for {1} with values {2} and {3}.", wmi::computersystemproduct, wmi::name, wmi::uuid);
22 } else {
23 result.product_name = wmi::get(vals, wmi::name);
24 result.uuid = wmi::get(vals, wmi::uuid);
25 }
26
27 vals = _wmi->query(wmi::bios, {wmi::manufacturer, wmi::serialnumber});
28 if (vals.empty()) {
29 LOG_DEBUG("WMI query returned no results for {1} with values {2} and {3}.", wmi::bios, wmi::serialnumber, wmi::manufacturer);
30 } else {
31 result.serial_number = wmi::get(vals, wmi::serialnumber);
32 result.manufacturer = wmi::get(vals, wmi::manufacturer);
33 }
34
35 return result;
36 }
37
38 }}} // namespace facter::facts::windows
+0
-32
lib/src/facts/windows/external_resolvers_factory.cc less more
0 #include <facter/facts/external_resolvers_factory.hpp>
1 #include <internal/facts/external/windows/powershell_resolver.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <boost/algorithm/string.hpp>
4 #include <boost/filesystem.hpp>
5
6 using namespace std;
7 using namespace boost::filesystem;
8
9 namespace facter { namespace facts {
10 bool external_resolvers_factory::powershell_resolver_can_resolve(string const& file)
11 {
12 try {
13 path p = file;
14 return boost::iends_with(file, ".ps1") && is_regular_file(p);
15 } catch (filesystem_error &e) {
16 LOG_TRACE("error reading status of path {1}: {2}", file, e.what());
17 return false;
18 }
19 }
20 shared_ptr<external::resolver> external_resolvers_factory::get_resolver(const string& path) {
21 auto resolver = get_common_resolver(path);
22 if (resolver)
23 return resolver;
24 if (powershell_resolver_can_resolve(path)) {
25 return make_shared<external::powershell_resolver>(path);
26 }
27
28 throw external::external_fact_no_resolver(leatherman::locale::_("No resolver for external facts file {1}", path));
29 }
30
31 }} // namespace facter::facts
+0
-31
lib/src/facts/windows/fips_resolver.cc less more
0 #include <internal/facts/windows/fips_resolver.hpp>
1 #include <leatherman/windows/registry.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <boost/lexical_cast.hpp>
4
5 using namespace std;
6
7 using boost::lexical_cast;
8 using boost::bad_lexical_cast;
9 using namespace leatherman::windows;
10
11 namespace facter { namespace facts { namespace windows {
12
13 fips_resolver::data fips_resolver::collect_data(collection& facts)
14 {
15 data result;
16
17 // Set a safe default
18 result.is_fips_mode_enabled = false;
19 unsigned long enabled;
20 try {
21 enabled = registry::get_registry_dword(registry::HKEY::LOCAL_MACHINE,
22 "System\\CurrentControlSet\\Control\\Lsa\\FipsAlgorithmPolicy\\", "Enabled");
23 result.is_fips_mode_enabled = enabled != 0;
24 } catch (registry_exception &e) {
25 LOG_DEBUG("failure getting fips_mode: {1}", e.what());
26 }
27 return result;
28 }
29
30 }}} // namespace facter::facts::windows
+0
-46
lib/src/facts/windows/identity_resolver.cc less more
0 #include <internal/facts/windows/identity_resolver.hpp>
1 #include <leatherman/windows/system_error.hpp>
2 #include <leatherman/windows/user.hpp>
3 #include <leatherman/windows/windows.hpp>
4 #include <leatherman/logging/logging.hpp>
5 #include <boost/nowide/convert.hpp>
6 #include <security.h>
7
8 using namespace std;
9 using namespace leatherman::windows;
10
11 namespace facter { namespace facts { namespace windows {
12
13 identity_resolver::data identity_resolver::collect_data(collection& facts)
14 {
15 data result;
16
17 // Groups are a lot more flexible on Windows than Linux. We only support
18 // identifying the user right now.
19 ULONG size = 0ul;
20 auto nameformat = NameSamCompatible;
21 GetUserNameExW(nameformat, nullptr, &size);
22 if (GetLastError() != ERROR_MORE_DATA) {
23 LOG_DEBUG("failure resolving identity facts: {1}", leatherman::windows::system_error());
24 return result;
25 }
26
27 // Use the string as a raw buffer that supports move and ref operations.
28 wstring buffer(size, '\0');
29 if (!GetUserNameExW(nameformat, &buffer[0], &size)) {
30 LOG_DEBUG("failure resolving identity facts: {1}", leatherman::windows::system_error());
31 return result;
32 }
33
34 // Resize the buffer to the returned string size.
35 buffer.resize(size);
36 result.user_name = boost::nowide::narrow(buffer);
37
38 // Check whether this thread is running with elevated privileges
39 // (or with the privileges of the local Administrators group on
40 // older versions of Windows not supporting privileges elevation).
41 result.privileged = user::is_admin();
42
43 return result;
44 }
45 }}} // facter::facts::windows
+0
-58
lib/src/facts/windows/kernel_resolver.cc less more
0 #include <leatherman/dynamic_library/dynamic_library.hpp>
1 #include <internal/facts/windows/kernel_resolver.hpp>
2 #include <facter/facts/os.hpp>
3 #include <leatherman/logging/logging.hpp>
4
5 #include <boost/optional.hpp>
6 #include <boost/format.hpp>
7 #include <windows.h>
8 #include <ntstatus.h>
9
10 using namespace std;
11 using namespace leatherman::dynamic_library;
12 using RtlGetVersionPtr = NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW);
13
14 namespace facter { namespace facts { namespace windows {
15
16 static boost::optional<string> get_release()
17 {
18 dynamic_library ntoskrnl;
19 if (! ntoskrnl.load("ntoskrnl.exe")) {
20 return boost::none;
21 }
22
23 auto rtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(
24 ntoskrnl.find_symbol("RtlGetVersion"));
25 if (! rtlGetVersion) {
26 return boost::none;
27 }
28
29 OSVERSIONINFOW versionInfo;
30 if (rtlGetVersion(&versionInfo) != STATUS_SUCCESS) {
31 LOG_DEBUG("failed to get the OS version information from RtlGetVersion");
32 return boost::none;
33 }
34
35 auto versionStr = (boost::format("%1%.%2%.%3%")
36 % versionInfo.dwMajorVersion
37 % versionInfo.dwMinorVersion
38 % versionInfo.dwBuildNumber).str();
39
40 return versionStr;
41 }
42
43 kernel_resolver::data kernel_resolver::collect_data(collection& facts)
44 {
45 data result;
46
47 auto release = get_release();
48 if (release) {
49 result.release = move(*release);
50 result.version = result.release;
51 }
52
53 result.name = os::windows;
54 return result;
55 }
56
57 }}} // namespace facter::facts::windows
+0
-25
lib/src/facts/windows/memory_resolver.cc less more
0 #include <internal/facts/windows/memory_resolver.hpp>
1 #include <leatherman/windows/system_error.hpp>
2 #include <leatherman/windows/windows.hpp>
3 #include <leatherman/logging/logging.hpp>
4 #include <psapi.h>
5
6 using namespace leatherman::windows;
7
8 namespace facter { namespace facts { namespace windows {
9
10 memory_resolver::data memory_resolver::collect_data(collection& facts)
11 {
12 PERFORMANCE_INFORMATION statex;
13 if (!GetPerformanceInfo(&statex, sizeof(statex))) {
14 LOG_DEBUG("resolving memory facts failed: {1}", system_error());
15 return {};
16 }
17
18 data result;
19 result.mem_total = statex.PhysicalTotal*statex.PageSize;
20 result.mem_free = statex.PhysicalAvailable*statex.PageSize;
21 return result;
22 }
23
24 }}} // namespace facter::facts::windows
+0
-231
lib/src/facts/windows/networking_resolver.cc less more
0 #include <internal/facts/windows/networking_resolver.hpp>
1 #include <leatherman/windows/registry.hpp>
2 #include <leatherman/windows/system_error.hpp>
3 #include <internal/util/windows/wsa.hpp>
4 #include <leatherman/windows/windows.hpp>
5 #include <leatherman/logging/logging.hpp>
6 #include <boost/range/combine.hpp>
7 #include <boost/nowide/convert.hpp>
8 #include <iomanip>
9 #include <Ws2tcpip.h>
10 #include <iphlpapi.h>
11
12 #ifdef interface
13 // Something's bleeding in and making it impossible to instantiate an interface object.
14 #undef interface
15 #endif
16
17 using namespace std;
18 using namespace facter::util;
19 using namespace facter::util::windows;
20 using namespace leatherman::windows;
21
22 namespace facter { namespace facts { namespace windows {
23
24 networking_resolver::networking_resolver()
25 {
26 }
27
28 static string get_computername(COMPUTER_NAME_FORMAT nameFormat)
29 {
30 DWORD size = 0u;
31 GetComputerNameExW(nameFormat, nullptr, &size);
32 if (GetLastError() != ERROR_MORE_DATA) {
33 LOG_DEBUG("failure resolving hostname: {1}", leatherman::windows::system_error());
34 return "";
35 }
36
37 wstring buffer(size, '\0');
38 if (!GetComputerNameExW(nameFormat, &buffer[0], &size)) {
39 LOG_DEBUG("failure resolving hostname: {1}", leatherman::windows::system_error());
40 return "";
41 }
42
43 buffer.resize(size);
44 return boost::nowide::narrow(buffer);
45 }
46
47 sockaddr_in networking_resolver::create_ipv4_mask(uint8_t masklen)
48 {
49 sockaddr_in mask = {AF_INET};
50 if (ConvertLengthToIpv4Mask(masklen, &mask.sin_addr.S_un.S_addr) != NO_ERROR) {
51 LOG_DEBUG("failed creating IPv4 mask of length {1}", masklen);
52 }
53 return mask;
54 }
55
56 sockaddr_in6 networking_resolver::create_ipv6_mask(uint8_t masklen)
57 {
58 sockaddr_in6 mask = {AF_INET6};
59 const uint8_t incr = 32u;
60 for (size_t i = 0; i < 16 && masklen > 0; i += 4, masklen -= min(masklen, incr)) {
61 if (ConvertLengthToIpv4Mask(min(masklen, incr), reinterpret_cast<PULONG>(&mask.sin6_addr.u.Byte[i])) != NO_ERROR) {
62 LOG_DEBUG("failed creating IPv6 mask with component of length {1}", incr);
63 break;
64 }
65 }
66 return mask;
67 }
68
69 sockaddr_in networking_resolver::mask_ipv4_address(sockaddr const* addr, sockaddr_in const& mask)
70 {
71 sockaddr_in masked = *reinterpret_cast<sockaddr_in const*>(addr);
72 masked.sin_addr.S_un.S_addr &= mask.sin_addr.S_un.S_addr;
73 return masked;
74 }
75
76 sockaddr_in6 networking_resolver::mask_ipv6_address(sockaddr const* addr, sockaddr_in6 const& mask)
77 {
78 sockaddr_in6 masked = *reinterpret_cast<sockaddr_in6 const*>(addr);
79 for (size_t i = 0; i < 8; ++i) {
80 masked.sin6_addr.u.Word[i] &= mask.sin6_addr.u.Word[i];
81 }
82 return masked;
83 }
84
85 networking_resolver::data networking_resolver::collect_data(collection& facts)
86 {
87 data result;
88
89 result.hostname = get_computername(ComputerNameDnsHostname);
90
91 try {
92 result.domain = registry::get_registry_string(registry::HKEY::LOCAL_MACHINE,
93 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\", "Domain");
94 } catch (registry_exception &e) {
95 LOG_DEBUG("failure getting networking::domain fact: {1}", e.what());
96 }
97
98 // Get linked list of adapters.
99 ULONG family = AF_UNSPEC;
100 ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
101
102 // Pre-allocate and try several times, because the adapter configuration may change between calls.
103 ULONG outBufLen = 15000;
104 vector<char> pAddresses(outBufLen);
105 DWORD err;
106 for (int i = 0; i < 3; ++i) {
107 err = GetAdaptersAddresses(family, flags, nullptr,
108 reinterpret_cast<PIP_ADAPTER_ADDRESSES>(pAddresses.data()), &outBufLen);
109 if (err == ERROR_SUCCESS) {
110 break;
111 } else if (err == ERROR_BUFFER_OVERFLOW) {
112 pAddresses.resize(outBufLen);
113 } else {
114 LOG_DEBUG("failure resolving networking facts: {1}", leatherman::windows::system_error(err));
115 return result;
116 }
117 }
118
119 if (err != ERROR_SUCCESS) {
120 LOG_DEBUG("failure resolving networking facts: {1}", leatherman::windows::system_error(err));
121 return result;
122 }
123
124 facter::util::windows::wsa winsock;
125
126 for (auto pCurAddr = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(pAddresses.data());
127 pCurAddr; pCurAddr = pCurAddr->Next) {
128 if (pCurAddr->OperStatus != IfOperStatusUp ||
129 (pCurAddr->IfType != IF_TYPE_ETHERNET_CSMACD && pCurAddr->IfType != IF_TYPE_IEEE80211)) {
130 continue;
131 }
132
133 if (result.domain.empty()) {
134 // If domain isn't set in the registry, fall back to the first DnsDomain encountered in the adapters.
135 result.domain = boost::nowide::narrow(pCurAddr->DnsSuffix);
136 }
137
138 interface net_interface;
139 net_interface.name = boost::nowide::narrow(pCurAddr->FriendlyName);
140
141 // Only supported on platforms after Windows Server 2003.
142 if (pCurAddr->Flags & IP_ADAPTER_DHCP_ENABLED && pCurAddr->Length >= sizeof(IP_ADAPTER_ADDRESSES_LH)) {
143 auto adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH&>(*pCurAddr);
144 if (adapter.Flags & IP_ADAPTER_DHCP_ENABLED) {
145 try {
146 net_interface.dhcp_server = winsock.saddress_to_string(adapter.Dhcpv4Server);
147 } catch (wsa_exception &e) {
148 LOG_DEBUG("failed to retrieve dhcp v4 server address for {1}: {2}", net_interface.name, e.what());
149 }
150 }
151 }
152
153 for (auto it = pCurAddr->FirstUnicastAddress; it; it = it->Next) {
154 string addr;
155 try {
156 addr = winsock.saddress_to_string(it->Address);
157 } catch (wsa_exception &e) {
158 string iptype =
159 (it->Address.lpSockaddr->sa_family == AF_INET) ? " v4"
160 : (it->Address.lpSockaddr->sa_family == AF_INET6) ? " v6"
161 : "";
162 LOG_DEBUG("failed to retrieve ip{1} address for {2}: {3}",
163 iptype, net_interface.name, e.what());
164 }
165
166 if (addr.empty()) {
167 continue;
168 }
169
170 if (it->Address.lpSockaddr->sa_family == AF_INET || it->Address.lpSockaddr->sa_family == AF_INET6) {
171 bool ipv6 = it->Address.lpSockaddr->sa_family == AF_INET6;
172
173 binding b;
174
175 // IpAddress6 contains interface identifier, and should be removed when returning the ipaddress6
176 b.address = ipv6 ? addr.substr(0, addr.find('%')) : addr;
177
178 // Need to do lookup based on the structure length.
179 auto adapterAddr = reinterpret_cast<IP_ADAPTER_UNICAST_ADDRESS_LH&>(*it);
180 if (ipv6) {
181 auto mask = create_ipv6_mask(adapterAddr.OnLinkPrefixLength);
182 auto masked = mask_ipv6_address(it->Address.lpSockaddr, mask);
183 b.netmask = winsock.address_to_string(mask);
184 b.network = winsock.address_to_string(masked);
185 // Network6 also contains the interface identifier, so it should be removed
186 b.network = b.network.substr(0, b.network.find('%'));
187 } else {
188 auto mask = create_ipv4_mask(adapterAddr.OnLinkPrefixLength);
189 auto masked = mask_ipv4_address(it->Address.lpSockaddr, mask);
190 b.netmask = winsock.address_to_string(mask);
191 b.network = winsock.address_to_string(masked);
192 }
193
194 if (ipv6) {
195 net_interface.ipv6_bindings.emplace_back(std::move(b));
196 } else {
197 net_interface.ipv4_bindings.emplace_back(std::move(b));
198 }
199
200 // http://support.microsoft.com/kb/894564 talks about how binding order is determined.
201 // GetAdaptersAddresses returns adapters in binding order. This way, the domain and primary_interface match.
202 // The old facter behavior didn't make a lot of sense (it would pick the last in binding order, not 1st).
203 // Only accept this as a primary interface if it has a non-link-local address.
204 if (result.primary_interface.empty() && (
205 (it->Address.lpSockaddr->sa_family == AF_INET && !ignored_ipv4_address(addr)) ||
206 (it->Address.lpSockaddr->sa_family == AF_INET6 && !ignored_ipv6_address(addr)))) {
207 result.primary_interface = net_interface.name;
208 }
209 }
210 }
211
212 stringstream macaddr;
213 for (DWORD i = 0u; i < pCurAddr->PhysicalAddressLength; ++i) {
214 macaddr << setfill('0') << setw(2) << hex << uppercase <<
215 static_cast<int>(pCurAddr->PhysicalAddress[i]) << ':';
216 }
217 net_interface.macaddress = macaddr.str();
218 if (!net_interface.macaddress.empty()) {
219 net_interface.macaddress.pop_back();
220 }
221
222 net_interface.mtu = pCurAddr->Mtu;
223
224 result.interfaces.emplace_back(move(net_interface));
225 }
226
227 return result;
228 }
229
230 }}} // namespace facter::facts::windows
+0
-205
lib/src/facts/windows/operating_system_resolver.cc less more
0 #include <internal/facts/windows/operating_system_resolver.hpp>
1 #include <leatherman/util/environment.hpp>
2 #include <leatherman/windows/registry.hpp>
3 #include <leatherman/windows/system_error.hpp>
4 #include <leatherman/windows/wmi.hpp>
5 #include <leatherman/windows/windows.hpp>
6 #include <facter/facts/fact.hpp>
7 #include <facter/facts/collection.hpp>
8 #include <facter/facts/scalar_value.hpp>
9 #include <facter/facts/os_family.hpp>
10 #include <leatherman/logging/logging.hpp>
11 #include <leatherman/util/regex.hpp>
12 #include <intrin.h>
13 #include <winnt.h>
14 #include <Shlobj.h>
15 #include <map>
16 #include <string>
17 #include <boost/filesystem.hpp>
18
19 using namespace std;
20 using namespace leatherman::util;
21 using namespace leatherman::windows;
22 using namespace boost::filesystem;
23
24 namespace facter { namespace facts { namespace windows {
25
26 static string get_hardware()
27 {
28 SYSTEM_INFO sysInfo;
29 GetNativeSystemInfo(&sysInfo);
30
31 switch (sysInfo.wProcessorArchitecture) {
32 case PROCESSOR_ARCHITECTURE_AMD64:
33 return "x86_64";
34 case PROCESSOR_ARCHITECTURE_ARM:
35 return "arm";
36 case PROCESSOR_ARCHITECTURE_IA64:
37 return "ia64";
38 case PROCESSOR_ARCHITECTURE_INTEL:
39 return "i" + to_string((sysInfo.wProcessorLevel > 5) ? 6 : sysInfo.wProcessorLevel) + "86";
40 default:
41 return "unknown";
42 }
43 }
44
45 static string get_architecture(string const& hardware)
46 {
47 // Use "x86" for 32-bit systems
48 if (re_search(hardware, boost::regex("i[3456]86"))) {
49 return "x86";
50 } else if (hardware == "x86_64") {
51 return "x64";
52 }
53 return hardware;
54 }
55
56 static string get_system32()
57 {
58 // When facter is a 32-bit process running on 64-bit windows (such as in a 32-bit puppet installation that
59 // includes native facter), system32 points to 32-bit executables; Windows invisibly redirects it. It also
60 // provides a link at %SYSTEMROOT%\sysnative for the 64-bit versions. Return the system path where OS-native
61 // executables can be found.
62 BOOL isWow = FALSE;
63 if (!IsWow64Process(GetCurrentProcess(), &isWow)) {
64 LOG_DEBUG("Could not determine if we are running in WOW64: {1}",
65 leatherman::windows::system_error());
66 }
67
68 string szPath;
69 if (!environment::get("SystemRoot", szPath)) {
70 LOG_DEBUG("error finding SYSTEMROOT: {1}",
71 leatherman::windows::system_error());
72 }
73
74 if (isWow) {
75 return ((path(szPath) / "sysnative").string());
76 } else {
77 return ((path(szPath) / "system32").string());
78 }
79 }
80
81 static string get_release_id()
82 {
83 string releaseID;
84 try {
85 releaseID = registry::get_registry_string(registry::HKEY::LOCAL_MACHINE,
86 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\", "ReleaseId");
87 } catch (registry_exception &e) {
88 LOG_DEBUG("failure getting ReleaseId: {1}", e.what());
89 }
90 return releaseID;
91 }
92
93 static string get_edition_id()
94 {
95 string editionID;
96 try {
97 editionID = registry::get_registry_string(registry::HKEY::LOCAL_MACHINE,
98 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\", "EditionID");
99 } catch (registry_exception &e) {
100 LOG_DEBUG("failure getting EditionID: {1}", e.what());
101 }
102 return editionID;
103 }
104
105 static string get_installation_type()
106 {
107 string installation_type;
108 try {
109 installation_type = registry::get_registry_string(registry::HKEY::LOCAL_MACHINE,
110 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\", "InstallationType");
111 } catch (registry_exception &e) {
112 LOG_DEBUG("failure getting InstallationType: {1}", e.what());
113 }
114 return installation_type;
115 }
116
117 static string get_product_name()
118 {
119 string product_name;
120 try {
121 product_name = registry::get_registry_string(registry::HKEY::LOCAL_MACHINE,
122 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\", "ProductName");
123 } catch (registry_exception &e) {
124 LOG_DEBUG("failure getting ProductName: {1}", e.what());
125 }
126 return product_name;
127 }
128
129
130 operating_system_resolver::operating_system_resolver(shared_ptr<wmi> wmi_conn) :
131 resolvers::operating_system_resolver(),
132 _wmi(move(wmi_conn))
133 {
134 }
135
136 operating_system_resolver::data operating_system_resolver::collect_data(collection& facts)
137 {
138 // Default to the base implementation
139 data result = resolvers::operating_system_resolver::collect_data(facts);
140
141 result.family = os_family::windows;
142 result.hardware = get_hardware();
143 result.architecture = get_architecture(result.hardware);
144 result.win.system32 = get_system32();
145 result.win.release_id = get_release_id();
146 result.win.edition_id = get_edition_id();
147 result.win.installation_type = get_installation_type();
148 result.win.product_name = get_product_name();
149
150 auto lastDot = result.release.rfind('.');
151 if (lastDot == string::npos) {
152 return result;
153 }
154
155 auto vals = _wmi->query(wmi::operatingsystem, {wmi::producttype, wmi::othertypedescription});
156 if (vals.empty()) {
157 return result;
158 }
159
160 // Override default release with Windows release names
161 auto version = result.release.substr(0, lastDot);
162 bool consumerrel = (wmi::get(vals, wmi::producttype) == "1");
163 if (version == "10.0") {
164 // Calculate the build number to distinguish between
165 // Windows Server 2016 and 2019. Note that the kernel
166 // version is written as <major>.<minor>.<build_number>
167 auto kernel_version_fact = facts.get<string_value>(fact::kernel_version);
168 if (! kernel_version_fact) {
169 LOG_DEBUG("Could not resolve the OS release and OS major version facts from the kernel version fact");
170 return result;
171 }
172 auto kernel_version = kernel_version_fact->value();
173 auto build_number_as_str = kernel_version.substr(
174 kernel_version.find_last_of('.') + 1);
175 auto build_number = stol(build_number_as_str);
176
177 if (consumerrel) {
178 result.release = "10";
179 } else if (build_number >= 17623L) {
180 result.release = "2019";
181 } else {
182 result.release = "2016";
183 }
184 } else if (version == "6.3") {
185 result.release = consumerrel ? "8.1" : "2012 R2";
186 } else if (version == "6.2") {
187 result.release = consumerrel ? "8" : "2012";
188 } else if (version == "6.1") {
189 result.release = consumerrel ? "7" : "2008 R2";
190 } else if (version == "6.0") {
191 result.release = consumerrel ? "Vista" : "2008";
192 } else if (version == "5.2") {
193 if (consumerrel) {
194 result.release = "XP";
195 } else {
196 result.release = (wmi::get(vals, wmi::othertypedescription) == "R2") ? "2003 R2" : "2003";
197 }
198 }
199 result.major = result.release;
200
201 return result;
202 }
203
204 }}} // namespace facter::facts::windows
+0
-99
lib/src/facts/windows/processor_resolver.cc less more
0 #include <facter/util/string.hpp>
1 #include <internal/facts/windows/processor_resolver.hpp>
2 #include <leatherman/windows/wmi.hpp>
3 #include <leatherman/util/regex.hpp>
4 #include <leatherman/logging/logging.hpp>
5
6 using namespace std;
7 using namespace leatherman::util;
8 using namespace leatherman::windows;
9 using facter::util::maybe_stoi;
10
11 namespace facter { namespace facts { namespace windows {
12
13 processor_resolver::processor_resolver(shared_ptr<wmi> wmi_conn) :
14 resolvers::processor_resolver(),
15 _wmi(move(wmi_conn))
16 {
17 }
18
19 // Returns physical_count, logical_count, models, isa, speed
20 static tuple<int, int, vector<string>, string, int64_t> get_processors(wmi const& _wmi)
21 {
22 vector<string> models;
23 string isa;
24 int logical_count = 0;
25
26 auto procs = _wmi.query(wmi::processor, {wmi::name, wmi::architecture});
27 if (procs.empty()) {
28 LOG_DEBUG("WMI processor Name, Architecture query returned no results.");
29 }
30
31 for (auto const& procobj : procs) {
32 models.emplace_back(wmi::get(procobj, wmi::name));
33 if (!isa.empty()) {
34 continue;
35 }
36
37 // Query for architecture and transform numerical ID into a string based on
38 // https://msdn.microsoft.com/en-us/library/aa394373%28v=vs.85%29.aspx.
39 // Use the architecture of the first result.
40 auto maybe_architecture = maybe_stoi(wmi::get(procobj, wmi::architecture));
41 if (!maybe_architecture) {
42 continue;
43 }
44
45 int architecture = maybe_architecture.get();
46 switch (architecture) {
47 case 0:
48 isa = "x86";
49 break;
50 case 1:
51 isa = "MIPS";
52 break;
53 case 2:
54 isa = "Alpha";
55 break;
56 case 3:
57 isa = "PowerPC";
58 break;
59 case 5:
60 isa = "ARM";
61 break;
62 case 6:
63 isa = "Itanium-based systems";
64 break;
65 case 9:
66 isa = "x64";
67 break;
68 default:
69 LOG_DEBUG("Unable to determine processor type: unknown architecture");
70 break;
71 }
72 }
73
74 // Query number of logical processors separately; it's not supported on Server 2003, and will cause
75 // the entire query to return empty if used.
76 auto logicalprocs = _wmi.query(wmi::processor, {wmi::numberoflogicalprocessors});
77 for (auto const& objs : logicalprocs) {
78 auto maybe_logical_count = maybe_stoi(wmi::get(objs, wmi::numberoflogicalprocessors));
79 if (maybe_logical_count) {
80 logical_count += maybe_logical_count.get();
81 }
82 }
83
84 if (logical_count == 0) {
85 logical_count = models.size();
86 }
87
88 return make_tuple(models.size(), logical_count, move(models), move(isa), 0);
89 }
90
91 processor_resolver::data processor_resolver::collect_data(collection& facts)
92 {
93 data result;
94 tie(result.physical_count, result.logical_count, result.models, result.isa, result.speed) = get_processors(*_wmi);
95 return result;
96 }
97
98 }}} // namespace facter::facts::windows
+0
-44
lib/src/facts/windows/ssh_resolver.cc less more
0 #include <internal/facts/ssh_resolver.hpp>
1
2 #include <leatherman/util/environment.hpp>
3 #include <leatherman/windows/file_util.hpp>
4 #include <leatherman/windows/system_error.hpp>
5 #include <leatherman/windows/user.hpp>
6 #include <leatherman/windows/windows.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <boost/algorithm/string.hpp>
9
10 using namespace std;
11 using namespace boost::filesystem;
12 using namespace leatherman::util;
13
14 namespace bs = boost::system;
15
16 namespace facter { namespace facts {
17
18 path ssh_resolver::retrieve_key_file(string const& filename)
19 {
20 string dataPath;
21
22 if (!environment::get("programdata", dataPath)) {
23 LOG_DEBUG("error finding programdata: {1}", leatherman::windows::system_error());
24 }
25
26 // https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement
27 // Since there is no user associated with the sshd service, the host keys are stored under \ProgramData\ssh.
28 string sshPath = ((path(dataPath) / "ssh").string());
29
30 // Search in sshPath for the fact's key file
31 path key_file;
32 key_file = sshPath;
33 key_file /= filename;
34
35 bs::error_code ec;
36 if (!is_regular_file(key_file, ec)) {
37 key_file.clear();
38 }
39
40 return key_file;
41 }
42
43 }} // namespace facter::facts
+0
-28
lib/src/facts/windows/timezone_resolver.cc less more
0 #include <internal/facts/windows/timezone_resolver.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <boost/nowide/convert.hpp>
3 #include <ctime>
4
5 using namespace std;
6
7 namespace facter { namespace facts { namespace windows {
8
9 string timezone_resolver::get_timezone()
10 {
11 time_t since_epoch = time(NULL);
12 tm localtime;
13
14 if (localtime_s(&localtime, &since_epoch)) {
15 LOG_WARNING("localtime failed: timezone is unavailable: {1} ({2})", strerror(errno), errno);
16 return {};
17 }
18
19 wchar_t buffer[256] = {};
20 if (wcsftime(buffer, (sizeof(buffer) / sizeof(wchar_t)) - 1, L"%Z", &localtime) == 0) {
21 LOG_WARNING("wcsftime failed: timezone is unavailable: {1} ({2})", strerror(errno), errno);
22 return {};
23 }
24 return boost::nowide::narrow(buffer);
25 }
26
27 }}} // facter::facts::windows
+0
-26
lib/src/facts/windows/uptime_resolver.cc less more
0 #include <internal/facts/windows/uptime_resolver.hpp>
1 #include <leatherman/windows/windows.hpp>
2 #include <leatherman/util/regex.hpp>
3 #include <leatherman/locale/locale.hpp>
4
5 // Mark string for translation (alias for leatherman::locale::format)
6 using leatherman::locale::_;
7
8 namespace facter { namespace facts { namespace windows {
9
10 using namespace std;
11 using namespace leatherman::util;
12 using namespace leatherman::windows;
13
14 uptime_resolver::uptime_resolver() :
15 resolvers::uptime_resolver()
16 {
17 }
18
19 int64_t uptime_resolver::get_uptime()
20 {
21 uint64_t tickCount = GetTickCount64();
22 return (int64_t)(tickCount / 1000); // seconds
23 }
24
25 }}} // namespace facter::facts::windows
+0
-64
lib/src/facts/windows/virtualization_resolver.cc less more
0 #include <internal/facts/windows/virtualization_resolver.hpp>
1 #include <leatherman/windows/wmi.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/collection.hpp>
4 #include <facter/facts/fact.hpp>
5 #include <facter/facts/vm.hpp>
6 #include <leatherman/logging/logging.hpp>
7 #include <vector>
8 #include <tuple>
9
10 using namespace std;
11 using namespace facter::facts;
12 using namespace leatherman::windows;
13
14 namespace facter { namespace facts { namespace windows {
15
16 virtualization_resolver::virtualization_resolver(shared_ptr<wmi> wmi_conn) :
17 resolvers::virtualization_resolver(),
18 _wmi(move(wmi_conn))
19 {
20 }
21
22 string virtualization_resolver::get_hypervisor(collection& facts)
23 {
24 static vector<tuple<string, string>> vms = {
25 make_tuple("VirtualBox", string(vm::virtualbox)),
26 make_tuple("VMware", string(vm::vmware)),
27 make_tuple("KVM", string(vm::kvm)),
28 make_tuple("Bochs", string(vm::bochs)),
29 make_tuple("Google", string(vm::gce)),
30 make_tuple("OpenStack", string(vm::openstack)),
31 make_tuple("AHV", string(vm::ahv)),
32 };
33
34 auto vals = _wmi->query(wmi::computersystem, {wmi::manufacturer, wmi::model});
35 if (vals.empty()) {
36 return {};
37 }
38
39 auto &manufacturer = wmi::get(vals, wmi::manufacturer);
40 auto &model = wmi::get(vals, wmi::model);
41
42 for (auto const& vm : vms) {
43 if (model.find(get<0>(vm)) != string::npos) {
44 return get<1>(vm);
45 }
46 }
47
48 if (model.find("Virtual Machine") != string::npos && manufacturer.find("Microsoft") != string::npos) {
49 return vm::hyperv;
50 }
51
52 if (manufacturer.find("Xen") != string::npos) {
53 return vm::xen;
54 }
55
56 if (manufacturer.find("Amazon EC2") != string::npos) {
57 return vm::kvm;
58 }
59
60 return {};
61 }
62
63 }}} // namespace facter::facts::windows
+0
-185
lib/src/java/facter.cc less more
0 #include "com_puppetlabs_Facter.h"
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/logging/logging.hpp>
6 #include <facter/export.h>
7 #include <boost/nowide/iostream.hpp>
8 #include <string>
9 #include <memory>
10
11 using namespace std;
12 using namespace facter::facts;
13 using namespace facter::logging;
14
15 static jclass object_class, long_class, double_class, boolean_class, hash_class;
16 static jmethodID long_constructor, double_constructor, boolean_constructor, hash_constructor, hash_put;
17 static std::unique_ptr<collection const> facts_collection;
18
19 static string to_string(JNIEnv* env, jstring str)
20 {
21 if (!str) {
22 return {};
23 }
24
25 auto ptr = env->GetStringUTFChars(str, nullptr);
26 if (!ptr) {
27 return {};
28 }
29
30 auto size = env->GetStringUTFLength(str);
31 string result(ptr, size);
32
33 env->ReleaseStringUTFChars(str, ptr);
34 return result;
35 }
36
37 static jclass find_class(JNIEnv* env, char const* name)
38 {
39 // Find the class and return a global reference
40 auto klass = env->FindClass(name);
41 if (!klass) {
42 return nullptr;
43 }
44 return static_cast<jclass>(env->NewGlobalRef(klass));
45 }
46
47 static jobject to_object(JNIEnv* env, value const* val)
48 {
49 if (!val) {
50 return nullptr;
51 }
52 if (auto ptr = dynamic_cast<string_value const*>(val)) {
53 return env->NewStringUTF(ptr->value().c_str());
54 }
55 if (auto ptr = dynamic_cast<integer_value const*>(val)) {
56 return env->NewObject(long_class, long_constructor, static_cast<jlong>(ptr->value()));
57 }
58 if (auto ptr = dynamic_cast<boolean_value const*>(val)) {
59 return env->NewObject(boolean_class, boolean_constructor, static_cast<jboolean>(ptr->value()));
60 }
61 if (auto ptr = dynamic_cast<double_value const*>(val)) {
62 return env->NewObject(double_class, double_constructor, static_cast<jdouble>(ptr->value()));
63 }
64 if (auto ptr = dynamic_cast<array_value const*>(val)) {
65 auto array = env->NewObjectArray(ptr->size(), object_class, nullptr);
66
67 // Recurse on each element of the array
68 jsize index = 0;
69 ptr->each([&](value const* element) {
70 env->SetObjectArrayElement(array, index++, to_object(env, element));
71 return true;
72 });
73 return array;
74 }
75 if (auto ptr = dynamic_cast<map_value const*>(val)) {
76 auto hashmap = env->NewObject(hash_class, hash_constructor, static_cast<jint>(ptr->size()));
77
78 // Recurse on each element in the map
79 ptr->each([&](string const& name, value const* element) {
80 env->CallObjectMethod(hashmap, hash_put, env->NewStringUTF(name.c_str()), to_object(env, element));
81 return true;
82 });
83 return hashmap;
84 }
85 return nullptr;
86 }
87
88 extern "C" {
89 LIBFACTER_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
90 {
91 JNIEnv* env;
92 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
93 return JNI_ERR;
94 }
95 // For the classes, we need global refs
96 object_class = find_class(env, "java/lang/Object");
97 if (!object_class) {
98 return JNI_ERR;
99 }
100 long_class = find_class(env, "java/lang/Long");
101 if (!long_class) {
102 return JNI_ERR;
103 }
104 double_class = find_class(env, "java/lang/Double");
105 if (!double_class) {
106 return JNI_ERR;
107 }
108 boolean_class = find_class(env, "java/lang/Boolean");
109 if (!boolean_class) {
110 return JNI_ERR;
111 }
112 hash_class = find_class(env, "java/util/HashMap");
113 if (!hash_class) {
114 return JNI_ERR;
115 }
116
117 // For the method ids, we can keep these cached as is
118 long_constructor = env->GetMethodID(long_class, "<init>", "(J)V");
119 double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
120 boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");;
121 hash_constructor = env->GetMethodID(hash_class, "<init>", "(I)V");
122 hash_put = env->GetMethodID(hash_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
123
124 setup_logging(boost::nowide::cerr);
125 set_level(level::warning);
126
127 // Create a non-const collection to start; we'll move it to the const pointer at the end of initialization
128 unique_ptr<collection> facts(new collection());
129
130 // Do not load the ruby facts as we're running under JRuby and not MRI
131 facts->add_default_facts(false);
132 facts->add_external_facts();
133
134 // Resolve all facts now for thread-safety
135 // We only support reading facts from JRuby, so the collection is assumed to be immutable after this
136 facts->resolve_facts();
137
138 // Move to the const pointer to prevent future modifications
139 facts_collection = std::move(facts);
140 return JNI_VERSION_1_6;
141 }
142
143 LIBFACTER_EXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
144 {
145 // Delete the fact collection
146 facts_collection.reset();
147
148 JNIEnv* env;
149 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
150 return;
151 }
152 // Free all of the global references above
153 if (object_class) {
154 env->DeleteGlobalRef(object_class);
155 object_class = nullptr;
156 }
157 if (long_class) {
158 env->DeleteGlobalRef(long_class);
159 long_class = nullptr;
160 }
161 if (double_class) {
162 env->DeleteGlobalRef(double_class);
163 double_class = nullptr;
164 }
165 if (boolean_class) {
166 env->DeleteGlobalRef(boolean_class);
167 boolean_class = nullptr;
168 }
169 if (hash_class) {
170 env->DeleteGlobalRef(hash_class);
171 hash_class = nullptr;
172 }
173 }
174
175 LIBFACTER_EXPORT jobject JNICALL Java_com_puppetlabs_Facter_lookup(JNIEnv* env, jclass klass, jstring name)
176 {
177 // Ensure initialized
178 if (!facts_collection) {
179 return nullptr;
180 }
181
182 return to_object(env, facts_collection->get_resolved(to_string(env, name)));
183 }
184 } // extern "C"
+0
-151
lib/src/logging/logging.cc less more
0 #include <facter/logging/logging.hpp>
1 #include <leatherman/util/environment.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/locale/locale.hpp>
4 #include <boost/filesystem.hpp>
5
6 // Mark string for translation (alias for leatherman::locale::format)
7 using leatherman::locale::_;
8
9 using namespace std;
10 using namespace leatherman::util;
11 namespace lm = leatherman::logging;
12
13 #ifdef LEATHERMAN_USE_LOCALES
14 static void setup_logging_internal(ostream& os, bool use_locale)
15 {
16 // Initialize boost filesystem's locale to a UTF-8 default.
17 // Logging gets setup the same way via the default 2nd argument.
18 if (use_locale) {
19 boost::filesystem::path::imbue(leatherman::locale::get_locale());
20 }
21 lm::setup_logging(os, "", PROJECT_NAME, use_locale);
22 }
23 #else
24 static void setup_logging_internal(ostream& os, bool)
25 {
26 lm::setup_logging(os, "", PROJECT_NAME, false);
27 }
28 #endif
29
30 static const char* lc_vars[] = {
31 "LC_CTYPE",
32 "LC_NUMERIC",
33 "LC_TIME",
34 "LC_COLLATE",
35 "LC_MONETARY",
36 "LC_MESSAGES",
37 "LC_PAPER",
38 "LC_ADDRESS",
39 "LC_TELEPHONE",
40 "LC_MEASUREMENT",
41 "LC_IDENTIFICATION",
42 "LC_ALL"
43 };
44
45 namespace facter { namespace logging {
46 istream& operator>>(istream& in, level& lvl)
47 {
48 lm::log_level lm_level;
49 in >> lm_level;
50 lvl = static_cast<level>(lm_level);
51 return in;
52 }
53
54 ostream& operator<<(ostream& os, level lvl)
55 {
56 os << static_cast<lm::log_level>(lvl);
57 return os;
58 }
59
60 void setup_logging(ostream& os)
61 {
62 try {
63 setup_logging_internal(os, true);
64 } catch (exception const&) {
65 for (auto var : lc_vars) {
66 environment::clear(var);
67 }
68 environment::set("LANG", "C");
69 environment::set("LC_ALL", "C");
70 try {
71 setup_logging_internal(os, true);
72 // We can't log the issue until after logging is setup. Rely on the exception for any other
73 // error reporting.
74 log(level::warning, "locale environment variables were bad; continuing with LANG=C LC_ALL=C");
75 } catch (exception const& e) {
76 // If we fail again even with a clean environment, we
77 // need to signal to our consumer that things went
78 // sideways.
79 //
80 // Since logging is busted, we raise an exception that
81 // signals to the consumer that a special action must
82 // be taken to alert the user.
83 try {
84 setup_logging_internal(os, false);
85 log(level::warning, "Could not initialize locale, even with LC_* variables cleared. Continuing without localization support");
86 } catch (exception const& e) {
87 throw locale_error(_("could not initialize logging, even with locale variables reset to LANG=C LC_ALL=C: {1}", e.what()));
88 }
89 }
90 }
91 }
92
93 void set_level(level lvl)
94 {
95 lm::set_level(static_cast<lm::log_level>(lvl));
96 }
97
98 level get_level()
99 {
100 return static_cast<level>(lm::get_level());
101 }
102
103 void set_colorization(bool color)
104 {
105 lm::set_colorization(color);
106 }
107
108 bool get_colorization()
109 {
110 return lm::get_colorization();
111 }
112
113 bool is_enabled(level lvl)
114 {
115 return lm::is_enabled(static_cast<lm::log_level>(lvl));
116 }
117
118 bool error_logged()
119 {
120 return lm::error_has_been_logged();
121 }
122
123 void clear_logged_errors()
124 {
125 lm::clear_error_logged_flag();
126 }
127
128 std::string translate(std::string const& msg)
129 {
130 #ifdef LEATHERMAN_I18N
131 return leatherman::locale::translate(msg);
132 #else
133 // When locales are disabled, use boost::format, which expects %N% style formatting
134 static const boost::regex match{"\\{(\\d+)\\}"};
135 static const std::string repl{"%\\1%"};
136 return boost::regex_replace(leatherman::locale::translate(msg), match, repl);
137 #endif
138 }
139
140 void log(level lvl, string const& message)
141 {
142 lm::log(LOG_NAMESPACE, static_cast<lm::log_level>(lvl), 0, translate(message));
143 }
144
145 void colorize(ostream &os, level lvl)
146 {
147 lm::colorize(os, static_cast<lm::log_level>(lvl));
148 }
149
150 }} // namespace facter::logging
+0
-249
lib/src/ruby/aggregate_resolution.cc less more
0 #include <internal/ruby/aggregate_resolution.hpp>
1 #include <internal/ruby/chunk.hpp>
2 #include <leatherman/locale/locale.hpp>
3
4 // Mark string for translation (alias for leatherman::locale::format)
5 using leatherman::locale::_;
6
7 using namespace std;
8 using namespace leatherman::ruby;
9
10 namespace facter { namespace ruby {
11
12 aggregate_resolution::aggregate_resolution()
13 {
14 auto const& ruby = api::instance();
15 _self = ruby.nil_value();
16 _block = ruby.nil_value();
17 }
18
19 VALUE aggregate_resolution::define()
20 {
21 auto const& ruby = api::instance();
22
23 // Define the Resolution class
24 VALUE klass = ruby.rb_define_class_under(ruby.lookup({"Facter", "Core"}), "Aggregate", *ruby.rb_cObject);
25 ruby.rb_define_alloc_func(klass, alloc);
26 ruby.rb_define_method(klass, "chunk", RUBY_METHOD_FUNC(ruby_chunk), -1);
27 ruby.rb_define_method(klass, "aggregate", RUBY_METHOD_FUNC(ruby_aggregate), 0);
28 resolution::define(klass);
29 return klass;
30 }
31
32 VALUE aggregate_resolution::create()
33 {
34 auto const& ruby = api::instance();
35 return ruby.rb_class_new_instance(0, nullptr, ruby.lookup({"Facter", "Core", "Aggregate"}));
36 }
37
38 VALUE aggregate_resolution::value()
39 {
40 auto const& ruby = api::instance();
41
42 // If given an aggregate block, build a hash and call the block
43 if (!ruby.is_nil(_block)) {
44 volatile VALUE result = ruby.rb_hash_new();
45
46 for (auto& chunk : _chunks) {
47 ruby.rb_hash_aset(
48 result,
49 chunk.first,
50 chunk.second.value(*this));
51 }
52 return ruby.rb_funcall(_block, ruby.rb_intern("call"), 1, result);
53 }
54
55 // Otherwise perform a default aggregation by doing a deep merge
56 volatile VALUE merged = ruby.nil_value();
57 for (auto& chunk : _chunks) {
58 volatile VALUE value = chunk.second.value(*this);
59 if (ruby.is_nil(merged)) {
60 merged = value;
61 continue;
62 }
63 merged = deep_merge(ruby, merged, value);
64 }
65 return merged;
66 }
67
68 VALUE aggregate_resolution::find_chunk(VALUE name)
69 {
70 auto const& ruby = api::instance();
71
72 if (ruby.is_nil(name)) {
73 return ruby.nil_value();
74 }
75
76 if (!ruby.is_symbol(name)) {
77 ruby.rb_raise(*ruby.rb_eTypeError, _("expected chunk name to be a Symbol").c_str());
78 }
79
80 auto it = _chunks.find(name);
81 if (it == _chunks.end()) {
82 return ruby.nil_value();
83 }
84 return it->second.value(*this);
85 }
86
87 void aggregate_resolution::define_chunk(VALUE name, VALUE options)
88 {
89 auto const& ruby = api::instance();
90
91 // A block is required
92 if (!ruby.rb_block_given_p()) {
93 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
94 }
95
96 if (!ruby.is_symbol(name)) {
97 ruby.rb_raise(*ruby.rb_eTypeError, _("expected chunk name to be a Symbol").c_str());
98 }
99
100 volatile VALUE dependencies = ruby.nil_value();
101 volatile VALUE block = ruby.rb_block_proc();
102
103 if (!ruby.is_nil(options))
104 {
105 ID require_id = ruby.rb_intern("require");
106 ruby.hash_for_each(options, [&](VALUE key, VALUE value) {
107 if (!ruby.is_symbol(key)) {
108 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Symbol for options key").c_str());
109 }
110 ID key_id = ruby.rb_to_id(key);
111 if (key_id == require_id) {
112 if (ruby.is_array((value))) {
113 ruby.array_for_each(value, [&](VALUE element) {
114 if (!ruby.is_symbol(element)) {
115 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Symbol or Array of Symbol for require option").c_str());
116 }
117 return true;
118 });
119 } else if (!ruby.is_symbol(value)) {
120 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Symbol or Array of Symbol for require option").c_str());
121 }
122 dependencies = value;
123 } else {
124 ruby.rb_raise(*ruby.rb_eArgError, _("unexpected option {1}", ruby.rb_id2name(key_id)).c_str());
125 }
126 return true;
127 });
128 }
129
130 auto it = _chunks.find(name);
131 if (it == _chunks.end()) {
132 it = _chunks.emplace(make_pair(name, ruby::chunk(dependencies, block))).first;
133 }
134 it->second.dependencies(dependencies);
135 it->second.block(block);
136 }
137
138 VALUE aggregate_resolution::alloc(VALUE klass)
139 {
140 auto const& ruby = api::instance();
141
142 // Create a resolution and wrap with a Ruby data object
143 unique_ptr<aggregate_resolution> r(new aggregate_resolution());
144 VALUE self = r->_self = ruby.rb_data_object_alloc(klass, r.get(), mark, free);
145 ruby.register_data_object(self);
146
147 // Release the smart pointer; ownership is now with Ruby's GC
148 r.release();
149 return self;
150 }
151
152 void aggregate_resolution::mark(void* data)
153 {
154 // Mark all VALUEs contained in the aggregate resolution
155 auto const& ruby = api::instance();
156 auto instance = reinterpret_cast<aggregate_resolution*>(data);
157
158 // Mark the base first
159 instance->resolution::mark();
160
161 // Mark the aggregate block
162 ruby.rb_gc_mark(instance->_block);
163
164 // Mark the chunks
165 for (auto& kvp : instance->_chunks) {
166 ruby.rb_gc_mark(kvp.first);
167 kvp.second.mark();
168 }
169 }
170
171 void aggregate_resolution::free(void* data)
172 {
173 auto instance = reinterpret_cast<aggregate_resolution*>(data);
174
175 // Unregister the data object
176 auto const& ruby = api::instance();
177 ruby.unregister_data_object(instance->_self);
178
179 // Delete the aggregate resolution
180 delete instance;
181 }
182
183 VALUE aggregate_resolution::ruby_chunk(int argc, VALUE* argv, VALUE self)
184 {
185 auto const& ruby = api::instance();
186 if (argc == 0 || argc > 2) {
187 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
188 }
189
190 ruby.to_native<aggregate_resolution>(self)->define_chunk(argv[0], argc > 1 ? argv[1] : ruby.nil_value());
191 return self;
192 }
193
194 VALUE aggregate_resolution::ruby_aggregate(VALUE self)
195 {
196 auto const& ruby = api::instance();
197
198 // A block is required
199 if (!ruby.rb_block_given_p()) {
200 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
201 }
202
203 ruby.to_native<aggregate_resolution>(self)->_block = ruby.rb_block_proc();
204 return self;
205 }
206
207 VALUE aggregate_resolution::ruby_merge_hashes(VALUE obj, VALUE context, int argc, VALUE argv[])
208 {
209 api const* ruby = reinterpret_cast<api const*>(context);
210 if (argc != 3) {
211 ruby->rb_raise(*ruby->rb_eArgError, _("wrong number of arguments ({1} for 3)", argc).c_str());
212 }
213
214 // Recurse on left and right
215 return deep_merge(*ruby, argv[1], argv[2]);
216 }
217
218 VALUE aggregate_resolution::deep_merge(api const& ruby, VALUE left, VALUE right)
219 {
220 volatile VALUE result = ruby.nil_value();
221
222 if (ruby.is_hash(left) && ruby.is_hash(right)) {
223 result = ruby.rb_block_call(left, ruby.rb_intern("merge"), 1, &right, RUBY_METHOD_FUNC(ruby_merge_hashes), reinterpret_cast<VALUE>(&ruby));
224 } else if (ruby.is_array(left) && ruby.is_array(right)) {
225 result = ruby.rb_funcall(left, ruby.rb_intern("+"), 1, right);
226 } else if (ruby.is_nil(right)) {
227 result = left;
228 } else if (ruby.is_nil(left)) {
229 result = right;
230 } else if (ruby.is_nil(left) && ruby.is_nil(right)) {
231 result = ruby.nil_value();
232 } else {
233 // Let the user know we couldn't merge the chunks
234 volatile VALUE inspect_left = ruby.rb_funcall(left, ruby.rb_intern("inspect"), 0);
235 volatile VALUE inspect_right = ruby.rb_funcall(right, ruby.rb_intern("inspect"), 0);
236 volatile VALUE class_left = ruby.rb_funcall(ruby.rb_funcall(left, ruby.rb_intern("class"), 0), ruby.rb_intern("to_s"), 0);
237 volatile VALUE class_right = ruby.rb_funcall(ruby.rb_funcall(right, ruby.rb_intern("class"), 0), ruby.rb_intern("to_s"), 0);
238 ruby.rb_raise(*ruby.rb_eRuntimeError, _("cannot merge {1}:{2} and {3}:{4}",
239 ruby.rb_string_value_ptr(&inspect_left),
240 ruby.rb_string_value_ptr(&class_left),
241 ruby.rb_string_value_ptr(&inspect_right),
242 ruby.rb_string_value_ptr(&class_right)).c_str());
243 }
244
245 return result;
246 }
247
248 }} // namespace facter::ruby
+0
-136
lib/src/ruby/chunk.cc less more
0 #include <internal/ruby/chunk.hpp>
1 #include <internal/ruby/aggregate_resolution.hpp>
2 #include <leatherman/locale/locale.hpp>
3
4 // Mark string for translation (alias for leatherman::locale::format)
5 using leatherman::locale::_;
6
7 using namespace std;
8 using namespace leatherman::ruby;
9
10 namespace facter { namespace ruby {
11
12 chunk::chunk(VALUE dependencies, VALUE block) :
13 _dependencies(dependencies),
14 _block(block),
15 _resolved(false),
16 _resolving(false)
17 {
18 auto const& ruby = api::instance();
19 _value = ruby.nil_value();
20 }
21
22 chunk::chunk(chunk&& other)
23 {
24 *this = move(other);
25 }
26
27 chunk& chunk::operator=(chunk&& other)
28 {
29 _dependencies = other._dependencies;
30 _block = other._block;
31 _value = other._value;
32 _resolved = other._resolved;
33 _resolving = other._resolving;
34 return *this;
35 }
36
37 VALUE chunk::value(aggregate_resolution& resolution)
38 {
39 auto const& ruby = api::instance();
40
41 // Prevent cycles by raising an exception
42 if (_resolving) {
43 ruby.rb_raise(*ruby.rb_eRuntimeError, _("chunk dependency cycle detected").c_str());
44 }
45
46 if (_resolved) {
47 return _value;
48 }
49
50 _resolving = true;
51
52 volatile VALUE value = ruby.nil_value();
53 int tag = 0;
54 {
55 // Declare all C++ objects here
56 vector<VALUE> values;
57
58 value = ruby.protect(tag, [&]{
59 // Do not declare any C++ objects inside the protect
60 // Their destructors will not be invoked if there is a Ruby exception
61 if (ruby.is_symbol(_dependencies)) {
62 values.push_back(resolution.find_chunk(_dependencies));
63 ruby.rb_gc_register_address(&values[0]);
64 } else if (ruby.is_array(_dependencies)) {
65 // Resize the vector now to ensure it is fully allocated before registering with GC
66 values.resize(ruby.num2size_t(ruby.rb_funcall(_dependencies, ruby.rb_intern("size"), 0)), ruby.nil_value());
67 for (auto& v : values) {
68 ruby.rb_gc_register_address(&v);
69 }
70
71 int i = 0;
72 ruby.array_for_each(_dependencies, [&](VALUE element) {
73 values[i++] = resolution.find_chunk(element);
74 return true;
75 });
76 }
77
78 // Call the block to get this chunk's value
79 return ruby.rb_funcallv(_block, ruby.rb_intern("call"), values.size(), values.data());
80 });
81
82 // Unregister all the values from the GC
83 for (auto& v : values) {
84 ruby.rb_gc_unregister_address(&v);
85 }
86 }
87
88 _resolving = false;
89
90 if (!tag) {
91 _value = value;
92 _resolved = true;
93 return _value;
94 }
95
96 // Now that the above block has exited, it's safe to jump to the given tag
97 ruby.rb_jump_tag(tag);
98 return ruby.nil_value();
99 }
100
101 VALUE chunk::dependencies() const
102 {
103 return _dependencies;
104 }
105
106 void chunk::dependencies(VALUE dependencies)
107 {
108 auto const& ruby = api::instance();
109 _dependencies = dependencies;
110 _value = ruby.nil_value();
111 _resolved = false;
112 }
113
114 VALUE chunk::block() const
115 {
116 return _block;
117 }
118
119 void chunk::block(VALUE block)
120 {
121 auto const& ruby = api::instance();
122 _block = block;
123 _value = ruby.nil_value();
124 _resolved = false;
125 }
126
127 void chunk::mark() const
128 {
129 auto const& ruby = api::instance();
130 ruby.rb_gc_mark(_dependencies);
131 ruby.rb_gc_mark(_block);
132 ruby.rb_gc_mark(_value);
133 }
134
135 }} // namespace facter::ruby
+0
-84
lib/src/ruby/confine.cc less more
0 #include <internal/ruby/confine.hpp>
1 #include <internal/ruby/module.hpp>
2 #include <algorithm>
3
4 using namespace std;
5 using namespace leatherman::ruby;
6
7 namespace facter { namespace ruby {
8
9 confine::confine(VALUE fact, VALUE expected, VALUE block) :
10 _fact(fact),
11 _expected(expected),
12 _block(block)
13 {
14 }
15
16 confine::confine(confine&& other)
17 {
18 *this = move(other);
19 }
20
21 confine& confine::operator=(confine&& other)
22 {
23 _fact = other._fact;
24 _expected = other._expected;
25 _block = other._block;
26 return *this;
27 }
28
29 bool confine::suitable(module& facter) const
30 {
31 auto const& ruby = api::instance();
32
33 // If given a fact, either call the block or check the values
34 if (!ruby.is_nil(_fact)) {
35 // Rather than calling facter.fact_value, we call through the Ruby API to get the fact value the same way Ruby Facter did
36 // This enables users to alter the behavior of confines during testing, like this:
37 // Facter.fact(:name).expects(:value).returns 'overrride'
38 volatile VALUE fact = ruby.rb_funcall(facter.self(), ruby.rb_intern("fact"), 1, _fact);
39 if (ruby.is_nil(fact)) {
40 return false;
41 }
42 // Get the value of the fact
43 volatile VALUE value = facter.normalize(ruby.rb_funcall(fact, ruby.rb_intern("value"), 0));
44 if (ruby.is_nil(value)) {
45 return false;
46 }
47 // Pass the value to the block if given one
48 if (!ruby.is_nil(_block)) {
49 volatile VALUE result = ruby.rb_funcall(_block, ruby.rb_intern("call"), 1, value);
50 return !ruby.is_nil(result) && !ruby.is_false(result);
51 }
52
53 // Otherwise, if it's an array, search for the value
54 if (ruby.is_array(_expected)) {
55 bool found = false;
56 ruby.array_for_each(_expected, [&](VALUE expected_value) {
57 expected_value = facter.normalize(expected_value);
58 found = ruby.equals(facter.normalize(expected_value), value);
59 return !found;
60 });
61 return found;
62 }
63 // Compare the value directly
64 return ruby.case_equals(facter.normalize(_expected), value);
65 }
66 // If we have only a block, execute it
67 if (!ruby.is_nil(_block)) {
68 volatile VALUE result = ruby.rb_funcall(_block, ruby.rb_intern("call"), 0);
69 return !ruby.is_nil(result) && !ruby.is_false(result);
70 }
71 return false;
72 }
73
74 void confine::mark() const
75 {
76 // Mark all VALUEs contained in the confine
77 auto const& ruby = api::instance();
78 ruby.rb_gc_mark(_fact);
79 ruby.rb_gc_mark(_expected);
80 ruby.rb_gc_mark(_block);
81 }
82
83 }} // namespace facter::ruby
+0
-398
lib/src/ruby/fact.cc less more
0 #include <internal/ruby/fact.hpp>
1 #include <internal/ruby/aggregate_resolution.hpp>
2 #include <internal/ruby/module.hpp>
3 #include <internal/ruby/simple_resolution.hpp>
4 #include <internal/ruby/ruby_value.hpp>
5 #include <facter/facts/collection.hpp>
6 #include <leatherman/util/environment.hpp>
7 #include <leatherman/logging/logging.hpp>
8 #include <leatherman/locale/locale.hpp>
9 #include <algorithm>
10
11 // Mark string for translation (alias for leatherman::locale::format)
12 using leatherman::locale::_;
13
14 using namespace std;
15 using namespace facter::facts;
16 using namespace leatherman::util;
17 using namespace leatherman::ruby;
18
19 namespace facter { namespace ruby {
20
21 // The maximum number of resolutions allowed for a fact
22 static const size_t MAXIMUM_RESOLUTIONS = 100;
23
24 fact::fact() :
25 _resolved(false),
26 _resolving(false),
27 _weight(0)
28 {
29 auto const& ruby = api::instance();
30 _self = ruby.nil_value();
31 _name = ruby.nil_value();
32 _value = ruby.nil_value();
33 }
34
35 VALUE fact::define()
36 {
37 auto const& ruby = api::instance();
38
39 VALUE klass = ruby.rb_define_class_under(ruby.lookup({ "Facter", "Util" }), "Fact", *ruby.rb_cObject);
40 ruby.rb_define_alloc_func(klass, alloc);
41 ruby.rb_define_method(klass, "initialize", RUBY_METHOD_FUNC(ruby_initialize), 1);
42 ruby.rb_define_method(klass, "name", RUBY_METHOD_FUNC(ruby_name), 0);
43 ruby.rb_define_method(klass, "value", RUBY_METHOD_FUNC(ruby_value), 0);
44 ruby.rb_define_method(klass, "resolution", RUBY_METHOD_FUNC(ruby_resolution), 1);
45 ruby.rb_define_method(klass, "define_resolution", RUBY_METHOD_FUNC(ruby_define_resolution), -1);
46 ruby.rb_define_method(klass, "flush", RUBY_METHOD_FUNC(ruby_flush), 0);
47 return klass;
48 }
49
50 VALUE fact::create(VALUE name)
51 {
52 auto const& ruby = api::instance();
53 return ruby.rb_class_new_instance(1, &name, ruby.lookup({"Facter", "Util", "Fact"}));
54 }
55
56 VALUE fact::name() const
57 {
58 return _name;
59 }
60
61 VALUE fact::value()
62 {
63 auto const& ruby = api::instance();
64 auto facter = module::current();
65
66 collection& facts = facter->facts();
67
68 // Prevent cycles by raising an exception
69 if (_resolving) {
70 ruby.rb_raise(*ruby.rb_eRuntimeError, _("cycle detected while requesting value of fact \"{1}\"", ruby.rb_string_value_ptr(&_name)).c_str());
71 }
72
73 if (_resolved) {
74 return _value;
75 }
76
77 // Sort the resolutions by weight (descending)
78 sort(_resolutions.begin(), _resolutions.end(), [&](VALUE first, VALUE second) {
79 auto res_first = ruby.to_native<resolution>(first);
80 auto res_second = ruby.to_native<resolution>(second);
81 return res_first->weight() > res_second->weight();
82 });
83
84 _resolving = true;
85 bool add = true;
86
87 vector<VALUE>::iterator it;
88 ruby.rescue([&]() {
89 volatile VALUE value = ruby.nil_value();
90 size_t weight = 0;
91
92 // Look through the resolutions and find the first allowed resolution that resolves
93 for (it = _resolutions.begin(); it != _resolutions.end(); ++it) {
94 auto res = ruby.to_native<resolution>(*it);
95 if (!res->suitable(*facter)) {
96 continue;
97 }
98 value = res->value();
99 if (!ruby.is_nil(value)) {
100 weight = res->weight();
101 break;
102 }
103 }
104
105 // Set the value to what was resolved
106 _value = value;
107 _weight = weight;
108
109 if (! ruby.is_nil(_value) && _weight != 0) {
110 return 0;
111 }
112
113 // There's two possibilities here:
114 // 1. None of our resolvers could resolve the value
115 // 2. A resolver of weight 0 resolved the value
116 //
117 // In both cases, we attempt to use the "built-in" fact's
118 // value. This serves as a fallback resolver for Case (1)
119 // while for Case (2), we want built-in values to take
120 // precedence over 0-weight custom facts.
121
122 auto builtin_value = facts[ruby.to_string(_name)];
123 if (! builtin_value) {
124 return 0;
125 }
126 auto builtin_ruby_value = facter->to_ruby(builtin_value);
127
128 // We need this check for Case (2). Otherwise, we risk
129 // overwriting our resolved value in the small chance
130 // that builtin_value exists, but its ruby value is
131 // nil.
132 if (! ruby.is_nil(builtin_ruby_value)) {
133 // Already in collection, do not add
134 add = false;
135 _value = builtin_ruby_value;
136 _weight = builtin_value->weight();
137 }
138
139 return 0;
140 }, [&](VALUE ex) {
141 LOG_ERROR("error while resolving custom fact \"{1}\": {2}", ruby.rb_string_value_ptr(&_name), ruby.exception_to_string(ex));
142
143 // Failed, so set to nil
144 _value = ruby.nil_value();
145 _weight = 0;
146 return 0;
147 });
148
149 if (add) {
150 facts.add_custom(ruby.to_string(_name), ruby.is_nil(_value) ? nullptr : make_value<ruby::ruby_value>(_value), _weight);
151 }
152
153 _resolved = true;
154 _resolving = false;
155 return _value;
156 }
157
158 void fact::value(VALUE v)
159 {
160 _value = v;
161 _resolved = true;
162 }
163
164 VALUE fact::find_resolution(VALUE name) const
165 {
166 auto const& ruby = api::instance();
167
168 if (ruby.is_nil(name)) {
169 return ruby.nil_value();
170 }
171
172 if (!ruby.is_string(name)) {
173 ruby.rb_raise(*ruby.rb_eTypeError, _("expected resolution name to be a String").c_str());
174 }
175
176 // Search for the resolution by name
177 auto it = find_if(_resolutions.begin(), _resolutions.end(), [&](VALUE self) {
178 return ruby.equals(ruby.to_native<resolution>(self)->name(), name);
179 });
180 if (it == _resolutions.end()) {
181 return ruby.nil_value();
182 }
183 return *it;
184 }
185
186 VALUE fact::define_resolution(VALUE name, VALUE options)
187 {
188 // Do not declare types with destructors; if you do, wrap below in a api::protect call
189 auto const& ruby = api::instance();
190
191 if (!ruby.is_nil(name) && !ruby.is_string(name) && !ruby.is_symbol(name)) {
192 ruby.rb_raise(*ruby.rb_eTypeError, _("expected resolution name to be a Symbol or String").c_str());
193 }
194
195 if (ruby.is_symbol(name)) {
196 name = ruby.rb_sym_to_s(name);
197 }
198
199 bool aggregate = false;
200 bool has_weight = false;
201 size_t weight = 0;
202 volatile VALUE resolution_value = ruby.nil_value();
203
204 // Read the options if provided
205 if (!ruby.is_nil(options)) {
206 ID simple_id = ruby.rb_intern("simple");
207 ID aggregate_id = ruby.rb_intern("aggregate");
208 ID type_id = ruby.rb_intern("type");
209 ID value_id = ruby.rb_intern("value");
210 ID weight_id = ruby.rb_intern("weight");
211 ID timeout_id = ruby.rb_intern("timeout");
212
213 if (!ruby.is_hash(options)) {
214 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Hash for the options").c_str());
215 }
216
217 ruby.hash_for_each(options, [&](VALUE key, VALUE value) {
218 if (!ruby.is_symbol(key)) {
219 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Symbol for options key").c_str());
220 }
221 ID key_id = ruby.rb_to_id(key);
222 if (key_id == type_id) {
223 // Handle the type option
224 if (!ruby.is_symbol(value)) {
225 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a Symbol for type option").c_str());
226 }
227 ID type_id = ruby.rb_to_id(value);
228 if (type_id != simple_id && type_id != aggregate_id) {
229 ruby.rb_raise(*ruby.rb_eArgError, _("expected simple or aggregate for resolution type but was given {1}", ruby.rb_id2name(type_id)).c_str());
230 }
231 aggregate = (type_id == aggregate_id);
232 } else if (key_id == value_id) {
233 // Handle the value option
234 resolution_value = value;
235 } else if (key_id == weight_id) {
236 // Handle the weight option
237 has_weight = true;
238 weight = ruby.num2size_t(value);
239 } else if (key_id == timeout_id) {
240 // Ignore timeout as it isn't supported
241 static bool timeout_warning = true;
242 if (timeout_warning) {
243 LOG_WARNING("timeout option is not supported for custom facts and will be ignored.")
244 timeout_warning = false;
245 }
246 } else {
247 ruby.rb_raise(*ruby.rb_eArgError, _("unexpected option {1}", ruby.rb_id2name(key_id)).c_str());
248 }
249 return true;
250 });
251 }
252
253 // Find or create the resolution
254 VALUE resolution_self = find_resolution(name);
255 if (ruby.is_nil(resolution_self)) {
256 if (_resolutions.size() == MAXIMUM_RESOLUTIONS) {
257 ruby.rb_raise(*ruby.rb_eRuntimeError, _("fact \"{1}\" already has the maximum number of resolutions allowed ({2}).", ruby.rb_string_value_ptr(&_name), MAXIMUM_RESOLUTIONS).c_str());
258 }
259
260 if (aggregate) {
261 _resolutions.push_back(aggregate_resolution::create());
262 } else {
263 _resolutions.push_back(simple_resolution::create());
264 }
265 resolution_self = _resolutions.back();
266 } else {
267 if (aggregate && !ruby.is_a(resolution_self, ruby.lookup({ "Facter", "Core", "Aggregate"}))) {
268 ruby.rb_raise(*ruby.rb_eArgError,
269 _("cannot define an aggregate resolution with name \"{1}\": a simple resolution with the same name already exists",
270 ruby.rb_string_value_ptr(&name)).c_str());
271 } else if (!aggregate && !ruby.is_a(resolution_self, ruby.lookup({ "Facter", "Util", "Resolution"}))) {
272 ruby.rb_raise(*ruby.rb_eArgError,
273 _("cannot define a simple resolution with name \"{1}\": an aggregate resolution with the same name already exists",
274 ruby.rb_string_value_ptr(&name)).c_str());
275 }
276 }
277
278 // Set the name, value, and weight
279 auto res = ruby.to_native<resolution>(resolution_self);
280 res->name(name);
281 res->value(resolution_value);
282 if (has_weight) {
283 res->weight(weight);
284 }
285
286 // Call the block if one was given
287 if (ruby.rb_block_given_p()) {
288 ruby.rb_funcall_passing_block(resolution_self, ruby.rb_intern("instance_eval"), 0, nullptr);
289 }
290 return resolution_self;
291 }
292
293 void fact::flush()
294 {
295 auto const& ruby = api::instance();
296
297 // Call flush on every resolution
298 for (auto r : _resolutions) {
299 ruby.to_native<resolution>(r)->flush();
300 }
301
302 // Reset the value
303 _resolved = false;
304 _value = ruby.nil_value();
305 }
306
307 VALUE fact::alloc(VALUE klass)
308 {
309 auto const& ruby = api::instance();
310
311 // Create a fact and wrap with a Ruby data object
312 unique_ptr<fact> f(new fact());
313 VALUE self = f->_self = ruby.rb_data_object_alloc(klass, f.get(), mark, free);
314 ruby.register_data_object(self);
315
316 // Release the smart pointer; ownership is now with Ruby's GC
317 f.release();
318 return self;
319 }
320
321 void fact::mark(void* data)
322 {
323 // Mark all VALUEs contained in the fact
324 auto const& ruby = api::instance();
325 auto instance = reinterpret_cast<fact*>(data);
326
327 // Mark the name and value
328 ruby.rb_gc_mark(instance->_name);
329 ruby.rb_gc_mark(instance->_value);
330
331 // Mark the resolutions
332 for (auto v : instance->_resolutions) {
333 ruby.rb_gc_mark(v);
334 }
335 }
336
337 void fact::free(void* data)
338 {
339 auto instance = reinterpret_cast<fact*>(data);
340
341 // Unregister the data object
342 auto const& ruby = api::instance();
343 ruby.unregister_data_object(instance->_self);
344
345 // Delete the fact
346 delete instance;
347 }
348
349 VALUE fact::ruby_initialize(VALUE self, VALUE name)
350 {
351 auto const& ruby = api::instance();
352
353 if (!ruby.is_string(name) && !ruby.is_symbol(name)) {
354 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a String or Symbol for fact name").c_str());
355 }
356
357 ruby.to_native<fact>(self)->_name = name;
358 return self;
359 }
360
361 VALUE fact::ruby_name(VALUE self)
362 {
363 auto const& ruby = api::instance();
364 return ruby.to_native<fact>(self)->name();
365 }
366
367 VALUE fact::ruby_value(VALUE self)
368 {
369 auto const& ruby = api::instance();
370 return ruby.to_native<fact>(self)->value();
371 }
372
373 VALUE fact::ruby_resolution(VALUE self, VALUE name)
374 {
375 auto const& ruby = api::instance();
376 return ruby.to_native<fact>(self)->find_resolution(name);
377 }
378
379 VALUE fact::ruby_define_resolution(int argc, VALUE* argv, VALUE self)
380 {
381 auto const& ruby = api::instance();
382
383 if (argc == 0 || argc > 2) {
384 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
385 }
386
387 return ruby.to_native<fact>(self)->define_resolution(argv[0], argc > 1 ? argv[1] : ruby.nil_value());
388 }
389
390 VALUE fact::ruby_flush(VALUE self)
391 {
392 auto const& ruby = api::instance();
393 ruby.to_native<fact>(self)->flush();
394 return ruby.nil_value();
395 }
396
397 }} // namespace facter::ruby
+0
-1171
lib/src/ruby/module.cc less more
0 #include <internal/ruby/module.hpp>
1 #include <internal/ruby/aggregate_resolution.hpp>
2 #include <internal/ruby/confine.hpp>
3 #include <internal/ruby/simple_resolution.hpp>
4 #include <facter/facts/collection.hpp>
5 #include <facter/logging/logging.hpp>
6 #include <facter/util/config.hpp>
7 #include <facter/version.h>
8 #include <facter/export.h>
9 #include <leatherman/util/environment.hpp>
10 #include <leatherman/execution/execution.hpp>
11 #include <leatherman/file_util/directory.hpp>
12 #include <leatherman/logging/logging.hpp>
13 #include <leatherman/locale/locale.hpp>
14 #include <boost/filesystem.hpp>
15 #include <boost/algorithm/string.hpp>
16 #include <boost/nowide/iostream.hpp>
17 #include <boost/nowide/convert.hpp>
18 #include <stdexcept>
19 #include <functional>
20 #include <facter/facts/scalar_value.hpp>
21 #include <facter/facts/array_value.hpp>
22 #include <facter/facts/map_value.hpp>
23 #include <internal/ruby/ruby_value.hpp>
24 #include <internal/facts/cache.hpp>
25
26 // Mark string for translation (alias for leatherman::locale::format)
27 using leatherman::locale::_;
28
29 using namespace std;
30 using namespace facter::facts;
31 using namespace facter::util::config;
32 using namespace leatherman::execution;
33 using namespace leatherman::file_util;
34 using namespace leatherman::util;
35 using namespace boost::filesystem;
36 using namespace leatherman::logging;
37 using namespace leatherman::ruby;
38
39 namespace facter { namespace ruby {
40
41 /**
42 * Helper for maintaining context when initialized via a Ruby require.
43 */
44 struct require_context
45 {
46 /**
47 * Constructs a new require context.
48 */
49 require_context()
50 {
51 // Create a collection and a facter module
52 boost::program_options::variables_map vm;
53 auto hocon_conf = load_default_config_file();
54 load_fact_settings(hocon_conf, vm);
55 set<string> blocklist;
56 if (vm.count("blocklist")) {
57 auto facts_to_block = vm["blocklist"].as<vector<string>>();
58 blocklist.insert(facts_to_block.begin(), facts_to_block.end());
59 }
60 auto ttls = load_ttls(hocon_conf);
61 _facts.reset(new collection(blocklist, ttls));
62 _module.reset(new module(*_facts));
63
64 // Ruby doesn't have a proper way of notifying extensions that the VM is shutting down
65 // The easiest way to get notified is to have a global data object that never gets collected
66 // until the VM shuts down
67 auto const& ruby = api::instance();
68 _canary = ruby.rb_data_object_alloc(*ruby.rb_cObject, this, nullptr, cleanup);
69 ruby.rb_gc_register_address(&_canary);
70 ruby.register_data_object(_canary);
71 }
72
73 /**
74 * Destructs the require context.
75 */
76 ~require_context()
77 {
78 _module.reset();
79 _facts.reset();
80
81 // Remove the finalizer and let Ruby collect the object
82 auto const& ruby = api::instance();
83 ruby.rb_gc_unregister_address(&_canary);
84 ruby.unregister_data_object(_canary);
85 }
86
87 /**
88 * Creates the require context.
89 */
90 static void create()
91 {
92 // Reset first before allocating a new context
93 reset();
94
95 _instance.reset(new require_context());
96 }
97
98 /**
99 * Resets the require context.
100 */
101 static void reset()
102 {
103 _instance.reset();
104 }
105
106 private:
107 static void cleanup(void* ptr)
108 {
109 if (ptr == _instance.get()) {
110 reset();
111 }
112 }
113
114 unique_ptr<collection> _facts;
115 unique_ptr<module> _module;
116 VALUE _canary;
117
118 static unique_ptr<require_context> _instance;
119 };
120
121 unique_ptr<require_context> require_context::_instance;
122 }}
123
124 // Exports for a Ruby extension.
125 extern "C" {
126 /**
127 * Called by the Ruby VM when native facter is required.
128 */
129 void LIBFACTER_EXPORT Init_libfacter()
130 {
131 bool logging_init_failed = false;
132 string logging_init_error_msg;
133 try {
134 facter::logging::setup_logging(boost::nowide::cerr);
135 set_level(log_level::warning);
136 } catch(facter::logging::locale_error const& e) {
137 logging_init_failed = true;
138 logging_init_error_msg = e.what();
139 }
140
141 // Initialize ruby
142 api* ruby = nullptr;
143 try {
144 ruby = &api::instance();
145 } catch (runtime_error& ex) {
146 if (!logging_init_failed) {
147 LOG_WARNING("{1}: facts requiring Ruby will not be resolved.", ex.what());
148 } else {
149 // This could only happen if some non-ruby library
150 // consumer called this function for some reason and
151 // we didn't have a libruby loaded. AND the locales
152 // are messed up so badly that even resetting locale
153 // environment variables fails. This seems so
154 // astronomically unlikely that I don't really think
155 // it's worth figuring out what we should do in this
156 // case - I'm honestly not even sure there's a correct
157 // behavior at this point.
158 // -- Branan
159 }
160 return;
161 }
162
163 ruby->initialize();
164
165 // If logging init failed, we'll raise a load error
166 // here. Otherwise we Create the context
167 if (logging_init_failed) {
168 ruby->rb_raise(*ruby->rb_eLoadError, _("could not initialize facter due to a locale error: {1}", logging_init_error_msg).c_str());
169 } else {
170 facter::ruby::require_context::create();
171 }
172 }
173 }
174
175 namespace facter { namespace ruby {
176
177 static string canonicalize(string p)
178 {
179 // Get the canonical/absolute directory name
180 // If it can be resolved, use canonical to avoid duplicate search paths.
181 // Fall back to absolute because canonical on Windows won't recognize paths as valid if
182 // they resolve to symlinks to non-NTFS volumes.
183 boost::system::error_code ec;
184 auto directory = canonical(p, ec);
185 if (ec) {
186 return absolute(p).string();
187 }
188 return directory.string();
189 }
190
191 map<VALUE, module*> module::_instances;
192
193 module::module(collection& facts, vector<string> const& paths, bool logging_hooks) :
194 _collection(facts),
195 _loaded_all(false)
196 {
197 auto const& ruby = api::instance();
198 if (!ruby.initialized()) {
199 throw runtime_error(_("Ruby API is not initialized.").c_str());
200 }
201
202 // Load global settigs from config file
203 load_global_settings(load_default_config_file(), _config_file_settings);
204
205 initialize_search_paths(paths);
206
207 // Register the block for logging callback with the GC
208 _on_message_block = ruby.nil_value();
209 ruby.rb_gc_register_address(&_on_message_block);
210
211 // Install a logging message handler
212 on_message([this](log_level level, string const& message) {
213 auto const& ruby = api::instance();
214
215 if (ruby.is_nil(_on_message_block)) {
216 return true;
217 }
218
219 // Call the block and don't log messages
220 ruby.rescue([&]() {
221 ruby.rb_funcall(_on_message_block, ruby.rb_intern("call"), 2, level_to_symbol(level), ruby.utf8_value(message));
222 return ruby.nil_value();
223 }, [&](VALUE) {
224 // Logging can take place from locations where we do not expect Ruby exceptions to be raised
225 // Therefore, intentionally swallow any exceptions.
226 return ruby.nil_value();
227 });
228 return false;
229 });
230
231 // Define the Facter module
232 _self = ruby.rb_define_module("Facter");
233 _instances[_self] = this;
234
235 VALUE core = ruby.rb_define_module_under(_self, "Core");
236 VALUE execution = ruby.rb_define_module_under(core, "Execution");
237 ruby.rb_define_module_under(_self, "Util");
238
239 // Define the methods on the Facter module
240 volatile VALUE version = ruby.utf8_value(LIBFACTER_VERSION);
241 ruby.rb_const_set(_self, ruby.rb_intern("FACTERVERSION"), version);
242 ruby.rb_define_singleton_method(_self, "version", RUBY_METHOD_FUNC(ruby_version), 0);
243 ruby.rb_define_singleton_method(_self, "add", RUBY_METHOD_FUNC(ruby_add), -1);
244 ruby.rb_define_singleton_method(_self, "define_fact", RUBY_METHOD_FUNC(ruby_define_fact), -1);
245 ruby.rb_define_singleton_method(_self, "value", RUBY_METHOD_FUNC(ruby_value), 1);
246 ruby.rb_define_singleton_method(_self, "[]", RUBY_METHOD_FUNC(ruby_fact), 1);
247 ruby.rb_define_singleton_method(_self, "fact", RUBY_METHOD_FUNC(ruby_fact), 1);
248 ruby.rb_define_singleton_method(_self, "debug", RUBY_METHOD_FUNC(ruby_debug), 1);
249 ruby.rb_define_singleton_method(_self, "debugonce", RUBY_METHOD_FUNC(ruby_debugonce), 1);
250 ruby.rb_define_singleton_method(_self, "warn", RUBY_METHOD_FUNC(ruby_warn), 1);
251 ruby.rb_define_singleton_method(_self, "warnonce", RUBY_METHOD_FUNC(ruby_warnonce), 1);
252 ruby.rb_define_singleton_method(_self, "log_exception", RUBY_METHOD_FUNC(ruby_log_exception), -1);
253 ruby.rb_define_singleton_method(_self, "debugging?", RUBY_METHOD_FUNC(ruby_get_debugging), 0);
254 ruby.rb_define_singleton_method(_self, "trace?", RUBY_METHOD_FUNC(ruby_get_trace), 0);
255 ruby.rb_define_singleton_method(_self, "flush", RUBY_METHOD_FUNC(ruby_flush), 0);
256 ruby.rb_define_singleton_method(_self, "list", RUBY_METHOD_FUNC(ruby_list), 0);
257 ruby.rb_define_singleton_method(_self, "to_hash", RUBY_METHOD_FUNC(ruby_to_hash), 0);
258 ruby.rb_define_singleton_method(_self, "each", RUBY_METHOD_FUNC(ruby_each), 0);
259 ruby.rb_define_singleton_method(_self, "clear", RUBY_METHOD_FUNC(ruby_clear), 0);
260 ruby.rb_define_singleton_method(_self, "reset", RUBY_METHOD_FUNC(ruby_reset), 0);
261 ruby.rb_define_singleton_method(_self, "loadfacts", RUBY_METHOD_FUNC(ruby_loadfacts), 0);
262 ruby.rb_define_singleton_method(_self, "search", RUBY_METHOD_FUNC(ruby_search), -1);
263 ruby.rb_define_singleton_method(_self, "search_path", RUBY_METHOD_FUNC(ruby_search_path), 0);
264 ruby.rb_define_singleton_method(_self, "search_external", RUBY_METHOD_FUNC(ruby_search_external), 1);
265 ruby.rb_define_singleton_method(_self, "search_external_path", RUBY_METHOD_FUNC(ruby_search_external_path), 0);
266
267 // Only define these if requested to do so
268 // This prevents consumers of Facter from altering the logging behavior
269 if (logging_hooks) {
270 ruby.rb_define_singleton_method(_self, "debugging", RUBY_METHOD_FUNC(ruby_set_debugging), 1);
271 ruby.rb_define_singleton_method(_self, "trace", RUBY_METHOD_FUNC(ruby_set_trace), 1);
272 ruby.rb_define_singleton_method(_self, "on_message", RUBY_METHOD_FUNC(ruby_on_message), 0);
273 }
274
275 // Define the execution module
276 ruby.rb_define_singleton_method(execution, "which", RUBY_METHOD_FUNC(ruby_which), 1);
277 ruby.rb_define_singleton_method(execution, "exec", RUBY_METHOD_FUNC(ruby_exec), 1);
278 ruby.rb_define_singleton_method(execution, "execute", RUBY_METHOD_FUNC(ruby_execute), -1);
279 ruby.rb_define_class_under(execution, "ExecutionFailure", *ruby.rb_eStandardError);
280
281 // Define the Fact and resolution classes
282 fact::define();
283 simple_resolution::define();
284 aggregate_resolution::define();
285
286 // Custom facts may `require 'facter'`
287 // To allow those custom facts to still function, add facter.rb to loaded features using the first directory in the load path
288 // Note: use forward slashes in the path even on Windows because that's what Ruby expects in $LOADED_FEATURES
289 volatile VALUE first = ruby.rb_ary_entry(ruby.rb_gv_get("$LOAD_PATH"), 0);
290 if (!ruby.is_nil(first)) {
291 ruby.rb_ary_push(ruby.rb_gv_get("$LOADED_FEATURES"), ruby.utf8_value(ruby.to_string(first) + "/facter.rb"));
292 }
293 }
294
295 module::~module()
296 {
297 _instances.erase(_self);
298
299 clear_facts(false);
300
301 try {
302 api& ruby = api::instance();
303
304 // Unregister the on message block
305 ruby.rb_gc_unregister_address(&_on_message_block);
306 on_message(nullptr);
307
308 // Undefine the module
309 ruby.rb_const_remove(*ruby.rb_cObject, ruby.rb_intern("Facter"));
310 } catch (runtime_error& ex) {
311 LOG_WARNING("{1}: Ruby cleanup ended prematurely", ex.what());
312 return;
313 }
314 }
315
316 void module::search(vector<string> const& paths)
317 {
318 for (auto dir : paths) {
319 _additional_search_paths.emplace_back(dir);
320 _search_paths.emplace_back(canonicalize(_additional_search_paths.back()));
321 }
322 }
323
324 void module::load_facts()
325 {
326 if (_loaded_all) {
327 return;
328 }
329
330 LOG_DEBUG("loading all custom facts.");
331
332 LOG_DEBUG("loading custom fact directories from config file");
333 if (_config_file_settings.count("custom-dir")) {
334 auto config_paths = _config_file_settings["custom-dir"].as<vector<string>>();
335 _search_paths.insert(_search_paths.end(), config_paths.begin(), config_paths.end());
336 }
337
338 for (auto const& directory : _search_paths) {
339 LOG_DEBUG("searching for custom facts in {1}.", directory);
340 each_file(directory, [&](string const& file) {
341 load_file(file);
342 return true;
343 }, "\\.rb$");
344 }
345
346 _loaded_all = true;
347 }
348
349 void module::resolve_facts()
350 {
351 // Before we do anything, call facts to ensure the collection is populated
352 facts();
353
354 load_facts();
355
356 auto const& ruby = api::instance();
357 LOG_DEBUG(_("loading external fact directories from config file"));
358 boost::program_options::variables_map vm;
359 auto hocon_conf = load_default_config_file();
360 load_fact_groups_settings(hocon_conf, vm);
361 vector<string> cached_custom_facts_list;
362 if (vm.count(cached_custom_facts)) {
363 auto facts_to_cache = vm[cached_custom_facts].as<vector<string>>();
364 cached_custom_facts_list.insert(cached_custom_facts_list.end(), facts_to_cache.begin(), facts_to_cache.end());
365 }
366
367 auto ttls = _collection.get_ttls();
368 bool custom_cache_file_loaded = false;
369 bool custom_cache_enabled = !cached_custom_facts_list.empty() || ttls.count(cached_custom_facts);
370 if (custom_cache_enabled) {
371 custom_cache_file_loaded = cache::load_cached_custom_facts(_collection, ttls.find(cached_custom_facts)->second);
372 }
373
374 auto cached_fact = ([&](string const& key) {
375 return custom_cache_file_loaded && std::find(cached_custom_facts_list.begin(), cached_custom_facts_list.end(), key) != cached_custom_facts_list.end();
376 });
377
378 for (auto const& kvp_name_value : _facts) {
379 if (!cached_fact(kvp_name_value.first)) {
380 ruby.to_native<fact>(kvp_name_value.second)->value();
381 }
382 }
383 if (custom_cache_enabled && !custom_cache_file_loaded) {
384 cache::write_cached_custom_facts(_collection, cached_custom_facts_list);
385 }
386 }
387
388 void module::clear_facts(bool clear_collection)
389 {
390 auto const& ruby = api::instance();
391
392 // Unregister all the facts
393 for (auto& kvp : _facts) {
394 ruby.rb_gc_unregister_address(&kvp.second);
395 }
396
397 // Clear the custom facts
398 _facts.clear();
399
400 // Clear the collection
401 if (clear_collection) {
402 _collection.clear();
403 }
404 }
405
406 VALUE module::fact_value(VALUE name)
407 {
408 auto const& ruby = api::instance();
409
410 VALUE fact_self = load_fact(name);
411 if (ruby.is_nil(fact_self)) {
412 return ruby.nil_value();
413 }
414
415 return ruby.to_native<fact>(fact_self)->value();
416 }
417
418 VALUE module::to_ruby(value const* val) const
419 {
420 auto const& ruby = api::instance();
421
422 if (!val) {
423 return ruby.nil_value();
424 }
425 if (auto ptr = dynamic_cast<facter::ruby::ruby_value const*>(val)) {
426 return ptr->value();
427 }
428 if (auto ptr = dynamic_cast<string_value const*>(val)) {
429 return ruby.utf8_value(ptr->value());
430 }
431 if (auto ptr = dynamic_cast<integer_value const*>(val)) {
432 return ruby.rb_ll2inum(static_cast<LONG_LONG>(ptr->value()));
433 }
434 if (auto ptr = dynamic_cast<boolean_value const*>(val)) {
435 return ptr->value() ? ruby.true_value() : ruby.false_value();
436 }
437 if (auto ptr = dynamic_cast<double_value const*>(val)) {
438 return ruby.rb_float_new_in_heap(ptr->value());
439 }
440 if (auto ptr = dynamic_cast<array_value const*>(val)) {
441 volatile VALUE array = ruby.rb_ary_new_capa(static_cast<long>(ptr->size()));
442 ptr->each([&](value const* element) {
443 ruby.rb_ary_push(array, to_ruby(element));
444 return true;
445 });
446 return array;
447 }
448 if (auto ptr = dynamic_cast<map_value const*>(val)) {
449 volatile VALUE hash = ruby.rb_hash_new();
450 ptr->each([&](string const& name, value const* element) {
451 ruby.rb_hash_aset(hash, ruby.utf8_value(name), to_ruby(element));
452 return true;
453 });
454 return hash;
455 }
456 return ruby.nil_value();
457 }
458
459 VALUE module::normalize(VALUE name) const
460 {
461 auto const& ruby = api::instance();
462
463 if (ruby.is_symbol(name)) {
464 name = ruby.rb_sym_to_s(name);
465 }
466 if (ruby.is_string(name)) {
467 name = ruby.rb_funcall(name, ruby.rb_intern("downcase"), 0);
468 }
469 return name;
470 }
471
472 collection& module::facts()
473 {
474 if (_collection.empty()) {
475 bool include_ruby_facts = true;
476 _collection.add_default_facts(include_ruby_facts);
477 _collection.add_external_facts(_external_search_paths);
478
479 auto const& ruby = api::instance();
480 _collection.add_environment_facts([&](string const& name) {
481 // Create a fact and explicitly set the value
482 // We honor environment variables above custom fact resolutions
483 ruby.to_native<fact>(create_fact(ruby.utf8_value(name)))->value(to_ruby(_collection[name]));
484 });
485 }
486 return _collection;
487 }
488
489 VALUE module::self() const
490 {
491 return _self;
492 }
493
494 module* module::current()
495 {
496 auto const& ruby = api::instance();
497 return from_self(ruby.lookup({"Facter"}));
498 }
499
500 static VALUE safe_eval(char const* scope, function<VALUE()> body)
501 {
502 try {
503 return body();
504 } catch (exception const& e) {
505 LOG_ERROR("{1} uncaught exception: {2}", scope, e.what());
506 }
507 return api::instance().nil_value();
508 }
509
510 VALUE module::ruby_version(VALUE self)
511 {
512 return safe_eval("Facter.version", [&]() {
513 auto const& ruby = api::instance();
514 return ruby.lookup({ "Facter", "FACTERVERSION" });
515 });
516 }
517
518 VALUE module::ruby_add(int argc, VALUE* argv, VALUE self)
519 {
520 return safe_eval("Facter.add", [&]() {
521 auto const& ruby = api::instance();
522
523 if (argc == 0 || argc > 2) {
524 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
525 }
526
527 VALUE fact_self = from_self(self)->create_fact(argv[0]);
528
529 // Read the resolution name from the options hash, if present
530 volatile VALUE name = ruby.nil_value();
531 VALUE options = argc == 2 ? argv[1] : ruby.nil_value();
532 if (!ruby.is_nil(options)) {
533 name = ruby.rb_funcall(options, ruby.rb_intern("delete"), 1, ruby.to_symbol("name"));
534 }
535
536 ruby.to_native<fact>(fact_self)->define_resolution(name, options);
537 return fact_self;
538 });
539 }
540
541 VALUE module::ruby_define_fact(int argc, VALUE* argv, VALUE self)
542 {
543 return safe_eval("Facter.define_fact", [&]() {
544 auto const& ruby = api::instance();
545
546 if (argc == 0 || argc > 2) {
547 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
548 }
549
550 VALUE fact_self = from_self(self)->create_fact(argv[0]);
551
552 // Call the block if one was given
553 if (ruby.rb_block_given_p()) {
554 ruby.rb_funcall_passing_block(fact_self, ruby.rb_intern("instance_eval"), 0, nullptr);
555 }
556 return fact_self;
557 });
558 }
559
560 VALUE module::ruby_value(VALUE self, VALUE name)
561 {
562 return safe_eval("Facter.value", [&]() {
563 return from_self(self)->fact_value(name);
564 });
565 }
566
567 VALUE module::ruby_fact(VALUE self, VALUE name)
568 {
569 return safe_eval("Facter.fact", [&]() {
570 return from_self(self)->load_fact(name);
571 });
572 }
573
574 VALUE module::ruby_debug(VALUE self, VALUE message)
575 {
576 return safe_eval("Facter.debug", [&]() {
577 auto const& ruby = api::instance();
578 LOG_DEBUG(ruby.to_string(message));
579 return ruby.nil_value();
580 });
581 }
582
583 VALUE module::ruby_debugonce(VALUE self, VALUE message)
584 {
585 return safe_eval("Facter.debugonce", [&]() {
586 auto const& ruby = api::instance();
587
588 string msg = ruby.to_string(message);
589 if (from_self(self)->_debug_messages.insert(msg).second) {
590 LOG_DEBUG(msg);
591 }
592 return ruby.nil_value();
593 });
594 }
595
596 VALUE module::ruby_warn(VALUE self, VALUE message)
597 {
598 return safe_eval("Facter.warn", [&]() {
599 auto const& ruby = api::instance();
600 LOG_WARNING(ruby.to_string(message));
601 return ruby.nil_value();
602 });
603 }
604
605 VALUE module::ruby_warnonce(VALUE self, VALUE message)
606 {
607 return safe_eval("Facter.warnonce", [&]() {
608 auto const& ruby = api::instance();
609
610 string msg = ruby.to_string(message);
611 if (from_self(self)->_warning_messages.insert(msg).second) {
612 LOG_WARNING(msg);
613 }
614 return ruby.nil_value();
615 });
616 }
617
618 VALUE module::ruby_set_debugging(VALUE self, VALUE value)
619 {
620 return safe_eval("Facter.debugging", [&]() {
621 auto const& ruby = api::instance();
622
623 if (ruby.is_true(value)) {
624 set_level(log_level::debug);
625 } else {
626 set_level(log_level::warning);
627 }
628 return ruby_get_debugging(self);
629 });
630 }
631
632 VALUE module::ruby_get_debugging(VALUE self)
633 {
634 return safe_eval("Facter.debugging?", [&]() {
635 auto const& ruby = api::instance();
636 return is_enabled(log_level::debug) ? ruby.true_value() : ruby.false_value();
637 });
638 }
639
640 VALUE module::ruby_set_trace(VALUE self, VALUE value)
641 {
642 return safe_eval("Facter.trace", [&]() {
643 auto& ruby = api::instance();
644 ruby.include_stack_trace(ruby.is_true(value));
645 return ruby_get_trace(self);
646 });
647 }
648
649 VALUE module::ruby_get_trace(VALUE self)
650 {
651 return safe_eval("Facter.trace?", [&]() {
652 auto const& ruby = api::instance();
653 return ruby.include_stack_trace() ? ruby.true_value() : ruby.false_value();
654 });
655 }
656
657 VALUE module::ruby_log_exception(int argc, VALUE* argv, VALUE self)
658 {
659 return safe_eval("Facter.log_exception", [&]() {
660 auto const& ruby = api::instance();
661
662 if (argc == 0 || argc > 2) {
663 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
664 }
665
666 string message;
667 if (argc == 2) {
668 // Use the given argument provided it is not a symbol equal to :default
669 if (!ruby.is_symbol(argv[1]) || ruby.rb_to_id(argv[1]) != ruby.rb_intern("default")) {
670 message = ruby.to_string(argv[1]);
671 }
672 }
673
674 LOG_ERROR(ruby.exception_to_string(argv[0], message));
675 return ruby.nil_value();
676 });
677 }
678
679 VALUE module::ruby_flush(VALUE self)
680 {
681 return safe_eval("Facter.flush", [&]() {
682 auto const& ruby = api::instance();
683
684 for (auto& kvp : from_self(self)->_facts)
685 {
686 ruby.to_native<fact>(kvp.second)->flush();
687 }
688 return ruby.nil_value();
689 });
690 }
691
692 VALUE module::ruby_list(VALUE self)
693 {
694 return safe_eval("Facter.list", [&]() {
695 auto const& ruby = api::instance();
696 module* instance = from_self(self);
697
698 instance->resolve_facts();
699
700 volatile VALUE array = ruby.rb_ary_new_capa(instance->facts().size());
701
702 instance->facts().each([&](string const& name, value const*) {
703 ruby.rb_ary_push(array, ruby.utf8_value(name));
704 return true;
705 });
706
707 return array;
708 });
709 }
710
711 VALUE module::ruby_to_hash(VALUE self)
712 {
713 return safe_eval("Facter.to_hash", [&]() {
714 auto const& ruby = api::instance();
715 module* instance = from_self(self);
716
717 instance->resolve_facts();
718
719 volatile VALUE hash = ruby.rb_hash_new();
720
721 instance->facts().each([&](string const& name, value const* val) {
722 ruby.rb_hash_aset(hash, ruby.utf8_value(name), instance->to_ruby(val));
723 return true;
724 });
725
726 return hash;
727 });
728 }
729
730 VALUE module::ruby_each(VALUE self)
731 {
732 return safe_eval("Facter.each", [&]() {
733 auto const& ruby = api::instance();
734 module* instance = from_self(self);
735
736 instance->resolve_facts();
737
738 instance->facts().each([&](string const& name, value const* val) {
739 ruby.rb_yield_values(2, ruby.utf8_value(name), instance->to_ruby(val));
740 return true;
741 });
742
743 return self;
744 });
745 }
746
747 VALUE module::ruby_clear(VALUE self)
748 {
749 return safe_eval("Facter.clear", [&]() {
750 auto const& ruby = api::instance();
751
752 ruby_flush(self);
753 ruby_reset(self);
754
755 return ruby.nil_value();
756 });
757 }
758
759 VALUE module::ruby_reset(VALUE self)
760 {
761 return safe_eval("Facter.reset", [&]() {
762 auto const& ruby = api::instance();
763 module* instance = from_self(self);
764
765 instance->clear_facts();
766 instance->initialize_search_paths({});
767 instance->_external_search_paths.clear();
768 instance->_loaded_all = false;
769 instance->_loaded_files.clear();
770
771 return ruby.nil_value();
772 });
773 }
774
775 VALUE module::ruby_loadfacts(VALUE self)
776 {
777 return safe_eval("Facter.loadfacts", [&]() {
778 auto const& ruby = api::instance();
779
780 from_self(self)->load_facts();
781 return ruby.nil_value();
782 });
783 }
784
785 VALUE module::ruby_search(int argc, VALUE* argv, VALUE self)
786 {
787 return safe_eval("Facter.search", [&]() {
788 auto const& ruby = api::instance();
789 module* instance = from_self(self);
790
791 for (int i = 0; i < argc; ++i) {
792 if (!ruby.is_string(argv[i])) {
793 continue;
794 }
795
796 instance->_additional_search_paths.emplace_back(ruby.to_string(argv[i]));
797 instance->_search_paths.emplace_back(canonicalize(instance->_additional_search_paths.back()));
798 }
799 return ruby.nil_value();
800 });
801 }
802
803 VALUE module::ruby_search_path(VALUE self)
804 {
805 return safe_eval("Facter.search_path", [&]() {
806 auto const& ruby = api::instance();
807 module* instance = from_self(self);
808
809 volatile VALUE array = ruby.rb_ary_new_capa(instance->_additional_search_paths.size());
810
811 for (auto const& path : instance->_additional_search_paths) {
812 ruby.rb_ary_push(array, ruby.utf8_value(path));
813 }
814 return array;
815 });
816 }
817
818 VALUE module::ruby_search_external(VALUE self, VALUE paths)
819 {
820 return safe_eval("Facter.search_external", [&]() {
821 auto const& ruby = api::instance();
822 module* instance = from_self(self);
823
824 ruby.array_for_each(paths, [&](VALUE element) {
825 if (!ruby.is_string(element)) {
826 return true;
827 }
828 instance->_external_search_paths.emplace_back(ruby.to_string(element));
829 return true;
830 });
831
832 // Add external path from config file
833 LOG_DEBUG(_("loading external fact directories from config file"));
834 if (instance->_config_file_settings.count("external-dir")) {
835 auto config_paths = instance->_config_file_settings["external-dir"].as<vector<string>>();
836 instance->_external_search_paths.insert(instance->_external_search_paths.end(), config_paths.begin(), config_paths.end());
837 }
838 return ruby.nil_value();
839 });
840 }
841
842 VALUE module::ruby_search_external_path(VALUE self)
843 {
844 return safe_eval("Facter.search_external_path", [&]() {
845 auto const& ruby = api::instance();
846 module* instance = from_self(self);
847
848 volatile VALUE array = ruby.rb_ary_new_capa(instance->_external_search_paths.size());
849
850 for (auto const& path : instance->_external_search_paths) {
851 ruby.rb_ary_push(array, ruby.utf8_value(path));
852 }
853 return array;
854 });
855 }
856
857 VALUE module::ruby_which(VALUE self, VALUE binary)
858 {
859 return safe_eval("Facter::Core::Execution::which", [&]() {
860 // Note: self is Facter::Core::Execution
861 auto const& ruby = api::instance();
862
863 string path = which(ruby.to_string(binary));
864 if (path.empty()) {
865 return ruby.nil_value();
866 }
867
868 return ruby.utf8_value(path);
869 });
870 }
871
872 VALUE module::ruby_exec(VALUE self, VALUE command)
873 {
874 return safe_eval("Facter::Core::Execution::exec", [&]() {
875 // Note: self is Facter::Core::Execution
876 auto const& ruby = api::instance();
877 return execute_command(ruby.to_string(command), ruby.nil_value(), false);
878 });
879 }
880
881 VALUE module::ruby_execute(int argc, VALUE* argv, VALUE self)
882 {
883 return safe_eval("Facter::Core::Execution::execute", [&]() {
884 // Note: self is Facter::Core::Execution
885 auto const& ruby = api::instance();
886
887 if (argc == 0 || argc > 2) {
888 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 2)", argc).c_str());
889 }
890
891 if (argc == 1) {
892 return execute_command(ruby.to_string(argv[0]), ruby.nil_value(), true);
893 }
894
895 // Unfortunately we have to call to_sym rather than using ID2SYM, which is Ruby version dependent
896 uint32_t timeout = 0;
897 volatile VALUE timeout_option = ruby.rb_hash_lookup(argv[1], ruby.to_symbol("timeout"));
898 if (ruby.is_integer(timeout_option)) {
899 timeout = ruby.num2size_t(timeout_option);
900 }
901
902 // Get the on_fail option (defaults to :raise)
903 bool raise = false;
904 volatile VALUE raise_value = ruby.to_symbol("raise");
905 volatile VALUE fail_option = ruby.rb_hash_lookup2(argv[1], ruby.to_symbol("on_fail"), raise_value);
906 if (ruby.equals(fail_option, raise_value)) {
907 raise = true;
908 fail_option = ruby.nil_value();
909 }
910
911 bool expand = true;
912 volatile VALUE expand_option = ruby.rb_hash_lookup2(argv[1], ruby.to_symbol("expand"), ruby.true_value());
913 if (ruby.is_false(expand_option)) {
914 expand = false;
915 }
916
917 return execute_command(ruby.to_string(argv[0]), fail_option, raise, timeout, expand);
918 });
919 }
920
921 VALUE module::ruby_on_message(VALUE self)
922 {
923 return safe_eval("Facter.on_message", [&]() {
924 auto const& ruby = api::instance();
925
926 from_self(self)->_on_message_block = ruby.rb_block_given_p() ? ruby.rb_block_proc() : ruby.nil_value();
927 return ruby.nil_value();
928 });
929 }
930
931 module* module::from_self(VALUE self)
932 {
933 auto it = _instances.find(self);
934 if (it == _instances.end()) {
935 auto const& ruby = api::instance();
936 ruby.rb_raise(*ruby.rb_eArgError, _("unexpected self value {1}", self).c_str());
937 return nullptr;
938 }
939 return it->second;
940 }
941
942 VALUE module::execute_command(std::string const& command, VALUE failure_default, bool raise, uint32_t timeout, bool expand)
943 {
944 auto const& ruby = api::instance();
945
946 // Expand the command only if expand is true,
947 std::string expanded;
948 try {
949 #ifdef HAS_LTH_EXPAND
950 expanded = expand_command(command, leatherman::util::environment::search_paths(), expand);
951 #else
952 expanded = expand_command(command);
953 LOG_DEBUG("Facter was compiled with a Leatherman version that cannot expand shell builtins. Falling back to the default behavior.");
954 #endif
955 } catch (const std::invalid_argument &ex) {
956 ruby.rb_raise(*ruby.rb_eArgError, _("Cause: {1}", ex.what()).c_str());
957 }
958 if (!expanded.empty()) {
959 try {
960 auto exec = execute(
961 command_shell,
962 {
963 command_args,
964 expanded
965 },
966 timeout,
967 {
968 execution_options::trim_output,
969 execution_options::merge_environment,
970 execution_options::redirect_stderr_to_null,
971 execution_options::preserve_arguments
972 });
973 // Ruby can encode some additional information in the
974 // lower 8 bits. None of those set means "process exited normally"
975 ruby.rb_last_status_set(exec.exit_code << 8, static_cast<rb_pid_t>(exec.pid));
976 return ruby.utf8_value(exec.output);
977 } catch (timeout_exception const& ex) {
978 // Always raise for timeouts
979 ruby.rb_raise(ruby.lookup({ "Facter", "Core", "Execution", "ExecutionFailure"}), ex.what());
980 }
981 }
982 // Command was not found
983 if (raise) {
984 if (expanded.empty()) {
985 ruby.rb_raise(ruby.lookup({ "Facter", "Core", "Execution", "ExecutionFailure"}), _("execution of command \"{1}\" failed: command not found.", command).c_str());
986 }
987 ruby.rb_raise(ruby.lookup({ "Facter", "Core", "Execution", "ExecutionFailure"}), _("execution of command \"{1}\" failed.", command).c_str());
988 }
989 return failure_default;
990 }
991
992 void module::initialize_search_paths(vector<string> const& paths)
993 {
994 auto const& ruby = api::instance();
995
996 _search_paths.clear();
997 _additional_search_paths.clear();
998
999 // Look for "facter" subdirectories on the load path
1000 for (auto const& directory : ruby.get_load_path()) {
1001 boost::system::error_code ec;
1002 // Use forward-slash to keep this consistent with Ruby conventions.
1003 auto dir = canonicalize(directory) + "/facter";
1004
1005 // Ignore facter itself if it's on the load path
1006 if (is_regular_file(dir, ec)) {
1007 continue;
1008 }
1009
1010 if (!is_directory(dir, ec)) {
1011 continue;
1012 }
1013 _search_paths.push_back(dir);
1014 }
1015
1016 // Append the FACTERLIB paths
1017 string variable;
1018 if (environment::get("FACTERLIB", variable)) {
1019 vector<string> env_paths;
1020 boost::split(env_paths, variable, bind(equal_to<char>(), placeholders::_1, environment::get_path_separator()), boost::token_compress_on);
1021 _search_paths.insert(_search_paths.end(), make_move_iterator(env_paths.begin()), make_move_iterator(env_paths.end()));
1022 }
1023
1024 // Insert the given paths last
1025 _search_paths.insert(_search_paths.end(), paths.begin(), paths.end());
1026
1027 // Do a canonical/absolute transform
1028 transform(_search_paths.begin(), _search_paths.end(), _search_paths.begin(), [](string const& directory) -> string {
1029 return canonicalize(directory);
1030 });
1031
1032 // Remove anything that is empty using the erase-remove idiom.
1033 _search_paths.erase(
1034 remove_if(begin(_search_paths), end(_search_paths), [](string const& path) { return path.empty(); }),
1035 end(_search_paths));
1036 }
1037
1038 VALUE module::load_fact(VALUE name)
1039 {
1040 auto const& ruby = api::instance();
1041
1042 name = normalize(name);
1043 string fact_name = ruby.to_string(name);
1044
1045 // First check to see if we have that fact already
1046 auto it = _facts.find(fact_name);
1047 if (it != _facts.end()) {
1048 return it->second;
1049 }
1050
1051 // Try to load it by file name
1052 if (!_loaded_all) {
1053 // Next, attempt to load it by file
1054 string filename = fact_name + ".rb";
1055 LOG_DEBUG("searching for custom fact \"{1}\".", fact_name);
1056
1057 for (auto const& directory : _search_paths) {
1058 LOG_DEBUG("searching for {1} in {2}.", filename, directory);
1059
1060 // Check to see if there's a file of a matching name in this directory
1061 path full_path = path(directory) / filename;
1062 boost::system::error_code ec;
1063 if (!is_regular_file(full_path, ec)) {
1064 continue;
1065 }
1066
1067 // Load the fact file
1068 load_file(full_path.string());
1069 }
1070
1071 // Check to see if we now have the fact
1072 it = _facts.find(fact_name);
1073 if (it != _facts.end()) {
1074 return it->second;
1075 }
1076 }
1077
1078 // Otherwise, check to see if it's already in the collection
1079 auto value = facts()[fact_name];
1080 if (value) {
1081 return create_fact(name);
1082 }
1083
1084 // Couldn't load the fact by file name, load all facts to try to find it
1085 load_facts();
1086
1087 // Check to see if we now have the fact
1088 it = _facts.find(fact_name);
1089 if (it != _facts.end()) {
1090 return it->second;
1091 }
1092
1093 // Couldn't find the fact
1094 LOG_DEBUG("custom fact \"{1}\" was not found.", fact_name);
1095 return ruby.nil_value();
1096 }
1097
1098 void module::load_file(std::string const& path)
1099 {
1100 // Only load the file if we haven't done so before
1101 if (!_loaded_files.insert(path).second) {
1102 return;
1103 }
1104
1105 auto const& ruby = api::instance();
1106
1107 LOG_INFO("loading custom facts from {1}.", path);
1108 ruby.rescue([&]() {
1109 // Do not construct C++ objects in a rescue callback
1110 // C++ stack unwinding will not take place if a Ruby exception is thrown!
1111 ruby.rb_load(ruby.utf8_value(path), 0);
1112 return 0;
1113 }, [&](VALUE ex) {
1114 LOG_ERROR("error while resolving custom facts in {1}: {2}", path, ruby.exception_to_string(ex));
1115 return 0;
1116 });
1117 }
1118
1119 VALUE module::create_fact(VALUE name)
1120 {
1121 auto const& ruby = api::instance();
1122
1123 if (!ruby.is_string(name) && !ruby.is_symbol(name)) {
1124 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a String or Symbol for fact name").c_str());
1125 }
1126
1127 name = normalize(name);
1128 string fact_name = ruby.to_string(name);
1129
1130 // First check to see if we have that fact already
1131 auto it = _facts.find(fact_name);
1132 if (it == _facts.end()) {
1133 // Before adding the first fact, call facts to ensure the collection is populated
1134 facts();
1135 // facts() may add entries to _facts, so check again to ensure entry has not already been added (avoid duplicate Ruby GC registration)
1136 it = _facts.find(fact_name);
1137 if (it == _facts.end()) {
1138 it = _facts.insert(make_pair(fact_name, fact::create(name))).first;
1139 ruby.rb_gc_register_address(&it->second);
1140 }
1141 }
1142 return it->second;
1143 }
1144
1145 VALUE module::level_to_symbol(log_level level)
1146 {
1147 auto const& ruby = api::instance();
1148
1149 char const* name = nullptr;
1150
1151 if (level == log_level::trace) {
1152 name = "trace";
1153 } else if (level == log_level::debug) {
1154 name = "debug";
1155 } else if (level == log_level::info) {
1156 name = "info";
1157 } else if (level == log_level::warning) {
1158 name = "warn";
1159 } else if (level == log_level::error) {
1160 name = "error";
1161 } else if (level == log_level::fatal) {
1162 name = "fatal";
1163 }
1164 if (!name) {
1165 ruby.rb_raise(*ruby.rb_eArgError, _("invalid log level specified.").c_str(), 0);
1166 }
1167 return ruby.to_symbol(name);
1168 }
1169
1170 }} // namespace facter::ruby
+0
-230
lib/src/ruby/resolution.cc less more
0 #include <internal/ruby/resolution.hpp>
1 #include <internal/ruby/module.hpp>
2 #include <leatherman/logging/logging.hpp>
3 #include <leatherman/locale/locale.hpp>
4
5 // Mark string for translation (alias for leatherman::locale::format)
6 using leatherman::locale::_;
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::ruby;
11
12 namespace facter { namespace ruby {
13
14 resolution::resolution() :
15 _has_weight(false),
16 _weight(0)
17 {
18 auto const& ruby = api::instance();
19 _name = ruby.nil_value();
20 _value = ruby.nil_value();
21 _flush_block = ruby.nil_value();
22 }
23
24 resolution::~resolution()
25 {
26 }
27
28 VALUE resolution::name() const
29 {
30 return _name;
31 }
32
33 void resolution::name(VALUE name)
34 {
35 _name = name;
36 }
37
38 size_t resolution::weight() const
39 {
40 if (_has_weight) {
41 return _weight;
42 }
43 return _confines.size();
44 }
45
46 void resolution::weight(size_t weight)
47 {
48 _has_weight = true;
49 _weight = weight;
50 }
51
52 VALUE resolution::value()
53 {
54 return _value;
55 }
56
57 void resolution::value(VALUE v)
58 {
59 _value = v;
60 }
61
62 bool resolution::suitable(module& facter) const
63 {
64 auto const& ruby = api::instance();
65
66 int tag = 0;
67 {
68 // Declare all C++ objects here
69 vector<ruby::confine>::const_iterator it;
70
71 VALUE result = ruby.protect(tag, [&]() {
72 // Do not declare any C++ objects inside the protect
73 // Their destructors will not be invoked if there is a Ruby exception
74 for (it = _confines.begin(); it != _confines.end(); ++it) {
75 if (!it->suitable(facter)) {
76 return ruby.false_value();
77 }
78 }
79 return ruby.true_value();
80 });
81
82 // If all confines were suitable, the resolution is considered to be suitable
83 if (!tag) {
84 return ruby.is_true(result);
85 }
86 }
87
88 // Now that the above block has exited, it's safe to jump to the given tag
89 ruby.rb_jump_tag(tag);
90 return false;
91 }
92
93 void resolution::flush() const
94 {
95 auto const& ruby = api::instance();
96
97 if (ruby.is_nil(_flush_block)) {
98 return;
99 }
100
101 ruby.rb_funcall(_flush_block, ruby.rb_intern("call"), 0);
102 }
103
104 void resolution::confine(VALUE confines)
105 {
106 auto const& ruby = api::instance();
107
108 if (ruby.is_nil(confines)) {
109 // No confines, only a block is required
110 if (!ruby.rb_block_given_p()) {
111 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
112 }
113 _confines.emplace_back(ruby::confine(ruby.nil_value(), ruby.nil_value(), ruby.rb_block_proc()));
114 } else {
115 if (ruby.is_symbol(confines)) {
116 confines = ruby.rb_sym_to_s(confines);
117 }
118 if (ruby.is_string(confines)) {
119 // Argument is a string and a is block required
120 if (!ruby.rb_block_given_p()) {
121 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
122 }
123 _confines.emplace_back(ruby::confine(confines, ruby.nil_value(), ruby.rb_block_proc()));
124 } else if (ruby.is_hash(confines)) {
125 // Argument is a hash (block should not be given)
126 if (ruby.rb_block_given_p()) {
127 ruby.rb_raise(*ruby.rb_eArgError, _("a block is unexpected when passing a Hash").c_str());
128 }
129 ruby.hash_for_each(confines, [&](VALUE key, VALUE value) {
130 if (ruby.is_symbol(key)) {
131 key = ruby.rb_sym_to_s(key);
132 }
133 if (!ruby.is_string(key)) {
134 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a String or Symbol for confine key").c_str());
135 }
136 if (ruby.is_symbol(value)) {
137 value = ruby.rb_sym_to_s(value);
138 }
139 _confines.emplace_back(ruby::confine(key, value, ruby.nil_value()));
140 return true;
141 });
142 } else {
143 ruby.rb_raise(*ruby.rb_eTypeError, _("expected argument to be a String, Symbol, or Hash").c_str());
144 }
145 }
146 }
147
148 void resolution::define(VALUE klass)
149 {
150 auto const& ruby = api::instance();
151 ruby.rb_define_method(klass, "confine", RUBY_METHOD_FUNC(ruby_confine), -1);
152 ruby.rb_define_method(klass, "has_weight", RUBY_METHOD_FUNC(ruby_has_weight), 1);
153 ruby.rb_define_method(klass, "name", RUBY_METHOD_FUNC(ruby_name), 0);
154 ruby.rb_define_method(klass, "timeout=", RUBY_METHOD_FUNC(ruby_timeout), 1);
155 ruby.rb_define_method(klass, "on_flush", RUBY_METHOD_FUNC(ruby_on_flush), 0);
156 }
157
158 void resolution::mark() const
159 {
160 auto const& ruby = api::instance();
161
162 // Mark the name and value
163 ruby.rb_gc_mark(_name);
164 ruby.rb_gc_mark(_value);
165 ruby.rb_gc_mark(_flush_block);
166
167 // Mark all of the confines
168 for (auto const& confine : _confines) {
169 confine.mark();
170 }
171 }
172
173 VALUE resolution::ruby_confine(int argc, VALUE* argv, VALUE self)
174 {
175 auto const& ruby = api::instance();
176
177 if (argc > 1) {
178 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 1)", argc).c_str());
179 }
180
181 ruby.to_native<resolution>(self)->confine(argc == 0 ? ruby.nil_value() : argv[0]);
182 return self;
183 }
184
185 VALUE resolution::ruby_has_weight(VALUE self, VALUE value)
186 {
187 auto const& ruby = api::instance();
188
189 int64_t val = ruby.rb_num2ll(value);
190 if (val < 0) {
191 ruby.rb_raise(*ruby.rb_eTypeError, "expected a non-negative value for has_weight (not %lld)", val);
192 }
193
194 auto instance = ruby.to_native<resolution>(self);
195 instance->_has_weight = true;
196 instance->_weight = static_cast<size_t>(val);
197 return self;
198 }
199
200 VALUE resolution::ruby_name(VALUE self)
201 {
202 auto const& ruby = api::instance();
203 return ruby.to_native<resolution>(self)->name();
204 }
205
206 VALUE resolution::ruby_timeout(VALUE self, VALUE timeout)
207 {
208 static bool timeout_warning = true;
209 if (timeout_warning) {
210 LOG_WARNING("timeout= is not supported for custom facts and will be ignored.")
211 timeout_warning = false;
212 }
213 // Do nothing as we don't support timeouts
214 return self;
215 }
216
217 VALUE resolution::ruby_on_flush(VALUE self)
218 {
219 auto const& ruby = api::instance();
220
221 if (!ruby.rb_block_given_p()) {
222 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
223 }
224
225 ruby.to_native<resolution>(self)->_flush_block = ruby.rb_block_proc();
226 return self;
227 }
228
229 }} // namespace facter::ruby
+0
-196
lib/src/ruby/ruby.cc less more
0 #include <facter/ruby/ruby.hpp>
1 #include <facter/logging/logging.hpp>
2 #include <internal/ruby/module.hpp>
3 #include <internal/ruby/ruby_value.hpp>
4 #include <leatherman/ruby/api.hpp>
5 #include <leatherman/logging/logging.hpp>
6 #ifdef _WIN32
7 #include <internal/util/windows/wsa.hpp>
8 #endif
9
10 #include <numeric>
11
12 using namespace std;
13 using namespace facter::facts;
14 using namespace leatherman::ruby;
15
16 static const char load_puppet[] =
17 "require 'puppet'\n"
18 "Puppet.initialize_settings\n"
19 "unless $LOAD_PATH.include?(Puppet[:libdir])\n"
20 " $LOAD_PATH << Puppet[:libdir]\n"
21 "end\n"
22 "Facter.reset\n"
23 "Facter.search_external([Puppet[:pluginfactdest]])\n"
24 "if Puppet.respond_to? :initialize_facts\n"
25 " Puppet.initialize_facts\n"
26 "else\n"
27 " Facter.add(:puppetversion) do\n"
28 " setcode { Puppet.version.to_s }\n"
29 " end\n"
30 "end\n";
31
32 // This struct redirects stdout to stderr in the ruby runtime for the
33 // duration of its lifetime. We use this to ensure that any custom
34 // facts writing to stdout during their initialization or execution
35 // won't corrupt json/yaml output from the facter executable.
36 struct RbStdoutGuard {
37 VALUE old_stdout;
38 api& ruby;
39
40 RbStdoutGuard(api& ruby) :ruby(ruby) {
41 LOG_DEBUG("Redirecting ruby's stdout to stderr");
42 auto rb_stderr = ruby.rb_gv_get("$stderr");
43 old_stdout = ruby.rb_gv_get("$stdout");
44 ruby.rb_gv_set("$stdout", rb_stderr);
45 }
46
47 ~RbStdoutGuard() {
48 LOG_DEBUG("Restoring Ruby's stdout");
49 ruby.rb_gv_set("$stdout", old_stdout);
50 }
51 };
52
53 namespace facter { namespace ruby {
54
55 bool initialize(bool include_stack_trace)
56 {
57 #ifdef FACTER_RUBY
58 api::ruby_lib_location = FACTER_RUBY;
59 #endif
60 try {
61 auto& ruby = api::instance();
62 ruby.initialize();
63 ruby.include_stack_trace(include_stack_trace);
64 } catch (runtime_error& ex) {
65 LOG_WARNING("{1}: facts requiring Ruby will not be resolved.", ex.what());
66 return false;
67 }
68 return true;
69 }
70
71 void load_custom_facts(collection& facts, bool initialize_puppet, bool redirect_stdout, vector<string> const& paths)
72 {
73 #ifdef _WIN32
74 // Initialize WSA before resolving custom facts. The Ruby runtime does this only when running
75 // in a Ruby process, it leaves it up to us when embedding it. See
76 // https://github.com/ruby/ruby/blob/v2_1_9/ruby.c#L2011-L2022 for comments.
77 // The only piece we seem to need out of rb_w32_sysinit is WSAStartup.
78 util::windows::wsa winsocket;
79
80 // Disable stdout buffering while loading custom facts, similar to `stderr` in `init_stdhandle`
81 // https://github.com/ruby/ruby/blob/9e41a75255d15765648279629fd3134cae076398/win32/win32.c#L2655
82 // This is needed in a specific case:
83 // - run facter from ruby with backticks
84 // - have a custom fact executing external command with backticks
85 // In this case, `\x00` character will be shown on stdout instead of fact output
86 // We suppose that somwhere between ruby(`facter my_fact`)<->c(rb_load)<->ruby(Facter.add)<->c(rb_funcall_passing_block)<->ruby(`echo test`)
87 // stdout gets the wchar end of string that will break it
88 setvbuf(stdout, NULL, _IONBF, 0);
89 #endif
90 api& ruby = api::instance();
91 module mod(facts, {}, !initialize_puppet);
92 if (initialize_puppet) {
93 try {
94 ruby.eval(load_puppet);
95 } catch (exception& ex) {
96 LOG_WARNING("Could not load puppet; some facts may be unavailable: {1}", ex.what());
97 }
98 }
99 mod.search(paths);
100 if (redirect_stdout) {
101 // Redirect stdout->stderr for custom facts.
102 RbStdoutGuard stdout_guard{ruby};
103 mod.resolve_facts();
104 } else {
105 mod.resolve_facts();
106 }
107 #ifdef _WIN32
108 // Enable stdout line buffering (disabled due custom facts loading)
109 setvbuf(stdout, NULL, _IOLBF, 0);
110 #endif
111 }
112
113 void load_custom_facts(collection& facts, vector<string> const& paths)
114 {
115 load_custom_facts(facts, false, false, paths);
116 }
117
118 void load_custom_facts(collection& facts, bool initialize_puppet, vector<string> const& paths)
119 {
120 load_custom_facts(facts, initialize_puppet, false, paths);
121 }
122
123 value const* lookup(value const* value, vector<string>::iterator segment, vector<string>::iterator end) {
124 auto rb_value = dynamic_cast<ruby_value const*>(value);
125 if (!rb_value) {
126 return nullptr;
127 }
128
129 // Check for a cached lookup
130 auto key = accumulate(segment, end, string{}, [](const string& a, const string& b) -> string {
131 if (b.find(".") != string::npos) {
132 return a+".\""+b+"\"";
133 } else {
134 return a+"."+b;
135 }
136 });
137 auto child_value = rb_value->child(key);
138 if (child_value) {
139 return child_value;
140 }
141
142 auto val = rb_value->value(); // now we're in ruby land
143 api& ruby = api::instance();
144
145 for (; segment != end; ++segment) {
146 if (ruby.is_array(val)) {
147 long index;
148 try {
149 index = stol(*segment);
150 } catch (logic_error&) {
151 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected an integral value.", *segment);
152 return nullptr;
153 }
154 if (index < 0) {
155 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected a non-negative value.", *segment);
156 return nullptr;
157 }
158 long length = ruby.array_len(val);
159 if (0 == length) {
160 LOG_DEBUG("cannot lookup an array element with \"{1}\": the array is empty.", *segment);
161 return nullptr;
162 }
163
164 if (index >= length) {
165 LOG_DEBUG("cannot lookup an array element with \"{1}\": expected an integral value between 0 and {2} (inclusive).", *segment, length - 1);
166 return nullptr;
167 }
168
169 val = ruby.rb_ary_entry(val, index);
170 } else if (ruby.is_hash(val)) {
171 // if we're anything but an array, we look up by name
172 auto key = ruby.utf8_value(*segment);
173 auto result = ruby.rb_hash_lookup(val, key);
174 if (ruby.is_nil(result)) {
175 // We also want to try looking up as a symbol
176 key = ruby.to_symbol(*segment);
177 result = ruby.rb_hash_lookup(val, key);
178 }
179 val = result;
180 } else {
181 LOG_DEBUG("cannot lookup element \"{1}\": container is not an array or hash", *segment);
182 }
183 if (ruby.is_nil(val)) {
184 return nullptr;
185 }
186 }
187 return rb_value->wrap_child(val, move(key));
188 }
189
190 void uninitialize()
191 {
192 api& ruby = api::instance();
193 ruby.uninitialize();
194 }
195 }} // namespace facter::ruby
+0
-288
lib/src/ruby/ruby_value.cc less more
0 #include <internal/ruby/ruby_value.hpp>
1 #include <facter/util/string.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <iomanip>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace facter::util;
9 using namespace rapidjson;
10 using namespace YAML;
11 using namespace leatherman::ruby;
12
13 namespace facter { namespace ruby {
14
15 ruby_value::ruby_value(VALUE value) :
16 _value(value)
17 {
18 auto const& ruby = api::instance();
19 ruby.rb_gc_register_address(&_value);
20 }
21
22 ruby_value::~ruby_value()
23 {
24 auto const& ruby = api::instance();
25 ruby.rb_gc_unregister_address(&_value);
26 }
27
28 ruby_value::ruby_value(ruby_value&& other) :
29 _value(other._value)
30 {
31 auto const& ruby = api::instance();
32 ruby.rb_gc_register_address(&_value);
33 }
34
35 ruby_value& ruby_value::operator=(ruby_value&& other)
36 {
37 _value = other._value;
38 return *this;
39 }
40
41 void ruby_value::to_json(json_allocator& allocator, json_value& value) const
42 {
43 auto const& ruby = api::instance();
44 to_json(ruby, _value, allocator, value);
45 }
46
47 ostream& ruby_value::write(ostream& os, bool quoted, unsigned int level) const
48 {
49 auto const& ruby = api::instance();
50 write(ruby, _value, os, quoted, level);
51 return os;
52 }
53
54 Emitter& ruby_value::write(Emitter& emitter) const
55 {
56 auto const& ruby = api::instance();
57 write(ruby, _value, emitter);
58 return emitter;
59 }
60
61 VALUE ruby_value::value() const
62 {
63 return _value;
64 }
65
66 void ruby_value::to_json(api const& ruby, VALUE value, json_allocator& allocator, json_value& json)
67 {
68 if (ruby.is_true(value)) {
69 json.SetBool(true);
70 return;
71 }
72 if (ruby.is_false(value)) {
73 json.SetBool(false);
74 return;
75 }
76 if (ruby.is_string(value) || ruby.is_symbol(value)) {
77 volatile VALUE temp = value;
78
79 if (ruby.is_symbol(value)) {
80 temp = ruby.rb_funcall(value, ruby.rb_intern("to_s"), 0);
81 }
82
83 size_t size = ruby.num2size_t(ruby.rb_funcall(temp, ruby.rb_intern("bytesize"), 0));
84 char const* str = ruby.rb_string_value_ptr(&temp);
85 json.SetString(str, size, allocator);
86 return;
87 }
88 if (ruby.is_integer(value)) {
89 json.SetInt64(ruby.rb_num2ll(value));
90 return;
91 }
92 if (ruby.is_float(value)) {
93 json.SetDouble(ruby.rb_num2dbl(value));
94 return;
95 }
96 if (ruby.is_array(value)) {
97 json.SetArray();
98 size_t size = ruby.num2size_t(ruby.rb_funcall(value, ruby.rb_intern("size"), 0));
99 json.Reserve(size, allocator);
100
101 ruby.array_for_each(value, [&](VALUE element) {
102 json_value child;
103 to_json(ruby, element, allocator, child);
104 json.PushBack(child, allocator);
105 return true;
106 });
107 return;
108 }
109 if (ruby.is_hash(value)) {
110 json.SetObject();
111
112 ruby.hash_for_each(value, [&](VALUE key, VALUE element) {
113 // If the key isn't a string, convert to string
114 if (!ruby.is_string(key)) {
115 key = ruby.rb_funcall(key, ruby.rb_intern("to_s"), 0);
116 }
117 json_value child;
118 to_json(ruby, element, allocator, child);
119 json.AddMember(json_value(ruby.rb_string_value_ptr(&key), allocator), child, allocator);
120 return true;
121 });
122 return;
123 }
124
125 json.SetNull();
126 }
127
128 void ruby_value::write(api const& ruby, VALUE value, ostream& os, bool quoted, unsigned int level)
129 {
130 if (ruby.is_true(value)) {
131 os << boolalpha << true << noboolalpha;
132 return;
133 }
134 if (ruby.is_false(value)) {
135 os << boolalpha << false << noboolalpha;
136 return;
137 }
138 if (ruby.is_string(value) || ruby.is_symbol(value)) {
139 volatile VALUE temp = value;
140
141 if (ruby.is_symbol(value)) {
142 temp = ruby.rb_funcall(value, ruby.rb_intern("to_s"), 0);
143 }
144
145 size_t size = ruby.num2size_t(ruby.rb_funcall(temp, ruby.rb_intern("bytesize"), 0));
146 char const* str = ruby.rb_string_value_ptr(&temp);
147
148 if (quoted) {
149 os << '"';
150 }
151 os.write(str, size);
152 if (quoted) {
153 os << '"';
154 }
155 return;
156 }
157 if (ruby.is_integer(value)) {
158 os << ruby.rb_num2ll(value);
159 return;
160 }
161 if (ruby.is_float(value)) {
162 os << ruby.rb_num2dbl(value);
163 return;
164 }
165 if (ruby.is_array(value)) {
166 auto size = ruby.num2size_t(ruby.rb_funcall(value, ruby.rb_intern("size"), 0));
167 if (size == 0) {
168 os << "[]";
169 return;
170 }
171
172 os << "[\n";
173 bool first = true;
174 ruby.array_for_each(value, [&](VALUE element) {
175 if (first) {
176 first = false;
177 } else {
178 os << ",\n";
179 }
180 fill_n(ostream_iterator<char>(os), level * 2, ' ');
181 write(ruby, element, os, true, level + 1);
182 return true;
183 });
184 os << "\n";
185 fill_n(ostream_iterator<char>(os), (level > 0 ? (level - 1) : 0) * 2, ' ');
186 os << "]";
187 return;
188 }
189 if (ruby.is_hash(value)) {
190 auto size = ruby.num2size_t(ruby.rb_funcall(value, ruby.rb_intern("size"), 0));
191 if (size == 0) {
192 os << "{}";
193 return;
194 }
195 os << "{\n";
196 bool first = true;
197 ruby.hash_for_each(value, [&](VALUE key, VALUE element) {
198 if (first) {
199 first = false;
200 } else {
201 os << ",\n";
202 }
203
204 // If the key isn't a string, convert to string
205 if (!ruby.is_string(key)) {
206 key = ruby.rb_funcall(key, ruby.rb_intern("to_s"), 0);
207 }
208
209 size_t size = ruby.num2size_t(ruby.rb_funcall(key, ruby.rb_intern("bytesize"), 0));
210 char const* str = ruby.rb_string_value_ptr(&key);
211
212 fill_n(ostream_iterator<char>(os), level * 2, ' ');
213 os.write(str, size);
214 os << " => ";
215 write(ruby, element, os, true, level + 1);
216 return true;
217 });
218 os << "\n";
219 fill_n(ostream_iterator<char>(os), (level > 0 ? (level - 1) : 0) * 2, ' ');
220 os << "}";
221 return;
222 }
223 }
224
225 void ruby_value::write(api const& ruby, VALUE value, YAML::Emitter& emitter)
226 {
227 if (ruby.is_true(value)) {
228 emitter << true;
229 return;
230 }
231 if (ruby.is_false(value)) {
232 emitter << false;
233 return;
234 }
235 if (ruby.is_string(value) || ruby.is_symbol(value)) {
236 auto str = ruby.to_string(value);
237 if (needs_quotation(str)) {
238 emitter << DoubleQuoted;
239 }
240 emitter << str;
241 return;
242 }
243 if (ruby.is_integer(value)) {
244 emitter << ruby.rb_num2ll(value);
245 return;
246 }
247 if (ruby.is_float(value)) {
248 emitter << ruby.rb_num2dbl(value);
249 return;
250 }
251 if (ruby.is_array(value)) {
252 emitter << BeginSeq;
253 ruby.array_for_each(value, [&](VALUE element) {
254 write(ruby, element, emitter);
255 return true;
256 });
257 emitter << EndSeq;
258 return;
259 }
260 if (ruby.is_hash(value)) {
261 emitter << BeginMap;
262 ruby.hash_for_each(value, [&](VALUE key, VALUE element) {
263 emitter << Key << ruby.to_string(key) << YAML::Value;
264 write(ruby, element, emitter);
265 return true;
266 });
267 emitter << EndMap;
268 return;
269 }
270
271 emitter << Null;
272 }
273
274 ruby_value const* ruby_value::wrap_child(VALUE child, string key) const {
275 return _children.emplace(move(key), std::unique_ptr<ruby_value>(new ruby_value(child))).first->second.get();
276 }
277
278 ruby_value const* ruby_value::child(const string& key) const {
279 auto child = _children.find(key);
280 if (child != end(_children)) {
281 return child->second.get();
282 } else {
283 return nullptr;
284 }
285 }
286
287 }} // namespace facter::ruby
+0
-156
lib/src/ruby/simple_resolution.cc less more
0 #include <internal/ruby/simple_resolution.hpp>
1 #include <internal/ruby/module.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/locale/locale.hpp>
4
5 // Mark string for translation (alias for leatherman::locale::format)
6 using leatherman::locale::_;
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace leatherman::execution;
11 using namespace leatherman::ruby;
12
13 namespace facter { namespace ruby {
14
15 simple_resolution::simple_resolution()
16 {
17 auto const& ruby = api::instance();
18 _self = ruby.nil_value();
19 _block = ruby.nil_value();
20 _command = ruby.nil_value();
21 }
22
23 VALUE simple_resolution::define()
24 {
25 auto const& ruby = api::instance();
26
27 // Define the Resolution class
28 VALUE klass = ruby.rb_define_class_under(ruby.lookup({"Facter", "Util"}), "Resolution", *ruby.rb_cObject);
29 ruby.rb_define_alloc_func(klass, alloc);
30 ruby.rb_define_method(klass, "setcode", RUBY_METHOD_FUNC(ruby_setcode), -1);
31
32 // Deprecated in Facter 2.0; implementing for backwards compatibility
33 ruby.rb_define_singleton_method(klass, "which", RUBY_METHOD_FUNC(ruby_which), 1);
34 ruby.rb_define_singleton_method(klass, "exec", RUBY_METHOD_FUNC(ruby_exec), 1);
35
36 resolution::define(klass);
37 return klass;
38 }
39
40 VALUE simple_resolution::create()
41 {
42 auto const& ruby = api::instance();
43 return ruby.rb_class_new_instance(0, nullptr, ruby.lookup({"Facter", "Util", "Resolution"}));
44 }
45
46 VALUE simple_resolution::value()
47 {
48 auto const& ruby = api::instance();
49
50 volatile VALUE value = resolution::value();
51
52 // If the resolution has a value, return it
53 if (!ruby.is_nil(value)) {
54 return value;
55 }
56
57 // If given a block, call it to resolve
58 if (!ruby.is_nil(_block)) {
59 return ruby.rb_funcall(_block, ruby.rb_intern("call"), 0);
60 }
61
62 if (ruby.is_nil(_command)) {
63 return ruby.nil_value();
64 }
65
66 // Otherwise, we were given a command so execute it
67 VALUE result = ruby.rb_funcall(ruby.lookup({ "Facter", "Core", "Execution" }), ruby.rb_intern("exec"), 1, _command);
68 if (ruby.is_nil(result) || ruby.is_true(ruby.rb_funcall(result, ruby.rb_intern("empty?"), 0))) {
69 return ruby.nil_value();
70 }
71 return result;
72 }
73
74 VALUE simple_resolution::alloc(VALUE klass)
75 {
76 auto const& ruby = api::instance();
77
78 // Create a resolution and wrap with a Ruby data object
79 unique_ptr<simple_resolution> r(new simple_resolution());
80 VALUE self = r->_self = ruby.rb_data_object_alloc(klass, r.get(), mark, free);
81 ruby.register_data_object(self);
82
83 // Release the smart pointer; ownership is now with Ruby's GC
84 r.release();
85 return self;
86 }
87
88 void simple_resolution::mark(void* data)
89 {
90 // Mark all VALUEs contained in the simple resolution
91 auto const& ruby = api::instance();
92 auto instance = reinterpret_cast<simple_resolution*>(data);
93
94 // Call the base first
95 instance->resolution::mark();
96
97 // Mark the setcode block and command
98 ruby.rb_gc_mark(instance->_block);
99 ruby.rb_gc_mark(instance->_command);
100 }
101
102 void simple_resolution::free(void* data)
103 {
104 auto instance = reinterpret_cast<simple_resolution*>(data);
105
106 // Unregister the data object
107 auto const& ruby = api::instance();
108 ruby.unregister_data_object(instance->_self);
109
110 // Delete the simple resolution
111 delete instance;
112 }
113
114 VALUE simple_resolution::ruby_setcode(int argc, VALUE* argv, VALUE self)
115 {
116 auto const& ruby = api::instance();
117
118 if (argc > 1) {
119 ruby.rb_raise(*ruby.rb_eArgError, _("wrong number of arguments ({1} for 1)", argc).c_str());
120 }
121
122 auto instance = ruby.to_native<simple_resolution>(self);
123
124 if (argc == 0) {
125 // No arguments, only a block is required
126 if (!ruby.rb_block_given_p()) {
127 ruby.rb_raise(*ruby.rb_eArgError, _("a block must be provided").c_str());
128 }
129 instance->_block = ruby.rb_block_proc();
130 } else if (argc == 1) {
131 VALUE arg = argv[0];
132 if (!ruby.is_string(arg) || ruby.is_true(ruby.rb_funcall(arg, ruby.rb_intern("empty?"), 0))) {
133 ruby.rb_raise(*ruby.rb_eTypeError, _("expected a non-empty String for first argument").c_str());
134 }
135 if (ruby.rb_block_given_p()) {
136 ruby.rb_raise(*ruby.rb_eArgError, _("a block is unexpected when passing a String").c_str());
137 }
138 instance->_command = arg;
139 }
140 return self;
141 }
142
143 VALUE simple_resolution::ruby_which(VALUE klass, VALUE binary)
144 {
145 auto const& ruby = api::instance();
146 return ruby.rb_funcall(ruby.lookup({ "Facter", "Core", "Execution" }), ruby.rb_intern("which"), 1, binary);
147 }
148
149 VALUE simple_resolution::ruby_exec(VALUE klass, VALUE command)
150 {
151 auto const& ruby = api::instance();
152 return ruby.rb_funcall(ruby.lookup({ "Facter", "Core", "Execution" }), ruby.rb_intern("exec"), 1, command);
153 }
154
155 }} // namespace facter::ruby
+0
-29
lib/src/util/bsd/scoped_ifaddrs.cc less more
0 #include <internal/util/bsd/scoped_ifaddrs.hpp>
1
2 using namespace std;
3 using namespace leatherman::util;
4
5 namespace facter { namespace util { namespace bsd {
6
7 scoped_ifaddrs::scoped_ifaddrs() :
8 scoped_resource(nullptr, free)
9 {
10 // Get the linked list of interfaces
11 if (getifaddrs(&_resource) == -1) {
12 _resource = nullptr;
13 }
14 }
15
16 scoped_ifaddrs::scoped_ifaddrs(ifaddrs* addrs) :
17 scoped_resource(move(addrs), free)
18 {
19 }
20
21 void scoped_ifaddrs::free(ifaddrs* addrs)
22 {
23 if (addrs) {
24 ::freeifaddrs(addrs);
25 }
26 }
27
28 }}} // namespace facter::util::bsd
+0
-97
lib/src/util/config/config.cc less more
0 #include <facter/util/config.hpp>
1 #include <hocon/program_options.hpp>
2 #include <leatherman/file_util/file.hpp>
3 #include <facter/logging/logging.hpp>
4
5 using namespace std;
6 using namespace hocon;
7 namespace po = boost::program_options;
8
9 namespace facter { namespace util { namespace config {
10
11 shared_config load_config_from(string config_path) {
12 if (leatherman::file_util::file_readable(config_path)) {
13 return hocon::config::parse_file_any_syntax(config_path)->resolve();
14 }
15 return nullptr;
16 }
17
18 void load_global_settings(shared_config hocon_config, po::variables_map& vm) {
19 if (hocon_config && hocon_config->has_path("global")) {
20 auto global_settings = hocon_config->get_object("global")->to_config();
21 po::store(hocon::program_options::parse_hocon<char>(global_settings, global_config_options(), true), vm);
22 }
23 }
24
25 void load_cli_settings(shared_config hocon_config, po::variables_map& vm) {
26 if (hocon_config && hocon_config->has_path("cli")) {
27 auto cli_settings = hocon_config->get_object("cli")->to_config();
28 po::store(hocon::program_options::parse_hocon<char>(cli_settings, cli_config_options(), true), vm);
29 }
30 }
31
32 void load_fact_settings(shared_config hocon_config, po::variables_map& vm) {
33 if (hocon_config && hocon_config->has_path("facts")) {
34 auto fact_settings = hocon_config->get_object("facts")->to_config();
35 po::store(hocon::program_options::parse_hocon<char>(fact_settings, fact_config_options(), true), vm);
36 }
37 }
38
39 void load_fact_groups_settings(shared_config hocon_config, po::variables_map& vm) {
40 if (hocon_config && hocon_config->has_path("fact-groups")) {
41 auto fact_groups_settings = hocon_config->get_object("fact-groups")->to_config();
42 po::store(hocon::program_options::parse_hocon<char>(fact_groups_settings, fact_groups_config_options(), true), vm);
43 }
44 }
45
46 po::options_description global_config_options() {
47 po::options_description global_options("");
48 global_options.add_options()
49 ("custom-dir", po::value<vector<string>>(), "A directory or list of directories to use for custom facts.")
50 ("external-dir", po::value<vector<string>>(), "A directory or list of directories to use for external facts.")
51 ("no-custom-facts", po::value<bool>()->default_value(false), "Disables custom facts.")
52 ("no-external-facts", po::value<bool>()->default_value(false), "Disables external facts.")
53 ("no-ruby", po::value<bool>()->default_value(false), "Disables loading Ruby, facts requiring Ruby, and custom facts.");
54 return global_options;
55 }
56
57 po::options_description cli_config_options() {
58 po::options_description cli_options("");
59 cli_options.add_options()
60 ("debug", po::value<bool>()->default_value(false), "Enable debug output.")
61 ("log-level", po::value<logging::level>()->default_value(logging::level::warning, "warn"), "Set logging level.\nSupported levels are: none, trace, debug, info, warn, error, and fatal.")
62 ("trace", po::value<bool>()->default_value(false), "Enable backtraces for custom facts.")
63 ("verbose", po::value<bool>()->default_value(false), "Enable verbose (info) output.");
64 return cli_options;
65 }
66
67 po::options_description fact_config_options() {
68 po::options_description fact_settings("");
69 fact_settings.add_options()
70 ("blocklist", po::value<vector<string>>(), "A set of facts to block.");
71 return fact_settings;
72 }
73
74 po::options_description fact_groups_config_options() {
75 po::options_description fact_groups_settings("");
76 fact_groups_settings.add_options()
77 ("cached-custom-facts", po::value<vector<string>>(), "A list of custom facts to be cached.");
78 return fact_groups_settings;
79 }
80
81 unordered_map<string, int64_t> load_ttls(shared_config hocon_config) {
82 unordered_map<string, int64_t> ttls;
83 if (hocon_config && hocon_config->has_path("facts.ttls")) {
84 auto ttl_objs = hocon_config->get_object_list("facts.ttls");
85 for (auto entry : ttl_objs) {
86 shared_config entry_conf = entry->to_config();
87 // triple-quote this string so that cpp-hocon will correctly parse it as a single path element
88 // and ignore otherwise reserved characters
89 string fact = entry->key_set().front();
90 int64_t duration = entry_conf->get_duration("\"\"\"" + fact + "\"\"\"", time_unit::SECONDS);
91 ttls.insert({ fact, duration });
92 }
93 }
94 return ttls;
95 }
96 }}} // namespace facter::util::config;
+0
-12
lib/src/util/config/posix/config.cc less more
0 #include <facter/util/config.hpp>
1
2 namespace facter { namespace util { namespace config {
3
4 hocon::shared_config load_default_config_file() {
5 return load_config_from(default_config_location());
6 }
7
8 std::string default_config_location() {
9 return "/etc/puppetlabs/facter/facter.conf";
10 }
11 }}} // namespace facter::util::config
+0
-14
lib/src/util/config/windows/config.cc less more
0 #include <facter/util/config.hpp>
1 #include <leatherman/windows/file_util.hpp>
2
3 namespace facter { namespace util { namespace config {
4
5 hocon::shared_config load_default_config_file() {
6 return load_config_from(default_config_location());
7 }
8
9 std::string default_config_location() {
10 return leatherman::windows::file_util::get_programdata_dir() +
11 "\\PuppetLabs\\facter\\etc\\facter.conf";
12 }
13 }}} // namespace facter::util::config
+0
-130
lib/src/util/freebsd/geom.cc less more
0 #include <internal/util/freebsd/geom.hpp>
1 #include <leatherman/locale/locale.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 using leatherman::locale::_;
5
6 using namespace std;
7
8 namespace facter { namespace util { namespace freebsd {
9
10 geom_exception::geom_exception(std::string const& message) :
11 runtime_error(message)
12 {
13 }
14
15
16
17 geom_config::geom_config(string name, string value)
18 {
19 _name = name;
20 _value = value;
21 }
22
23 string
24 geom_config::name()
25 {
26 return _name;
27 }
28
29 string
30 geom_config::value()
31 {
32 return _value;
33 }
34
35
36
37 geom_object_with_config::geom_object_with_config(struct gconf *conf)
38 {
39 struct gconfig *config;
40 LIST_FOREACH(config, conf, lg_config) {
41 if (!config->lg_val) {
42 LOG_DEBUG(_("Skipping config {1} because it has a null value", config->lg_name));
43 continue;
44 }
45 _configs.push_back(geom_config(config->lg_name, config->lg_val));
46 }
47 }
48
49 string
50 geom_object_with_config::config(string name) {
51 for (auto config : _configs) {
52 if (config.name() == name)
53 return config.value();
54 }
55 return "";
56 }
57
58
59
60 geom_provider::geom_provider(struct gprovider* provider) :
61 geom_object_with_config(&provider->lg_config)
62 {
63 _name = provider->lg_name;
64 _mode = provider->lg_mode;
65 _mediasize = provider->lg_mediasize;
66 _sectorsize = provider->lg_sectorsize;
67 _stripeoffset = provider->lg_stripeoffset;
68 _stripesize = provider->lg_stripesize;
69 }
70
71 string
72 geom_provider::name()
73 {
74 return _name;
75 }
76
77 off_t
78 geom_provider::mediasize()
79 {
80 return _mediasize;
81 }
82
83
84
85 geom_geom::geom_geom(struct ggeom *geom) :
86 geom_object_with_config(&geom->lg_config)
87 {
88 _name = geom->lg_name;
89 struct gprovider *provider;
90 LIST_FOREACH(provider, &geom->lg_provider, lg_provider) {
91 providers.push_back(geom_provider(provider));
92 }
93 }
94
95 string
96 geom_geom::name()
97 {
98 return _name;
99 }
100
101
102
103 geom_class::geom_class(string type)
104 {
105 if (geom_gettree(&_mesh) < 0) {
106 throw geom_exception(_("Unable to get GEOM tree"));
107 }
108
109 LIST_FOREACH(_class, &(_mesh.lg_class), lg_class) {
110 if (type == string(_class->lg_name))
111 break;
112 }
113
114 if (!_class) {
115 throw geom_exception(_("The GEOM class \"{1}\" was not found", type));
116 }
117
118 struct ggeom *geom;
119 LIST_FOREACH(geom, &(_class->lg_geom), lg_geom) {
120 geoms.push_back(geom_geom(geom));
121 }
122 }
123
124 geom_class::~geom_class()
125 {
126 geom_deletetree(&_mesh);
127 }
128
129 }}} // namespace facter::util::freebsd
+0
-41
lib/src/util/posix/scoped_addrinfo.cc less more
0 #include <internal/util/posix/scoped_addrinfo.hpp>
1
2 using namespace std;
3 using namespace leatherman::util;
4
5 namespace facter { namespace util { namespace posix {
6
7 scoped_addrinfo::scoped_addrinfo(string const& hostname) :
8 scoped_resource(nullptr, free)
9 {
10 addrinfo hints;
11 memset(&hints, 0, sizeof hints);
12 hints.ai_family = AF_UNSPEC;
13 hints.ai_socktype = SOCK_STREAM;
14 hints.ai_flags = AI_CANONNAME;
15
16 _result = getaddrinfo(hostname.c_str(), nullptr, &hints, &_resource);
17 if (_result != 0) {
18 _resource = nullptr;
19 }
20 }
21
22 scoped_addrinfo::scoped_addrinfo(addrinfo* info) :
23 scoped_resource(move(info), free),
24 _result(0)
25 {
26 }
27
28 int scoped_addrinfo::result() const
29 {
30 return _result;
31 }
32
33 void scoped_addrinfo::free(addrinfo* info)
34 {
35 if (info) {
36 ::freeaddrinfo(info);
37 }
38 }
39
40 }}} // namespace facter::util::posix
+0
-20
lib/src/util/posix/scoped_descriptor.cc less more
0 #include <internal/util/posix/scoped_descriptor.hpp>
1
2 using namespace std;
3 using namespace leatherman::util;
4
5 namespace facter { namespace util { namespace posix {
6
7 scoped_descriptor::scoped_descriptor(int descriptor) :
8 scoped_resource(move(descriptor), close)
9 {
10 }
11
12 void scoped_descriptor::close(int descriptor)
13 {
14 if (descriptor >= 0) {
15 ::close(descriptor);
16 }
17 }
18
19 }}} // namespace facter::util::posix
+0
-36
lib/src/util/posix/utmpx_file.cc less more
0 #include <internal/util/posix/utmpx_file.hpp>
1 #include <leatherman/locale/locale.hpp>
2 #include <leatherman/logging/logging.hpp>
3
4 // Mark string for translation (alias for leatherman::locale::format)
5 using leatherman::locale::_;
6
7 using namespace std;
8
9 namespace facter { namespace util { namespace posix {
10
11 bool utmpx_file::instance_exists = false;
12
13 utmpx_file::utmpx_file() {
14 if (utmpx_file::instance_exists) {
15 throw logic_error(_("only one utmpx_file instance can exist at a time!"));
16 }
17
18 utmpx_file::instance_exists = true;
19 reset();
20 }
21
22 utmpx_file::~utmpx_file() {
23 endutxent();
24 utmpx_file::instance_exists = false;
25 }
26
27 const utmpx* utmpx_file::query(utmpx const& query) const {
28 LOG_DEBUG(_("Reading the utmpx file ..."));
29 return getutxid(&query);
30 }
31
32 void utmpx_file::reset() const {
33 setutxent();
34 }
35 }}} // namespace facter::util::posix
+0
-29
lib/src/util/scoped_bio.cc less more
0 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1 #include <internal/util/scoped_bio.hpp>
2
3 using namespace std;
4 using namespace leatherman::util;
5
6 namespace facter { namespace util {
7
8 // Remove const-ness before calling BIO_new. This is "unsafe",
9 // but in isolation here will not cause issues. Allows the code to work
10 // with both OpenSSL 1.0 and 1.1.
11 scoped_bio::scoped_bio(const BIO_METHOD* method) :
12 scoped_resource(BIO_new(const_cast<BIO_METHOD*>(method)), free)
13 {
14 }
15
16 scoped_bio::scoped_bio(BIO* bio) :
17 scoped_resource(move(bio), free)
18 {
19 }
20
21 void scoped_bio::free(BIO* bio)
22 {
23 if (bio) {
24 BIO_free(bio);
25 }
26 }
27
28 }} // namespace facter::util
+0
-26
lib/src/util/scoped_file.cc less more
0 #include <internal/util/scoped_file.hpp>
1 #include <boost/nowide/cstdio.hpp>
2
3 using namespace std;
4 using namespace leatherman::util;
5
6 namespace facter { namespace util {
7
8 scoped_file::scoped_file(string const& path, string const& mode) :
9 scoped_resource(boost::nowide::fopen(path.c_str(), mode.c_str()), close)
10 {
11 }
12
13 scoped_file::scoped_file(FILE* file) :
14 scoped_resource(move(file), close)
15 {
16 }
17
18 void scoped_file::close(FILE* file)
19 {
20 if (file) {
21 fclose(file);
22 }
23 }
24
25 }} // namespace facter::util
+0
-157
lib/src/util/solaris/k_stat.cc less more
0 #include <internal/util/solaris/k_stat.hpp>
1 #include <sys/kstat.h>
2 #include <cstring>
3 #include <leatherman/locale/locale.hpp>
4
5 // Mark string for translation (alias for leatherman::locale::format)
6 using leatherman::locale::_;
7
8 using namespace std;
9
10 namespace facter { namespace util { namespace solaris {
11 k_stat::k_stat() {
12 if (ctrl == nullptr) {
13 throw kstat_exception(_("kstat_open failed"));
14 }
15 }
16
17 vector<k_stat_entry> k_stat::operator[](string const& module)
18 {
19 return lookup(module, -1, {});
20 }
21
22 vector<k_stat_entry> k_stat::operator[](pair<string, int> const& entry)
23 {
24 return lookup(entry.first, entry.second, {});
25 }
26
27 vector<k_stat_entry> k_stat::operator[](pair<string, string> const& entry)
28 {
29 return lookup(entry.first, -1, entry.second);
30 }
31
32 // I believe precedence to be sufficiently obvious in this function,
33 // but this is a worthwhile diagnostic in most cases. We disable it
34 // here, instead of globally.
35 #pragma GCC diagnostic push
36 #pragma GCC diagnostic ignored "-Wparentheses"
37 vector<k_stat_entry> k_stat::lookup(string const& module, int instance, string const& name)
38 {
39 kstat_t* kp = static_cast<kstat_ctl_t*>(ctrl)->kc_chain;
40 vector<k_stat_entry> arr;
41 do {
42 if (!module.empty() && module != kp->ks_module ||
43 !name.empty() && name != kp->ks_name ||
44 instance != -1 && instance != kp->ks_instance) {
45 continue;
46 }
47 while (kstat_read(ctrl, kp, 0) == -1) {
48 if (errno == EAGAIN) {
49 continue;
50 } else {
51 throw kstat_exception(_("kstat_read failed: {1} ({2})",
52 string(strerror(errno)),
53 to_string(errno)));
54 }
55 }
56 arr.push_back(k_stat_entry(kp));
57 } while (kp = kp->ks_next);
58
59 return arr;
60 }
61 #pragma GCC diagnostic pop
62
63 k_stat_entry::k_stat_entry(kstat_t* kp) :
64 k_stat(kp)
65 {
66 }
67
68 int k_stat_entry::instance()
69 {
70 return k_stat->ks_instance;
71 }
72
73 string k_stat_entry::klass()
74 {
75 return k_stat->ks_class;
76 }
77
78 string k_stat_entry::module()
79 {
80 return k_stat->ks_module;
81 }
82
83 string k_stat_entry::name()
84 {
85 return k_stat->ks_name;
86 }
87
88 kstat_named_t* k_stat_entry::lookup(const string& attrib) const
89 {
90 kstat_named_t* knp = reinterpret_cast<kstat_named_t*>(kstat_data_lookup(k_stat, const_cast<char*>(attrib.c_str())));
91 if (knp == nullptr) {
92 throw kstat_exception(_("kstat_data_lookup failed for {1}", attrib));
93 }
94 return knp;
95 }
96
97 kstat_named_t* k_stat_entry::lookup(int datatype, const string& attrib) const
98 {
99 kstat_named_t* knp = reinterpret_cast<kstat_named_t*>(kstat_data_lookup(k_stat, const_cast<char*>(attrib.c_str())));
100 if (knp == nullptr) {
101 throw kstat_exception(_("kstat_data_lookup failed for {1}", attrib));
102 }
103 if (knp->data_type != datatype) {
104 throw kstat_exception(_("invalid datatype {1} {2}", attrib, to_string(knp->data_type)));
105 }
106 return knp;
107 }
108
109 template<>
110 ulong_t k_stat_entry::value(const std::string& attrib) const
111 {
112 return lookup(KSTAT_DATA_ULONG, attrib)->value.ul;
113 }
114
115 template<>
116 long k_stat_entry::value(const std::string& attrib) const
117 {
118 return lookup(KSTAT_DATA_LONG, attrib)->value.l;
119 }
120
121 template<>
122 int32_t k_stat_entry::value(const std::string& attrib) const
123 {
124 return lookup(KSTAT_DATA_INT32, attrib)->value.i32;
125 }
126
127 template<>
128 uint32_t k_stat_entry::value(const std::string& attrib) const
129 {
130 return lookup(KSTAT_DATA_UINT32, attrib)->value.ui32;
131 }
132
133 template<>
134 int64_t k_stat_entry::value(const std::string& attrib) const
135 {
136 return lookup(KSTAT_DATA_INT64, attrib)->value.i64;
137 }
138
139 template<>
140 uint64_t k_stat_entry::value(const std::string& attrib) const
141 {
142 return lookup(KSTAT_DATA_UINT64, attrib)->value.ui64;
143 }
144
145 template<>
146 std::string k_stat_entry::value(const std::string& attrib) const
147 {
148 auto res = lookup(attrib);
149 if (res->data_type == KSTAT_DATA_STRING) {
150 return res->value.str.addr.ptr;
151 } else if (res->data_type == KSTAT_DATA_CHAR) {
152 return string(res->value.c);
153 }
154 throw kstat_exception(_("invalid datatype {1} {2}", attrib, to_string(res->data_type)));
155 }
156 }}} // namespace facter::util::solaris
+0
-31
lib/src/util/solaris/scoped_kstat.cc less more
0 #include <internal/util/solaris/scoped_kstat.hpp>
1
2 using namespace std;
3 using namespace leatherman::util;
4
5 namespace facter { namespace util { namespace solaris {
6
7 scoped_kstat::scoped_kstat() :
8 scoped_resource(nullptr, close)
9 {
10 _resource = kstat_open();
11 }
12
13 scoped_kstat::scoped_kstat(kstat_ctl* ctrl) :
14 scoped_resource(move(ctrl), free)
15 {
16 }
17
18 void scoped_kstat::close(kstat_ctl* ctrl)
19 {
20 if (ctrl) {
21 ::kstat_close(ctrl);
22 }
23 }
24
25 kstat_exception::kstat_exception(std::string const& message) :
26 runtime_error(message)
27 {
28 }
29
30 }}} // namespace facter::util::solaris
+0
-170
lib/src/util/string.cc less more
0 #include <facter/util/string.hpp>
1 #include <boost/regex.hpp>
2 #include <sstream>
3 #include <iomanip>
4 #include <algorithm>
5 #include <iterator>
6 #include <cmath>
7 #include <limits>
8
9 using namespace std;
10
11 namespace facter { namespace util {
12
13 string to_hex(uint8_t const* bytes, size_t length, bool uppercase)
14 {
15 ostringstream ss;
16 if (bytes) {
17 ss << hex << (uppercase ? std::uppercase : std::nouppercase) << setfill('0');
18 for (size_t i = 0; i < length; ++i) {
19 ss << setw(2) << static_cast<int>(bytes[i]);
20 }
21 }
22 return ss.str();
23 }
24
25 void each_line(string const& s, function<bool(string&)> callback)
26 {
27 string line;
28 istringstream in(s);
29 while (getline(in, line)) {
30 // Handle Windows CR in the string.
31 if (line.size() && line.back() == '\r') {
32 line.pop_back();
33 }
34 if (!callback(line)) {
35 break;
36 }
37 }
38 }
39
40 string si_string(uint64_t size)
41 {
42 static char prefixes[] = { 'K', 'M', 'G', 'T', 'P', 'E' };
43
44 if (size < 1024) {
45 return to_string(size) + " bytes";
46 }
47 unsigned int exp = floor(log2(size) / 10.0);
48 double converted = round(100.0 * (size / pow(1024.0, exp))) / 100.0;
49
50 // Check to see if rounding up gets us to 1024; if so, move to the next unit
51 if (fabs(converted - 1024.0) < numeric_limits<double>::epsilon()) {
52 converted = 1.00;
53 ++exp;
54 }
55
56 // If we exceed the SI prefix (we shouldn't, but just in case), just return the bytes
57 if (exp - 1 >= sizeof(prefixes)) {
58 return to_string(size) + " bytes";
59 }
60
61 ostringstream ss;
62 ss << fixed << setprecision(2) << converted << " " << prefixes[exp - 1] << "iB";
63 return ss.str();
64 }
65
66 string percentage(uint64_t used, uint64_t total)
67 {
68 if (total == 0 || used >= total) {
69 return "100%";
70 }
71 if (used == 0) {
72 return "0%";
73 }
74 double converted = round(10000.0 * (used / static_cast<double>(total))) / 100.0;
75 // Check to see if we would round up to 100%; if so, keep it at 99.99%
76 if (fabs(converted - 100.0) < numeric_limits<double>::epsilon()) {
77 converted = 99.99;
78 }
79 ostringstream ss;
80 ss << fixed << setprecision(2) << converted << "%";
81 return ss.str();
82 }
83
84 string frequency(int64_t freq)
85 {
86 static char prefixes[] = { 'k', 'M', 'G', 'T' };
87
88 if (freq < 1000) {
89 return to_string(freq) + " Hz";
90 }
91 unsigned int exp = floor(log10(freq) / 3.0);
92 double converted = round(100.0 * (freq / pow(1000.0, exp))) / 100.0;
93
94 // Check to see if rounding up gets us to 1000; if so, move to the next unit
95 if (fabs(converted - 1000.0) < numeric_limits<double>::epsilon()) {
96 converted = 1.00;
97 ++exp;
98 }
99
100 // If we exceed the SI prefix (we shouldn't, but just in case), just return the speed in Hz
101 if (exp - 1 >= sizeof(prefixes)) {
102 return to_string(freq) + " Hz";
103 }
104
105 ostringstream ss;
106 ss << fixed << setprecision(2) << converted << " " << prefixes[exp - 1] << "Hz";
107 return ss.str();
108 }
109
110 bool needs_quotation(string const& str)
111 {
112 // Empty strings should be quoted
113 if (str.empty()) {
114 return true;
115 }
116
117 // Taken from http://yaml.org/type/bool.html.
118 // The string would be interpreted as a boolean, so quote it.
119 static boost::regex yaml_bool("y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF");
120 if (boost::regex_match(str, yaml_bool)) {
121 return true;
122 }
123
124 // A string starting with the ':' special character interferes with parsing.
125 // Other string patterns containing ':' can be unexpectedly parsed as sexagesimal.
126 // To be safe quote all strings that contain ':' characters.
127 if (str.find(':') != string::npos) {
128 return true;
129 }
130
131 // Poor man's check for a numerical string or list of numbers
132 // May start with - or +
133 // May contain one . and one or more ,
134 // All other characters should be digits
135 // Note: Ruby YAML interprets 1,2,3 as a number, but not 1.2.3.
136 // It doesn't appear to honor locales when parsing YAML.
137 bool has_dot = false;
138 for (size_t i = 0; i < str.size(); ++i) {
139 char c = str[i];
140 if (i == 0 && (c == '+' || c == '-')) {
141 continue;
142 }
143 if (c == ',') {
144 continue;
145 }
146 if (c == '.') {
147 if (has_dot) {
148 return false;
149 }
150 has_dot = true;
151 continue;
152 }
153 if (!isdigit(c)) {
154 return false;
155 }
156 }
157
158 // Numerical strings should be quoted
159 return true;
160 }
161
162 boost::optional<int> maybe_stoi(string const& str) {
163 try {
164 return stoi(str);
165 } catch (invalid_argument&) {
166 return boost::optional<int>();
167 }
168 }
169 }} // namespace facter::util
+0
-69
lib/src/util/windows/wsa.cc less more
0 #include <internal/util/windows/wsa.hpp>
1 #include <leatherman/logging/logging.hpp>
2 #include <leatherman/locale/locale.hpp>
3 #include <boost/format.hpp>
4 #include <boost/nowide/convert.hpp>
5 #include <Ws2tcpip.h>
6
7 // Mark string for translation (alias for leatherman::locale::format)
8 using leatherman::locale::_;
9
10 using namespace std;
11
12 namespace facter { namespace util { namespace windows {
13
14 wsa_exception::wsa_exception(string const& message) :
15 runtime_error(message)
16 {
17 }
18
19 string format_err(string const& s, int err)
20 {
21 return str(boost::format("%1% (%2%)") % s % boost::io::group(hex, showbase, err));
22 }
23
24 wsa::wsa()
25 {
26 LOG_DEBUG("initializing Winsock");
27 WSADATA wsaData;
28 auto wVersionRequested = MAKEWORD(2, 2);
29 auto err = WSAStartup(wVersionRequested, &wsaData);
30
31 if (err != 0) {
32 throw wsa_exception(format_err(_("WSAStartup failed with error"), err));
33 }
34
35 if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
36 throw wsa_exception(_("could not find a usable version of Winsock.dll"));
37 }
38 }
39
40 wsa::~wsa()
41 {
42 WSACleanup();
43 }
44
45 string wsa::saddress_to_string(SOCKET_ADDRESS const& addr) const
46 {
47 if (!addr.lpSockaddr) {
48 return {};
49 }
50
51 DWORD size = INET6_ADDRSTRLEN+1;
52 wchar_t buffer[INET6_ADDRSTRLEN+1];
53 if (0 != WSAAddressToStringW(addr.lpSockaddr, addr.iSockaddrLength, NULL, buffer, &size)) {
54 throw wsa_exception(format_err(_("address to string translation failed"), WSAGetLastError()));
55 }
56
57 return boost::nowide::narrow(buffer);
58 }
59
60 void wsa::string_fill_sockaddr(sockaddr *sock, std::string const& addr, int size) const
61 {
62 auto addrW = boost::nowide::widen(addr);
63 if (0 != WSAStringToAddressW(&addrW[0], sock->sa_family, NULL, sock, &size)) {
64 throw wsa_exception(format_err(_("string to address translation failed"), WSAGetLastError()));
65 }
66 }
67
68 }}} // namespace facter::util::windows
+0
-74
lib/src/util/yaml.cc less more
0 #include <internal/util/yaml.hpp>
1
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <boost/algorithm/string.hpp>
7
8 #include <yaml-cpp/yaml.h>
9
10 using namespace std;
11 using namespace YAML;
12 using namespace facter::facts;
13
14 namespace facter { namespace util { namespace yaml {
15
16 void add_value(
17 string const& name,
18 Node const& node,
19 collection& facts,
20 vector<std::string>& names,
21 array_value* array_parent,
22 map_value* map_parent)
23 {
24 unique_ptr<value> val;
25 // For scalars, code the value into a specific value type
26 if (node.IsScalar()) {
27 bool bool_val;
28 int64_t int_val;
29 double double_val;
30 // If the node tag is "!", it was a quoted scalar and should be treated as a string
31 if (node.Tag() != "!") {
32 if (convert<bool>::decode(node, bool_val)) {
33 val = make_value<boolean_value>(bool_val);
34 } else if (convert<int64_t>::decode(node, int_val)) {
35 val = make_value<integer_value>(int_val);
36 } else if (convert<double>::decode(node, double_val)) {
37 val = make_value<double_value>(double_val);
38 }
39 }
40 if (!val) {
41 val = make_value<string_value>(node.as<string>());
42 }
43 } else if (node.IsSequence()) {
44 // For arrays, convert to a array value
45 auto array = make_value<array_value>();
46 for (auto const& child : node) {
47 add_value({}, child, facts, names, array.get());
48 }
49 val = move(array);
50 } else if (node.IsMap()) {
51 // For maps, convert to a map value
52 auto map = make_value<map_value>();
53 for (auto const& child : node) {
54 add_value(child.first.as<string>(), child.second, facts, names, nullptr, map.get());
55 }
56 val = move(map);
57 } else if (!node.IsNull()) {
58 // Ignore nodes we don't understand
59 return;
60 }
61
62 // Put the value in the array, map, or directly as a top-level fact
63 if (array_parent) {
64 array_parent->add(move(val));
65 } else if (map_parent) {
66 map_parent->add(string(name), move(val));
67 } else {
68 facts.add_external(boost::to_lower_copy(name), move(val));
69 names.push_back(boost::to_lower_copy(name));
70 }
71 }
72 }}}
73
+0
-26
lib/tasks/spec.rake less more
0 ['rubygems',
1 'rspec',
2 'rspec/core/rake_task',
3 'rcov',].each do |lib|
4 begin
5 require lib
6 rescue LoadError
7 end
8 end
9
10 if defined?(RSpec::Core::RakeTask)
11 desc "Run all specs"
12 RSpec::Core::RakeTask.new do |t|
13 t.pattern ='spec/{unit,integration}/**/*_spec.rb'
14 t.fail_on_error = true
15 end
16
17 RSpec::Core::RakeTask.new('spec:rcov') do |t|
18 t.pattern ='spec/{unit,integration}/**/*_spec.rb'
19 t.fail_on_error = true
20 if defined?(Rcov)
21 t.rcov = true
22 t.rcov_opts = ['--exclude', 'spec/*,test/*,results/*,/usr/lib/*,/usr/local/lib/*,gems/*']
23 end
24 end
25 end
+0
-157
lib/tests/CMakeLists.txt less more
0 cmake_minimum_required(VERSION 3.2.2)
1
2 find_package(Ruby 1.9)
3
4 # Set the common (platform-independent) sources
5 set(LIBFACTER_TESTS_COMMON_SOURCES
6 "facts/array_value.cc"
7 "facts/boolean_value.cc"
8 "facts/double_value.cc"
9 "facts/external/json_resolver.cc"
10 "facts/external/text_resolver.cc"
11 "facts/external/yaml_resolver.cc"
12 "facts/collection.cc"
13 "facts/external_resolvers_factory.cc"
14 "facts/integer_value.cc"
15 "facts/map_value.cc"
16 "facts/resolvers/augeas_resolver.cc"
17 "facts/resolvers/disk_resolver.cc"
18 "facts/resolvers/dmi_resolver.cc"
19 "facts/resolvers/filesystem_resolver.cc"
20 "facts/resolvers/identity_resolver.cc"
21 "facts/resolvers/kernel_resolver.cc"
22 "facts/resolvers/ldom_resolver.cc"
23 "facts/resolvers/memory_resolver.cc"
24 "facts/resolvers/networking_resolver.cc"
25 "facts/resolvers/operating_system_resolver.cc"
26 "facts/resolvers/processor_resolver.cc"
27 "facts/resolvers/ruby_resolver.cc"
28 "facts/resolvers/ssh_resolver.cc"
29 "facts/resolvers/system_profiler_resolver.cc"
30 "facts/resolvers/timezone_resolver.cc"
31 "facts/resolvers/uptime_resolver.cc"
32 "facts/resolvers/virtualization_resolver.cc"
33 "facts/resolvers/xen_resolver.cc"
34 "facts/resolvers/zfs_resolver.cc"
35 "facts/resolvers/zone_resolver.cc"
36 "facts/resolvers/zpool_resolver.cc"
37 "facts/schema.cc"
38 "facts/cache.cc"
39 "facts/string_value.cc"
40 "logging/logging.cc"
41 "log_capture.cc"
42 "main.cc"
43 "mock_server.cc"
44 "util/string.cc"
45 "fixtures.cc"
46 "collection_fixture.cc"
47 "cwrapper.cc"
48 )
49
50 # Set compiler-specific flags
51 set(CMAKE_CXX_FLAGS ${FACTER_CXX_FLAGS})
52
53 # Add the ruby tests if there's a ruby installed
54 if (RUBY_FOUND)
55 set(LIBFACTER_TESTS_COMMON_SOURCES
56 ${LIBFACTER_TESTS_COMMON_SOURCES}
57 "ruby/ruby.cc"
58 "ruby/ruby_dirfacts.cc"
59 "ruby/ruby_helper.cc")
60
61 if (WIN32)
62 set(LIBFACTER_TESTS_COMMON_SOURCES
63 ${LIBFACTER_TESTS_COMMON_SOURCES}
64 "ruby/windows/ruby.cc")
65 endif()
66 endif()
67
68 # Set the POSIX sources if on a POSIX platform
69 if (UNIX)
70 set(LIBFACTER_TESTS_CATEGORY_SOURCES
71 "facts/posix/collection.cc"
72 "facts/posix/external_resolvers_factory.cc"
73 "facts/posix/uptime_resolver.cc"
74 "facts/external/posix/execution_resolver.cc"
75 "util/posix/scoped_addrinfo.cc"
76 "util/posix/scoped_descriptor.cc"
77 )
78 set(POSIX_TESTS_LIBRARIES ${POSIX_LIBRARIES})
79 endif()
80
81 set(LIBFACTER_TESTS_PLATFORM_LIBRARIES
82 ${LIBFACTER_PLATFORM_LIBRARIES}
83 )
84
85 if (WIN32)
86 list(APPEND LIBFACTER_TESTS_PLATFORM_LIBRARIES Mswsock)
87 set(LIBFACTER_TESTS_CATEGORY_SOURCES
88 "facts/windows/collection.cc"
89 "facts/windows/external_resolvers_factory.cc"
90 "facts/windows/networking_resolver.cc"
91 "facts/windows/operating_system_resolver.cc"
92 "facts/external/windows/execution_resolver.cc"
93 "facts/external/windows/powershell_resolver.cc"
94 )
95 endif()
96
97 if (OPENSSL_FOUND)
98 set(LIBFACTER_TESTS_CATEGORY_SOURCES ${LIBFACTER_TESTS_CATEGORY_SOURCES} "util/scoped_bio.cc")
99 endif()
100
101 # Set the platform-specific sources
102 if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
103 set(LIBFACTER_TESTS_PLATFORM_SOURCES
104 "util/bsd/scoped_ifaddrs.cc"
105 )
106 elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
107 set(LIBFACTER_TESTS_PLATFORM_SOURCES
108 "facts/linux/dmi_resolver.cc"
109 "facts/linux/filesystem_resolver.cc"
110 "facts/linux/virtualization_resolver.cc"
111 "facts/linux/processor_fixture.cc"
112 "facts/linux/processor_resolver.cc"
113 "util/bsd/scoped_ifaddrs.cc"
114 )
115 endif()
116
117 if (JRUBY_SUPPORT)
118 include_directories(${JNI_INCLUDE_DIRS})
119 set(LIBFACTER_TESTS_COMMON_SOURCES ${LIBFACTER_TESTS_COMMON_SOURCES} java/facter.cc)
120 endif()
121
122 include_directories(
123 ../inc
124 ${Boost_INCLUDE_DIRS}
125 ${OPENSSL_INCLUDE_DIRS}
126 ${YAMLCPP_INCLUDE_DIRS}
127 ${LEATHERMAN_CATCH_INCLUDE}
128 ${CPPHOCON_INCLUDE_DIRS}
129 )
130
131 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
132 if (WIN32)
133 # On Windows with GCC 5.2, Boost.System emits warnings that aren't correctly
134 # suppressed by pragmas. Explicitly skip them.
135 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable")
136 endif()
137
138 add_executable(libfacter_test $<TARGET_OBJECTS:libfactersrc>
139 ${LIBFACTER_TESTS_COMMON_SOURCES}
140 ${LIBFACTER_TESTS_PLATFORM_SOURCES}
141 ${LIBFACTER_TESTS_CATEGORY_SOURCES})
142 target_link_libraries(libfacter_test
143 ${LIBS}
144 ${LIBFACTER_TESTS_PLATFORM_LIBRARIES})
145
146 if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND BOOST_STATIC AND LEATHERMAN_USE_LOCALES)
147 target_link_libraries(libfacter_test iconv)
148 endif()
149
150 target_compile_definitions(libfacter_test PRIVATE "-Dlibfacter_EXPORTS")
151
152 # Generate a file containing the path to the fixtures
153 configure_file (
154 "fixtures.hpp.in"
155 "${CMAKE_CURRENT_LIST_DIR}/fixtures.hpp"
156 )
+0
-15
lib/tests/collection_fixture.cc less more
0 #include "collection_fixture.hpp"
1
2 namespace facter { namespace testing {
3
4 using namespace std;
5
6 collection_fixture::collection_fixture(set<string> const& blocklist,
7 unordered_map<string, int64_t> const& ttls) : collection(blocklist, ttls) { }
8
9 vector<string> collection_fixture::get_external_fact_directories() const
10 {
11 return {};
12 }
13
14 }} // namespace facter::testing
+0
-19
lib/tests/collection_fixture.hpp less more
0 #pragma once
1
2 #include <facter/facts/collection.hpp>
3 #include <vector>
4 #include <string>
5
6 namespace facter { namespace testing {
7
8 class collection_fixture : public facter::facts::collection
9 {
10 public:
11 collection_fixture(std::set<std::string> const& blocklist = std::set<std::string>(),
12 std::unordered_map<std::string, int64_t> const& ttls = std::unordered_map<std::string, int64_t>{});
13
14 protected:
15 virtual std::vector<std::string> get_external_fact_directories() const override;
16 };
17
18 }} // namespace facter::testing
+0
-22
lib/tests/cwrapper.cc less more
0 #include <catch.hpp>
1 #include <facter/cwrapper.hpp>
2
3 SCENARIO("using the C wrapper function to collect default facts") {
4 GIVEN("a get_default_facts invocation") {
5 char *result {nullptr};
6
7 THEN("no error should be thrown") {
8 REQUIRE_NOTHROW(get_default_facts(&result));
9 }
10
11 THEN("the function execution should succeed") {
12 REQUIRE(get_default_facts(&result) == 0);
13 }
14
15 THEN("the function stores the collected facts and sets the pointer arg") {
16 REQUIRE(result == nullptr);
17 get_default_facts(&result);
18 REQUIRE_FALSE(result == nullptr);
19 }
20 }
21 }
+0
-140
lib/tests/facts/array_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/array_value.hpp>
2 #include <facter/facts/scalar_value.hpp>
3 #include <rapidjson/document.h>
4 #include <yaml-cpp/yaml.h>
5 #include <sstream>
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace rapidjson;
10 using namespace YAML;
11
12 SCENARIO("using an array fact value") {
13 array_value value;
14 REQUIRE(value.empty());
15 GIVEN("a null value to add") {
16 value.add(nullptr);
17 THEN("it should still be empty") {
18 REQUIRE(value.empty());
19 }
20 }
21 GIVEN("an out of range index") {
22 THEN("get() raises out_of_range") {
23 REQUIRE_THROWS_AS(value.get<string_value>(0), std::out_of_range&);
24 }
25 THEN("operator[] returns nullptr") {
26 REQUIRE_FALSE(value[42]);
27 }
28 }
29 GIVEN("a string value to add") {
30 value.add(make_value<string_value>("hello"));
31 THEN("it should contain the string value") {
32 REQUIRE_FALSE(value.empty());
33 REQUIRE(value.size() == 1u);
34 REQUIRE(value.get<string_value>(0));
35 REQUIRE(value.get<string_value>(0)->value() == "hello");
36 }
37 }
38 GIVEN("an integer value to add") {
39 value.add(make_value<integer_value>(123));
40 THEN("it should contain the string value") {
41 REQUIRE_FALSE(value.empty());
42 REQUIRE(value.size() == 1u);
43 REQUIRE(value.get<integer_value>(0));
44 REQUIRE(value.get<integer_value>(0)->value() == 123);
45 }
46 }
47 GIVEN("multiple values to add") {
48 auto subarray = make_value<array_value>();
49 subarray->add(make_value<string_value>("element"));
50 value.add(make_value<string_value>("1"));
51 value.add(make_value<integer_value>(2));
52 value.add(move(subarray));
53 THEN("it should contain the values in order they were added") {
54 REQUIRE(value.size() == 3u);
55 auto string_val = value.get<string_value>(0);
56 REQUIRE(string_val);
57 REQUIRE(string_val->value() == "1");
58 auto int_val = value.get<integer_value>(1);
59 REQUIRE(int_val);
60 REQUIRE(int_val->value() == 2);
61 auto subarray = value.get<array_value>(2);
62 REQUIRE(subarray);
63 REQUIRE(subarray->size() == 1u);
64 string_val = subarray->get<string_value>(0);
65 REQUIRE(string_val);
66 REQUIRE(string_val->value() == "element");
67 }
68 THEN("each is enumerated in order") {
69 size_t index = 0u;
70 value.each([&](struct value const* val) {
71 if (index == 0u) {
72 auto string_val = dynamic_cast<string_value const*>(val);
73 REQUIRE(string_val);
74 REQUIRE(string_val->value() == "1");
75 } else if (index == 1u) {
76 auto int_val = dynamic_cast<integer_value const*>(val);
77 REQUIRE(int_val);
78 REQUIRE(int_val->value() == 2);
79 } else if (index == 2u) {
80 auto subarray = dynamic_cast<array_value const*>(val);
81 REQUIRE(subarray);
82 REQUIRE(subarray->size() == 1u);
83 auto string_val = subarray->get<string_value>(0);
84 REQUIRE(string_val);
85 REQUIRE(string_val->value() == "element");
86 } else {
87 FAIL("should not be reached");
88 }
89 ++index;
90 return true;
91 });
92 REQUIRE(index == value.size());
93 }
94 WHEN("serialized to JSON") {
95 THEN("it should contain the same values") {
96 json_value json;
97 json_allocator allocator;
98 value.to_json(allocator, json);
99 REQUIRE(json.IsArray());
100 REQUIRE(json.Size() == 3);
101 REQUIRE(json[0u].IsString());
102 REQUIRE(string(json[0u].GetString()) == "1");
103 REQUIRE(json[1u].IsNumber());
104 REQUIRE(json[1u].GetInt64() == 2ll);
105 REQUIRE(json[2u].IsArray());
106 REQUIRE(json[2u].Size() == 1);
107 REQUIRE(json[2u][0u].IsString());
108 REQUIRE(string(json[2u][0u].GetString()) == "element");
109 }
110 }
111 WHEN("serialized to text") {
112 THEN("it should contain the same values") {
113 ostringstream stream;
114 value.write(stream);
115 REQUIRE(stream.str() == "[\n \"1\",\n 2,\n [\n \"element\"\n ]\n]");
116 }
117 }
118 WHEN("serialized to YAML") {
119 THEN("it should contain the same values") {
120 Emitter emitter;
121 value.write(emitter);
122 REQUIRE(string(emitter.c_str()) == "- \"1\"\n- 2\n-\n - element");
123 }
124 }
125 }
126 GIVEN("each with a callback that returns false") {
127 THEN("it should stop enumerating") {
128 value.add(make_value<integer_value>(1));
129 value.add(make_value<integer_value>(2));
130 value.add(make_value<integer_value>(3));
131 size_t index = 0u;
132 value.each([&](struct value const* val) {
133 ++index;
134 return false;
135 });
136 REQUIRE(index == 1u);
137 }
138 }
139 }
+0
-67
lib/tests/facts/boolean_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <sstream>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace rapidjson;
9 using namespace YAML;
10
11 SCENARIO("using a boolean fact value") {
12 GIVEN("true to the constructor") {
13 boolean_value value(true);
14 REQUIRE(value.value());
15 WHEN("serialized to JSON") {
16 THEN("it should serialize as true") {
17 json_value json;
18 json_allocator allocator;
19 value.to_json(allocator, json);
20 REQUIRE(json.IsBool());
21 REQUIRE(json.GetBool());
22 }
23 }
24 WHEN("serialized to YAML") {
25 THEN("it should serialize as true") {
26 Emitter emitter;
27 value.write(emitter);
28 REQUIRE(string(emitter.c_str()) == "true");
29 }
30 }
31 WHEN("serialized to text") {
32 THEN("it should serialize to text as true") {
33 ostringstream stream;
34 value.write(stream);
35 REQUIRE(stream.str() == "true");
36 }
37 }
38 }
39 GIVEN("false to the constructor") {
40 boolean_value value(false);
41 REQUIRE_FALSE(value.value());
42 WHEN("serialized to JSON") {
43 THEN("it should serialize as false") {
44 json_value json;
45 json_allocator allocator;
46 value.to_json(allocator, json);
47 REQUIRE(json.IsBool());
48 REQUIRE_FALSE(json.GetBool());
49 }
50 }
51 WHEN("serialized to YAML") {
52 THEN("it should serialize as false") {
53 Emitter emitter;
54 value.write(emitter);
55 REQUIRE(string(emitter.c_str()) == "false");
56 }
57 }
58 WHEN("serialized to text") {
59 THEN("it should serialize to text as false") {
60 ostringstream stream;
61 value.write(stream);
62 REQUIRE(stream.str() == "false");
63 }
64 }
65 }
66 }
+0
-100
lib/tests/facts/cache.cc less more
0 #include <catch.hpp>
1 #include "../fixtures.hpp"
2
3 #include<internal/facts/cache.hpp>
4 #include <facter/facts/scalar_value.hpp>
5
6 #include <leatherman/file_util/file.hpp>
7
8 using namespace std;
9 using namespace facter::testing;
10 using namespace facter::facts;
11 namespace boost_file = boost::filesystem;
12
13 struct simple_resolver : facter::facts::resolver
14 {
15 simple_resolver() : resolver("test", { "foo" })
16 {
17 }
18
19 virtual void resolve(collection& facts) override
20 {
21 facts.add("foo", make_value<string_value>("bar"));
22 }
23
24 bool is_blockable() const override
25 {
26 return true;
27 }
28 };
29
30 SCENARIO("refreshing cache") {
31 boost_file::path cache_dir(LIBFACTER_TESTS_DIRECTORY + string("/fixtures/cache"));
32
33 GIVEN("a resolver that needs to be cached") {
34 collection_fixture facts;
35 auto test_res = make_shared<simple_resolver>();
36 boost_file::create_directories(cache_dir);
37
38 THEN("new JSON files should be written") {
39 auto cache_file = (cache_dir / "test").string();
40 REQUIRE_FALSE(leatherman::file_util::file_readable(cache_file));
41
42 cache::refresh_cache(test_res, cache_file, facts);
43 REQUIRE(leatherman::file_util::file_readable(cache_file));
44 string contents;
45 load_fixture("cache/test", contents);
46 REQUIRE(contents.find("foo") != string::npos);
47 }
48 }
49
50 // Clean up directory
51 boost_file::remove_all(cache_dir);
52 }
53
54 SCENARIO("loading facts from cache") {
55 boost_file::path cache_dir(LIBFACTER_TESTS_DIRECTORY + string("/fixtures/cache"));
56
57 GIVEN("an existing cache directory with cached fact") {
58 collection_fixture facts;
59 auto test_res = make_shared<simple_resolver>();
60 boost_file::create_directories(cache_dir);
61 auto cache_file = cache_dir / "test";
62 leatherman::file_util::atomic_write_to_file("{ \"foo\" : \"bar\" }", cache_file.string());
63
64 THEN("facts should be loaded from the cache") {
65 cache::load_facts_from_cache(cache_file, test_res, facts);
66 REQUIRE(facts.get_resolved("foo"));
67 }
68 }
69
70 // Clean up directory
71 boost_file::remove_all(cache_dir);
72 }
73
74 SCENARIO("cleaning the cache") {
75 boost_file::path cache_dir(LIBFACTER_TESTS_DIRECTORY + string("/fixtures/cache"));
76
77 GIVEN("an existing cache directory with a fact that should no longer be cached") {
78 boost_file::create_directories(cache_dir);
79 // unused cache file
80 auto unused_cache_file = (cache_dir / "unused").string();
81 leatherman::file_util::atomic_write_to_file("{ \"foo\" : \"bar\" }",
82 unused_cache_file);
83 // used cached file
84 auto used_cache_file = (cache_dir / "test").string();
85 leatherman::file_util::atomic_write_to_file("{ \"foo\" : \"bar\" }",
86 used_cache_file);
87
88 THEN("unused cache file should be deleted") {
89 // cache "test", but not "unused"
90 cache::clean_cache(unordered_map<string, int64_t>({
91 make_pair("test", 600)
92 }), cache_dir.string());
93 REQUIRE_FALSE(leatherman::file_util::file_readable(unused_cache_file));
94 REQUIRE(leatherman::file_util::file_readable(used_cache_file));
95 }
96 }
97
98 boost_file::remove_all(cache_dir);
99 }
+0
-575
lib/tests/facts/collection.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/resolver.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <leatherman/util/environment.hpp>
7 #include "../fixtures.hpp"
8 #include <sstream>
9
10 using namespace std;
11 using namespace facter::facts;
12 using namespace leatherman::util;
13 using namespace facter::testing;
14
15 struct simple_resolver : facter::facts::resolver
16 {
17 simple_resolver() : resolver("test", { "foo" })
18 {
19 }
20
21 virtual void resolve(collection& facts) override
22 {
23 facts.add("foo", make_value<string_value>("bar"));
24 }
25
26 bool is_blockable() const override
27 {
28 return true;
29 }
30 };
31
32 struct unblockable_resolver : facter::facts::resolver
33 {
34 unblockable_resolver() : resolver("unblockable", { "foo" })
35 {
36 }
37
38 virtual void resolve(collection& facts) override
39 {
40 facts.add("foo", make_value<string_value>("bar"));
41 }
42
43 bool is_blockable() const override
44 {
45 return false;
46 }
47 };
48
49 struct multi_resolver : facter::facts::resolver
50 {
51 multi_resolver() : resolver("test", { "foo", "bar" })
52 {
53 }
54
55 virtual void resolve(collection& facts) override
56 {
57 facts.add("foo", make_value<string_value>("bar"));
58 facts.add("bar", make_value<string_value>("foo"));
59 }
60
61 bool is_blockable() const override
62 {
63 return true;
64 }
65 };
66
67 struct temp_variable
68 {
69 temp_variable(string&& name, string const& value) :
70 _name(move(name))
71 {
72 environment::set(_name, value);
73 }
74
75 ~temp_variable()
76 {
77 environment::clear(_name);
78 }
79
80 string _name;
81 };
82
83 SCENARIO("using the fact collection") {
84 collection_fixture facts;
85 REQUIRE(facts.size() == 0u);
86 REQUIRE(facts.empty());
87
88 GIVEN("default facts") {
89 facts.add_default_facts(true);
90 THEN("facts should resolve") {
91 REQUIRE(facts.size() > 0u);
92 REQUIRE_FALSE(facts.empty());
93 }
94 }
95 GIVEN("a hidden fact and a revealed fact") {
96 facts.add("foo", make_value<string_value>("bar"));
97 facts.add("hidden_foo", make_value<string_value>("hidden_bar", true));
98 THEN("they should be in the collection") {
99 REQUIRE(facts.size() == 2u);
100 REQUIRE_FALSE(facts.empty());
101 auto fact = facts.get<string_value>("foo");
102 REQUIRE(fact);
103 REQUIRE(fact->value() == "bar");
104 fact = dynamic_cast<string_value const *>(facts["foo"]);
105 REQUIRE(fact);
106 REQUIRE(fact->value() == "bar");
107 auto hidden_fact = facts.get<string_value>("hidden_foo");
108 REQUIRE(hidden_fact);
109 REQUIRE(hidden_fact->value() == "hidden_bar");
110 hidden_fact = dynamic_cast<string_value const *>(facts["hidden_foo"]);
111 REQUIRE(hidden_fact);
112 REQUIRE(hidden_fact->value() == "hidden_bar");
113 }
114 WHEN("writing default facts") {
115 THEN("it should serialize the revealed fact to JSON") {
116 ostringstream ss;
117 facts.write(ss, format::json);
118 REQUIRE(ss.str() == "{\n \"foo\": \"bar\"\n}");
119 }
120 THEN("it should serialize the revealed fact to YAML") {
121 ostringstream ss;
122 facts.write(ss, format::yaml);
123 REQUIRE(ss.str() == "foo: bar");
124 }
125 THEN("it should serialize the revealed fact to text") {
126 ostringstream ss;
127 facts.write(ss, format::hash);
128 REQUIRE(ss.str() == "foo => bar");
129 }
130 }
131 WHEN("writing all (hidden) facts") {
132 THEN("it should serialize both facts to JSON") {
133 ostringstream ss;
134 facts.write(ss, format::json, set<string>{}, true, false);
135 REQUIRE(ss.str() == "{\n \"foo\": \"bar\",\n \"hidden_foo\": \"hidden_bar\"\n}");
136 }
137 THEN("it should serialize both facts to YAML") {
138 ostringstream ss;
139 facts.write(ss, format::yaml, set<string>{}, true, false);
140 REQUIRE(ss.str() == "foo: bar\nhidden_foo: hidden_bar");
141 }
142 THEN("it should serialize both facts to text") {
143 ostringstream ss;
144 facts.write(ss, format::hash, set<string>{}, true, false);
145 REQUIRE(ss.str() == "foo => bar\nhidden_foo => hidden_bar");
146 }
147 }
148 WHEN("querying facts") {
149 THEN("it should serialize both facts to JSON") {
150 ostringstream ss;
151 facts.write(ss, format::json, {"foo", "hidden_foo"});
152 REQUIRE(ss.str() == "{\n \"foo\": \"bar\",\n \"hidden_foo\": \"hidden_bar\"\n}");
153 }
154 THEN("it should serialize both facts to YAML") {
155 ostringstream ss;
156 facts.write(ss, format::yaml, {"foo", "hidden_foo"});
157 REQUIRE(ss.str() == "foo: bar\nhidden_foo: hidden_bar");
158 }
159 THEN("it should serialize both facts to text") {
160 ostringstream ss;
161 facts.write(ss, format::hash, {"foo", "hidden_foo"});
162 REQUIRE(ss.str() == "foo => bar\nhidden_foo => hidden_bar");
163 }
164 }
165 WHEN("querying hidden facts") {
166 THEN("it should serialize both facts to JSON") {
167 ostringstream ss;
168 facts.write(ss, format::json, {"foo", "hidden_foo"}, true, false);
169 REQUIRE(ss.str() == "{\n \"foo\": \"bar\",\n \"hidden_foo\": \"hidden_bar\"\n}");
170 }
171 THEN("it should serialize both facts to YAML") {
172 ostringstream ss;
173 facts.write(ss, format::yaml, {"foo", "hidden_foo"}, true, false);
174 REQUIRE(ss.str() == "foo: bar\nhidden_foo: hidden_bar");
175 }
176 THEN("it should serialize both facts to text") {
177 ostringstream ss;
178 facts.write(ss, format::hash, {"foo", "hidden_foo"}, true, false);
179 REQUIRE(ss.str() == "foo => bar\nhidden_foo => hidden_bar");
180 }
181 }
182 }
183 GIVEN("a resolver that adds a single fact") {
184 facts.add(make_shared<simple_resolver>());
185 THEN("it should resolve facts into the collection") {
186 REQUIRE(facts.size() == 1u);
187 REQUIRE_FALSE(facts.empty());
188 auto fact = facts.get<string_value>("foo");
189 REQUIRE(fact);
190 REQUIRE(fact->value() == "bar");
191 fact = dynamic_cast<string_value const *>(facts["foo"]);
192 REQUIRE(fact);
193 REQUIRE(fact->value() == "bar");
194 }
195 WHEN("serializing to JSON") {
196 THEN("it should contain the same values") {
197 ostringstream ss;
198 facts.write(ss, format::json);
199 REQUIRE(ss.str() == "{\n \"foo\": \"bar\"\n}");
200 }
201 }
202 WHEN("serializing to YAML") {
203 THEN("it should contain the same values") {
204 ostringstream ss;
205 facts.write(ss, format::yaml);
206 REQUIRE(ss.str() == "foo: bar");
207 }
208 }
209 WHEN("serializing to text") {
210 GIVEN("only a single query") {
211 THEN("it should output only the value") {
212 ostringstream ss;
213 facts.write(ss, format::hash, {"foo"});
214 REQUIRE(ss.str() == "bar");
215 }
216 }
217 GIVEN("no queries") {
218 THEN("it should contain the same values") {
219 ostringstream ss;
220 facts.write(ss, format::hash);
221 REQUIRE(ss.str() == "foo => bar");
222 }
223 }
224 }
225 }
226 GIVEN("a resolver that adds multiple facts") {
227 facts.add(make_shared<multi_resolver>());
228 THEN("it should enumerate the facts in order") {
229 int index = 0;
230 facts.each([&](string const &name, value const *val) {
231 auto string_val = dynamic_cast<string_value const *>(val);
232 REQUIRE(string_val);
233 if (index == 0) {
234 REQUIRE(name == "bar");
235 REQUIRE(string_val->value() == "foo");
236 } else if (index == 1) {
237 REQUIRE(name == "foo");
238 REQUIRE(string_val->value() == "bar");
239 } else {
240 FAIL("should not be reached");
241 }
242 ++index;
243 return true;
244 });
245 }
246 WHEN("serializing to JSON") {
247 THEN("it should contain the same values") {
248 ostringstream ss;
249 facts.write(ss, format::json);
250 REQUIRE(ss.str() == "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}");
251 }
252 }
253 WHEN("serializing to YAML") {
254 THEN("it should contain the same values") {
255 ostringstream ss;
256 facts.write(ss, format::yaml);
257 REQUIRE(ss.str() == "bar: foo\nfoo: bar");
258 }
259 }
260 WHEN("serializing to text") {
261 THEN("it should contain the same values") {
262 ostringstream ss;
263 facts.write(ss, format::hash);
264 REQUIRE(ss.str() == "bar => foo\nfoo => bar");
265 }
266 }
267 }
268 GIVEN("external facts paths to search") {
269 facts.add_external_facts({
270 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/yaml",
271 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/json",
272 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/text",
273 });
274 REQUIRE_FALSE(facts.empty());
275 REQUIRE(facts.size() == 20u);
276 WHEN("YAML files are present") {
277 THEN("facts should be added") {
278 REQUIRE(facts.get<string_value>("yaml_fact1"));
279 REQUIRE(facts.get<integer_value>("yaml_fact2"));
280 REQUIRE(facts.get<boolean_value>("yaml_fact3"));
281 REQUIRE(facts.get<double_value>("yaml_fact4"));
282 REQUIRE(facts.get<array_value>("yaml_fact5"));
283 REQUIRE(facts.get<map_value>("yaml_fact6"));
284 REQUIRE(facts.get<string_value>("yaml_fact7"));
285 REQUIRE(facts.get<string_value>("not_bool"));
286 REQUIRE(facts.get<string_value>("not_int"));
287 REQUIRE(facts.get<string_value>("not_double"));
288 }
289 }
290 WHEN("JSON files are present") {
291 THEN("facts should be added") {
292 REQUIRE(facts.get<string_value>("json_fact1"));
293 REQUIRE(facts.get<integer_value>("json_fact2"));
294 REQUIRE(facts.get<boolean_value>("json_fact3"));
295 REQUIRE(facts.get<double_value>("json_fact4"));
296 REQUIRE(facts.get<array_value>("json_fact5"));
297 REQUIRE(facts.get<map_value>("json_fact6"));
298 REQUIRE(facts.get<string_value>("json_fact7"));
299 }
300 }
301 WHEN("text files are present") {
302 THEN("facts should be added") {
303 REQUIRE(facts.get<string_value>("txt_fact1"));
304 REQUIRE(facts.get<string_value>("txt_fact2"));
305 REQUIRE_FALSE(facts.get<string_value>("txt_fact3"));
306 REQUIRE(facts.get<string_value>("txt_fact4"));
307 }
308 }
309 }
310 GIVEN("structured fact data") {
311 auto map = make_value<map_value>();
312 map->add("string", make_value<string_value>("hello"));
313 map->add("integer", make_value<integer_value>(5));
314 map->add("double", make_value<double_value>(0.3));
315 map->add("boolean", make_value<boolean_value>(true));
316 auto submap = make_value<map_value>();
317 submap->add("foo", make_value<string_value>("bar"));
318 map->add("submap", move(submap));
319 submap = make_value<map_value>();
320 submap->add("jam", make_value<string_value>("cakes"));
321 map->add("name.with.dots", move(submap));
322 auto array = make_value<array_value>();
323 array->add(make_value<string_value>("foo"));
324 array->add(make_value<integer_value>(10));
325 array->add(make_value<double_value>(2.3));
326 array->add(make_value<boolean_value>(false));
327 submap = make_value<map_value>();
328 submap->add("bar", make_value<string_value>("baz"));
329 array->add(move(submap));
330 map->add("array", move(array));
331 facts.add("map", move(map));
332 facts.add("string", make_value<string_value>("world"));
333
334 WHEN("queried with a matching top level name") {
335 THEN("a value should be returned") {
336 auto mvalue = facts.query<map_value>("map");
337 REQUIRE(mvalue);
338 REQUIRE(mvalue->size() == 7u);
339 }
340 }
341 WHEN("queried with a non-matching top level name") {
342 THEN("it should return null") {
343 REQUIRE_FALSE(facts.query<string_value>("does not exist"));
344 }
345 }
346 WHEN("querying for a sub element of a type that is not a map") {
347 THEN("it should return null") {
348 REQUIRE_FALSE(facts.query<string_value>("string.foo"));
349 }
350 }
351 WHEN("queried with for a sub element") {
352 THEN("a value should be returned") {
353 auto svalue = facts.query<string_value>("map.string");
354 REQUIRE(svalue);
355 REQUIRE(svalue->value() == "hello");
356 auto ivalue = facts.query<integer_value>("map.integer");
357 REQUIRE(ivalue);
358 REQUIRE(ivalue->value() == 5);
359 auto dvalue = facts.query<double_value>("map.double");
360 REQUIRE(dvalue);
361 REQUIRE(dvalue->value() == Approx(0.3));
362 auto bvalue = facts.query<boolean_value>("map.boolean");
363 REQUIRE(bvalue);
364 REQUIRE(bvalue->value());
365 auto mvalue = facts.query<map_value>("map.submap");
366 REQUIRE(mvalue);
367 REQUIRE(mvalue->size() == 1u);
368 }
369 }
370 WHEN("querying along a path of map values") {
371 THEN("a value should be returned") {
372 auto svalue = facts.query<string_value>("map.submap.foo");
373 REQUIRE(svalue);
374 REQUIRE(svalue->value() == "bar");
375 }
376 }
377 WHEN("querying into an array with an in-bounds index") {
378 THEN("a value should be returned") {
379 auto avalue = facts.query<array_value>("map.array");
380 REQUIRE(avalue);
381 REQUIRE(avalue->size() == 5u);
382 for (size_t i = 0; i < avalue->size(); ++i) {
383 REQUIRE(facts.query("map.array." + to_string(i)));
384 }
385 }
386 }
387 WHEN("querying into an array with a non-numeric index") {
388 THEN("it should return null") {
389 REQUIRE_FALSE(facts.query("map.array.foo"));
390 }
391 }
392 WHEN("uerying into an array with an out-of-bounds index") {
393 THEN("it should return null") {
394 REQUIRE_FALSE(facts.query("map.array.5"));
395 }
396 }
397 WHEN("querying into an element inside of an array") {
398 THEN("it should return a value") {
399 auto svalue = facts.query<string_value>("map.array.4.bar");
400 REQUIRE(svalue);
401 REQUIRE(svalue->value() == "baz");
402 }
403 }
404 WHEN("a fact name contains dots") {
405 THEN("it not return a value unless quoted") {
406 REQUIRE_FALSE(facts.query("map.name.with.dots"));
407 }
408 THEN("it should return a value when quoted") {
409 auto svalue = facts.query<string_value>("map.\"name.with.dots\".jam");
410 REQUIRE(svalue);
411 REQUIRE(svalue->value() == "cakes");
412 }
413 }
414 }
415 GIVEN("a fact from an environment variable") {
416 auto var = temp_variable("FACTER_Foo", "bar");
417 bool added = false;
418 facts.add_environment_facts([&](string const& name) {
419 added = name == "foo";
420 });
421 REQUIRE(added);
422
423 THEN("the fact should be present in the collection") {
424 REQUIRE(facts.size() == 1u);
425 auto value = facts.get<string_value>("foo");
426 REQUIRE(value);
427 REQUIRE(value->value() == "bar");
428 }
429 }
430 GIVEN("a fact from an environment with the same name as a built-in fact") {
431 facts.add_default_facts(true);
432 auto var = temp_variable("FACTER_KERNEL", "overridden");
433 bool added = false;
434 facts.add_environment_facts([&](string const& name) {
435 added = name == "kernel";
436 });
437 REQUIRE(added);
438
439 THEN("it should override the built-in fact's value") {
440 auto value = facts.get<string_value>("kernel");
441 REQUIRE(value);
442 REQUIRE(value->value() == "overridden");
443 }
444 }
445 GIVEN("two external fact directories to search") {
446 facts.add_external_facts({
447 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/foo",
448 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/bar"
449 });
450 THEN("it should have the fact value from the last file loaded") {
451 REQUIRE(facts.size() == 1u);
452 REQUIRE(facts.get<string_value>("foo"));
453 REQUIRE(facts.get<string_value>("foo")->value() == "set in bar/foo.yaml");
454 }
455 facts.clear();
456 facts.add_external_facts({
457 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/bar",
458 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/foo"
459 });
460 THEN("it should have the fact value from the last file loaded") {
461 REQUIRE(facts.size() == 1u);
462 REQUIRE(facts.get<string_value>("foo"));
463 REQUIRE(facts.get<string_value>("foo")->value() == "set in foo/foo.yaml");
464 }
465 }
466 }
467
468 class collection_override : public collection
469 {
470 protected:
471 virtual vector<string> get_external_fact_directories() const override
472 {
473 return {LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/foo"};
474 }
475 };
476
477 SCENARIO("using the fact collection with a default external fact path") {
478 collection_override facts;
479 REQUIRE(facts.size() == 0u);
480 REQUIRE(facts.empty());
481
482 GIVEN("a specified external fact directory with an overriding fact to search") {
483 facts.add_external_facts({
484 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/ordering/bar"
485 });
486 THEN("it should have the fact value from the last file loaded") {
487 REQUIRE(facts.size() == 1u);
488 REQUIRE(facts.get<string_value>("foo"));
489 REQUIRE(facts.get<string_value>("foo")->value() == "set in bar/foo.yaml");
490 }
491 }
492
493 GIVEN("a specified external fact directory with new facts to search") {
494 facts.add_external_facts({
495 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/text",
496 });
497 REQUIRE_FALSE(facts.empty());
498 REQUIRE(facts.size() == 4u);
499 THEN("facts from both directories should be added") {
500 REQUIRE(facts.get<string_value>("foo"));
501 REQUIRE(facts.get<string_value>("txt_fact1"));
502 REQUIRE(facts.get<string_value>("txt_fact2"));
503 REQUIRE_FALSE(facts.get<string_value>("txt_fact3"));
504 REQUIRE(facts.get<string_value>("txt_fact4"));
505 }
506 }
507 }
508
509 SCENARIO("using the fact collection with a blocklist") {
510 collection_fixture facts({ "test" });
511 REQUIRE(facts.size() == 0u);
512 REQUIRE(facts.empty());
513
514 GIVEN("a resolver that adds a single fact") {
515 facts.add(make_shared<simple_resolver>());
516 THEN("the fact should not be resolved") {
517 REQUIRE(facts.size() == 0u);
518 REQUIRE_FALSE(facts.get<string_value>("foo"));
519 }
520 }
521 GIVEN("a resolver that adds multiple facts") {
522 facts.add(make_shared<multi_resolver>());
523 THEN("none of the facts should resolve") {
524 REQUIRE(facts.size() == 0u);
525 REQUIRE_FALSE(facts.get<string_value>("foo"));
526 REQUIRE_FALSE(facts.get<string_value>("bar"));
527 }
528 }
529 }
530
531 SCENARIO("querying blockable and cacheable fact groups") {
532 collection_fixture facts;
533 REQUIRE(facts.size() == 0u);
534 REQUIRE(facts.empty());
535
536 GIVEN("a blockable resolver that adds a single fact") {
537 facts.add(make_shared<simple_resolver>());
538 THEN("resolver and its fact should be listed as blockable") {
539 auto blockable = facts.get_blockable_fact_groups();
540 REQUIRE(blockable.size() == 1);
541 REQUIRE(blockable["test"] == vector<string>({"foo"}));
542 }
543 THEN("resolver and its fact should be listed as in the collection") {
544 auto facts_present = facts.get_fact_groups();
545 REQUIRE(facts_present.size() == 1);
546 REQUIRE(facts_present["test"] == vector<string>({"foo"}));
547 }
548 }
549 GIVEN("a blockable resolver that adds multiple facts") {
550 facts.add(make_shared<multi_resolver>());
551 THEN("resolver and its facts should be listed as blockable") {
552 auto blockable = facts.get_blockable_fact_groups();
553 REQUIRE(blockable.size() == 1);
554 REQUIRE(blockable["test"] == vector<string>({"foo", "bar"}));
555 }
556 THEN("resolver and its facts should be listed as in the collection") {
557 auto facts_present = facts.get_fact_groups();
558 REQUIRE(facts_present.size() == 1);
559 REQUIRE(facts_present["test"] == vector<string>({"foo", "bar"}));
560 }
561 }
562 GIVEN("a unblockable resolver that adds a single fact") {
563 facts.add(make_shared<unblockable_resolver>());
564 THEN("resolver and its facts should not be listed as blockable") {
565 auto blockable = facts.get_blockable_fact_groups();
566 REQUIRE(blockable.size() == 0);
567 }
568 THEN("resolver and its fact should be listed as in the collection") {
569 auto facts_present = facts.get_fact_groups();
570 REQUIRE(facts_present.size() == 1);
571 REQUIRE(facts_present["unblockable"] == vector<string>({"foo"}));
572 }
573 }
574 }
+0
-40
lib/tests/facts/double_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <sstream>
5 #include <cmath>
6 #include <limits>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace rapidjson;
11 using namespace YAML;
12
13 SCENARIO("using a double fact value") {
14 double_value value(42.4242);
15 REQUIRE(value.value() == Approx(42.4242));
16 WHEN("serialized to JSON") {
17 THEN("it should have the same value") {
18 json_value json;
19 json_allocator allocator;
20 value.to_json(allocator, json);
21 REQUIRE(json.IsNumber());
22 REQUIRE(json.GetDouble() == Approx(42.4242));
23 }
24 }
25 WHEN("serialized to YAML") {
26 THEN("it should have the same value") {
27 Emitter emitter;
28 value.write(emitter);
29 REQUIRE(string(emitter.c_str()) == "42.4242");
30 }
31 }
32 WHEN("serialized to text") {
33 THEN("it should have the same value") {
34 ostringstream stream;
35 value.write(stream);
36 REQUIRE(stream.str() == "42.4242");
37 }
38 }
39 }
+0
-53
lib/tests/facts/external/json_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/json_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include "../../fixtures.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::external;
11 using namespace facter::testing;
12
13 SCENARIO("resolving external JSON facts") {
14 collection_fixture facts;
15
16 GIVEN("a non-existent file to resolve") {
17 THEN("it should throw an exception") {
18 json_resolver resolver("doesnotexist.json");
19 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
20 }
21 }
22 GIVEN("invalid JSON") {
23 THEN("it should throw an exception") {
24 json_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/json/invalid.json");
25 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
26 }
27 }
28 GIVEN("valid JSON") {
29 THEN("it should populate the facts") {
30 json_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/json/facts.json");
31 resolver.resolve(facts);
32 REQUIRE_FALSE(facts.empty());
33 REQUIRE(facts.get<string_value>("json_fact1"));
34 REQUIRE(facts.get<string_value>("json_fact1")->value() == "foo");
35 REQUIRE(facts.get<integer_value>("json_fact2"));
36 REQUIRE(facts.get<integer_value>("json_fact2")->value() == 5);
37 REQUIRE(facts.get<boolean_value>("json_fact3"));
38 REQUIRE(facts.get<boolean_value>("json_fact3")->value());
39 REQUIRE(facts.get<double_value>("json_fact4"));
40 REQUIRE(facts.get<double_value>("json_fact4")->value() == Approx(5.1));
41 auto array = facts.get<array_value>("json_fact5");
42 REQUIRE(array);
43 REQUIRE(array->size() == 3u);
44 auto map = facts.get<map_value>("json_fact6");
45 REQUIRE(map);
46 REQUIRE(map->size() == 2u);
47 REQUIRE(facts.get<string_value>("json_fact7"));
48 REQUIRE_FALSE(facts.get<string_value>("JSON_fact7"));
49 REQUIRE(facts.get<string_value>("json_fact7")->value() == "bar");
50 }
51 }
52 }
+0
-57
lib/tests/facts/external/posix/execution_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/execution_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/util/string.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include "../../../fixtures.hpp"
7 #include "../../../log_capture.hpp"
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::util;
12 using namespace facter::facts::external;
13 using namespace facter::logging;
14 using namespace facter::testing;
15 using namespace leatherman::util;
16
17 SCENARIO("resolving external executable facts") {
18 collection_fixture facts;
19
20 GIVEN("an executable file") {
21 WHEN("the execution fails") {
22 THEN("an exception is thrown") {
23 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution/failed");
24 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
25 }
26 }
27 WHEN("the execution succeeds") {
28 THEN("it populates facts") {
29 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution/facts");
30 resolver.resolve(facts);
31 REQUIRE(!facts.empty());
32 REQUIRE(facts.get<string_value>("exe_fact1"));
33 REQUIRE(facts.get<string_value>("exe_fact1")->value() == "value1");
34 REQUIRE(facts.get<string_value>("exe_fact2"));
35 REQUIRE(facts.get<string_value>("exe_fact2")->value() == "");
36 REQUIRE_FALSE(facts.get<string_value>("exe_fact3"));
37 REQUIRE(facts.get<string_value>("exe_fact4"));
38 REQUIRE_FALSE(facts.get<string_value>("EXE_fact4"));
39 REQUIRE(facts.get<string_value>("exe_fact4")->value() == "value2");
40 }
41 }
42 WHEN("messages are logged to stderr") {
43 THEN("a warning is generated") {
44 log_capture capture(level::warning);
45 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution/error_message");
46 resolver.resolve(facts);
47 REQUIRE(facts.size() == 1u);
48 REQUIRE(facts.get<string_value>("foo"));
49 REQUIRE(facts.get<string_value>("foo")->value() == "bar");
50 auto output = capture.result();
51 CAPTURE(output);
52 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - external fact file \".*/error_message\" had output on stderr: error message!")));
53 }
54 }
55 }
56 }
+0
-36
lib/tests/facts/external/text_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/text_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include "../../fixtures.hpp"
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace facter::facts::external;
9 using namespace facter::testing;
10
11 SCENARIO("resolving external text facts") {
12 collection_fixture facts;
13
14 GIVEN("a non-existent file to resolve") {
15 THEN("it should throw an exception") {
16 text_resolver resolver("doesnotexist.txt");
17 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
18 }
19 }
20 GIVEN("a text file to resolve") {
21 THEN("it should populate the facts") {
22 text_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/text/facts.txt");
23 resolver.resolve(facts);
24 REQUIRE_FALSE(facts.empty());
25 REQUIRE(facts.get<string_value>("txt_fact1"));
26 REQUIRE(facts.get<string_value>("txt_fact1")->value() == "value1");
27 REQUIRE(facts.get<string_value>("txt_fact2"));
28 REQUIRE(facts.get<string_value>("txt_fact2")->value() == "");
29 REQUIRE_FALSE(facts.get<string_value>("txt_fact3"));
30 REQUIRE(facts.get<string_value>("txt_fact4"));
31 REQUIRE_FALSE(facts.get<string_value>("TXT_Fact4"));
32 REQUIRE(facts.get<string_value>("txt_fact4")->value() == "value2");
33 }
34 }
35 }
+0
-56
lib/tests/facts/external/windows/execution_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/execution_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/util/string.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include "../../../fixtures.hpp"
7 #include "../../../log_capture.hpp"
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::facts::external;
12 using namespace facter::logging;
13 using namespace facter::testing;
14 using namespace leatherman::util;
15
16 SCENARIO("resolving external executable facts") {
17 collection_fixture facts;
18
19 GIVEN("an executable file") {
20 WHEN("the execution fails") {
21 THEN("an exception is thrown") {
22 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/failed.cmd");;
23 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
24 }
25 }
26 WHEN("the execution succeeds") {
27 THEN("it populates facts") {
28 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/facts.bat");
29 resolver.resolve(facts);
30 REQUIRE(!facts.empty());
31 REQUIRE(facts.get<string_value>("exe_fact1"));
32 REQUIRE(facts.get<string_value>("exe_fact1")->value() == "value1");
33 REQUIRE(facts.get<string_value>("exe_fact2"));
34 REQUIRE(facts.get<string_value>("exe_fact2")->value() == "");
35 REQUIRE_FALSE(facts.get<string_value>("exe_fact3"));
36 REQUIRE(facts.get<string_value>("exe_fact4"));
37 REQUIRE_FALSE(facts.get<string_value>("EXE_fact4"));
38 REQUIRE(facts.get<string_value>("exe_fact4")->value() == "value2");
39 }
40 }
41 WHEN("messages are logged to stderr") {
42 THEN("a warning is generated") {
43 log_capture capture(level::warning);
44 execution_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/error_message.bat");
45 resolver.resolve(facts);
46 REQUIRE(facts.size() == 1u);
47 REQUIRE(facts.get<string_value>("foo"));
48 REQUIRE(facts.get<string_value>("foo")->value() == "bar");
49 auto output = capture.result();
50 CAPTURE(output);
51 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - external fact file \".*error_message.bat\" had output on stderr: error message!")));
52 }
53 }
54 }
55 }
+0
-118
lib/tests/facts/external/windows/powershell_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/windows/powershell_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <facter/util/string.hpp>
5 #include <leatherman/util/regex.hpp>
6 #include <facter/facts/array_value.hpp>
7 #include <facter/facts/map_value.hpp>
8 #include "../../../fixtures.hpp"
9 #include "../../../log_capture.hpp"
10
11 using namespace std;
12 using namespace facter::facts;
13 using namespace leatherman::util;
14 using namespace facter::logging;
15 using namespace facter::testing;
16 using namespace facter::facts::external;
17
18 SCENARIO("resolving external powershell facts") {
19 collection_fixture facts;
20
21 GIVEN("a powershell file") {
22 WHEN("the execution fails") {
23 THEN("an exception is thrown") {
24 powershell_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/failed.ps1");
25 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
26 }
27 }
28 WHEN("the execution succeeds") {
29 THEN("it populates facts") {
30 powershell_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/facts.ps1");
31 resolver.resolve(facts);
32 REQUIRE(!facts.empty());
33 REQUIRE(facts.get<string_value>("ps1_fact1"));
34 REQUIRE(facts.get<string_value>("ps1_fact1")->value() == "value1");
35 REQUIRE(facts.get<string_value>("ps1_fact2"));
36 REQUIRE(facts.get<string_value>("ps1_fact2")->value() == "");
37 REQUIRE_FALSE(facts.get<string_value>("ps1_fact3"));
38 REQUIRE(facts.get<string_value>("ps1_fact4"));
39 REQUIRE_FALSE(facts.get<string_value>("PS1_fact4"));
40 REQUIRE(facts.get<string_value>("ps1_fact4")->value() == "value2");
41 }
42 }
43 WHEN("the output is json") {
44 THEN("it populates facts from the json") {
45 powershell_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/json.ps1");
46 resolver.resolve(facts);
47 REQUIRE(!facts.empty());
48
49 REQUIRE_FALSE(facts.get<string_value>("PS1_JSON_FACT1"));
50 REQUIRE(facts.get<string_value>("ps1_json_fact1"));
51 REQUIRE(facts.get<string_value>("ps1_json_fact1")->value() == "value1");
52
53 REQUIRE(facts.get<integer_value>("ps1_json_fact2"));
54 REQUIRE(facts.get<integer_value>("ps1_json_fact2")->value() == 2);
55
56 REQUIRE(facts.get<boolean_value>("ps1_json_fact3"));
57 REQUIRE(facts.get<boolean_value>("ps1_json_fact3")->value());
58
59 auto array = facts.get<array_value>("ps1_json_fact4");
60 REQUIRE(array);
61 REQUIRE(array->size() == 2u);
62
63 REQUIRE_FALSE(facts.get<boolean_value>("ps1_json_fact5"));
64
65 auto map = facts.get<map_value>("ps1_json_fact6");
66 REQUIRE(map);
67 REQUIRE(map->size() == 2u);
68 }
69 }
70 WHEN("the output is yaml") {
71 THEN("it populates facts from the yaml") {
72 powershell_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/yaml.ps1");
73 resolver.resolve(facts);
74 REQUIRE(!facts.empty());
75
76 REQUIRE_FALSE(facts.get<integer_value>("PS1_YAML_FACT1"));
77 REQUIRE(facts.get<string_value>("ps1_yaml_fact1"));
78 REQUIRE(facts.get<string_value>("ps1_yaml_fact1")->value() == "yaml");
79
80 REQUIRE(facts.get<integer_value>("ps1_yaml_fact2"));
81 REQUIRE(facts.get<integer_value>("ps1_yaml_fact2")->value() == 2);
82
83 REQUIRE(facts.get<string_value>("ps1_yaml_fact3"));
84 REQUIRE(facts.get<string_value>("ps1_yaml_fact3")->value() == "one value\nbut\nmany lines\n");
85
86 auto array1 = facts.get<array_value>("ps1_yaml_fact4");
87 REQUIRE(array1);
88 REQUIRE(array1->size() == 2u);
89
90 auto array2 = facts.get<array_value>("ps1_yaml_fact5");
91 REQUIRE(array2);
92 REQUIRE(array2->size() == 3u);
93
94 auto map1 = facts.get<map_value>("ps1_yaml_fact6");
95 REQUIRE(map1);
96 REQUIRE(map1->size() == 3u);
97
98 auto map2 = facts.get<map_value>("ps1_yaml_fact7");
99 REQUIRE(map2);
100 REQUIRE(map2->size() == 1u);
101 }
102 }
103 WHEN("messages are logged to stderr") {
104 THEN("a warning is generated") {
105 log_capture capture(level::warning);
106 powershell_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/error_message.ps1");
107 resolver.resolve(facts);
108 REQUIRE(facts.size() == 1u);
109 REQUIRE(facts.get<string_value>("foo"));
110 REQUIRE(facts.get<string_value>("foo")->value() == "bar");
111 auto output = capture.result();
112 CAPTURE(output);
113 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - external fact file \".*error_message.ps1\" had output on stderr: error message!")));
114 }
115 }
116 }
117 }
+0
-61
lib/tests/facts/external/yaml_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/external/yaml_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include "../../fixtures.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::external;
11 using namespace facter::testing;
12
13 SCENARIO("resolving external YAML facts") {
14 collection_fixture facts;
15
16 GIVEN("a non-existent file to resolve") {
17 THEN("it should throw an exception") {
18 yaml_resolver resolver("doesnotexist.yaml");
19 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
20 }
21 }
22 GIVEN("invalid YAML") {
23 THEN("it should throw an exception") {
24 yaml_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/yaml/invalid.yaml");
25 REQUIRE_THROWS_AS(resolver.resolve(facts), external_fact_exception&);
26 }
27 }
28 GIVEN("valid YAML") {
29 THEN("it should populate the facts") {
30 yaml_resolver resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/yaml/facts.yaml");
31 resolver.resolve(facts);
32 REQUIRE_FALSE(facts.empty());
33 REQUIRE(facts.get<string_value>("yaml_fact1"));
34 REQUIRE(facts.get<string_value>("yaml_fact1")->value() == "foo");
35 REQUIRE(facts.get<integer_value>("yaml_fact2"));
36 REQUIRE(facts.get<integer_value>("yaml_fact2")->value() == 5);
37 REQUIRE(facts.get<boolean_value>("yaml_fact3"));
38 REQUIRE(facts.get<boolean_value>("yaml_fact3")->value());
39 REQUIRE(facts.get<double_value>("yaml_fact4"));
40 REQUIRE(facts.get<double_value>("yaml_fact4")->value() == Approx(5.1));
41 auto array = facts.get<array_value>("yaml_fact5");
42 REQUIRE(array);
43 REQUIRE(array->size() == 3u);
44 auto map = facts.get<map_value>("yaml_fact6");
45 REQUIRE(map);
46 REQUIRE(map->size() == 2u);
47 REQUIRE(facts.get<string_value>("yaml_fact7"));
48 REQUIRE_FALSE(facts.get<string_value>("YAML_fact7"));
49 REQUIRE(facts.get<string_value>("yaml_fact7")->value() == "bar");
50 REQUIRE(facts.get<string_value>("yaml_fact7")->value() == "bar");
51 REQUIRE(facts.get<string_value>("yaml_fact7")->value() == "bar");
52 REQUIRE(facts.get<string_value>("not_bool"));
53 REQUIRE(facts.get<string_value>("not_bool")->value() == "true");
54 REQUIRE(facts.get<string_value>("not_int"));
55 REQUIRE(facts.get<string_value>("not_int")->value() == "123");
56 REQUIRE(facts.get<string_value>("not_double"));
57 REQUIRE(facts.get<string_value>("not_double")->value() == "123.456");
58 }
59 }
60 }
+0
-33
lib/tests/facts/external_resolvers_factory.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/external_resolvers_factory.hpp>
2 #include "../fixtures.hpp"
3
4 using namespace std;
5 using namespace facter::facts;
6
7 SCENARIO("checking common external file resolvers factory") {
8 external_resolvers_factory erf;
9 GIVEN("a file with unknown extension") {
10 THEN("should throw no resolver exception") {
11 REQUIRE_THROWS_AS(erf.get_resolver("foo.bin"), external::external_fact_no_resolver&);
12 }
13 }
14 GIVEN("a JSON file extension") {
15 THEN("it should be able to resolve") {
16 REQUIRE(erf.get_resolver("foo.json"));
17 REQUIRE(erf.get_resolver("FoO.jsOn"));
18 }
19 }
20 GIVEN("a text file extension") {
21 THEN("it should be able to resolve") {
22 REQUIRE(erf.get_resolver("foo.txt"));
23 REQUIRE(erf.get_resolver("FoO.TxT"));
24 }
25 }
26 GIVEN("a YAML file extension") {
27 THEN("it should be able to resolve") {
28 REQUIRE(erf.get_resolver("foo.yaml"));
29 REQUIRE(erf.get_resolver("FoO.yAmL"));
30 }
31 }
32 }
+0
-69
lib/tests/facts/integer_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <sstream>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace rapidjson;
9 using namespace YAML;
10
11 SCENARIO("using an integer fact value") {
12 GIVEN("a small integer value") {
13 int expected_value = 42;
14 integer_value value(expected_value);
15 REQUIRE(value.value() == expected_value);
16 WHEN("serialized to JSON") {
17 THEN("it should have the same value") {
18 json_value json;
19 json_allocator allocator;
20 value.to_json(allocator, json);
21 REQUIRE(json.IsNumber());
22 REQUIRE(json.GetInt64() == expected_value);
23 }
24 }
25 WHEN("serialized to YAML") {
26 THEN("it should have the same value") {
27 Emitter emitter;
28 value.write(emitter);
29 REQUIRE(string(emitter.c_str()) == "42");
30 }
31 }
32 WHEN("serialized to text") {
33 THEN("it should have the same value") {
34 ostringstream stream;
35 value.write(stream);
36 REQUIRE(stream.str() == "42");
37 }
38 }
39 }
40 GIVEN("a very large integer value") {
41 int64_t expected_value = 1LL << 62;
42 integer_value value(expected_value);
43 REQUIRE(value.value() == expected_value);
44 WHEN("serialized to JSON") {
45 THEN("it should have the same value") {
46 json_value json;
47 json_allocator allocator;
48 value.to_json(allocator, json);
49 REQUIRE(json.IsNumber());
50 REQUIRE(json.GetInt64() == expected_value);
51 }
52 }
53 WHEN("serialized to YAML") {
54 THEN("it should have the same value") {
55 Emitter emitter;
56 value.write(emitter);
57 REQUIRE(string(emitter.c_str()) == "4611686018427387904");
58 }
59 }
60 WHEN("serialized to text") {
61 THEN("it should have the same value") {
62 ostringstream stream;
63 value.write(stream);
64 REQUIRE(stream.str() == "4611686018427387904");
65 }
66 }
67 }
68 }
+0
-136
lib/tests/facts/linux/dmi_resolver.cc less more
0 #include <catch.hpp>
1 #include <facter/util/string.hpp>
2 #include <internal/facts/linux/dmi_resolver.hpp>
3 #include "../../fixtures.hpp"
4
5 using namespace std;
6 using namespace facter::util;
7 using namespace facter::testing;
8 using namespace facter::facts::linux;
9
10 struct dmi_output : facter::facts::linux::dmi_resolver
11 {
12 explicit dmi_output(string const& output)
13 {
14 data result;
15 int dmi_type = -1;
16
17 each_line(output, [&](string& line) {
18 parse_dmidecode_output(result, line, dmi_type);
19 return true;
20 });
21
22 bios_vendor = std::move(result.bios_vendor);
23 bios_version = std::move(result.bios_version);
24 bios_release_date = std::move(result.bios_release_date);
25 board_asset_tag = std::move(result.board_asset_tag);
26 board_manufacturer = std::move(result.board_manufacturer);
27 board_product_name = std::move(result.board_product_name);
28 board_serial_number = std::move(result.board_serial_number);
29 chassis_asset_tag = std::move(result.chassis_asset_tag);
30 manufacturer = std::move(result.manufacturer);
31 product_name = std::move(result.product_name);
32 serial_number = std::move(result.serial_number);
33 uuid = std::move(result.uuid);
34 chassis_type = std::move(result.chassis_type);
35 }
36
37 string bios_vendor;
38 string bios_version;
39 string bios_release_date;
40 string board_asset_tag;
41 string board_manufacturer;
42 string board_product_name;
43 string board_serial_number;
44 string chassis_asset_tag;
45 string manufacturer;
46 string product_name;
47 string serial_number;
48 string uuid;
49 string chassis_type;
50 };
51
52 SCENARIO("parsing empty dmidecode output") {
53 string contents;
54 REQUIRE(load_fixture("facts/linux/dmidecode/none.txt", contents));
55 dmi_output output(contents);
56
57 THEN("all fields should be empty") {
58 REQUIRE(output.bios_vendor.empty());
59 REQUIRE(output.bios_version.empty());
60 REQUIRE(output.bios_release_date.empty());
61 REQUIRE(output.board_asset_tag.empty());
62 REQUIRE(output.board_manufacturer.empty());
63 REQUIRE(output.board_product_name.empty());
64 REQUIRE(output.board_serial_number.empty());
65 REQUIRE(output.chassis_asset_tag.empty());
66 REQUIRE(output.manufacturer.empty());
67 REQUIRE(output.serial_number.empty());
68 REQUIRE(output.product_name.empty());
69 REQUIRE(output.uuid.empty());
70 REQUIRE(output.chassis_type.empty());
71 }
72 }
73
74 SCENARIO("parsing full dmidecode output") {
75 string contents;
76 REQUIRE(load_fixture("facts/linux/dmidecode/full.txt", contents));
77 dmi_output output(contents);
78
79 THEN("all fields should be populated") {
80 REQUIRE(output.bios_vendor == "innotek GmbH");
81 REQUIRE(output.bios_version == "VirtualBox");
82 REQUIRE(output.bios_release_date == "12/01/2006");
83 REQUIRE(output.board_asset_tag == "Not Specified");
84 REQUIRE(output.board_manufacturer == "Oracle Corporation");
85 REQUIRE(output.board_product_name == "VirtualBox");
86 REQUIRE(output.board_serial_number == "0");
87 REQUIRE(output.chassis_asset_tag == "Not Specified");
88 REQUIRE(output.manufacturer == "innotek GmbH");
89 REQUIRE(output.serial_number == "0");
90 REQUIRE(output.product_name == "VirtualBox");
91 REQUIRE(output.uuid == "735AE71B-8655-4AE2-9CA9-172C1BBEDAB5");
92 REQUIRE(output.chassis_type == "Other");
93 }
94 }
95
96 SCENARIO("parsing full dmidecode output in an alternative format") {
97 string contents;
98 REQUIRE(load_fixture("facts/linux/dmidecode/full_alternative.txt", contents));
99 dmi_output output(contents);
100
101 THEN("all fields should be populated") {
102 REQUIRE(output.bios_vendor == "innotek GmbH");
103 REQUIRE(output.bios_version == "VirtualBox");
104 REQUIRE(output.bios_release_date == "12/01/2006");
105 REQUIRE(output.board_asset_tag == "Not Specified");
106 REQUIRE(output.board_manufacturer == "Oracle Corporation");
107 REQUIRE(output.board_product_name == "VirtualBox");
108 REQUIRE(output.board_serial_number == "0");
109 REQUIRE(output.chassis_asset_tag == "Not Specified");
110 REQUIRE(output.manufacturer == "innotek GmbH");
111 REQUIRE(output.serial_number == "0");
112 REQUIRE(output.product_name == "VirtualBox");
113 REQUIRE(output.uuid == "735AE71B-8655-4AE2-9CA9-172C1BBEDAB5");
114 REQUIRE(output.chassis_type == "Other");
115 }
116 }
117
118 SCENARIO("Verify chassis_type for new id's", "id"){
119 // new ids are not mapped to unkown
120 for (int id = 25; id < 33; id++){
121 REQUIRE(dmi_resolver::to_chassis_description(std::to_string(id)) != "Unknown");
122 }
123 // new ids are recognised
124 REQUIRE(dmi_resolver::to_chassis_description("24") == "Sealed-Case PC");
125 REQUIRE(dmi_resolver::to_chassis_description("25") == "Multi-system");
126 REQUIRE(dmi_resolver::to_chassis_description("26") == "CompactPCI");
127 REQUIRE(dmi_resolver::to_chassis_description("27") == "AdvancedTCA");
128 REQUIRE(dmi_resolver::to_chassis_description("28") == "Blade");
129 REQUIRE(dmi_resolver::to_chassis_description("29") == "Blade Enclosure");
130 REQUIRE(dmi_resolver::to_chassis_description("30") == "Tablet");
131 REQUIRE(dmi_resolver::to_chassis_description("31") == "Convertible");
132 REQUIRE(dmi_resolver::to_chassis_description("32") == "Detachable");
133 // out of range id are maped to unknown
134 REQUIRE(dmi_resolver::to_chassis_description("33") == "Unknown");
135 }
+0
-41
lib/tests/facts/linux/filesystem_resolver.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/fact.hpp>
2 #include <facter/facts/map_value.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <internal/facts/linux/filesystem_resolver.hpp>
5 #include "../../collection_fixture.hpp"
6 #include <sstream>
7
8 using namespace std;
9 using namespace facter::facts::linux;
10 using namespace facter::testing;
11
12 SCENARIO("blkid output with non-printable ASCII characters") {
13 REQUIRE(filesystem_resolver::safe_convert("") == "");
14 REQUIRE(filesystem_resolver::safe_convert("hello") == "hello");
15 REQUIRE(filesystem_resolver::safe_convert("\"hello\"") == "\\\"hello\\\"");
16 REQUIRE(filesystem_resolver::safe_convert("\\hello\\") == "\\\\hello\\\\");
17 REQUIRE(filesystem_resolver::safe_convert("i am \xE0\xB2\xA0\x5F\xE0\xB2\xA0") == "i am M-`M-2M- _M-`M-2M- ");
18 }
19
20 SCENARIO("using the filesystem resolver") {
21 // Create fact struct to store results
22 collection_fixture facts;
23 WHEN("populating facts") {
24 // Add filesystem resolver
25 facts.add(make_shared<filesystem_resolver>());
26 THEN("filesystem, mountpoints, and partition facts should resolve") {
27 REQUIRE(facts.size() != 0u);
28 REQUIRE(facts.query<facter::facts::string_value>("filesystems"));
29 REQUIRE(facts.query<facter::facts::map_value>("mountpoints"));
30 REQUIRE(facts.query<facter::facts::map_value>("partitions"));
31 }
32 THEN("non-tmpfs proc and sys mounts should not exist") {
33 REQUIRE_FALSE(facts.query<facter::facts::map_value>("mountpoints./proc/"));
34 REQUIRE_FALSE(facts.query<facter::facts::map_value>("mountpoints./sys/"));
35 }
36 THEN("non-tmpfs mounts should exist") {
37 REQUIRE(facts.query<facter::facts::map_value>("mountpoints./"));
38 }
39 }
40 }
+0
-258
lib/tests/facts/linux/processor_fixture.cc less more
0 #include "processor_fixture.hpp"
1 #include <boost/algorithm/string/join.hpp>
2 #include <algorithm>
3 #include <array>
4 #include <sstream>
5
6 using namespace std;
7 using key_cmp = std::function<bool(std::string const&, std::string const&)>;
8 using architecture_type = test_linux_processor_resolver::ArchitectureType;
9 namespace fs = boost::filesystem;
10
11 static void reset_directory(fs::path const& path)
12 {
13 fs::remove_all(path);
14 fs::create_directories(path);
15 }
16
17 template <typename T>
18 static void write_value(boost::filesystem::path const& path, T const& value) {
19 boost::nowide::ofstream ofs(path.string());
20 ofs << value;
21 }
22
23 template <typename container>
24 static int key_index(const container& keys, const std::string& key)
25 {
26 return find(keys.begin(), keys.end(), key) - keys.begin();
27 }
28
29 static key_cmp default_cpu_info_key_cmp = [](const std::string& k1, const std::string& k2)
30 {
31 static array<string, 8> KEYS = {
32 "processor",
33 "vendor_id",
34 "cpu_family",
35 "model",
36 "model name",
37 "stepping",
38 "microcode",
39 "physical id"
40 };
41
42 return key_index(KEYS, k1) < key_index(KEYS, k2);
43 };
44
45 static key_cmp power_cpu_info_key_cmp = [](const std::string& k1, const std::string& k2)
46 {
47 static array<string, 4> KEYS = {
48 "processor",
49 "cpu",
50 "clock",
51 "revision"
52 };
53
54 return key_index(KEYS, k1) < key_index(KEYS, k2);
55 };
56
57 linux_cpu_fixture::linux_cpu_fixture(boost::filesystem::path const& sys_dir, int id, std::string const& model_name)
58 {
59 _cpuroot = sys_dir / ("cpu" + to_string(id));
60 fs::create_directory(_cpuroot);
61
62 _topology = _cpuroot / "topology";
63 fs::create_directory(_topology);
64
65 _cpufreq = _cpuroot / "cpufreq";
66 fs::create_directory(_cpufreq);
67
68 init_info(model_name);
69 }
70
71 void linux_cpu_fixture::set_logical_id(std::string const& logical_id)
72 {
73 _info["processor"] = logical_id;
74 }
75
76 void linux_cpu_fixture::set_physical_id(std::string const& physical_id)
77 {
78 write_value(_topology / "physical_package_id", physical_id);
79 _info["physical id"] = physical_id;
80 }
81
82 void linux_cpu_fixture::set_speed(int64_t speed)
83 {
84 write_value(_cpufreq / "cpuinfo_max_freq", speed);
85 }
86
87 void linux_cpu_fixture::set_info(std::string const& key, std::string const& value)
88 {
89 _info[key] = value;
90 }
91
92 void linux_cpu_fixture::erase_info(std::string const& key)
93 {
94 _info.erase(key);
95 }
96
97 void linux_cpu_fixture::erase_all_info()
98 {
99 _info = map<string, string, key_cmp>(default_cpu_info_key_cmp);
100 }
101
102 std::string linux_cpu_fixture::get_info()
103 {
104 ostringstream buf;
105 for (auto& entry : _info) {
106 buf << entry.first << " : " << entry.second << endl;
107 }
108 return buf.str();
109 }
110
111 void linux_cpu_fixture::init_info(std::string const& model_name)
112 {
113 _info = map<string, string, key_cmp>(default_cpu_info_key_cmp);
114
115 _info["vendor_id"] = "GenuineIntel";
116 _info["cpu_family"] = "6";
117 _info["model"] = "69";
118 _info["model name"] = model_name;
119 _info["stepping"] = "1";
120 _info["microcode"] = "0x17";
121 }
122
123
124 linux_power_cpu_fixture::linux_power_cpu_fixture(boost::filesystem::path const& sys_dir, int id, std::string const& model_name)
125 : linux_cpu_fixture(sys_dir, id, model_name)
126 {
127 // what's called in the base class is not the right "init_info" method, so
128 // need to call this again.
129 init_info(model_name);
130 }
131
132 void linux_power_cpu_fixture::set_physical_id(std::string const& physical_id)
133 {
134 linux_cpu_fixture::set_physical_id(physical_id);
135
136 // Power's /proc/cpuinfo file does not have physical id entries
137 // so we need to erase the one written in the base class
138 erase_info("physical id");
139 }
140
141 void linux_power_cpu_fixture::set_speed(int64_t speed)
142 {
143 linux_cpu_fixture::set_speed(speed);
144 _info["clock"] = to_string(speed) + "MHz";
145 }
146
147 void linux_power_cpu_fixture::init_info(std::string const& model_name)
148 {
149 _info = map<string, string, key_cmp>(power_cpu_info_key_cmp);
150
151 _info["cpu"] = model_name;
152 _info["revision"] = "2.1 (pvr 004b 0201)";
153 }
154
155
156 linux_processor_fixture::linux_processor_fixture(std::string const& root, test_linux_processor_resolver::ArchitectureType arch_type)
157 : _next_id(0), _root(fs::path(root))
158 {
159 fs::create_directory(_root);
160
161 _proc = _root / "proc";
162 _sys = _root / "sys" / "devices" / "system" / "cpu";
163
164 reset(arch_type);
165 }
166
167 linux_processor_fixture::~linux_processor_fixture()
168 {
169 fs::remove_all(_root);
170 }
171
172 int linux_processor_fixture::add_cpu(std::string const& model_name)
173 {
174 _cpus.push_back(unique_ptr<linux_cpu_fixture>(make_cpu(_sys, _next_id, model_name)));
175 return _next_id++;
176 }
177
178 linux_cpu_fixture& linux_processor_fixture::get_cpu(int id)
179 {
180 return *(_cpus[id]);
181 }
182
183 void linux_processor_fixture::write_cpuinfo()
184 {
185 vector<string> cpu_infos;
186 for (auto& cpu : _cpus) {
187 cpu_infos.push_back(cpu->get_info());
188 }
189
190 write_value(_proc / "cpuinfo", boost::algorithm::join(cpu_infos, "\n\n"));
191 }
192
193 void linux_processor_fixture::add_to_sys_dir(std::string const& dir_name)
194 {
195 fs::create_directory(_sys / dir_name);
196 }
197
198 void linux_processor_fixture::clear_sys_dir()
199 {
200 reset_directory(_sys);
201 }
202
203 void linux_processor_fixture::reset(test_linux_processor_resolver::ArchitectureType arch_type)
204 {
205 clear_sys_dir();
206 reset_directory(_proc);
207
208 _cpus = std::vector<std::unique_ptr<linux_cpu_fixture>>();
209 _next_id = 0;
210
211 make_cpu = [arch_type](fs::path const& sys_dir, int id, std::string const& model_name) {
212 return (arch_type == architecture_type::X86) ?
213 new linux_cpu_fixture(sys_dir, id, model_name)
214 : new linux_power_cpu_fixture(sys_dir, id, model_name);
215 };
216 }
217
218
219 test_linux_processor_resolver::test_linux_data test_linux_processor_resolver::collect_cpu_data(std::string const& root)
220 {
221 test_linux_data data;
222 add_cpu_data(data, root);
223 return data;
224 }
225
226 test_linux_processor_resolver::ArchitectureType test_linux_processor_resolver::architecture_type(test_linux_data const& data, std::string const& root)
227 {
228 return facter::facts::linux::processor_resolver::architecture_type(data, root);
229 }
230
231 std::vector<int> setup_linux_processor_fixture(linux_processor_fixture& fixture, std::vector<std::tuple<std::string, std::string, std::string, boost::optional<int64_t>>> const& cpu_params)
232 {
233 std::vector<int> ids;
234
235 for (auto& cpu_param : cpu_params) {
236 auto& model_name = get<0>(cpu_param);
237 auto& logical_id = get<1>(cpu_param);
238 auto& physical_id = get<2>(cpu_param);
239 auto& speed = get<3>(cpu_param);
240
241 int id = fixture.add_cpu(model_name);
242 auto& cpu = fixture.get_cpu(id);
243 cpu.set_logical_id(logical_id);
244 cpu.set_physical_id(physical_id);
245 if (speed) {
246 cpu.set_speed(speed.get());
247 }
248 ids.push_back(id);
249 }
250
251 // create garbage directories to ensure that only the cpu<x> directories
252 // are checked, where x >= 0
253 fixture.add_to_sys_dir("cpuabc");
254 fixture.add_to_sys_dir("cpugarbage");
255
256 return ids;
257 }
+0
-77
lib/tests/facts/linux/processor_fixture.hpp less more
0 #pragma once
1
2 #include <internal/facts/linux/processor_resolver.hpp>
3 #include <map>
4 #include <functional>
5 #include <vector>
6 #include <tuple>
7 #include <memory>
8 #include <boost/filesystem.hpp>
9 #include <boost/optional.hpp>
10 #include <boost/nowide/iostream.hpp>
11 #include <boost/nowide/fstream.hpp>
12
13 struct linux_cpu_fixture
14 {
15 explicit linux_cpu_fixture(boost::filesystem::path const& sys_dir, int id, std::string const& model_name);
16
17 void set_logical_id(std::string const& logical_id);
18 virtual void set_physical_id(std::string const& physical_id);
19 virtual void set_speed(int64_t speed);
20 void set_info(std::string const& key, std::string const& value);
21 void erase_info(std::string const& key);
22 void erase_all_info();
23 std::string get_info();
24
25 protected:
26 virtual void init_info(std::string const& model_name);
27
28 std::map<std::string, std::string, std::function<bool(std::string const&, std::string const&)>> _info;
29
30 boost::filesystem::path _cpuroot;
31 boost::filesystem::path _topology;
32 boost::filesystem::path _cpufreq;
33 };
34
35 struct linux_power_cpu_fixture : linux_cpu_fixture
36 {
37 explicit linux_power_cpu_fixture(boost::filesystem::path const& sys_dir, int id, std::string const& model_name);
38
39 virtual void set_physical_id(std::string const& physical_id) override;
40 virtual void set_speed(int64_t speed) override;
41
42 protected:
43 virtual void init_info(std::string const& model_name) override;
44 };
45
46 struct test_linux_processor_resolver : facter::facts::linux::processor_resolver
47 {
48 using facter::facts::linux::processor_resolver::ArchitectureType;
49 struct test_linux_data : facter::facts::resolvers::processor_resolver::data {};
50 test_linux_data collect_cpu_data(std::string const& root);
51 ArchitectureType architecture_type(test_linux_data const& data, std::string const& root);
52 };
53
54 struct linux_processor_fixture
55 {
56 explicit linux_processor_fixture(std::string const& root, test_linux_processor_resolver::ArchitectureType arch_type);
57 ~linux_processor_fixture();
58 int add_cpu(std::string const& model_name);
59 linux_cpu_fixture& get_cpu(int id);
60 void write_cpuinfo();
61 void add_to_sys_dir(std::string const& dir_name);
62 void clear_sys_dir();
63 void reset(test_linux_processor_resolver::ArchitectureType arch_type = test_linux_processor_resolver::ArchitectureType::X86);
64
65 private:
66 std::vector<std::unique_ptr<linux_cpu_fixture>> _cpus;
67 std::function<linux_cpu_fixture*(boost::filesystem::path const&, int, std::string const&)> make_cpu;
68 int _next_id;
69
70 boost::filesystem::path _root;
71 boost::filesystem::path _proc;
72 boost::filesystem::path _sys;
73 };
74
75 // a cpu is defined as (Model name, Logical id, Physical id, (Optional) Speed)
76 std::vector<int> setup_linux_processor_fixture(linux_processor_fixture& fixture, std::vector<std::tuple<std::string, std::string, std::string, boost::optional<int64_t>>> const& cpu_params);
+0
-348
lib/tests/facts/linux/processor_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/linux/filesystem_resolver.hpp>
2 #include <boost/filesystem.hpp>
3 #include <array>
4 #include "processor_fixture.hpp"
5
6 using namespace std;
7 using namespace facter::facts::linux;
8 using cpu_param = tuple<string, string, string, boost::optional<int64_t>>;
9 using architecture_type = test_linux_processor_resolver::ArchitectureType;
10 using test_data = test_linux_processor_resolver::test_linux_data;
11 namespace fs = boost::filesystem;
12
13 SCENARIO("determing the architecture of a linux machine") {
14 test_linux_processor_resolver resolver;
15 GIVEN("that the isa fact was successfully calculated") {
16 WHEN("it starts with ppc64") {
17 THEN("POWER is returned for the machine's architecture") {
18 test_data data;
19 array<string, 3> inputs{{"ppc64", "ppc64el", "ppc64le"}};
20 for (auto& input : inputs) {
21 data.isa = input;
22 REQUIRE(resolver.architecture_type(data, "non-existent-root") == architecture_type::POWER);
23 }
24 }
25 }
26 WHEN("it does not start with ppc64") {
27 THEN("X86 is returned for the machine's architecture") {
28 test_data data;
29 array<string, 3> inputs{{"x86_64", "i386", "amd64"}};
30 for (auto& input : inputs) {
31 data.isa = input;
32 REQUIRE(resolver.architecture_type(data, "non-existent-root") == architecture_type::X86);
33 }
34 }
35 }
36 }
37 GIVEN("that the isa fact was not successfully calculated") {
38 string root_dir = fs::unique_path("temp_processor_resolver_root%%%%-%%%%-%%%%-%%%%").string();
39 linux_processor_fixture fixture(root_dir, architecture_type::X86);
40 WHEN("the /proc/cpuinfo file has the x86 structure") {
41 THEN("X86 is returned for the machine's architecture") {
42 fixture.reset();
43 vector<cpu_param> cpu_params({
44 make_tuple("Model A", "0", "0", 10),
45 make_tuple("Model B", "1", "1", 15),
46 });
47 setup_linux_processor_fixture(fixture, cpu_params);
48 fixture.write_cpuinfo();
49
50 test_data data;
51 REQUIRE(resolver.architecture_type(data, "non-existent-root") == architecture_type::X86);
52 }
53 }
54 WHEN("the proc/cpu/info file almost has the power structure") {
55 THEN("X86 is returned for the machine's architecture") {
56 fixture.reset(architecture_type::POWER);
57 vector<cpu_param> cpu_params({
58 make_tuple("Model A", "0", "0", 10),
59 make_tuple("Model B", "1", "1", 15),
60 make_tuple("Model C", "2", "2", 15),
61 make_tuple("Model D", "3", "3", 15)
62 });
63
64 vector<int> ids = setup_linux_processor_fixture(fixture, cpu_params);
65 fixture.get_cpu(ids[0]).erase_info("processor");
66 fixture.get_cpu(ids[1]).erase_info("cpu");
67 fixture.get_cpu(ids[2]).erase_info("clock");
68 fixture.get_cpu(ids[3]).erase_info("revision");
69 fixture.write_cpuinfo();
70
71 test_data data;
72 REQUIRE(resolver.architecture_type(data, root_dir) == architecture_type::X86);
73 }
74 }
75 WHEN("the proc/cpu/info file has the power structure") {
76 THEN("POWER is returned for the machine's architecture") {
77 fixture.reset(architecture_type::POWER);
78 vector<cpu_param> cpu_params({
79 make_tuple("Model A", "0", "0", 10),
80 make_tuple("Model B", "1", "1", 15),
81 make_tuple("Model C", "2", "2", 15),
82 make_tuple("Model D", "3", "3", 15)
83 });
84 vector<int> ids = setup_linux_processor_fixture(fixture, cpu_params);
85 fixture.write_cpuinfo();
86
87 test_data data;
88 REQUIRE(resolver.architecture_type(data, root_dir) == architecture_type::POWER);
89 }
90 }
91 }
92 }
93
94 SCENARIO("resolving processor-specific facts for linux machines") {
95 string root_dir = fs::unique_path("temp_processor_resolver_root%%%%-%%%%-%%%%-%%%%").string();
96 linux_processor_fixture fixture(root_dir, architecture_type::X86);
97 test_linux_processor_resolver resolver;
98
99 GIVEN("an x86 architecture") {
100 WHEN("/sys/devices/system/cpu contains cpu information and /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq exists") {
101 THEN("the processor facts are correctly resolved, and the speed is set to cpu0's speed") {
102 fixture.reset();
103
104 // here, speed is in KHz
105 vector<cpu_param> cpu_params({
106 make_tuple("Model A", "0", "0", 10),
107 // ensure that duplicate CPUs are not counted twice in the
108 // physical count
109 make_tuple("Model B", "1", "0", 15),
110 make_tuple("Model C", "2", "1", 20),
111 // ensure that some arbitrary string is also recognized as a valid physical cpu
112 make_tuple("Model D", "3", "some physical id", 35),
113 // ensure that empty CPU ids are recognized as a valid physical cpu
114 make_tuple("Model E", "4", "", 35)
115 });
116 setup_linux_processor_fixture(fixture, cpu_params);
117 fixture.write_cpuinfo();
118 auto result = resolver.collect_cpu_data(root_dir);
119
120 REQUIRE(result.logical_count == 5);
121 REQUIRE(result.physical_count == 4);
122
123 array<string, 5> EXPECTED_MODELS{{"Model A", "Model B", "Model C", "Model D", "Model E"}};
124 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
125 for (size_t i = 0; i < result.models.size(); ++i) {
126 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
127 }
128
129 REQUIRE(result.speed == 10000);
130 }
131 }
132
133 WHEN("/sys/devices/system/cpu contains cpu information and /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq does not exist") {
134 THEN("the processor facts are correctly resolved, but the speed is not calculated") {
135 fixture.reset();
136 // here, speed is in KHz
137 vector<cpu_param> cpu_params({
138 make_tuple("Model A", "0", "0", boost::optional<int64_t>()),
139 // ensure that duplicate CPUs are not counted twice in the
140 // physical count
141 make_tuple("Model B", "1", "0", 15),
142 make_tuple("Model C", "2", "1", 20),
143 // ensure that some arbitrary string is also recognized as a valid physical cpu
144 make_tuple("Model D", "3", "some physical id", 35),
145 // ensure that empty CPU ids are recognized as a valid physical cpu
146 make_tuple("Model E", "4", "", 35)
147 });
148
149 setup_linux_processor_fixture(fixture, cpu_params);
150 fixture.write_cpuinfo();
151 auto result = resolver.collect_cpu_data(root_dir);
152
153 REQUIRE(result.logical_count == 5);
154 REQUIRE(result.physical_count == 4);
155
156 array<string, 5> EXPECTED_MODELS{{"Model A", "Model B", "Model C", "Model D", "Model E"}};
157 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
158 for (size_t i = 0; i < result.models.size(); ++i) {
159 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
160 }
161
162 REQUIRE(result.speed == 0);
163 }
164 }
165
166 WHEN("/sys/devices/system/cpu does not contain cpu information") {
167 THEN("the processor facts are correctly resolved, with the logical and physical counts are obtained from /proc/cpuinfo") {
168 fixture.reset();
169
170 // here, speed is in KHz
171 vector<cpu_param> cpu_params({
172 make_tuple("Model A", "0", "0", boost::optional<int64_t>()),
173 // ensure that duplicate CPUs are not counted twice in the
174 // physical count
175 make_tuple("Model B", "1", "0", 15),
176 make_tuple("Model C", "2", "1", 20),
177 make_tuple("Model D", "3", "2", 35),
178 // ensure that a CPU with an empty logical ID's model name
179 // is not collected
180 make_tuple("Model E", "", "3", 35)
181 });
182 setup_linux_processor_fixture(fixture, cpu_params);
183 fixture.write_cpuinfo();
184 fixture.clear_sys_dir();
185
186 // for this test, we want to ensure that the processor facts are obtained from
187 // /proc/cpuinfo only.
188 REQUIRE(fs::is_empty(fs::path(root_dir) / "sys" / "devices" / "system" / "cpu"));
189 auto result = resolver.collect_cpu_data(root_dir);
190
191 REQUIRE(result.logical_count == 5);
192 REQUIRE(result.physical_count == 4);
193
194 array<string, 4> EXPECTED_MODELS{{"Model A", "Model B", "Model C", "Model D"}};
195 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
196 for (size_t i = 0; i < result.models.size(); ++i) {
197 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
198 }
199
200 REQUIRE(result.speed == 0);
201 }
202 }
203
204 WHEN("/sys/devices/system/cpu contains cpu information but some cpus are offline") {
205 THEN("the processor facts are correctly resolved for online cpus") {
206 fixture.reset();
207
208 // here, speed is in KHz
209 vector<cpu_param> cpu_params({
210 make_tuple("Model A", "0", "0", 10),
211 // ensure that duplicate CPUs are not counted twice in the
212 // physical count
213 make_tuple("Model B", "1", "0", 15),
214 make_tuple("Model C", "2", "1", 20),
215 make_tuple("Model D", "3", "2", 35),
216 make_tuple("Model E", "4", "3", 35)
217 });
218 vector<int> ids = setup_linux_processor_fixture(fixture, cpu_params);
219 fixture.write_cpuinfo();
220
221 // for this test, we want to ensure that Model D and Model E cpus are disabled, meaning the last 2 cpus will not have the topology folder
222 int number_cpu_to_disable = 2;
223 vector<cpu_param> disabled_cpu_params(cpu_params.end() - number_cpu_to_disable, cpu_params.end());
224
225 for (auto& cpu_param : disabled_cpu_params) {
226 string logical_id = get<1>(cpu_param);
227 // Remove the topology foder for offline cpus
228 string topology_folder = root_dir + "/sys/devices/system/cpu"+ "/cpu"+logical_id + "/topology";
229 fs::remove_all(topology_folder);
230 }
231
232 auto result = resolver.collect_cpu_data(root_dir);
233
234 // logical and physical count should count cpus without offline cpus
235 REQUIRE(result.logical_count == 3);
236 REQUIRE(result.physical_count == 2);
237 }
238 }
239 }
240 GIVEN("a power architecture") {
241 WHEN("/sys/devices/system/cpu contains cpu information and /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq exists") {
242 THEN("the processor facts are correctly resolved, with the speed being read from the 'clock' entry") {
243 fixture.reset(architecture_type::POWER);
244
245 // here, speed is in KHz
246 vector<cpu_param> cpu_params({
247 // ensure that speed is read from the "clock" entry
248 make_tuple("Model A", "0", "0", boost::optional<int64_t>()),
249 // ensure that duplicate CPUs are not counted twice in the
250 // physical count
251 make_tuple("Model B", "1", "0", 15),
252 make_tuple("Model C", "2", "1", 20),
253 // ensure that some arbitrary string is also recognized as a valid physical cpu
254 make_tuple("Model D", "3", "some physical id", 35),
255 // ensure that empty CPU ids are recognized as a valid physical cpu
256 make_tuple("Model E", "4", "", 35),
257 // ensure that negative ids are not included in the physical count
258 // this entry simulates an invalid power cpu
259 make_tuple("Invalid CPU", "5", "-1", 35),
260 });
261 vector<int> ids = setup_linux_processor_fixture(fixture, cpu_params);
262 fixture.get_cpu(ids.back()).erase_all_info();
263 fixture.write_cpuinfo();
264 auto result = resolver.collect_cpu_data(root_dir);
265
266 REQUIRE(result.logical_count == 5);
267 REQUIRE(result.physical_count == 4);
268
269 array<string, 5> EXPECTED_MODELS{{"Model A", "Model B", "Model C", "Model D", "Model E"}};
270 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
271 for (size_t i = 0; i < result.models.size(); ++i) {
272 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
273 }
274
275 REQUIRE(result.speed == 15000);
276 }
277 }
278
279 WHEN("/sys/devices/system/cpu contains cpu information but cpu0 is invalid") {
280 THEN("the processor facts are correctly resolved, but the speed is not calculated") {
281 fixture.reset(architecture_type::POWER);
282
283 // here, speed is in KHz
284 vector<cpu_param> cpu_params({
285 make_tuple("Model A", "0", "-1", 15),
286 make_tuple("Model B", "0", "-1", -1)
287 });
288 vector<int> ids = setup_linux_processor_fixture(fixture, cpu_params);
289 fixture.get_cpu(ids.front()).erase_all_info();
290 fixture.write_cpuinfo();
291
292 auto result = resolver.collect_cpu_data(root_dir);
293
294 REQUIRE(result.logical_count == 1);
295 // /proc/cpuinfo does not have any "physical_id" entries for power architectures
296 REQUIRE(result.physical_count == 0);
297
298 array<string, 1> EXPECTED_MODELS{{"Model B"}};
299 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
300 for (size_t i = 0; i < result.models.size(); ++i) {
301 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
302 }
303
304 REQUIRE(result.speed == 0);
305 }
306 }
307
308 WHEN("/sys/devices/system/cpu does not contain cpu information") {
309 THEN("the processor facts are correctly resolved, with the speed being read from the 'clock' entry, but the physical count is not computed") {
310 fixture.reset(architecture_type::POWER);
311
312 // here, speed is in KHz
313 vector<cpu_param> cpu_params({
314 make_tuple("Model A", "0", "0", boost::optional<int64_t>()),
315 // ensure that duplicate CPUs are not counted twice in the
316 // physical count
317 make_tuple("Model B", "1", "0", 15),
318 make_tuple("Model C", "2", "1", 20),
319 make_tuple("Model D", "3", "2", 35),
320 // ensure that a CPU with an empty logical ID's model name
321 // is not collected
322 make_tuple("Model E", "", "3", 35)
323 });
324 setup_linux_processor_fixture(fixture, cpu_params);
325 fixture.write_cpuinfo();
326 fixture.clear_sys_dir();
327
328 // for this test, we want to ensure that the processor facts are obtained from
329 // /proc/cpuinfo only.
330 REQUIRE(fs::is_empty(fs::path(root_dir) / "sys" / "devices" / "system" / "cpu"));
331 auto result = resolver.collect_cpu_data(root_dir);
332
333 REQUIRE(result.logical_count == 5);
334 // /proc/cpuinfo does not have any "physical_id" entries for power architectures
335 REQUIRE(result.physical_count == 0);
336
337 array<string, 4> EXPECTED_MODELS{{"Model A", "Model B", "Model C", "Model D"}};
338 REQUIRE(result.models.size() == EXPECTED_MODELS.size());
339 for (size_t i = 0; i < result.models.size(); ++i) {
340 REQUIRE(result.models[i] == EXPECTED_MODELS[i]);
341 }
342
343 REQUIRE(result.speed == 15000);
344 }
345 }
346 }
347 }
+0
-48
lib/tests/facts/linux/virtualization_resolver.cc less more
0 #include <catch.hpp>
1 #include <facter/util/string.hpp>
2 #include <internal/facts/linux/virtualization_resolver.hpp>
3 #include "../../fixtures.hpp"
4 #include <iostream>
5
6 using namespace std;
7 using namespace facter::util;
8 using namespace facter::facts;
9 using namespace facter::facts::resolvers;
10 using namespace facter::testing;
11
12 struct peek_resolver : linux::virtualization_resolver
13 {
14 using virtualization_resolver::get_azure_from_leases_file;
15 };
16
17 SCENARIO("azure") {
18 collection_fixture facts;
19
20 WHEN("leases file does not exist") {
21 auto result = peek_resolver::get_azure_from_leases_file("does-not-exist");
22 THEN("azure is empty") {
23 REQUIRE(result == "");
24 }
25 }
26
27 WHEN("leases file contains 'option 245'") {
28 auto result = peek_resolver::get_azure_from_leases_file(string(LIBFACTER_TESTS_DIRECTORY) + "/fixtures/facts/linux/cloud/azure");
29 THEN("it reports azure") {
30 REQUIRE(result == "azure");
31 }
32 }
33
34 WHEN("leases file contains 'option unknown-245'") {
35 auto result = peek_resolver::get_azure_from_leases_file(string(LIBFACTER_TESTS_DIRECTORY) + "/fixtures/facts/linux/cloud/azure-unknown");
36 THEN("it reports azure") {
37 REQUIRE(result == "azure");
38 }
39 }
40
41 WHEN("leases file does not contain correct option") {
42 auto result = peek_resolver::get_azure_from_leases_file(string(LIBFACTER_TESTS_DIRECTORY) + "/fixtures/facts/linux/cloud/not-azure");
43 THEN("it does not report azure") {
44 REQUIRE(result == "");
45 }
46 }
47 }
+0
-116
lib/tests/facts/map_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/map_value.hpp>
2 #include <facter/facts/array_value.hpp>
3 #include <facter/facts/scalar_value.hpp>
4 #include <rapidjson/document.h>
5 #include <yaml-cpp/yaml.h>
6 #include <sstream>
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace rapidjson;
11 using namespace YAML;
12
13 SCENARIO("using a map fact value") {
14 map_value value;
15 REQUIRE(value.empty());
16 GIVEN("a null value to add") {
17 value.add("key", nullptr);
18 THEN("it should still be empty") {
19 REQUIRE(value.empty());
20 }
21 }
22 GIVEN("elements to insert") {
23 value.add("string", make_value<string_value>("hello"));
24 value.add("integer", make_value<integer_value>(5));
25 auto array_element = make_value<array_value>();
26 array_element->add(make_value<string_value>("1"));
27 array_element->add(make_value<integer_value>(2));
28 value.add("array", move(array_element));
29 auto map_element = make_value<map_value>();
30 map_element->add("foo", make_value<string_value>("bar"));
31 value.add("map", move(map_element));
32
33 THEN("it should contain the elements that were added") {
34 REQUIRE(value.size() == 4u);
35 auto str = value.get<string_value>("string");
36 REQUIRE(str);
37 REQUIRE(str->value() == "hello");
38
39 auto integer = value.get<integer_value>("integer");
40 REQUIRE(integer);
41 REQUIRE(integer->value() == 5);
42
43 auto array = value.get<array_value>("array");
44 REQUIRE(array);
45 REQUIRE(array->size() == 2u);
46 str = array->get<string_value>(0);
47 REQUIRE(str);
48 REQUIRE(str->value() == "1");
49 integer = array->get<integer_value>(1);
50 REQUIRE(integer);
51 REQUIRE(integer->value() == 2);
52
53 auto mapval = value.get<map_value>("map");
54 REQUIRE(mapval);
55 REQUIRE(mapval->size() == 1u);
56 str = mapval->get<string_value>("foo");
57 REQUIRE(str);
58 REQUIRE(str->value() == "bar");
59 }
60 THEN("elements should be in sort order") {
61 int index = 0;
62 value.each([&](string const& name, struct value const* val) {
63 if (index == 0) {
64 REQUIRE(name == "array");
65 } else if (index == 1) {
66 REQUIRE(name == "integer");
67 } else if (index == 2) {
68 REQUIRE(name == "map");
69 } else if (index == 3) {
70 REQUIRE(name == "string");
71 } else {
72 FAIL("should not be reached");
73 return false;
74 }
75 ++index;
76 return true;
77 });
78 }
79 WHEN("serialized to JSON") {
80 THEN("it should contain the same values") {
81 json_value json;
82 json_allocator allocator;
83 value.to_json(allocator, json);
84 REQUIRE(json.IsObject());
85 REQUIRE(json["string"].IsString());
86 REQUIRE(string(json["string"].GetString()) == "hello");
87 REQUIRE(json["integer"].IsNumber());
88 REQUIRE(json["integer"].GetInt64() == 5);
89 REQUIRE(json["array"].IsArray());
90 REQUIRE(json["array"].Size() == 2);
91 REQUIRE(json["array"][0u].IsString());
92 REQUIRE(string(json["array"][0u].GetString()) == "1");
93 REQUIRE(json["array"][1u].IsNumber());
94 REQUIRE(json["array"][1u].GetInt64() == 2);
95 REQUIRE(json["map"].IsObject());
96 REQUIRE(json["map"]["foo"].IsString());
97 REQUIRE(string(json["map"]["foo"].GetString()) == "bar");
98 }
99 }
100 WHEN("serialized to text") {
101 THEN("it should contain the same values") {
102 ostringstream stream;
103 value.write(stream);
104 REQUIRE(stream.str() == "{\n array => [\n \"1\",\n 2\n ],\n integer => 5,\n map => {\n foo => \"bar\"\n },\n string => \"hello\"\n}");
105 }
106 }
107 WHEN("serialized to text") {
108 THEN("it should contain the same values") {
109 Emitter emitter;
110 value.write(emitter);
111 REQUIRE(string(emitter.c_str()) == "array:\n - \"1\"\n - 2\ninteger: 5\nmap:\n foo: bar\nstring: hello");
112 }
113 }
114 }
115 }
+0
-39
lib/tests/facts/posix/collection.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/resolver.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include "../../fixtures.hpp"
7 #include <sstream>
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::testing;
12
13 SCENARIO("resolving external executable facts into a collection") {
14 collection_fixture facts;
15 REQUIRE(facts.size() == 0u);
16 GIVEN("an absolute path") {
17 facts.add_external_facts({
18 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution",
19 });
20 THEN("facts should resolve") {
21 REQUIRE(facts.size() == 4u);
22 REQUIRE(facts.get<string_value>("exe_fact1"));
23 REQUIRE(facts.get<string_value>("exe_fact2"));
24 REQUIRE_FALSE(facts.get<string_value>("exe_fact3"));
25 REQUIRE(facts.get<string_value>("exe_fact4"));
26 REQUIRE(facts.get<string_value>("foo"));
27 }
28 }
29 GIVEN("a relative path") {
30 test_with_relative_path fixture("foo", "bar", "#! /usr/bin/env sh\necho local_exec_fact=value");
31 facts.add_external_facts({ "foo" });
32 THEN("facts should resolve") {
33 REQUIRE(facts.size() == 1u);
34 REQUIRE(facts.get<string_value>("local_exec_fact"));
35 REQUIRE(facts.get<string_value>("local_exec_fact")->value() == "value");
36 }
37 }
38 }
+0
-27
lib/tests/facts/posix/external_resolvers_factory.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/external_resolvers_factory.hpp>
2 #include "../../fixtures.hpp"
3
4 using namespace std;
5 using namespace facter::facts;
6
7 SCENARIO("checking external file posix resolvers factory") {
8 external_resolvers_factory erf;
9 GIVEN("a non-executable file") {
10 THEN("the file cannot be resolved") {
11 REQUIRE_THROWS_AS(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution/not_executable"),
12 external::external_fact_no_resolver&);
13 }
14 }
15 GIVEN("a executable file") {
16 THEN("the file can be resolved") {
17 REQUIRE(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/posix/execution/facts"));
18 }
19 }
20 GIVEN("a relative path not on PATH") {
21 THEN("the file cannot be resolved") {
22 REQUIRE_THROWS_AS(erf.get_resolver("foo/bar.bat"),
23 external::external_fact_no_resolver&);
24 }
25 }
26 }
+0
-52
lib/tests/facts/posix/uptime_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/posix/uptime_resolver.hpp>
2 #include <map>
3
4 using namespace std;
5 using namespace facter::facts;
6
7 SCENARIO("parsing uptime formats") {
8 WHEN("given an uptime to parse") {
9 static const map<string, int> test_cases = {
10 {" 4:42pm up 1 min(s), 0 users, load average: 0.95, 0.25, 0.09", 1*60},
11 {"13:16 up 58 mins, 2 users, load average: 0.00, 0.02, 0.05", 58*60},
12 {"13:18 up 1 hr, 1 user, load average: 0.58, 0.23, 0.14", 1*60*60 },
13 {" 10:14pm up 3 hr(s), 0 users, load average: 0.00, 0.00, 0.00", 3*60*60 },
14 {"14:18 up 2 hrs, 0 users, load average: 0.33, 0.27, 0.29", 2*60*60 },
15 {" 9:01pm up 1:47, 0 users, load average: 0.00, 0.00, 0.00", 1*60*60 + 47*60},
16 {"13:19 up 1:01, 1 user, load average: 0.10, 0.26, 0.21", 1*60*60 + 1*60},
17 {"10:49 up 22:31, 0 users, load average: 0.26, 0.34, 0.27", 22*60*60 + 31*60},
18 {"12:18 up 1 day, 0 users, load average: 0.74, 0.20, 0.10", 1*24*60*60 },
19 {" 2:48pm up 1 day(s), 0 users, load average: 0.21, 0.20, 0.17", 1*24*60*60 },
20 {"12:18 up 2 days, 0 users, load average: 0.50, 0.27, 0.16", 2*24*60*60 },
21 {" 1:56pm up 25 day(s), 2 users, load average: 0.59, 0.56, 0.50", 25*24*60*60 },
22 {" 1:29pm up 485 days, 0 users, load average: 0.00, 0.01, 0.01", 485*24*60*60 },
23 {" 18:11:24 up 69 days, 0 min, 0 users, load average: 0.00, 0.00, 0.00", 69*24*60*60 },
24 {"12:19 up 1 day, 1 min, 0 users, load average: 0.07, 0.16, 0.13", 1*24*60*60 + 1*60},
25 {" 3:23pm up 25 day(s), 27 min(s), 2 users, load average: 0.49, 0.45, 0.46", 25*24*60*60 + 27*60},
26 {" 02:42PM up 1 day, 39 mins, 0 users, load average: 1.49, 1.74, 1.80", 1*24*60*60 + 39*60},
27 {" 18:13:13 up 245 days, 44 min, 1 user, load average: 0.00, 0.00, 0.00", 245*24*60*60 + 44*60},
28 {" 6:09pm up 350 days, 2 min, 1 user, load average: 0.02, 0.03, 0.00", 350*24*60*60 + 2*60},
29 {" 1:07pm up 174 day(s), 16 hr(s), 0 users, load average: 0.05, 0.04, 0.03", 174*24*60*60 + 16*60*60 },
30 {" 02:34PM up 621 days, 18 hrs, 0 users, load average: 2.67, 2.52, 2.56", 621*24*60*60 + 18*60*60 },
31 {" 3:30am up 108 days, 1 hr, 31 users, load average: 0.39, 0.40, 0.41", 108*24*60*60 + 1*60*60 },
32 {"13:18 up 1 day, 1 hr, 0 users, load average: 0.78, 0.33, 0.18", 1*24*60*60 + 1*60*60 },
33 {"14:18 up 1 day, 2 hrs, 0 users, load average: 1.17, 0.48, 0.41", 1*24*60*60 + 2*60*60 },
34 {"15:56 up 152 days, 17 hrs, 0 users, load average: 0.01, 0.06, 0.07", 152*24*60*60 + 17*60*60 },
35 {" 5:37pm up 25 days, 21:00, 0 users, load average: 0.01, 0.02, 0.00", 25*24*60*60 + 21*60*60 },
36 {" 8:59pm up 94 day(s), 3:17, 46 users, load average: 0.66, 0.67, 0.70", 94*24*60*60 + 3*60*60 + 17*60},
37 {" 3:01pm up 4496 day(s), 21:19, 32 users, load average: 0.61, 0.62, 0.62", 4496*24*60*60 + 21*60*60 + 19*60},
38 {" 02:42PM up 41 days, 2:38, 0 users, load average: 0.38, 0.70, 0.55", 41*24*60*60 + 2*60*60 + 38*60},
39 {" 18:13:29 up 25 days, 21:36, 0 users, load average: 0.00, 0.00, 0.00", 25*24*60*60 + 21*60*60 + 36*60},
40 {" 13:36:05 up 118 days, 1:15, 1 user, load average: 0.00, 0.00, 0.00", 118*24*60*60 + 1*60*60 + 15*60},
41 {"10:27am up 1 day 7:26, 1 user, load average: 0.00, 0.00, 0.00", 1*24*60*60 + 7*60*60 + 26*60},
42 {"22:45pm up 0:-6, 1 user, load average: 0.00, 0.00, 0.00", 6*60},
43 {"22:45pm up 1 day 0:-6, 1 user, load average: 0.00, 0.00, 0.00", 1*24*60*60 + 6*60}
44 };
45 THEN("it parses each format correctly") {
46 for (auto const& t : test_cases) {
47 REQUIRE(posix::uptime_resolver::parse_uptime(t.first) == t.second);
48 }
49 }
50 }
51 }
+0
-58
lib/tests/facts/resolvers/augeas_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/augeas_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_augeas_resolver : augeas_resolver
14 {
15 protected:
16 virtual string get_version() override
17 {
18 return {};
19 }
20 };
21
22 struct fixed_augeas_resolver : augeas_resolver
23 {
24 protected:
25 virtual string get_version() override
26 {
27 return "foo";
28 }
29 };
30
31 SCENARIO("using the augeas resolver") {
32 collection_fixture facts;
33 WHEN("no version is returned") {
34 facts.add(make_shared<empty_augeas_resolver>());
35 THEN("the fact is not present") {
36 REQUIRE(facts.size() == 0u);
37 }
38 }
39 WHEN("an augeas version is returned") {
40 facts.add(make_shared<fixed_augeas_resolver>());
41 THEN("a structured fact is returned") {
42 REQUIRE(facts.size() == 2u);
43 auto augeas = facts.get<map_value>(fact::augeas);
44 REQUIRE(augeas);
45 REQUIRE(augeas->size() == 1u);
46 auto version = augeas->get<string_value>("version");
47 REQUIRE(version);
48 REQUIRE(version->value() == "foo");
49 }
50 THEN("flat facts are added") {
51 REQUIRE(facts.size() == 2u);
52 auto augeasversion = facts.get<string_value>(fact::augeasversion);
53 REQUIRE(augeasversion);
54 REQUIRE(augeasversion->value() == "foo");
55 }
56 }
57 }
+0
-114
lib/tests/facts/resolvers/disk_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/disk_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct test_disk_resolver : disk_resolver
14 {
15 void add_disk(string name, string vendor, string model, string product, uint64_t size)
16 {
17 disk d;
18 d.name = move(name);
19 d.vendor = move(vendor);
20 d.model = move(model);
21 d.product = move(product);
22 d.size = size;
23 disks.emplace_back(move(d));
24 }
25
26 protected:
27 virtual data collect_data(collection& facts) override
28 {
29 data result;
30 result.disks = move(disks);
31 return result;
32 }
33
34 private:
35 vector<disk> disks;
36 };
37
38 SCENARIO("using the disk resolver") {
39 collection_fixture facts;
40 auto resolver = make_shared<test_disk_resolver>();
41 facts.add(resolver);
42 GIVEN("no disks present") {
43 THEN("facts should not be added") {
44 REQUIRE(facts.size() == 0u);
45 }
46 }
47 GIVEN("five present disks") {
48 const unsigned int count = 5;
49 for (unsigned int i = 0; i < count; ++i) {
50 string num = to_string(i);
51 resolver->add_disk("disk" + num, "vendor" + num, "model" + num, "product" + num, 12345 + i);
52 }
53 THEN("a structured fact should be added") {
54 auto disks = facts.get<map_value>(fact::disks);
55 REQUIRE(disks);
56 REQUIRE(disks->size() == count);
57
58 for (unsigned int i = 0; i < count; ++i) {
59 string num = to_string(i);
60
61 auto disk = disks->get<map_value>("disk" + num);
62 REQUIRE(disk);
63 REQUIRE(disk->size() == 5u);
64
65 auto model = disk->get<string_value>("model");
66 REQUIRE(model);
67 REQUIRE(model->value() == "model" + num);
68
69 auto product = disk->get<string_value>("product");
70 REQUIRE(product);
71 REQUIRE(product->value() == "product" + num);
72
73 auto size = disk->get<string_value>("size");
74 REQUIRE(size);
75 REQUIRE(size->value() == "12.06 KiB");
76
77 auto size_bytes = disk->get<integer_value>("size_bytes");
78 REQUIRE(size_bytes);
79 REQUIRE(size_bytes->value() == 12345 + i);
80
81 auto vendor = disk->get<string_value>("vendor");
82 REQUIRE(vendor);
83 REQUIRE(vendor->value() == "vendor" + num);
84 }
85 }
86 THEN("flat facts should be added") {
87 string names;
88 for (unsigned int i = 0; i < count; ++i) {
89 string num = to_string(i);
90
91 auto model = facts.get<string_value>("blockdevice_disk" + num + "_model");
92 REQUIRE(model);
93 REQUIRE(model->value() == "model" + num);
94
95 auto size = facts.get<integer_value>("blockdevice_disk" + num + "_size");
96 REQUIRE(size);
97 REQUIRE(size->value() == 12345 + i);
98
99 auto vendor = facts.get<string_value>("blockdevice_disk" + num + "_vendor");
100 REQUIRE(vendor);
101 REQUIRE(vendor->value() == "vendor" + num);
102
103 if (names.size() > 0u) {
104 names += ",";
105 }
106 names += "disk" + num;
107 }
108 auto devices = facts.get<string_value>(fact::block_devices);
109 REQUIRE(devices);
110 REQUIRE(devices->value() == names);
111 }
112 }
113 }
+0
-152
lib/tests/facts/resolvers/dmi_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/dmi_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_dmi_resolver : dmi_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_dmi_resolver : dmi_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.bios_vendor = fact::bios_vendor;
29 result.bios_version = fact::bios_version;
30 result.bios_release_date = fact::bios_release_date;
31 result.board_asset_tag = fact::board_asset_tag;
32 result.board_manufacturer = fact::board_manufacturer;
33 result.board_product_name = fact::board_product_name;
34 result.board_serial_number = fact::board_serial_number;
35 result.chassis_asset_tag = fact::chassis_asset_tag;
36 result.manufacturer = fact::manufacturer;
37 result.product_name = fact::product_name;
38 result.serial_number = fact::serial_number;
39 result.uuid = fact::uuid;
40 result.chassis_type = fact::chassis_type;
41 return result;
42 }
43 };
44
45 SCENARIO("using the DMI resolver") {
46 collection_fixture facts;
47 WHEN("data is not present") {
48 facts.add(make_shared<empty_dmi_resolver>());
49 THEN("facts should not be added") {
50 REQUIRE(facts.size() == 0u);
51 }
52 }
53 WHEN("data is present") {
54 facts.add(make_shared<test_dmi_resolver>());
55 THEN("a structured fact is added") {
56 auto dmi = facts.get<map_value>(fact::dmi);
57 REQUIRE(dmi);
58 REQUIRE(dmi->size() == 5u);
59
60 auto bios = dmi->get<map_value>("bios");
61 REQUIRE(bios);
62 REQUIRE(bios->size() == 3u);
63
64 auto value = bios->get<string_value>("release_date");
65 REQUIRE(value);
66 REQUIRE(value->value() == string(fact::bios_release_date));
67
68 value = bios->get<string_value>("vendor");
69 REQUIRE(value);
70 REQUIRE(value->value() == string(fact::bios_vendor));
71
72 value = bios->get<string_value>("version");
73 REQUIRE(value);
74 REQUIRE(value->value() == string(fact::bios_version));
75
76 auto board = dmi->get<map_value>("board");
77 REQUIRE(board);
78 REQUIRE(board->size() == 4u);
79
80 value = board->get<string_value>("asset_tag");
81 REQUIRE(value);
82 REQUIRE(value->value() == string(fact::board_asset_tag));
83
84 value = board->get<string_value>("manufacturer");
85 REQUIRE(value);
86 REQUIRE(value->value() == string(fact::board_manufacturer));
87
88 value = board->get<string_value>("product");
89 REQUIRE(value);
90 REQUIRE(value->value() == string(fact::board_product_name));
91
92 value = board->get<string_value>("serial_number");
93 REQUIRE(value);
94 REQUIRE(value->value() == string(fact::board_serial_number));
95
96 auto chassis = dmi->get<map_value>("chassis");
97 REQUIRE(chassis);
98 REQUIRE(chassis->size() == 2u);
99
100 value = chassis->get<string_value>("asset_tag");
101 REQUIRE(value);
102 REQUIRE(value->value() == string(fact::chassis_asset_tag));
103
104 value = chassis->get<string_value>("type");
105 REQUIRE(value);
106 REQUIRE(value->value() == string(fact::chassis_type));
107
108 value = dmi->get<string_value>("manufacturer");
109 REQUIRE(value);
110 REQUIRE(value->value() == string(fact::manufacturer));
111
112 auto product = dmi->get<map_value>("product");
113 REQUIRE(product);
114 REQUIRE(product->size() == 3u);
115
116 value = product->get<string_value>("name");
117 REQUIRE(value);
118 REQUIRE(value->value() == string(fact::product_name));
119
120 value = product->get<string_value>("serial_number");
121 REQUIRE(value);
122 REQUIRE(value->value() == string(fact::serial_number));
123
124 value = product->get<string_value>("uuid");
125 REQUIRE(value);
126 REQUIRE(value->value() == string(fact::uuid));
127 }
128 THEN("flat facts are added") {
129 static vector<string> const names = {
130 fact::bios_vendor,
131 fact::bios_version,
132 fact::bios_release_date,
133 fact::board_asset_tag,
134 fact::board_manufacturer,
135 fact::board_product_name,
136 fact::board_serial_number,
137 fact::chassis_asset_tag,
138 fact::manufacturer,
139 fact::product_name,
140 fact::serial_number,
141 fact::uuid,
142 fact::chassis_type,
143 };
144 for (auto const& name : names) {
145 auto fact = facts.get<string_value>(name);
146 REQUIRE(fact);
147 REQUIRE(fact->value() == name);
148 }
149 }
150 }
151 }
+0
-207
lib/tests/facts/resolvers/filesystem_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/filesystem_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include <facter/facts/array_value.hpp>
7 #include "../../collection_fixture.hpp"
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::facts::resolvers;
12 using namespace facter::testing;
13
14 struct test_filesystem_resolver : filesystem_resolver
15 {
16 void add_mountpoint(string name, string device, string filesystem, uint64_t size, uint64_t available, uint64_t reserved, vector<string> options)
17 {
18 mountpoint mp;
19 mp.name = move(name);
20 mp.device = move(device);
21 mp.filesystem = move(filesystem);
22 mp.size = size;
23 mp.available = available;
24 mp.free = available + reserved;
25 mp.options = move(options);
26 mountpoints.emplace_back(move(mp));
27 }
28
29 void add_filesystem(string filesystem)
30 {
31 filesystems.emplace(move(filesystem));
32 }
33
34 void add_partition(string name, string filesystem, uint64_t size, string uuid, string partuuid, string label, string partlabel, string mount, string backing_file)
35 {
36 partition p;
37 p.name = move(name);
38 p.filesystem = move(filesystem);
39 p.size = size;
40 p.uuid = move(uuid);
41 p.partition_uuid = move(partuuid);
42 p.label = move(label);
43 p.partition_label = move(partlabel);
44 p.mount = move(mount);
45 p.backing_file = move(backing_file);
46 partitions.emplace_back(move(p));
47 }
48
49 protected:
50 virtual data collect_data(collection& facts) override
51 {
52 data result;
53 result.mountpoints = move(mountpoints);
54 result.filesystems = move(filesystems);
55 result.partitions = move(partitions);
56 return result;
57 }
58
59 vector<mountpoint> mountpoints;
60 set<string> filesystems;
61 vector<partition> partitions;
62 };
63
64 SCENARIO("using the file system resolver") {
65 collection_fixture facts;
66 auto resolver = make_shared<test_filesystem_resolver>();
67 facts.add(resolver);
68
69 WHEN("data is not present") {
70 THEN("facts should not be added") {
71 REQUIRE(facts.size() == 0u);
72 }
73 }
74 WHEN("mount point data is present") {
75 const unsigned int count = 5;
76 for (unsigned int i = 0; i < count; ++i) {
77 string num = to_string(i);
78 resolver->add_mountpoint("mount" + num, "device" + num, "filesystem" + num, 12345, 1000, 0, {"option1" + num, "option2" + num, "option3" + num});
79 }
80 THEN("a structured fact is added") {
81 REQUIRE(facts.size() == 1u);
82 auto mountpoints = facts.get<map_value>(fact::mountpoints);
83 REQUIRE(mountpoints);
84 REQUIRE(mountpoints->size() == 5u);
85 for (unsigned int i = 0; i < count; ++i) {
86 string num = to_string(i);
87
88 auto mountpoint = mountpoints->get<map_value>("mount" + num);
89 REQUIRE(mountpoint);
90 REQUIRE(mountpoint->size() == 10u);
91
92 auto available = mountpoint->get<string_value>("available");
93 REQUIRE(available);
94 REQUIRE(available->value() == "1000 bytes");
95
96 auto available_bytes = mountpoint->get<integer_value>("available_bytes");
97 REQUIRE(available_bytes);
98 REQUIRE(available_bytes->value() == 1000);
99
100 auto capacity = mountpoint->get<string_value>("capacity");
101 REQUIRE(capacity);
102 REQUIRE(capacity->value() == "91.90%");
103
104 auto device = mountpoint->get<string_value>("device");
105 REQUIRE(device);
106 REQUIRE(device->value() == "device" + num);
107
108 auto filesystem = mountpoint->get<string_value>("filesystem");
109 REQUIRE(filesystem);
110 REQUIRE(filesystem->value() == "filesystem" + num);
111
112 auto options = mountpoint->get<array_value>("options");
113 REQUIRE(options);
114 REQUIRE(options->size() == 3u);
115 REQUIRE(options->get<string_value>(0)->value() == "option1" + num);
116 REQUIRE(options->get<string_value>(1)->value() == "option2" + num);
117 REQUIRE(options->get<string_value>(2)->value() == "option3" + num);
118
119 auto size = mountpoint->get<string_value>("size");
120 REQUIRE(size);
121 REQUIRE(size->value() == "12.06 KiB");
122
123 auto size_bytes = mountpoint->get<integer_value>("size_bytes");
124 REQUIRE(size_bytes);
125 REQUIRE(size_bytes->value() == 12345);
126
127 auto used = mountpoint->get<string_value>("used");
128 REQUIRE(used);
129 REQUIRE(used->value() == "11.08 KiB");
130
131 auto used_bytes = mountpoint->get<integer_value>("used_bytes");
132 REQUIRE(used_bytes);
133 REQUIRE(used_bytes->value() == 12345 - 1000);
134 }
135 }
136 }
137 WHEN("file system data is present") {
138 resolver->add_filesystem("foo");
139 resolver->add_filesystem("bar");
140 resolver->add_filesystem("baz");
141
142 THEN("a flat fact is added") {
143 REQUIRE(facts.size() == 1u);
144 auto filesystems = facts.get<string_value>(fact::filesystems);
145 REQUIRE(filesystems);
146 REQUIRE(filesystems->value() == "bar,baz,foo");
147 }
148 }
149 WHEN("partition data is present") {
150 const unsigned int count = 5;
151 for (unsigned int i = 0; i < count; ++i) {
152 string num = to_string(i);
153 resolver->add_partition("partition" + num, "filesystem" + num, 12345 + i, "uuid" + num, "partuuid" + num, "label" + num, "partlabel" + num, "mount" + num, "file" + num);
154 }
155 THEN("a structured fact is added") {
156 REQUIRE(facts.size() == 1u);
157
158 auto partitions = facts.get<map_value>(fact::partitions);
159 REQUIRE(partitions);
160
161 for (unsigned int i = 0; i < count; ++i) {
162 string num = to_string(i);
163
164 auto partition = partitions->get<map_value>("partition" + num);
165 REQUIRE(partition);
166 REQUIRE(partition->size() == 9u);
167
168 auto filesystem = partition->get<string_value>("filesystem");
169 REQUIRE(filesystem);
170 REQUIRE(filesystem->value() == "filesystem" + num);
171
172 auto label = partition->get<string_value>("label");
173 REQUIRE(label);
174 REQUIRE(label->value() == "label" + num);
175
176 auto partlabel = partition->get<string_value>("partlabel");
177 REQUIRE(partlabel);
178 REQUIRE(partlabel->value() == "partlabel" + num);
179
180 auto mount = partition->get<string_value>("mount");
181 REQUIRE(mount);
182 REQUIRE(mount->value() == "mount" + num);
183
184 auto partuuid = partition->get<string_value>("partuuid");
185 REQUIRE(partuuid);
186 REQUIRE(partuuid->value() == "partuuid" + num);
187
188 auto uuid = partition->get<string_value>("uuid");
189 REQUIRE(uuid);
190 REQUIRE(uuid->value() == "uuid" + num);
191
192 auto size_bytes = partition->get<integer_value>("size_bytes");
193 REQUIRE(size_bytes);
194 REQUIRE(size_bytes->value() == 12345 + i);
195
196 auto size = partition->get<string_value>("size");
197 REQUIRE(size);
198 REQUIRE(size->value() == "12.06 KiB");
199
200 auto file = partition->get<string_value>("backing_file");
201 REQUIRE(file);
202 REQUIRE(file->value() == "file" + num);
203 }
204 }
205 }
206 }
+0
-83
lib/tests/facts/resolvers/identity_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/identity_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_identity_resolver : identity_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_identity_resolver : identity_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.group_id = 123;
29 result.group_name = "foo";
30 result.user_id = 456;
31 result.user_name = "bar";
32 result.privileged = false;
33 return result;
34 }
35 };
36
37 SCENARIO("using the identity resolver") {
38 collection_fixture facts;
39 WHEN("data is not present") {
40 facts.add(make_shared<empty_identity_resolver>());
41 THEN("facts should not be added") {
42 REQUIRE(facts.size() == 0u);
43 }
44 }
45 WHEN("data is present") {
46 facts.add(make_shared<test_identity_resolver>());
47 THEN("a structured fact is added") {
48 auto identity = facts.get<map_value>(fact::identity);
49 REQUIRE(identity);
50 REQUIRE(identity->size() == 5u);
51
52 auto name = identity->get<string_value>("group");
53 REQUIRE(name);
54 REQUIRE(name->value() == "foo");
55
56 auto id = identity->get<integer_value>("gid");
57 REQUIRE(id);
58 REQUIRE(id->value() == 123);
59
60 name = identity->get<string_value>("user");
61 REQUIRE(name);
62 REQUIRE(name->value() == "bar");
63
64 id = identity->get<integer_value>("uid");
65 REQUIRE(id);
66 REQUIRE(id->value() == 456);
67
68 auto privileged = identity->get<boolean_value>("privileged");
69 REQUIRE(privileged);
70 REQUIRE(privileged->value() == false);
71 }
72 THEN("flat facts are added") {
73 auto name = facts.get<string_value>(fact::gid);
74 REQUIRE(name);
75 REQUIRE(name->value() == "foo");
76
77 name = facts.get<string_value>(fact::id);
78 REQUIRE(name);
79 REQUIRE(name->value() == "bar");
80 }
81 }
82 }
+0
-61
lib/tests/facts/resolvers/kernel_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/kernel_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include "../../collection_fixture.hpp"
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace facter::facts::resolvers;
10 using namespace facter::testing;
11
12 struct empty_kernel_resolver : kernel_resolver
13 {
14 protected:
15 virtual data collect_data(collection& facts) override
16 {
17 return {};
18 }
19 };
20
21 struct test_kernel_resolver : kernel_resolver
22 {
23 protected:
24 virtual data collect_data(collection& facts) override
25 {
26 data result;
27 result.name = "foo";
28 result.release = "1.2.3-foo";
29 result.version = "1.2.3";
30 return result;
31 }
32 };
33
34 SCENARIO("using the kernel resolver") {
35 collection_fixture facts;
36 WHEN("data is not present") {
37 facts.add(make_shared<empty_kernel_resolver>());
38 THEN("facts should not be added") {
39 REQUIRE(facts.size() == 0u);
40 }
41 }
42 WHEN("data is present") {
43 facts.add(make_shared<test_kernel_resolver>());
44 THEN("flat facts are added") {
45 REQUIRE(facts.size() == 4u);
46 auto kernel = facts.get<string_value>(fact::kernel);
47 REQUIRE(kernel);
48 REQUIRE(kernel->value() == "foo");
49 auto release = facts.get<string_value>(fact::kernel_release);
50 REQUIRE(release);
51 REQUIRE(release->value() == "1.2.3-foo");
52 auto version = facts.get<string_value>(fact::kernel_version);
53 REQUIRE(version);
54 REQUIRE(version->value() == "1.2.3");
55 auto major = facts.get<string_value>(fact::kernel_major_version);
56 REQUIRE(major);
57 REQUIRE(major->value() == "1.2");
58 }
59 }
60 }
+0
-85
lib/tests/facts/resolvers/ldom_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/ldom_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_ldom_resolver : ldom_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 data result;
19 return result;
20 }
21 };
22
23 struct test_ldom_resolver : ldom_resolver
24 {
25 protected:
26 virtual data collect_data(collection& facts) override
27 {
28 ldom_info single_value;
29 single_value.key = "domainname";
30 single_value.values.insert({ "domainname", "somedomain"});
31
32 ldom_info multi_value;
33 multi_value.key = "domainrole";
34 multi_value.values.insert({ "impl", "true"});
35 multi_value.values.insert({ "io", "false"});
36
37 data result;
38 result.ldom.emplace_back(single_value);
39 result.ldom.emplace_back(multi_value);
40
41 return result;
42 }
43 };
44
45 SCENARIO("Using the Solaris LDom resolver") {
46 collection_fixture facts;
47 WHEN("data is not present") {
48 facts.add(make_shared<empty_ldom_resolver>());
49 THEN("no LDom facts should be added") {
50 REQUIRE(facts.size() == 0u);
51 }
52 }
53 WHEN("data is present") {
54 facts.add(make_shared<test_ldom_resolver>());
55 THEN("a structured fact is added") {
56 REQUIRE(facts.size() == 4u);
57 auto ldom = facts.get<map_value>(fact::ldom);
58 REQUIRE(ldom);
59 REQUIRE(ldom->size() == 2u);
60
61 auto sval = ldom->get<string_value>("domainname");
62 REQUIRE(sval);
63 REQUIRE(sval->value() == "somedomain");
64
65 auto mval = ldom->get<map_value>("domainrole");
66 REQUIRE(mval);
67 REQUIRE(mval->size() == 2u);
68 }
69 THEN("flat facts are added") {
70 REQUIRE(facts.size() == 4u);
71 auto sval_2 = facts.get<string_value>("ldom_domainname");
72 REQUIRE(sval_2);
73 REQUIRE(sval_2->value() == "somedomain");
74
75 auto sval_3 = facts.get<string_value>("ldom_domainrole_impl");
76 REQUIRE(sval_3);
77 REQUIRE(sval_3->value() == "true");
78
79 auto sval_4 = facts.get<string_value>("ldom_domainrole_io");
80 REQUIRE(sval_4);
81 REQUIRE(sval_4->value() == "false");
82 }
83 }
84 }
+0
-135
lib/tests/facts/resolvers/memory_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/memory_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_memory_resolver : memory_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_memory_resolver : memory_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.swap_encryption = encryption_status::encrypted;
29 result.mem_total = 10 * 1024 * 1024;
30 result.mem_free = 5 * 1024 * 1024;
31 result.swap_total = 20 * 1024 * 1024;
32 result.swap_free = 4 * 1024 * 1024;
33 return result;
34 }
35 };
36
37 SCENARIO("using the memory resolver") {
38 collection_fixture facts;
39 WHEN("data is not present") {
40 facts.add(make_shared<empty_memory_resolver>());
41 THEN("facts should not be added") {
42 REQUIRE(facts.size() == 0u);
43 }
44 }
45 WHEN("data is present") {
46 facts.add(make_shared<test_memory_resolver>());
47 THEN("a structured fact is added") {
48 REQUIRE(facts.size() == 10u);
49 auto memory = facts.get<map_value>(fact::memory);
50 REQUIRE(memory);
51 auto info = memory->get<map_value>("swap");
52 REQUIRE(info);
53 REQUIRE(info->size() == 8u);
54 auto available = info->get<string_value>("available");
55 REQUIRE(available);
56 REQUIRE(available->value() == "4.00 MiB");
57 auto available_bytes = info->get<integer_value>("available_bytes");
58 REQUIRE(available_bytes);
59 REQUIRE(available_bytes->value() == 4194304);
60 auto capacity = info->get<string_value>("capacity");
61 REQUIRE(capacity);
62 REQUIRE(capacity->value() == "80.00%");
63 auto encrypted = info->get<boolean_value>("encrypted");
64 REQUIRE(encrypted);
65 REQUIRE(encrypted->value());
66 auto total = info->get<string_value>("total");
67 REQUIRE(total);
68 REQUIRE(total->value() == "20.00 MiB");
69 auto total_bytes = info->get<integer_value>("total_bytes");
70 REQUIRE(total_bytes);
71 REQUIRE(total_bytes->value() == 20971520);
72 auto used = info->get<string_value>("used");
73 REQUIRE(used);
74 REQUIRE(used->value() == "16.00 MiB");
75 auto used_bytes = info->get<integer_value>("used_bytes");
76 REQUIRE(used_bytes);
77 REQUIRE(used_bytes->value() == 16777216);
78 info = memory->get<map_value>("system");
79 REQUIRE(info);
80 REQUIRE(info->size() == 7u);
81 available = info->get<string_value>("available");
82 REQUIRE(available);
83 REQUIRE(available->value() == "5.00 MiB");
84 available_bytes = info->get<integer_value>("available_bytes");
85 REQUIRE(available_bytes);
86 REQUIRE(available_bytes->value() == 5242880);
87 capacity = info->get<string_value>("capacity");
88 REQUIRE(capacity);
89 REQUIRE(capacity->value() == "50.00%");
90 total = info->get<string_value>("total");
91 REQUIRE(total);
92 REQUIRE(total->value() == "10.00 MiB");
93 total_bytes = info->get<integer_value>("total_bytes");
94 REQUIRE(total_bytes);
95 REQUIRE(total_bytes->value() == 10485760);
96 used = info->get<string_value>("used");
97 REQUIRE(used);
98 REQUIRE(used->value() == "5.00 MiB");
99 used_bytes = info->get<integer_value>("used_bytes");
100 REQUIRE(used_bytes);
101 REQUIRE(used_bytes->value() == 5242880);
102 }
103 THEN("flat facts are added") {
104 REQUIRE(facts.size() == 10u);
105 auto memoryfree = facts.get<string_value>(fact::memoryfree);
106 REQUIRE(memoryfree);
107 REQUIRE(memoryfree->value() == "5.00 MiB");
108 auto memoryfree_mb = facts.get<double_value>(fact::memoryfree_mb);
109 REQUIRE(memoryfree_mb);
110 REQUIRE(memoryfree_mb->value() == Approx(5.0));
111 auto memorysize = facts.get<string_value>(fact::memorysize);
112 REQUIRE(memorysize);
113 REQUIRE(memorysize->value() == "10.00 MiB");
114 auto memorysize_mb = facts.get<double_value>(fact::memorysize_mb);
115 REQUIRE(memorysize_mb);
116 REQUIRE(memorysize_mb->value() == Approx(10.0));
117 auto swapencrypted = facts.get<boolean_value>(fact::swapencrypted);
118 REQUIRE(swapencrypted);
119 REQUIRE(swapencrypted->value());
120 auto swapfree = facts.get<string_value>(fact::swapfree);
121 REQUIRE(swapfree);
122 REQUIRE(swapfree->value() == "4.00 MiB");
123 auto swapfree_mb = facts.get<double_value>(fact::swapfree_mb);
124 REQUIRE(swapfree_mb);
125 REQUIRE(swapfree_mb->value() == Approx(4.0));
126 auto swapsize = facts.get<string_value>(fact::swapsize);
127 REQUIRE(swapsize);
128 REQUIRE(swapsize->value() == "20.00 MiB");
129 auto swapsize_mb = facts.get<double_value>(fact::swapsize_mb);
130 REQUIRE(swapsize_mb);
131 REQUIRE(swapsize_mb->value() == Approx(20.0));
132 }
133 }
134 }
+0
-513
lib/tests/facts/resolvers/networking_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/networking_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include "../../collection_fixture.hpp"
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::facts::resolvers;
12 using namespace facter::testing;
13
14 struct empty_networking_resolver : networking_resolver
15 {
16 protected:
17 virtual data collect_data(collection& facts) override
18 {
19 return {};
20 }
21 };
22
23 struct test_hostname_resolver : networking_resolver
24 {
25 protected:
26 virtual data collect_data(collection& facts) override
27 {
28 data result;
29 result.hostname = "hostname";
30 return result;
31 }
32 };
33
34 struct test_domain_resolver : networking_resolver
35 {
36 protected:
37 virtual data collect_data(collection& facts) override
38 {
39 data result;
40 result.domain = "domain";
41 return result;
42 }
43 };
44
45 struct test_fqdn_resolver : networking_resolver
46 {
47 protected:
48 virtual data collect_data(collection& facts) override
49 {
50 data result;
51 result.fqdn = "fqdn";
52 return result;
53 }
54 };
55
56 struct test_missing_fqdn_resolver : networking_resolver
57 {
58 protected:
59 virtual data collect_data(collection& facts) override
60 {
61 data result;
62 result.hostname = "hostname";
63 result.domain = "domain.com";
64 return result;
65 }
66 };
67
68 struct test_domain_with_trailing_dot : networking_resolver
69 {
70 protected:
71 virtual data collect_data(collection& facts) override
72 {
73 data result;
74 result.domain = "domain.com.";
75 return result;
76 }
77 };
78
79 struct test_interface_resolver : networking_resolver
80 {
81 protected:
82 virtual data collect_data(collection& facts) override
83 {
84 data result;
85 for (int i = 0; i < 5; ++i) {
86 string num = to_string(i);
87
88 interface iface;
89 iface.name = "iface_" + num;
90 iface.dhcp_server = "dhcp_" + num;
91 for (int binding_index = 0; binding_index < 2; ++binding_index) {
92 string binding_num = to_string(binding_index);
93 iface.ipv4_bindings.emplace_back(binding { "ip_" + num + "_" + binding_num, "netmask_" + num + "_" + binding_num, "network_" + num + "_" + binding_num });
94 iface.ipv6_bindings.emplace_back(binding { "ip6_" + num + "_" + binding_num, "netmask6_" + num + "_" + binding_num, "network6_" + num + "_" + binding_num });
95 }
96 iface.macaddress = "macaddress_" + num;
97 iface.mtu = i;
98 result.interfaces.emplace_back(move(iface));
99 }
100 result.primary_interface = "iface_2";
101 return result;
102 }
103 };
104
105 struct primary_interface_resolver : networking_resolver
106 {
107 protected:
108 virtual data collect_data(collection& facts) override
109 {
110 data result;
111
112 interface lo0;
113 lo0.name = "lo0";
114 lo0.ipv4_bindings.emplace_back(binding { "127.0.0.1", "255.255.255.0", "127.0.0.0" });
115 lo0.ipv6_bindings.emplace_back(binding { "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::1" });
116 result.interfaces.emplace_back(move(lo0));
117
118 interface en0;
119 en0.name = "en0";
120 en0.ipv4_bindings.emplace_back(binding { "123.123.123.123", "255.255.255.0", "123.123.123.0" });
121 en0.ipv6_bindings.emplace_back(binding { "fe80::6203:8ff:fe95:2d16", "ffff:ffff:ffff:ffff::", "fe80::" });
122 result.interfaces.emplace_back(move(en0));
123
124 interface en1;
125 en1.name = "en1";
126 en1.ipv4_bindings.emplace_back(binding { "123.123.123.124", "255.255.255.0", "123.123.123.0" });
127 en1.ipv6_bindings.emplace_back(binding { "fe80::6203:8ff:fe95:2d17", "ffff:ffff:ffff:ffff::", "fe80::" });
128 result.interfaces.emplace_back(move(en1));
129
130 return result;
131 }
132 };
133
134 SCENARIO("using the networking resolver") {
135 collection_fixture facts;
136 WHEN("data is not present") {
137 facts.add(make_shared<empty_networking_resolver>());
138 THEN("facts should not be added") {
139 REQUIRE(facts.size() == 0u);
140 }
141 }
142 WHEN("only hostname is present") {
143 facts.add(make_shared<test_hostname_resolver>());
144 REQUIRE(facts.size() == 3u);
145 THEN("a flat fact is added") {
146 auto hostname = facts.get<string_value>(fact::hostname);
147 REQUIRE(hostname);
148 REQUIRE(hostname->value() == "hostname");
149 }
150 THEN("a structured fact is added") {
151 auto networking = facts.get<map_value>(fact::networking);
152 REQUIRE(networking);
153 REQUIRE(networking->size() == 2u);
154
155 auto hostname = networking->get<string_value>("hostname");
156 REQUIRE(hostname);
157 REQUIRE(hostname->value() == "hostname");
158
159 auto fqdn = networking->get<string_value>("fqdn");
160 REQUIRE(fqdn);
161 REQUIRE(fqdn->value() == "hostname");
162 }
163 THEN("the FQDN fact is the hostname") {
164 auto fqdn = facts.get<string_value>(fact::fqdn);
165 REQUIRE(fqdn);
166 REQUIRE(fqdn->value() == "hostname");
167 }
168 }
169 WHEN("only domain is present") {
170 facts.add(make_shared<test_domain_resolver>());
171 REQUIRE(facts.size() == 2u);
172 THEN("a flat fact is added") {
173 auto domain = facts.get<string_value>(fact::domain);
174 REQUIRE(domain);
175 REQUIRE(domain->value() == "domain");
176 }
177 THEN("a structured fact is added") {
178 auto networking = facts.get<map_value>(fact::networking);
179 REQUIRE(networking);
180 REQUIRE(networking->size() == 1u);
181
182 auto domain = networking->get<string_value>("domain");
183 REQUIRE(domain);
184 REQUIRE(domain->value() == "domain");
185 }
186 THEN("the FQDN fact is not present") {
187 auto fqdn = facts.get<string_value>(fact::fqdn);
188 REQUIRE_FALSE(fqdn);
189 }
190 }
191 WHEN("domain contains a trailing dot, the dot is removed") {
192 facts.add(make_shared<test_domain_with_trailing_dot>());
193 REQUIRE(facts.size() == 2u);
194 THEN("a flat fact is added") {
195 auto domain = facts.get<string_value>(fact::domain);
196 REQUIRE(domain);
197 REQUIRE(domain->value() == "domain.com");
198 }
199 THEN("a structured fact is added") {
200 auto networking = facts.get<map_value>(fact::networking);
201 REQUIRE(networking);
202 REQUIRE(networking->size() == 1u);
203
204 auto domain = networking->get<string_value>("domain");
205 REQUIRE(domain);
206 REQUIRE(domain->value() == "domain.com");
207 }
208 THEN("the FQDN fact is not present") {
209 auto fqdn = facts.get<string_value>(fact::fqdn);
210 REQUIRE_FALSE(fqdn);
211 }
212 }
213 WHEN("FQDN is present") {
214 facts.add(make_shared<test_fqdn_resolver>());
215 REQUIRE(facts.size() == 2u);
216 THEN("a flat fact is added") {
217 auto fqdn = facts.get<string_value>(fact::fqdn);
218 REQUIRE(fqdn);
219 REQUIRE(fqdn->value() == "fqdn");
220 }
221 THEN("a structured fact is added") {
222 auto networking = facts.get<map_value>(fact::networking);
223 REQUIRE(networking);
224 REQUIRE(networking->size() == 1u);
225 auto fqdn = networking->get<string_value>("fqdn");
226 REQUIRE(fqdn);
227 REQUIRE(fqdn->value() == "fqdn");
228 }
229 THEN("the FQDN fact is present") {
230 auto fqdn = facts.get<string_value>(fact::fqdn);
231 REQUIRE(fqdn);
232 REQUIRE(fqdn->value() == "fqdn");
233 }
234 }
235 WHEN("FQDN is not present") {
236 facts.add(make_shared<test_missing_fqdn_resolver>());
237 REQUIRE(facts.size() == 4u);
238 THEN("the FQDN fact is the combination of hostname and domain") {
239 auto svalue = facts.get<string_value>(fact::hostname);
240 REQUIRE(svalue);
241 REQUIRE(svalue->value() == "hostname");
242 svalue = facts.get<string_value>(fact::domain);
243 REQUIRE(svalue);
244 REQUIRE(svalue->value() == "domain.com");
245 svalue = facts.get<string_value>(fact::fqdn);
246 REQUIRE(svalue);
247 REQUIRE(svalue->value() == "hostname.domain.com");
248 }
249 THEN("the FQDN in the structured fact is the combination of hostname and domain") {
250 auto networking = facts.get<map_value>(fact::networking);
251 REQUIRE(networking);
252 REQUIRE(networking->size() == 3u);
253 auto svalue = networking->get<string_value>("hostname");
254 REQUIRE(svalue);
255 REQUIRE(svalue->value() == "hostname");
256 svalue = networking->get<string_value>("domain");
257 REQUIRE(svalue);
258 REQUIRE(svalue->value() == "domain.com");
259 svalue = networking->get<string_value>("fqdn");
260 REQUIRE(svalue);
261 REQUIRE(svalue->value() == "hostname.domain.com");
262 }
263 }
264 WHEN("network interfaces are present") {
265 facts.add(make_shared<test_interface_resolver>());
266 REQUIRE(facts.size() == 56u);
267 THEN("the DHCP servers fact is present") {
268 auto dhcp_servers = facts.get<map_value>(fact::dhcp_servers);
269 REQUIRE(dhcp_servers);
270 REQUIRE(dhcp_servers->size() == 6u);
271 for (unsigned int i = 0; i < 5; ++i) {
272 string num = to_string(i);
273 auto server = dhcp_servers->get<string_value>("iface_" + num);
274 REQUIRE(server);
275 REQUIRE(server->value() == "dhcp_" + num);
276 }
277 auto dhcp = dhcp_servers->get<string_value>("system");
278 REQUIRE(dhcp);
279 REQUIRE(dhcp->value() == "dhcp_2");
280 }
281 THEN("the interface names fact is present") {
282 auto interfaces_list = facts.get<string_value>(fact::interfaces);
283 REQUIRE(interfaces_list);
284 REQUIRE(interfaces_list->value() == "iface_0,iface_1,iface_2,iface_3,iface_4");
285 }
286 THEN("the interface flat facts are present") {
287 for (unsigned int i = 0; i < 5; ++i) {
288 string num = to_string(i);
289 auto ip = facts.get<string_value>(fact::ipaddress + string("_iface_") + num);
290 REQUIRE(ip);
291 REQUIRE(ip->value() == "ip_" + num + "_0");
292 auto ip6 = facts.get<string_value>(fact::ipaddress6 + string("_iface_") + num);
293 REQUIRE(ip6);
294 REQUIRE(ip6->value() == "ip6_" + num + "_0");
295 auto macaddress = facts.get<string_value>(fact::macaddress + string("_iface_") + num);
296 REQUIRE(macaddress);
297 REQUIRE(macaddress->value() == "macaddress_" + num);
298 auto mtu = facts.get<integer_value>(fact::mtu + string("_iface_") + num);
299 REQUIRE(mtu);
300 REQUIRE(mtu->value() == i);
301 auto netmask = facts.get<string_value>(fact::netmask + string("_iface_") + num);
302 REQUIRE(netmask);
303 REQUIRE(netmask->value() == "netmask_" + num + "_0");
304 auto netmask6 = facts.get<string_value>(fact::netmask6 + string("_iface_") + num);
305 REQUIRE(netmask6);
306 REQUIRE(netmask6->value() == "netmask6_" + num + "_0");
307 auto network = facts.get<string_value>(fact::network + string("_iface_") + num);
308 REQUIRE(network);
309 REQUIRE(network->value() == "network_" + num + "_0");
310 auto network6 = facts.get<string_value>(fact::network6 + string("_iface_") + num);
311 REQUIRE(network6);
312 REQUIRE(network6->value() == "network6_" + num + "_0");
313 auto scope6 = facts.get<string_value>(fact::scope6 + string("_iface_") + num);
314 REQUIRE(scope6);
315 }
316 }
317 THEN("the system fact facts are present") {
318 auto ip = facts.get<string_value>(fact::ipaddress);
319 REQUIRE(ip);
320 REQUIRE(ip->value() == "ip_2_0");
321 auto ip6 = facts.get<string_value>(fact::ipaddress6);
322 REQUIRE(ip6);
323 REQUIRE(ip6->value() == "ip6_2_0");
324 auto macaddress = facts.get<string_value>(fact::macaddress);
325 REQUIRE(macaddress);
326 REQUIRE(macaddress->value() == "macaddress_2");
327 auto netmask = facts.get<string_value>(fact::netmask);
328 REQUIRE(netmask);
329 REQUIRE(netmask->value() == "netmask_2_0");
330 auto netmask6 = facts.get<string_value>(fact::netmask6);
331 REQUIRE(netmask6);
332 REQUIRE(netmask6->value() == "netmask6_2_0");
333 auto network = facts.get<string_value>(fact::network);
334 REQUIRE(network);
335 REQUIRE(network->value() == "network_2_0");
336 auto network6 = facts.get<string_value>(fact::network6);
337 REQUIRE(network6);
338 REQUIRE(network6->value() == "network6_2_0");
339 auto scope6 = facts.get<string_value>(fact::scope6);
340 REQUIRE(scope6);
341 }
342 THEN("the networking structured fact is present") {
343 auto networking = facts.get<map_value>(fact::networking);
344 REQUIRE(networking);
345 REQUIRE(networking->size() == 12u);
346 auto primary = networking->get<string_value>("primary");
347 REQUIRE(primary);
348 REQUIRE(primary->value() == "iface_2");
349 auto dhcp = networking->get<string_value>("dhcp");
350 REQUIRE(dhcp);
351 REQUIRE(dhcp->value() == "dhcp_2");
352 auto ip = networking->get<string_value>("ip");
353 REQUIRE(ip);
354 REQUIRE(ip->value() == "ip_2_0");
355 auto ip6 = networking->get<string_value>("ip6");
356 REQUIRE(ip6);
357 REQUIRE(ip6->value() == "ip6_2_0");
358 auto macaddress = networking->get<string_value>("mac");
359 REQUIRE(macaddress);
360 REQUIRE(macaddress->value() == "macaddress_2");
361 auto netmask = networking->get<string_value>("netmask");
362 REQUIRE(netmask);
363 REQUIRE(netmask->value() == "netmask_2_0");
364 auto netmask6 = networking->get<string_value>("netmask6");
365 REQUIRE(netmask6);
366 REQUIRE(netmask6->value() == "netmask6_2_0");
367 auto network = networking->get<string_value>("network");
368 REQUIRE(network);
369 REQUIRE(network->value() == "network_2_0");
370 auto network6 = networking->get<string_value>("network6");
371 REQUIRE(network6);
372 REQUIRE(network6->value() == "network6_2_0");
373 auto scope6 = networking->get<string_value>("scope6");
374 REQUIRE(scope6);
375 auto mtu = networking->get<integer_value>("mtu");
376 REQUIRE(mtu);
377 REQUIRE(mtu->value() == 2);
378 auto interfaces = networking->get<map_value>("interfaces");
379 REQUIRE(interfaces);
380 for (unsigned int i = 0; i < 5; ++i) {
381 string num = to_string(i);
382 auto interface = interfaces->get<map_value>("iface_" + num);
383 REQUIRE(interface);
384 dhcp = interface->get<string_value>("dhcp");
385 REQUIRE(dhcp);
386 REQUIRE(dhcp->value() == "dhcp_" + num);
387 ip = interface->get<string_value>("ip");
388 REQUIRE(ip);
389 REQUIRE(ip->value() == "ip_" + num + "_0");
390 ip6 = interface->get<string_value>("ip6");
391 REQUIRE(ip6);
392 REQUIRE(ip6->value() == "ip6_" + num + "_0");
393 macaddress = interface->get<string_value>("mac");
394 REQUIRE(macaddress);
395 REQUIRE(macaddress->value() == "macaddress_" + num);
396 netmask = interface->get<string_value>("netmask");
397 REQUIRE(netmask);
398 REQUIRE(netmask->value() == "netmask_" + num + "_0");
399 netmask6 = interface->get<string_value>("netmask6");
400 REQUIRE(netmask6);
401 REQUIRE(netmask6->value() == "netmask6_" + num + "_0");
402 network = interface->get<string_value>("network");
403 REQUIRE(network);
404 REQUIRE(network->value() == "network_" + num + "_0");
405 network6 = interface->get<string_value>("network6");
406 REQUIRE(network6);
407 REQUIRE(network6->value() == "network6_" + num + "_0");
408 mtu = interface->get<integer_value>("mtu");
409 REQUIRE(mtu);
410 REQUIRE(mtu->value() == i);
411 auto bindings = interface->get<array_value>("bindings");
412 REQUIRE(bindings);
413 REQUIRE(bindings->size() == 2u);
414 for (size_t binding_index = 0; binding_index < bindings->size(); ++binding_index) {
415 auto interface_num = to_string(binding_index);
416 auto binding = bindings->get<map_value>(binding_index);
417 REQUIRE(binding);
418 auto address = binding->get<string_value>("address");
419 REQUIRE(address);
420 REQUIRE(address->value() == "ip_" + num + "_" + interface_num);
421 auto netmask = binding->get<string_value>("netmask");
422 REQUIRE(netmask);
423 REQUIRE(netmask->value() == "netmask_" + num + "_" + interface_num);
424 auto network = binding->get<string_value>("network");
425 REQUIRE(network);
426 REQUIRE(network->value() == "network_" + num + "_" + interface_num);
427 }
428 bindings = interface->get<array_value>("bindings6");
429 REQUIRE(bindings);
430 REQUIRE(bindings->size() == 2u);
431 for (size_t binding_index = 0; binding_index < bindings->size(); ++binding_index) {
432 auto interface_num = to_string(binding_index);
433 auto binding = bindings->get<map_value>(binding_index);
434 REQUIRE(binding);
435 auto address = binding->get<string_value>("address");
436 REQUIRE(address);
437 REQUIRE(address->value() == "ip6_" + num + "_" + interface_num);
438 auto netmask = binding->get<string_value>("netmask");
439 REQUIRE(netmask);
440 REQUIRE(netmask->value() == "netmask6_" + num + "_" + interface_num);
441 auto network = binding->get<string_value>("network");
442 REQUIRE(network);
443 REQUIRE(network->value() == "network6_" + num + "_" + interface_num);
444 }
445 }
446 }
447 }
448 WHEN("the primary interface is not resolved") {
449 facts.add(make_shared<primary_interface_resolver>());
450 THEN("the first interface with a valid address should be treated as primary") {
451 REQUIRE(facts.query<string_value>("networking.primary"));
452 REQUIRE(facts.query<string_value>("networking.primary")->value() == "en0");
453 }
454 }
455 }
456
457 SCENARIO("ignored IPv4 addresses") {
458 char const* ignored_addresses[] = {
459 "",
460 "127.0.0.1",
461 "127.0.0.2",
462 "127.1.0.0",
463 "127.0.1.0",
464 "169.254.7.14",
465 "169.254.0.0",
466 "169.254.255.255"
467 };
468 for (auto s : ignored_addresses) {
469 CAPTURE(s);
470 REQUIRE(networking_resolver::ignored_ipv4_address(s));
471 }
472 char const* accepted_addresses[] = {
473 "169.253.0.0",
474 "169.255.0.0",
475 "100.100.100.100",
476 "0.0.0.0",
477 "1.1.1.1",
478 "10.0.18.142",
479 "192.168.0.1",
480 "255.255.255.255",
481 "128.0.0.1",
482 };
483 for (auto s : accepted_addresses) {
484 CAPTURE(s);
485 REQUIRE_FALSE(networking_resolver::ignored_ipv4_address(s));
486 }
487 }
488
489
490 SCENARIO("ignore IPv6 adddresses") {
491 char const* ignored_addresses[] = {
492 "",
493 "::1",
494 "fe80::9c84:7ca1:794b:12ed",
495 "fe80::75f2:2f55:823b:a513%10"
496 };
497 for (auto s : ignored_addresses) {
498 CAPTURE(s);
499 REQUIRE(networking_resolver::ignored_ipv6_address(s));
500 }
501 char const* accepted_addresses[] = {
502 "::fe80:75f2:2f55:823b:a513",
503 "fe7f::75f2:2f55:823b:a513%10",
504 "::2",
505 "::fe01",
506 "::fe80"
507 };
508 for (auto s : accepted_addresses) {
509 CAPTURE(s);
510 REQUIRE_FALSE(networking_resolver::ignored_ipv6_address(s));
511 }
512 }
+0
-275
lib/tests/facts/resolvers/operating_system_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/operating_system_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_os_resolver : operating_system_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_os_resolver : operating_system_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.name = "Archlinux";
29 result.family = "Archlinux";
30 result.release = "1.2.3";
31 result.specification_version = "1.4";
32 result.distro.id = "Arch";
33 result.distro.release = "1.2.3";
34 result.distro.codename = "awesomesauce";
35 result.distro.description = "best distro ever";
36 result.osx.product = "Mac OS X";
37 result.osx.build = "14A388b";
38 result.osx.version = "10.10";
39 result.win.release_id = "1904";
40 result.win.edition_id = "ServerStandard";
41 result.win.installation_type = "Server";
42 result.win.product_name = "Windows 2019 Standard Edition";
43 result.win.system32 = "C:\\WINDOWS\\sysnative";
44 result.architecture = "amd64";
45 result.hardware = "x86-64";
46 result.selinux.supported = true;
47 result.selinux.enabled = true;
48 result.selinux.enforced = true;
49 result.selinux.current_mode = "current mode";
50 result.selinux.config_mode = "config mode";
51 result.selinux.config_policy = "config policy";
52 result.selinux.policy_version = "policy version";
53 return result;
54 }
55 };
56
57 SCENARIO("using the operating system resolver") {
58 collection_fixture facts;
59 WHEN("data is not present") {
60 facts.add(make_shared<empty_os_resolver>());
61 THEN("facts should not be added") {
62 REQUIRE(facts.size() == 0u);
63 }
64 }
65 WHEN("data is present") {
66 facts.add(make_shared<test_os_resolver>());
67 REQUIRE(facts.size() == 30u);
68 THEN("a structured fact is added") {
69 auto os = facts.get<map_value>(fact::os);
70 REQUIRE(os);
71 REQUIRE(os->size() == 9u);
72 auto distro = os->get<map_value>("distro");
73 REQUIRE(distro);
74 REQUIRE(distro->size() == 5u);
75 auto codename = distro->get<string_value>("codename");
76 REQUIRE(codename);
77 REQUIRE(codename->value() == "awesomesauce");
78 auto description = distro->get<string_value>("description");
79 REQUIRE(description);
80 REQUIRE(description->value() == "best distro ever");
81 auto id = distro->get<string_value>("id");
82 REQUIRE(description);
83 REQUIRE(id->value() == "Arch");
84 auto lsbrelease = distro->get<string_value>("specification");
85 REQUIRE(lsbrelease);
86 REQUIRE(lsbrelease->value() == "1.4");
87 auto release_attribute = distro->get<map_value>("release");
88 REQUIRE(release_attribute);
89 REQUIRE(release_attribute->size() == 3u);
90 auto release = release_attribute->get<string_value>("full");
91 REQUIRE(release);
92 REQUIRE(release->value() == "1.2.3");
93 auto major = release_attribute->get<string_value>("major");
94 REQUIRE(major);
95 REQUIRE(major->value() == "1");
96 auto minor = release_attribute->get<string_value>("minor");
97 REQUIRE(minor);
98 REQUIRE(minor->value() == "2");
99 auto family = os->get<string_value>("family");
100 REQUIRE(family);
101 REQUIRE(family->value() == "Archlinux");
102 auto name = os->get<string_value>("name");
103 REQUIRE(name);
104 REQUIRE(name->value() == "Archlinux");
105 auto architecture = os->get<string_value>("architecture");
106 REQUIRE(architecture);
107 REQUIRE(architecture->value() == "amd64");
108 auto hardware = os->get<string_value>("hardware");
109 REQUIRE(hardware);
110 REQUIRE(hardware->value() == "x86-64");
111 release_attribute = os->get<map_value>("release");
112 REQUIRE(release_attribute);
113 REQUIRE(release_attribute->size() == 3u);
114 release = release_attribute->get<string_value>("full");
115 REQUIRE(release);
116 REQUIRE(release->value() == "1.2.3");
117 major = release_attribute->get<string_value>("major");
118 REQUIRE(major);
119 REQUIRE(major->value() == "1");
120 minor = release_attribute->get<string_value>("minor");
121 REQUIRE(minor);
122 REQUIRE(minor->value() == "2");
123 auto macosx = os->get<map_value>("macosx");
124 REQUIRE(macosx);
125 REQUIRE(macosx->size() == 3u);
126 auto product = macosx->get<string_value>("product");
127 REQUIRE(product);
128 REQUIRE(product->value() == "Mac OS X");
129 auto build = macosx->get<string_value>("build");
130 REQUIRE(build);
131 REQUIRE(build->value() == "14A388b");
132 release_attribute = macosx->get<map_value>("version");
133 REQUIRE(release_attribute);
134 REQUIRE(release_attribute->size() == 3u);
135 release = release_attribute->get<string_value>("full");
136 REQUIRE(release);
137 REQUIRE(release->value() == "10.10");
138 major = release_attribute->get<string_value>("major");
139 REQUIRE(major);
140 REQUIRE(major->value() == "10.10");
141 minor = release_attribute->get<string_value>("minor");
142 REQUIRE(minor);
143 REQUIRE(minor->value() == "0");
144 auto windows = os->get<map_value>("windows");
145 REQUIRE(windows);
146 REQUIRE(windows->size() == 5u);
147 auto edition_id = facts.get<string_value>(fact::windows_edition_id);
148 REQUIRE(edition_id);
149 REQUIRE(edition_id->value() == "ServerStandard");
150 auto installation_type = facts.get<string_value>(fact::windows_installation_type);
151 REQUIRE(installation_type);
152 REQUIRE(installation_type->value() == "Server");
153 auto product_name = facts.get<string_value>(fact::windows_product_name);
154 REQUIRE(product_name);
155 REQUIRE(product_name->value() == "Windows 2019 Standard Edition");
156 auto release_id = facts.get<string_value>(fact::windows_release_id);
157 REQUIRE(release_id);
158 REQUIRE(release_id->value() == "1904");
159 auto system32 = windows->get<string_value>("system32");
160 REQUIRE(system32);
161 REQUIRE(system32->value() == "C:\\WINDOWS\\sysnative");
162 auto selinux = os->get<map_value>("selinux");
163 REQUIRE(selinux);
164 REQUIRE(selinux->size() == 6u);
165 auto bval = selinux->get<boolean_value>("enabled");
166 REQUIRE(bval);
167 REQUIRE(bval->value());
168 bval = selinux->get<boolean_value>("enforced");
169 REQUIRE(bval);
170 REQUIRE(bval->value());
171 auto sval = selinux->get<string_value>("policy_version");
172 REQUIRE(sval);
173 REQUIRE(sval->value() == "policy version");
174 sval = selinux->get<string_value>("current_mode");
175 REQUIRE(sval);
176 REQUIRE(sval->value() == "current mode");
177 sval = selinux->get<string_value>("config_mode");
178 REQUIRE(sval);
179 REQUIRE(sval->value() == "config mode");
180 sval = selinux->get<string_value>("config_policy");
181 REQUIRE(sval);
182 REQUIRE(sval->value() == "config policy");
183 }
184 THEN("flat facts are added") {
185 auto name = facts.get<string_value>(fact::operating_system);
186 REQUIRE(name);
187 REQUIRE(name->value() == "Archlinux");
188 auto architecture = facts.get<string_value>(fact::architecture);
189 REQUIRE(architecture);
190 REQUIRE(architecture->value() == "amd64");
191 auto hardware = facts.get<string_value>(fact::hardware_model);
192 REQUIRE(hardware);
193 REQUIRE(hardware->value() == "x86-64");
194 auto release = facts.get<string_value>(fact::operating_system_release);
195 REQUIRE(release);
196 REQUIRE(release->value() == "1.2.3");
197 auto major = facts.get<string_value>(fact::operating_system_major_release);
198 REQUIRE(major);
199 REQUIRE(major->value() == "1");
200 auto family = facts.get<string_value>(fact::os_family);
201 REQUIRE(family);
202 REQUIRE(family->value() == "Archlinux");
203 auto codename = facts.get<string_value>(fact::lsb_dist_codename);
204 REQUIRE(codename);
205 REQUIRE(codename->value() == "awesomesauce");
206 auto description = facts.get<string_value>(fact::lsb_dist_description);
207 REQUIRE(description);
208 REQUIRE(description->value() == "best distro ever");
209 auto id = facts.get<string_value>(fact::lsb_dist_id);
210 REQUIRE(id);
211 REQUIRE(id->value() == "Arch");
212 release = facts.get<string_value>(fact::lsb_dist_release);
213 REQUIRE(release);
214 REQUIRE(release->value() == "1.2.3");
215 major = facts.get<string_value>(fact::lsb_dist_major_release);
216 REQUIRE(major);
217 REQUIRE(major->value() == "1");
218 auto minor = facts.get<string_value>(fact::lsb_dist_minor_release);
219 REQUIRE(minor);
220 REQUIRE(minor->value() == "2");
221 auto lsbrelease = facts.get<string_value>(fact::lsb_release);
222 REQUIRE(lsbrelease);
223 REQUIRE(lsbrelease->value() == "1.4");
224 auto build = facts.get<string_value>(fact::macosx_buildversion);
225 REQUIRE(build);
226 REQUIRE(build->value() == "14A388b");
227 auto product = facts.get<string_value>(fact::macosx_productname);
228 REQUIRE(product);
229 REQUIRE(product->value() == "Mac OS X");
230 release = facts.get<string_value>(fact::macosx_productversion);
231 REQUIRE(release);
232 REQUIRE(release->value() == "10.10");
233 major = facts.get<string_value>(fact::macosx_productversion_major);
234 REQUIRE(major);
235 REQUIRE(major->value() == "10.10");
236 minor = facts.get<string_value>(fact::macosx_productversion_minor);
237 REQUIRE(minor);
238 REQUIRE(minor->value() == "0");
239 auto edition_id = facts.get<string_value>(fact::windows_edition_id);
240 REQUIRE(edition_id);
241 REQUIRE(edition_id->value() == "ServerStandard");
242 auto installation_type = facts.get<string_value>(fact::windows_installation_type);
243 REQUIRE(installation_type);
244 REQUIRE(installation_type->value() == "Server");
245 auto product_name = facts.get<string_value>(fact::windows_product_name);
246 REQUIRE(product_name);
247 REQUIRE(product_name->value() == "Windows 2019 Standard Edition");
248 auto release_id = facts.get<string_value>(fact::windows_release_id);
249 REQUIRE(release_id);
250 REQUIRE(release_id->value() == "1904");
251 auto system32 = facts.get<string_value>(fact::windows_system32);
252 REQUIRE(system32);
253 REQUIRE(system32->value() == "C:\\WINDOWS\\sysnative");
254 auto bval = facts.get<boolean_value>(fact::selinux);
255 REQUIRE(bval);
256 REQUIRE(bval->value());
257 bval = facts.get<boolean_value>(fact::selinux_enforced);
258 REQUIRE(bval);
259 REQUIRE(bval->value());
260 auto sval = facts.get<string_value>(fact::selinux_policyversion);
261 REQUIRE(sval);
262 REQUIRE(sval->value() == "policy version");
263 sval = facts.get<string_value>(fact::selinux_current_mode);
264 REQUIRE(sval);
265 REQUIRE(sval->value() == "current mode");
266 sval = facts.get<string_value>(fact::selinux_config_mode);
267 REQUIRE(sval);
268 REQUIRE(sval->value() == "config mode");
269 sval = facts.get<string_value>(fact::selinux_config_policy);
270 REQUIRE(sval);
271 REQUIRE(sval->value() == "config policy");
272 }
273 }
274 }
+0
-157
lib/tests/facts/resolvers/processor_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/processor_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include <facter/facts/array_value.hpp>
7 #include "../../collection_fixture.hpp"
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::facts::resolvers;
12 using namespace facter::testing;
13
14 struct empty_processor_resolver : processor_resolver
15 {
16 protected:
17 virtual data collect_data(collection& facts) override
18 {
19 return {};
20 }
21 };
22
23 struct test_processor_resolver : processor_resolver
24 {
25 protected:
26 virtual data collect_data(collection& facts) override
27 {
28 data result;
29 result.isa = "isa";
30 result.logical_count = 4;
31 result.physical_count = 2;
32 result.models = {
33 "processor1",
34 "processor2",
35 "processor3",
36 "processor4"
37 };
38 result.speed = 10 * 1000 * 1000 * 1000ull;
39 return result;
40 }
41 };
42
43 struct test_processor_resolver_no_physical_count : processor_resolver
44 {
45 protected:
46 virtual data collect_data(collection& facts) override
47 {
48 data result;
49 result.isa = "isa";
50 result.logical_count = 4;
51 result.models = {
52 "processor1",
53 "processor2",
54 "processor3",
55 "processor4"
56 };
57 result.speed = 10 * 1000 * 1000 * 1000ull;
58 return result;
59 }
60 };
61
62 SCENARIO("using the processor resolver") {
63 collection_fixture facts;
64 WHEN("data is not present") {
65 facts.add(make_shared<empty_processor_resolver>());
66 THEN("facts should not be added") {
67 REQUIRE(facts.size() == 0u);
68 }
69 }
70 WHEN("typical data is present") {
71 facts.add(make_shared<test_processor_resolver>());
72 THEN("a structured fact is added") {
73 REQUIRE(facts.size() == 8u);
74 auto processors = facts.get<map_value>(fact::processors);
75 REQUIRE(processors);
76 REQUIRE(processors->size() == 5u);
77 auto count = processors->get<integer_value>("count");
78 REQUIRE(count);
79 REQUIRE(count->value() == 4);
80 count = processors->get<integer_value>("physicalcount");
81 REQUIRE(count);
82 REQUIRE(count->value() == 2);
83 auto isa = processors->get<string_value>("isa");
84 REQUIRE(isa);
85 REQUIRE(isa->value() == "isa");
86 auto models = processors->get<array_value>("models");
87 REQUIRE(models);
88 REQUIRE(models->size() == 4u);
89 for (size_t i = 0; i < 4; ++i) {
90 auto model = models->get<string_value>(i);
91 REQUIRE(model);
92 REQUIRE(model->value() == "processor" + to_string(i + 1));
93 }
94 auto speed = processors->get<string_value>("speed");
95 REQUIRE(speed);
96 REQUIRE(speed->value() == "10.00 GHz");
97 }
98 THEN("flat facts are added") {
99 REQUIRE(facts.size() == 8u);
100 auto count = facts.get<integer_value>(fact::physical_processor_count);
101 REQUIRE(count);
102 REQUIRE(count->value() == 2);
103 count = facts.get<integer_value>(fact::processor_count);
104 REQUIRE(count);
105 REQUIRE(count->value() == 4);
106 auto isa = facts.get<string_value>(fact::hardware_isa);
107 REQUIRE(isa);
108 REQUIRE(isa->value() == "isa");
109 for (size_t i = 0; i < 4; ++i) {
110 auto model = facts.get<string_value>(fact::processor + to_string(i));
111 REQUIRE(model);
112 REQUIRE(model->value() == "processor" + to_string(i + 1));
113 }
114 }
115 }
116 WHEN("data is present with no physical count") {
117 facts.add(make_shared<test_processor_resolver_no_physical_count>());
118 THEN("a structured fact is added, not including physical_processor_count") {
119 REQUIRE(facts.size() == 7u);
120 auto processors = facts.get<map_value>(fact::processors);
121 REQUIRE(processors);
122 REQUIRE(processors->size() == 4u);
123 auto count = processors->get<integer_value>("count");
124 REQUIRE(count);
125 REQUIRE(count->value() == 4);
126 auto isa = processors->get<string_value>("isa");
127 REQUIRE(isa);
128 REQUIRE(isa->value() == "isa");
129 auto models = processors->get<array_value>("models");
130 REQUIRE(models);
131 REQUIRE(models->size() == 4u);
132 for (size_t i = 0; i < 4; ++i) {
133 auto model = models->get<string_value>(i);
134 REQUIRE(model);
135 REQUIRE(model->value() == "processor" + to_string(i + 1));
136 }
137 auto speed = processors->get<string_value>("speed");
138 REQUIRE(speed);
139 REQUIRE(speed->value() == "10.00 GHz");
140 }
141 THEN("standard flat facts are added, not including physical_processor_count") {
142 REQUIRE(facts.size() == 7u);
143 auto count = facts.get<integer_value>(fact::processor_count);
144 REQUIRE(count);
145 REQUIRE(count->value() == 4);
146 auto isa = facts.get<string_value>(fact::hardware_isa);
147 REQUIRE(isa);
148 REQUIRE(isa->value() == "isa");
149 for (size_t i = 0; i < 4; ++i) {
150 auto model = facts.get<string_value>(fact::processor + to_string(i));
151 REQUIRE(model);
152 REQUIRE(model->value() == "processor" + to_string(i + 1));
153 }
154 }
155 }
156 }
+0
-74
lib/tests/facts/resolvers/ruby_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/ruby_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_ruby_resolver : ruby_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_ruby_resolver : ruby_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.platform = "i386-mingw32";
29 result.sitedir = "C:/Ruby21/lib/ruby/site_ruby/2.1.0";
30 result.version = "2.1.4";
31 return result;
32 }
33 };
34
35 SCENARIO("using the ruby resolver") {
36 collection_fixture facts;
37 WHEN("data is not present") {
38 facts.add(make_shared<empty_ruby_resolver>());
39 THEN("facts should not be added") {
40 REQUIRE(facts.size() == 0u);
41 }
42 }
43 WHEN("data is present") {
44 facts.add(make_shared<test_ruby_resolver>());
45 THEN("a structured fact is added") {
46 REQUIRE(facts.size() == 4u);
47 auto ruby = facts.get<map_value>(fact::ruby);
48 REQUIRE(ruby);
49 REQUIRE(ruby->size() == 3u);
50 auto platform = ruby->get<string_value>("platform");
51 REQUIRE(platform);
52 REQUIRE(platform->value() == "i386-mingw32");
53 auto sitedir = ruby->get<string_value>("sitedir");
54 REQUIRE(sitedir);
55 REQUIRE(sitedir->value() == "C:/Ruby21/lib/ruby/site_ruby/2.1.0");
56 auto version = ruby->get<string_value>("version");
57 REQUIRE(version);
58 REQUIRE(version->value() == "2.1.4");
59 }
60 THEN("flat facts are added") {
61 REQUIRE(facts.size() == 4u);
62 auto platform = facts.get<string_value>(fact::rubyplatform);
63 REQUIRE(platform);
64 REQUIRE(platform->value() == "i386-mingw32");
65 auto sitedir = facts.get<string_value>(fact::rubysitedir);
66 REQUIRE(sitedir);
67 REQUIRE(sitedir->value() == "C:/Ruby21/lib/ruby/site_ruby/2.1.0");
68 auto version = facts.get<string_value>(fact::rubyversion);
69 REQUIRE(version);
70 REQUIRE(version->value() == "2.1.4");
71 }
72 }
73 }
+0
-114
lib/tests/facts/resolvers/ssh_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/ssh_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace boost::filesystem;
10 using namespace facter::facts;
11 using namespace facter::facts::resolvers;
12 using namespace facter::testing;
13
14 struct empty_ssh_resolver : ssh_resolver
15 {
16 protected:
17 data collect_data(collection& facts) override
18 {
19 return {};
20 }
21
22 virtual path retrieve_key_file(std::string const& filename) override
23 {
24 return path("");
25 }
26 };
27
28 struct test_ssh_resolver : ssh_resolver
29 {
30 protected:
31 data collect_data(collection& facts) override
32 {
33 data result;
34 result.dsa.key = "dsa:key";
35 result.dsa.type = "dsa:type";
36 result.dsa.digest.sha1 = "dsa:sha1";
37 result.dsa.digest.sha256 = "dsa:sha256";
38 result.ecdsa.key = "ecdsa:key";
39 result.ecdsa.type = "ecdsa:type";
40 result.ecdsa.digest.sha1 = "ecdsa:sha1";
41 result.ecdsa.digest.sha256 = "ecdsa:sha256";
42 result.ed25519.key = "ed25519:key";
43 result.ed25519.type = "ed25519:type";
44 result.ed25519.digest.sha1 = "ed25519:sha1";
45 result.ed25519.digest.sha256 = "ed25519:sha256";
46 result.rsa.key = "rsa:key";
47 result.rsa.type = "rsa:type";
48 result.rsa.digest.sha1 = "rsa:sha1";
49 result.rsa.digest.sha256 = "rsa:sha256";
50 return result;
51 }
52
53 virtual path retrieve_key_file(std::string const& filename) override
54 {
55 return path(filename);
56 }
57 };
58
59 SCENARIO("using the ssh resolver") {
60 collection_fixture facts;
61 WHEN("data is not present") {
62 facts.add(make_shared<empty_ssh_resolver>());
63 THEN("facts should not be added") {
64 REQUIRE(facts.size() == 0u);
65 }
66 }
67 WHEN("data is present") {
68 static const vector<string> algorithms = {
69 "dsa",
70 "ecdsa",
71 "ed25519",
72 "rsa"
73 };
74 facts.add(make_shared<test_ssh_resolver>());
75 THEN("a structured fact is added") {
76 REQUIRE(facts.size() == 9u);
77 auto ssh = facts.get<map_value>(fact::ssh);
78 REQUIRE(ssh);
79 REQUIRE(ssh->size() == 4u);
80 for (auto const& algorithm : algorithms) {
81 auto entry = ssh->get<map_value>(algorithm);
82 REQUIRE(entry);
83 REQUIRE(entry->size() == 3u);
84 auto key = entry->get<string_value>("key");
85 REQUIRE(key);
86 REQUIRE(key->value() == algorithm + ":key");
87 auto type = entry->get<string_value>("type");
88 REQUIRE(type);
89 REQUIRE(type->value() == algorithm + ":type");
90 auto fingerprints = entry->get<map_value>("fingerprints");
91 REQUIRE(fingerprints);
92 REQUIRE(fingerprints->size() == 2u);
93 auto fingerprint = fingerprints->get<string_value>("sha1");
94 REQUIRE(fingerprint);
95 REQUIRE(fingerprint->value() == algorithm + ":sha1");
96 fingerprint = fingerprints->get<string_value>("sha256");
97 REQUIRE(fingerprint);
98 REQUIRE(fingerprint->value() == algorithm + ":sha256");
99 }
100 }
101 THEN("flat facts are added") {
102 REQUIRE(facts.size() == 9u);
103 for (auto const& algorithm : algorithms) {
104 auto key = facts.get<string_value>("ssh" + algorithm + "key");
105 REQUIRE(key);
106 REQUIRE(key->value() == algorithm + ":key");
107 auto fingerprint = facts.get<string_value>("sshfp_" + algorithm);
108 REQUIRE(fingerprint);
109 REQUIRE(fingerprint->value() == algorithm + ":sha1\n" + algorithm + ":sha256");
110 }
111 }
112 }
113 }
+0
-135
lib/tests/facts/resolvers/system_profiler_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/system_profiler_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_system_profiler_resolver : system_profiler_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 return {};
19 }
20 };
21
22 struct test_system_profiler_resolver : system_profiler_resolver
23 {
24 protected:
25 virtual data collect_data(collection& facts) override
26 {
27 data result;
28 result.boot_mode = "boot_mode";
29 result.boot_rom_version = "boot_rom_version";
30 result.boot_volume = "boot_volume";
31 result.processor_name = "processor_name";
32 result.processor_speed = "processor_speed";
33 result.kernel_version = "kernel_version";
34 result.l2_cache_per_core = "l2_cache_per_core";
35 result.l3_cache = "l3_cache";
36 result.computer_name = "computer_name";
37 result.model_identifier = "model_identifier";
38 result.model_name = "model_name";
39 result.cores = "cores";
40 result.system_version = "system_version";
41 result.processors = "processors";
42 result.memory = "memory";
43 result.hardware_uuid = "hardware_uuid";
44 result.secure_virtual_memory = "secure_virtual_memory";
45 result.serial_number = "serial_number";
46 result.smc_version = "smc_version";
47 result.uptime = "uptime";
48 result.username = "username";
49 return result;
50 }
51 };
52
53 SCENARIO("using the system profiler resolver") {
54 collection_fixture facts;
55 WHEN("data is not present") {
56 facts.add(make_shared<empty_system_profiler_resolver>());
57 THEN("facts should not be added") {
58 REQUIRE(facts.size() == 0u);
59 }
60 }
61 WHEN("data is present") {
62 static const vector<string> algorithms = {
63 "dsa",
64 "ecdsa",
65 "ed25519",
66 "rsa"
67 };
68 facts.add(make_shared<test_system_profiler_resolver>());
69 THEN("a structured fact is added") {
70 auto system_profiler = facts.get<map_value>(fact::system_profiler);
71 REQUIRE(system_profiler);
72 REQUIRE(system_profiler->size() == 21u);
73 static const vector<string> names = {
74 "boot_mode",
75 "boot_rom_version",
76 "boot_volume",
77 "processor_name",
78 "processor_speed",
79 "kernel_version",
80 "l2_cache_per_core" ,
81 "l3_cache",
82 "computer_name",
83 "model_identifier",
84 "model_name",
85 "cores",
86 "system_version",
87 "processors",
88 "memory",
89 "hardware_uuid",
90 "secure_virtual_memory",
91 "serial_number",
92 "smc_version",
93 "uptime",
94 "username"
95 };
96 for (auto const& name : names) {
97 auto sval = system_profiler->get<string_value>(name);
98 REQUIRE(sval);
99 REQUIRE(sval->value() == name);
100 }
101 }
102 THEN("flat facts are added") {
103 REQUIRE(facts.size() == 22u);
104 static const map<string, string> check = {
105 { string(fact::sp_boot_mode), "boot_mode" },
106 { string(fact::sp_boot_rom_version), "boot_rom_version" },
107 { string(fact::sp_boot_volume), "boot_volume" },
108 { string(fact::sp_cpu_type), "processor_name" },
109 { string(fact::sp_current_processor_speed), "processor_speed" },
110 { string(fact::sp_kernel_version), "kernel_version" },
111 { string(fact::sp_l2_cache_core), "l2_cache_per_core" },
112 { string(fact::sp_l3_cache), "l3_cache" },
113 { string(fact::sp_local_host_name), "computer_name" },
114 { string(fact::sp_machine_model), "model_identifier" },
115 { string(fact::sp_machine_name), "model_name" },
116 { string(fact::sp_number_processors), "cores" },
117 { string(fact::sp_os_version), "system_version" },
118 { string(fact::sp_packages), "processors" },
119 { string(fact::sp_physical_memory), "memory" },
120 { string(fact::sp_platform_uuid), "hardware_uuid" },
121 { string(fact::sp_secure_vm), "secure_virtual_memory" },
122 { string(fact::sp_serial_number), "serial_number" },
123 { string(fact::sp_smc_version_system), "smc_version" },
124 { string(fact::sp_uptime), "uptime" },
125 { string(fact::sp_user_name), "username" }
126 };
127 for (auto const& kvp : check) {
128 auto sval = facts.get<string_value>(kvp.first);
129 REQUIRE(sval);
130 REQUIRE(sval->value() == kvp.second);
131 }
132 }
133 }
134 }
+0
-49
lib/tests/facts/resolvers/timezone_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/timezone_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include "../../collection_fixture.hpp"
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace facter::facts::resolvers;
10 using namespace facter::testing;
11
12 struct empty_timezone_resolver : timezone_resolver
13 {
14 protected:
15 virtual string get_timezone() override
16 {
17 return {};
18 }
19 };
20
21 struct test_timezone_resolver : timezone_resolver
22 {
23 protected:
24 virtual string get_timezone() override
25 {
26 return "PDT";
27 }
28 };
29
30 // TestRail C69635
31 SCENARIO("using the timezone resolver") {
32 collection_fixture facts;
33 WHEN("data is not present") {
34 facts.add(make_shared<empty_timezone_resolver>());
35 THEN("facts should not be added") {
36 REQUIRE(facts.size() == 0u);
37 }
38 }
39 WHEN("data is present") {
40 facts.add(make_shared<test_timezone_resolver>());
41 THEN("a flat fact is added") {
42 REQUIRE(facts.size() == 1u);
43 auto timezone = facts.get<string_value>(fact::timezone);
44 REQUIRE(timezone);
45 REQUIRE(timezone->value() == "PDT");
46 }
47 }
48 }
+0
-151
lib/tests/facts/resolvers/uptime_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/uptime_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct test_less_than_day_resolver : uptime_resolver
14 {
15 protected:
16 virtual int64_t get_uptime() override
17 {
18 // 13 hours, 35 minutes, 6 seconds
19 return (13 * 60 * 60) + (35 * 60) + 6;
20 }
21 };
22
23 struct test_one_day_resolver : uptime_resolver
24 {
25 protected:
26 virtual int64_t get_uptime() override
27 {
28 // 1 day, 2 hours, 12 minutes, 22 seconds
29 return (1 * 24 * 60 * 60) + (2 * 60 * 60) + (12 * 60) + 22;
30 }
31 };
32
33 struct test_more_than_day_resolver : uptime_resolver
34 {
35 protected:
36 virtual int64_t get_uptime() override
37 {
38 // 3 day, 4 hours, 19 minutes, 45 seconds
39 return (3 * 24 * 60 * 60) + (4 * 60 * 60) + (19 * 60) + 45;
40 }
41 };
42
43 SCENARIO("using the uptime resolver") {
44 collection_fixture facts;
45 WHEN("the uptime is less than one day") {
46 facts.add(make_shared<test_less_than_day_resolver>());
47 THEN("a structured fact with 'hours' uptime is added") {
48 auto system_uptime = facts.get<map_value>(fact::system_uptime);
49 REQUIRE(system_uptime);
50 REQUIRE(system_uptime->size() == 4u);
51 auto time = system_uptime->get<integer_value>("seconds");
52 REQUIRE(time);
53 REQUIRE(time->value() == 48906);
54 time = system_uptime->get<integer_value>("hours");
55 REQUIRE(time);
56 REQUIRE(time->value() == 13);
57 time = system_uptime->get<integer_value>("days");
58 REQUIRE(time);
59 REQUIRE(time->value() == 0);
60 auto uptime = system_uptime->get<string_value>("uptime");
61 REQUIRE(uptime);
62 REQUIRE(uptime->value() == "13:35 hours");
63 }
64 THEN("flat facts with 'hours' uptime is added") {
65 REQUIRE(facts.size() == 5u);
66 auto time = facts.get<integer_value>(fact::uptime_seconds);
67 REQUIRE(time);
68 REQUIRE(time->value() == 48906);
69 time = facts.get<integer_value>(fact::uptime_hours);
70 REQUIRE(time);
71 REQUIRE(time->value() == 13);
72 time = facts.get<integer_value>(fact::uptime_days);
73 REQUIRE(time);
74 REQUIRE(time->value() == 0);
75 auto uptime = facts.get<string_value>(fact::uptime);
76 REQUIRE(uptime);
77 REQUIRE(uptime->value() == "13:35 hours");
78 }
79 }
80 WHEN("the uptime is one day") {
81 facts.add(make_shared<test_one_day_resolver>());
82 THEN("a structured fact with '1 day' uptime is added") {
83 auto system_uptime = facts.get<map_value>(fact::system_uptime);
84 REQUIRE(system_uptime);
85 REQUIRE(system_uptime->size() == 4u);
86 auto time = system_uptime->get<integer_value>("seconds");
87 REQUIRE(time);
88 REQUIRE(time->value() == 94342);
89 time = system_uptime->get<integer_value>("hours");
90 REQUIRE(time);
91 REQUIRE(time->value() == 26);
92 time = system_uptime->get<integer_value>("days");
93 REQUIRE(time);
94 REQUIRE(time->value() == 1);
95 auto uptime = system_uptime->get<string_value>("uptime");
96 REQUIRE(uptime);
97 REQUIRE(uptime->value() == "1 day");
98 }
99 THEN("flat facts with '1 day' uptime is added") {
100 REQUIRE(facts.size() == 5u);
101 auto time = facts.get<integer_value>(fact::uptime_seconds);
102 REQUIRE(time);
103 REQUIRE(time->value() == 94342);
104 time = facts.get<integer_value>(fact::uptime_hours);
105 REQUIRE(time);
106 REQUIRE(time->value() == 26);
107 time = facts.get<integer_value>(fact::uptime_days);
108 REQUIRE(time);
109 REQUIRE(time->value() == 1);
110 auto uptime = facts.get<string_value>(fact::uptime);
111 REQUIRE(uptime);
112 REQUIRE(uptime->value() == "1 day");
113 }
114 }
115 WHEN("the uptime is more than one day") {
116 facts.add(make_shared<test_more_than_day_resolver>());
117 THEN("a structured fact with 'x days' uptime is added") {
118 auto system_uptime = facts.get<map_value>(fact::system_uptime);
119 REQUIRE(system_uptime);
120 REQUIRE(system_uptime->size() == 4u);
121 auto time = system_uptime->get<integer_value>("seconds");
122 REQUIRE(time);
123 REQUIRE(time->value() == 274785);
124 time = system_uptime->get<integer_value>("hours");
125 REQUIRE(time);
126 REQUIRE(time->value() == 76);
127 time = system_uptime->get<integer_value>("days");
128 REQUIRE(time);
129 REQUIRE(time->value() == 3);
130 auto uptime = system_uptime->get<string_value>("uptime");
131 REQUIRE(uptime);
132 REQUIRE(uptime->value() == "3 days");
133 }
134 THEN("flat facts with 'x days' uptime is added") {
135 REQUIRE(facts.size() == 5u);
136 auto time = facts.get<integer_value>(fact::uptime_seconds);
137 REQUIRE(time);
138 REQUIRE(time->value() == 274785);
139 time = facts.get<integer_value>(fact::uptime_hours);
140 REQUIRE(time);
141 REQUIRE(time->value() == 76);
142 time = facts.get<integer_value>(fact::uptime_days);
143 REQUIRE(time);
144 REQUIRE(time->value() == 3);
145 auto uptime = facts.get<string_value>(fact::uptime);
146 REQUIRE(uptime);
147 REQUIRE(uptime->value() == "3 days");
148 }
149 }
150 }
+0
-175
lib/tests/facts/resolvers/virtualization_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/virtualization_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/vm.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_virtualization_resolver : virtualization_resolver
14 {
15 protected:
16 string get_hypervisor(collection& facts) override
17 {
18 return "";
19 }
20 string get_cloud_provider(collection& facts) override
21 {
22 return "";
23 }
24 };
25
26 struct unknown_hypervisor_resolver : virtualization_resolver
27 {
28 protected:
29 string get_hypervisor(collection& facts) override
30 {
31 return "foobar";
32 }
33 string get_cloud_provider(collection& facts) override
34 {
35 return "provider";
36 }
37 };
38
39 struct unknown_non_virtual_hypervisor_resolver : virtualization_resolver
40 {
41 protected:
42 string get_hypervisor(collection& facts) override
43 {
44 return "foobar";
45 }
46 string get_cloud_provider(collection& facts) override
47 {
48 return "provider";
49 }
50
51 bool is_virtual(string const& hypervisor) override
52 {
53 return hypervisor != "foobar";
54 }
55 };
56
57 struct known_hypervisor_resolver : virtualization_resolver
58 {
59 protected:
60 string get_hypervisor(collection& facts) override
61 {
62 return vm::docker;
63 }
64 string get_cloud_provider(collection& facts) override
65 {
66 return "provider";
67 }
68 };
69
70 struct matched_product_hypervisor_resolver : virtualization_resolver
71 {
72 protected:
73 string get_hypervisor(collection& facts) override
74 {
75 facts.add(fact::product_name, make_value<string_value>("VMware"));
76 auto result = get_fact_vm(facts);
77 facts.remove(fact::product_name);
78 return result;
79 }
80 string get_cloud_provider(collection& facts) override
81 {
82 return "provider";
83 }
84 };
85
86 struct matched_vendor_hypervisor_resolver : virtualization_resolver
87 {
88 protected:
89 string get_hypervisor(collection& facts) override
90 {
91 facts.add(fact::bios_vendor, make_value<string_value>("Amazon EC2"));
92 auto result = get_fact_vm(facts);
93 facts.remove(fact::bios_vendor);
94 return result;
95 }
96 string get_cloud_provider(collection& facts) override
97 {
98 return "provider";
99 }
100 };
101
102 SCENARIO("using the virtualization resolver") {
103 collection_fixture facts;
104 WHEN("no hypervisor is returned") {
105 facts.add(make_shared<empty_virtualization_resolver>());
106 THEN("the system is reported as physical") {
107 REQUIRE(facts.size() == 2u);
108 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
109 REQUIRE(is_virt);
110 REQUIRE_FALSE(is_virt->value());
111 auto hypervisor = facts.get<string_value>(fact::virtualization);
112 REQUIRE(hypervisor);
113 REQUIRE(hypervisor->value() == "physical");
114 }
115 }
116 WHEN("an unknown virtual hypervisor is returned") {
117 facts.add(make_shared<unknown_hypervisor_resolver>());
118 THEN("the system is reported as virtual") {
119 REQUIRE(facts.size() == 3u);
120 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
121 REQUIRE(is_virt);
122 REQUIRE(is_virt->value());
123 auto hypervisor = facts.get<string_value>(fact::virtualization);
124 REQUIRE(hypervisor);
125 REQUIRE(hypervisor->value() == "foobar");
126 }
127 }
128 WHEN("an unknown physical hypervisor is returned") {
129 facts.add(make_shared<unknown_non_virtual_hypervisor_resolver>());
130 THEN("the system is reported as virtual") {
131 REQUIRE(facts.size() == 3u);
132 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
133 REQUIRE(is_virt);
134 REQUIRE_FALSE(is_virt->value());
135 auto hypervisor = facts.get<string_value>(fact::virtualization);
136 REQUIRE(hypervisor);
137 REQUIRE(hypervisor->value() == "foobar");
138 }
139 }
140 WHEN("a known hypervisor is returned") {
141 facts.add(make_shared<known_hypervisor_resolver>());
142 THEN("the system is reported as virtual") {
143 REQUIRE(facts.size() == 3u);
144 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
145 REQUIRE(is_virt);
146 REQUIRE(is_virt->value());
147 auto hypervisor = facts.get<string_value>(fact::virtualization);
148 REQUIRE(hypervisor);
149 REQUIRE(hypervisor->value() == string(vm::docker));
150 }
151 }
152
153 WHEN("a hypervisor is matched from product name") {
154 facts.add(make_shared<matched_product_hypervisor_resolver>());
155 THEN("the system is reported as virtual") {
156 REQUIRE(facts.size() == 3u);
157 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
158 REQUIRE(is_virt);
159 REQUIRE(is_virt->value());
160 auto hypervisor = facts.get<string_value>(fact::virtualization);
161 REQUIRE(hypervisor);
162 REQUIRE(hypervisor->value() == string(vm::vmware));
163 }
164 }
165 WHEN("a hypervisor is matched from the vendor name") {
166 facts.add(make_shared<matched_vendor_hypervisor_resolver>());
167 auto is_virt = facts.get<boolean_value>(fact::is_virtual);
168 REQUIRE(is_virt);
169 REQUIRE(is_virt->value());
170 auto hypervisor = facts.get<string_value>(fact::virtualization);
171 REQUIRE(hypervisor);
172 REQUIRE(hypervisor->value() == string(vm::kvm));
173 }
174 }
+0
-100
lib/tests/facts/resolvers/xen_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/xen_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/vm.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <facter/facts/array_value.hpp>
8 #include "../../collection_fixture.hpp"
9
10 using namespace std;
11 using namespace facter::facts;
12 using namespace facter::facts::resolvers;
13 using namespace facter::testing;
14
15 struct empty_xen_resolver : xen_resolver
16 {
17 protected:
18 virtual string xen_command() override
19 {
20 return "";
21 }
22
23 virtual data collect_data(collection& facts) override
24 {
25 data result;
26 return result;
27 }
28 };
29
30 struct test_xen_resolver : xen_resolver
31 {
32 protected:
33 virtual string xen_command() override
34 {
35 return "";
36 }
37
38 virtual data collect_data(collection& facts) override
39 {
40 data result;
41 result.domains = { "domain1", "domain2" };
42 return result;
43 }
44 };
45
46 // CATCH doesn't behave well with constexpr, so create memory for
47 // the string here before using it in the test.
48 constexpr static char const* xen_privileged = vm::xen_privileged;
49
50 SCENARIO("using the Xen resolver on a privileged VM") {
51 collection_fixture facts;
52 facts.add(fact::virtualization, make_value<string_value>(xen_privileged));
53 WHEN("data is not present") {
54 facts.add(make_shared<empty_xen_resolver>());
55 THEN("facts should not be added") {
56 REQUIRE(facts.size() == 1u);
57 }
58 }
59 WHEN("data is present") {
60 facts.add(make_shared<test_xen_resolver>());
61 THEN("flat facts are added") {
62 REQUIRE(facts.size() == 3u);
63 auto value = facts.get<string_value>(fact::xendomains);
64 REQUIRE(value);
65 REQUIRE(value->value() == "domain1,domain2");
66 }
67
68 THEN("structured facts are added") {
69 REQUIRE(facts.size() == 3u);
70 auto xen = facts.get<map_value>(fact::xen);
71 REQUIRE(xen);
72 REQUIRE(xen->size() == 1u);
73 auto domains = xen->get<array_value>("domains");
74 REQUIRE(domains);
75 REQUIRE(domains->size() == 2u);
76 for (size_t i = 0; i < 2; ++i) {
77 auto domain = domains->get<string_value>(i);
78 REQUIRE(domain);
79 REQUIRE(domain->value() == "domain" + to_string(i+1));
80 }
81 }
82 }
83 }
84
85 SCENARIO("using the Xen resolver on an unprivileged machine") {
86 collection_fixture facts;
87 WHEN("data is not present") {
88 facts.add(make_shared<empty_xen_resolver>());
89 THEN("facts should not be added") {
90 REQUIRE(facts.size() == 0u);
91 }
92 }
93 WHEN("data is present") {
94 facts.add(make_shared<test_xen_resolver>());
95 THEN("facts should not be added") {
96 REQUIRE(facts.size() == 0u);
97 }
98 }
99 }
+0
-65
lib/tests/facts/resolvers/zfs_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/zfs_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include "../../collection_fixture.hpp"
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace facter::facts::resolvers;
10 using namespace facter::testing;
11
12 struct empty_zfs_resolver : zfs_resolver
13 {
14 protected:
15 virtual string zfs_command() override
16 {
17 return "";
18 }
19
20 virtual data collect_data(collection& facts) override
21 {
22 data result;
23 return result;
24 }
25 };
26
27 struct test_zfs_resolver : zfs_resolver
28 {
29 protected:
30 virtual string zfs_command() override
31 {
32 return "";
33 }
34
35 virtual data collect_data(collection& facts) override
36 {
37 data result;
38 result.version = "1";
39 result.versions = { "1", "2", "3" };
40 return result;
41 }
42 };
43
44 SCENARIO("using the ZFS resolver") {
45 collection_fixture facts;
46 WHEN("data is not present") {
47 facts.add(make_shared<empty_zfs_resolver>());
48 THEN("facts should not be added") {
49 REQUIRE(facts.size() == 0u);
50 }
51 }
52 WHEN("data is present") {
53 facts.add(make_shared<test_zfs_resolver>());
54 THEN("flat facts are added") {
55 REQUIRE(facts.size() == 2u);
56 auto value = facts.get<string_value>(fact::zfs_version);
57 REQUIRE(value);
58 REQUIRE(value->value() == "1");
59 value = facts.get<string_value>(fact::zfs_versionnumbers);
60 REQUIRE(value);
61 REQUIRE(value->value() == "1,2,3");
62 }
63 }
64 }
+0
-122
lib/tests/facts/resolvers/zone_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/zone_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include <facter/facts/map_value.hpp>
6 #include "../../collection_fixture.hpp"
7
8 using namespace std;
9 using namespace facter::facts;
10 using namespace facter::facts::resolvers;
11 using namespace facter::testing;
12
13 struct empty_zone_resolver : zone_resolver
14 {
15 protected:
16 virtual data collect_data(collection& facts) override
17 {
18 data result;
19 return result;
20 }
21 };
22
23 struct test_zone_resolver : zone_resolver
24 {
25 protected:
26 virtual data collect_data(collection& facts) override
27 {
28 data result;
29 result.current_zone_name = "current";
30
31 zone z;
32 z.uuid = "uuid";
33 z.status = "status";
34 z.path = "path";
35 z.name = "name";
36 z.brand = "brand";
37 z.id = "id";
38 z.ip_type = "ip type";
39 result.zones.emplace_back(move(z));
40 return result;
41 }
42 };
43
44 SCENARIO("using the Solaris zone resolver") {
45 collection_fixture facts;
46 WHEN("data is not present") {
47 facts.add(make_shared<empty_zone_resolver>());
48 THEN("only the zone count should be added") {
49 REQUIRE(facts.size() == 1u);
50 auto value = facts.get<integer_value>(fact::zones);
51 REQUIRE(value);
52 REQUIRE(value->value() == 0);
53 }
54 }
55 WHEN("data is present") {
56 facts.add(make_shared<test_zone_resolver>());
57 THEN("a structured fact is added") {
58 REQUIRE(facts.size() == 10u);
59 auto mval = facts.get<map_value>(fact::solaris_zones);
60 REQUIRE(mval);
61 REQUIRE(mval->size() == 2u);
62 auto sval = mval->get<string_value>("current");
63 REQUIRE(sval);
64 REQUIRE(sval->value() == "current");
65 mval = mval->get<map_value>("zones");
66 REQUIRE(mval);
67 REQUIRE(mval->size() == 1u);
68 mval = mval->get<map_value>("name");
69 REQUIRE(mval);
70 REQUIRE(mval->size() == 6u);
71 sval = mval->get<string_value>("uuid");
72 REQUIRE(sval);
73 REQUIRE(sval->value() == "uuid");
74 sval = mval->get<string_value>("status");
75 REQUIRE(sval);
76 REQUIRE(sval->value() == "status");
77 sval = mval->get<string_value>("path");
78 REQUIRE(sval);
79 REQUIRE(sval->value() == "path");
80 sval = mval->get<string_value>("brand");
81 REQUIRE(sval);
82 REQUIRE(sval->value() == "brand");
83 sval = mval->get<string_value>("id");
84 REQUIRE(sval);
85 REQUIRE(sval->value() == "id");
86 sval = mval->get<string_value>("ip_type");
87 REQUIRE(sval);
88 REQUIRE(sval->value() == "ip type");
89 }
90 THEN("flat facts are added") {
91 REQUIRE(facts.size() == 10u);
92 auto ival = facts.get<integer_value>(fact::zones);
93 REQUIRE(ival);
94 REQUIRE(ival->value() == 1);
95 auto sval = facts.get<string_value>(fact::zonename);
96 REQUIRE(sval);
97 REQUIRE(sval->value() == "current");
98 sval = facts.get<string_value>(string("zone_name_") + fact::zone_iptype);
99 REQUIRE(sval);
100 REQUIRE(sval->value() == "ip type");
101 sval = facts.get<string_value>(string("zone_name_") + fact::zone_brand);
102 REQUIRE(sval);
103 REQUIRE(sval->value() == "brand");
104 sval = facts.get<string_value>(string("zone_name_") + fact::zone_uuid);
105 REQUIRE(sval);
106 REQUIRE(sval->value() == "uuid");
107 sval = facts.get<string_value>(string("zone_name_") + fact::zone_id);
108 REQUIRE(sval);
109 REQUIRE(sval->value() == "id");
110 sval = facts.get<string_value>(string("zone_name_") + fact::zone_name);
111 REQUIRE(sval);
112 REQUIRE(sval->value() == "name");
113 sval = facts.get<string_value>(string("zone_name_") + fact::zone_path);
114 REQUIRE(sval);
115 REQUIRE(sval->value() == "path");
116 sval = facts.get<string_value>(string("zone_name_") + fact::zone_status);
117 REQUIRE(sval);
118 REQUIRE(sval->value() == "status");
119 }
120 }
121 }
+0
-69
lib/tests/facts/resolvers/zpool_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/resolvers/zpool_resolver.hpp>
2 #include <facter/facts/collection.hpp>
3 #include <facter/facts/fact.hpp>
4 #include <facter/facts/scalar_value.hpp>
5 #include "../../collection_fixture.hpp"
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace facter::facts::resolvers;
10 using namespace facter::testing;
11
12 struct empty_zpool_resolver : zpool_resolver
13 {
14 protected:
15 virtual string zpool_command() override
16 {
17 return "";
18 }
19
20 virtual data collect_data(collection& facts) override
21 {
22 data result;
23 return result;
24 }
25 };
26
27 struct test_zpool_resolver : zpool_resolver
28 {
29 protected:
30 virtual string zpool_command() override
31 {
32 return "";
33 }
34
35 virtual data collect_data(collection& facts) override
36 {
37 data result;
38 result.version = "1";
39 result.versions = { "1", "2", "3" };
40 result.feature_flags = { "async_destroy", "lz4_compress", "enabled_txg" };
41 return result;
42 }
43 };
44
45 SCENARIO("using the zpool resolver") {
46 collection_fixture facts;
47 WHEN("data is not present") {
48 facts.add(make_shared<empty_zpool_resolver>());
49 THEN("facts should not be added") {
50 REQUIRE(facts.size() == 0u);
51 }
52 }
53 WHEN("data is present") {
54 facts.add(make_shared<test_zpool_resolver>());
55 THEN("flat facts are added") {
56 REQUIRE(facts.size() == 3u);
57 auto value = facts.get<string_value>(fact::zpool_version);
58 REQUIRE(value);
59 REQUIRE(value->value() == "1");
60 value = facts.get<string_value>(fact::zpool_versionnumbers);
61 REQUIRE(value);
62 REQUIRE(value->value() == "1,2,3");
63 value = facts.get<string_value>(fact::zpool_featureflags);
64 REQUIRE(value);
65 REQUIRE(value->value() == "async_destroy,lz4_compress,enabled_txg");
66 }
67 }
68 }
+0
-807
lib/tests/facts/schema.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/fact.hpp>
3 #include <facter/facts/vm.hpp>
4 #include <facter/facts/resolver.hpp>
5 #include <facter/facts/array_value.hpp>
6 #include <facter/facts/map_value.hpp>
7 #include <facter/facts/scalar_value.hpp>
8 #include <leatherman/util/regex.hpp>
9 #include <yaml-cpp/yaml.h>
10 #include <boost/nowide/fstream.hpp>
11 #include "../fixtures.hpp"
12
13 // Include all base resolvers here
14 #include <internal/facts/resolvers/augeas_resolver.hpp>
15 #include <internal/facts/resolvers/disk_resolver.hpp>
16 #include <internal/facts/resolvers/dmi_resolver.hpp>
17 #include <internal/facts/resolvers/ec2_resolver.hpp>
18 #include <internal/facts/resolvers/filesystem_resolver.hpp>
19 #include <internal/facts/resolvers/fips_resolver.hpp>
20 #include <internal/facts/resolvers/gce_resolver.hpp>
21 #include <internal/facts/resolvers/hypervisors_resolver.hpp>
22 #include <internal/facts/resolvers/identity_resolver.hpp>
23 #include <internal/facts/resolvers/kernel_resolver.hpp>
24 #include <internal/facts/resolvers/ldom_resolver.hpp>
25 #include <internal/facts/resolvers/load_average_resolver.hpp>
26 #include <internal/facts/resolvers/memory_resolver.hpp>
27 #include <internal/facts/resolvers/networking_resolver.hpp>
28 #include <internal/facts/resolvers/operating_system_resolver.hpp>
29 #include <internal/facts/resolvers/path_resolver.hpp>
30 #include <internal/facts/resolvers/processor_resolver.hpp>
31 #include <internal/facts/resolvers/ruby_resolver.hpp>
32 #include <internal/facts/resolvers/ssh_resolver.hpp>
33 #include <internal/facts/resolvers/system_profiler_resolver.hpp>
34 #include <internal/facts/resolvers/timezone_resolver.hpp>
35 #include <internal/facts/resolvers/uptime_resolver.hpp>
36 #include <internal/facts/resolvers/virtualization_resolver.hpp>
37 #include <internal/facts/resolvers/xen_resolver.hpp>
38 #include <internal/facts/resolvers/zfs_resolver.hpp>
39 #include <internal/facts/resolvers/zone_resolver.hpp>
40 #include <internal/facts/resolvers/zpool_resolver.hpp>
41
42 using namespace std;
43 using namespace boost::filesystem;
44 using namespace facter::facts;
45 using namespace leatherman::util;
46 using namespace facter::testing;
47 using Catch::Matchers::AnyOf;
48
49 // For every base resolver, implement a resolver that outputs the minimum values to pass schema validation
50 // We don't care about the actual data in the facts, only that it conforms to the schema
51
52 struct augeas_resolver : resolvers::augeas_resolver
53 {
54 protected:
55 virtual string get_version() override
56 {
57 return "1.1.0";
58 }
59 };
60
61 struct disk_resolver : resolvers::disk_resolver
62 {
63 protected:
64 virtual data collect_data(collection& facts) override
65 {
66 data result;
67 result.disks.push_back({
68 "name",
69 "vendor",
70 "model",
71 "product",
72 1234,
73 "serial_number"
74 });
75 return result;
76 }
77 };
78
79 struct dmi_resolver : resolvers::dmi_resolver
80 {
81 protected:
82 virtual data collect_data(collection& facts) override
83 {
84 data result;
85 result.bios_vendor = fact::bios_vendor;
86 result.bios_version = fact::bios_version;
87 result.bios_release_date = fact::bios_release_date;
88 result.board_asset_tag = fact::board_asset_tag;
89 result.board_manufacturer = fact::board_manufacturer;
90 result.board_product_name = fact::board_product_name;
91 result.board_serial_number = fact::board_serial_number;
92 result.chassis_asset_tag = fact::chassis_asset_tag;
93 result.manufacturer = fact::manufacturer;
94 result.product_name = fact::product_name;
95 result.serial_number = fact::serial_number;
96 result.uuid = fact::uuid;
97 result.chassis_type = fact::chassis_type;
98 return result;
99 }
100 };
101
102 struct ec2_resolver : resolvers::ec2_resolver
103 {
104 void resolve(collection& facts) override
105 {
106 facts.add(fact::ec2_metadata, make_value<map_value>());
107 facts.add(fact::ec2_userdata, make_value<string_value>("user data"));
108 }
109 };
110
111 struct filesystem_resolver : resolvers::filesystem_resolver
112 {
113 protected:
114 virtual data collect_data(collection& facts) override
115 {
116 data result;
117
118 mountpoint mp;
119 mp.name = "name";
120 mp.device = "device";
121 mp.filesystem = "filesystem";
122 mp.size = 1234;
123 mp.available = 12345;
124 mp.options.push_back("option");
125 result.mountpoints.emplace_back(move(mp));
126
127 result.filesystems.insert("filesystem");
128
129 partition p;
130 p.name = "name";
131 p.filesystem = "filesystem";
132 p.size = 1234;
133 p.uuid = "uuid";
134 p.partition_uuid = "partuuid";
135 p.label = "label";
136 p.partition_label = "partlabel";
137 p.mount = "mount";
138 p.backing_file = "/foo/bar";
139 result.partitions.emplace_back(move(p));
140 return result;
141 }
142 };
143
144 struct fips_resolver : resolvers::fips_resolver
145 {
146 protected:
147 virtual data collect_data(collection& facts) override
148 {
149 data result;
150 result.is_fips_mode_enabled = false;
151 return result;
152 }
153 };
154
155 using hypervisor_data = std::unordered_map<std::string, std::unordered_map<std::string, boost::variant<std::string, bool, int>>>;
156
157 struct hypervisors_resolver_test : resolvers::hypervisors_resolver_base
158 {
159 virtual hypervisor_data collect_data(collection& facts)
160 {
161 hypervisor_data results;
162
163 unordered_map<std::string, boost::variant<std::string, bool, int>> metadata {
164 {"string_value", string{"string"}}, // boost assumes const char* values are bools when bool is among the variants
165 {"integer_value", 42},
166 {"boolean_value", true},
167 };
168
169 results.insert({"hypervisor_name", metadata});
170
171 return results;
172 }
173 };
174
175 struct identity_resolver : resolvers::identity_resolver
176 {
177 protected:
178 virtual data collect_data(collection& facts) override
179 {
180 data result;
181 result.group_id = 123;
182 result.group_name = "group";
183 result.user_id = 456;
184 result.user_name = "user";
185 result.privileged = false;
186 return result;
187 }
188 };
189
190 struct kernel_resolver : resolvers::kernel_resolver
191 {
192 protected:
193 virtual data collect_data(collection& facts) override
194 {
195 data result;
196 result.name = "kernel";
197 result.release = "1.2.3-kernel";
198 result.version = "1.2.3";
199 return result;
200 }
201 };
202
203 struct load_average_resolver : resolvers::load_average_resolver
204 {
205 protected:
206 virtual boost::optional<std::tuple<double, double, double> > get_load_averages() override
207 {
208 return make_tuple(0.12, 3.45, 6.78);
209 }
210 };
211
212 struct memory_resolver : resolvers::memory_resolver
213 {
214 protected:
215 virtual data collect_data(collection& facts) override
216 {
217 data result;
218 result.swap_encryption = encryption_status::encrypted;
219 result.mem_total = 10 * 1024 * 1024;
220 result.mem_free = 5 * 1024 * 1024;
221 result.swap_total = 20 * 1024 * 1024;
222 result.swap_free = 4 * 1024 * 1024;
223 return result;
224 }
225 };
226
227 struct networking_resolver : resolvers::networking_resolver
228 {
229 protected:
230 virtual data collect_data(collection& facts) override
231 {
232 data result;
233
234 result.hostname = "hostname";
235 result.domain = "domain";
236 result.fqdn = "hostname.domain";
237 result.primary_interface = "interface1";
238
239 interface iface;
240 iface.name = "interface1";
241 iface.dhcp_server = "192.168.1.1";
242 iface.ipv4_bindings.emplace_back(binding { "127.0.0.1", "255.0.0.0", "127.0.0.0"});
243 iface.ipv4_bindings.emplace_back(binding { "123.123.123.123", "255.255.255.0", "123.123.123.0"});
244 iface.ipv6_bindings.emplace_back(binding { "fe80::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::1"});
245 iface.ipv6_bindings.emplace_back(binding { "fe80::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::2"});
246 iface.macaddress = "00:00:00:00:00:00";
247 iface.mtu = 12345;
248 result.interfaces.emplace_back(move(iface));
249 return result;
250 }
251 };
252
253 struct operating_system_resolver : resolvers::operating_system_resolver
254 {
255 protected:
256 virtual data collect_data(collection& facts) override
257 {
258 data result;
259 result.name = "name";
260 result.family = "family";
261 result.release = "1.2.3";
262 result.major = "1.2";
263 result.minor = "3";
264 result.specification_version = "1.4";
265 result.distro.id = "id";
266 result.distro.release = "1.2.3";
267 result.distro.codename = "codename";
268 result.distro.description = "description";
269 result.freebsd.branch = "BRANCH";
270 result.freebsd.patchlevel = "4";
271 result.osx.product = "product";
272 result.osx.build = "build";
273 result.osx.version = "10.10";
274 result.win.release_id = "1904";
275 result.win.edition_id = "ServerStandard";
276 result.win.installation_type = "Server";
277 result.win.product_name = "Windows 2019 Standard Edition";
278 result.win.system32 = "system32";
279 result.architecture = "arch";
280 result.hardware = "hardware";
281 result.selinux.supported = true;
282 result.selinux.enabled = true;
283 result.selinux.enforced = true;
284 result.selinux.current_mode = "current mode";
285 result.selinux.config_mode = "config mode";
286 result.selinux.config_policy = "config policy";
287 result.selinux.policy_version = "policy version";
288 return result;
289 }
290 };
291
292 struct processor_resolver : resolvers::processor_resolver
293 {
294 protected:
295 virtual data collect_data(collection& facts) override
296 {
297 data result;
298 result.isa = "isa";
299 result.logical_count = 4;
300 result.physical_count = 2;
301 result.models = {
302 "processor1",
303 "processor2",
304 "processor3",
305 "processor4"
306 };
307 result.speed = 10 * 1000 * 1000 * 1000ull;
308 return result;
309 }
310 };
311
312 struct ruby_resolver : resolvers::ruby_resolver
313 {
314 protected:
315 virtual data collect_data(collection& facts) override
316 {
317 data result;
318 result.platform = "platform";
319 result.sitedir = "sitedir";
320 result.version = "2.1.5";
321 return result;
322 }
323 };
324
325 struct ssh_resolver : resolvers::ssh_resolver
326 {
327 protected:
328 data collect_data(collection& facts) override
329 {
330 data result;
331 result.dsa.key = "dsa:key";
332 result.dsa.type = "dsa:type";
333 result.dsa.digest.sha1 = "dsa:sha1";
334 result.dsa.digest.sha256 = "dsa:sha256";
335 result.ecdsa.key = "ecdsa:key";
336 result.ecdsa.type = "ecdsa:type";
337 result.ecdsa.digest.sha1 = "ecdsa:sha1";
338 result.ecdsa.digest.sha256 = "ecdsa:sha256";
339 result.ed25519.key = "ed25519:key";
340 result.ed25519.type = "ed25519:type";
341 result.ed25519.digest.sha1 = "ed25519:sha1";
342 result.ed25519.digest.sha256 = "ed25519:sha256";
343 result.rsa.key = "rsa:key";
344 result.rsa.type = "rsa:type";
345 result.rsa.digest.sha1 = "rsa:sha1";
346 result.rsa.digest.sha256 = "rsa:sha256";
347 return result;
348 }
349
350 virtual path retrieve_key_file(std::string const& filename) override
351 {
352 return path(filename);
353 }
354 };
355
356 struct system_profiler_resolver : resolvers::system_profiler_resolver
357 {
358 protected:
359 virtual data collect_data(collection& facts) override
360 {
361 data result;
362 result.boot_mode = "boot_mode";
363 result.boot_rom_version = "boot_rom_version";
364 result.boot_volume = "boot_volume";
365 result.processor_name = "processor_name";
366 result.processor_speed = "processor_speed";
367 result.kernel_version = "kernel_version";
368 result.l2_cache_per_core = "l2_cache_per_core";
369 result.l3_cache = "l3_cache";
370 result.computer_name = "computer_name";
371 result.model_identifier = "model_identifier";
372 result.model_name = "model_name";
373 result.cores = "cores";
374 result.system_version = "system_version";
375 result.processors = "processors";
376 result.memory = "memory";
377 result.hardware_uuid = "hardware_uuid";
378 result.secure_virtual_memory = "secure_virtual_memory";
379 result.serial_number = "serial_number";
380 result.smc_version = "smc_version";
381 result.uptime = "uptime";
382 result.username = "username";
383 return result;
384 }
385 };
386
387 struct timezone_resolver : resolvers::timezone_resolver
388 {
389 protected:
390 virtual string get_timezone() override
391 {
392 return "PDT";
393 }
394 };
395
396 struct uptime_resolver : resolvers::uptime_resolver
397 {
398 protected:
399 virtual int64_t get_uptime() override
400 {
401 return 1;
402 }
403 };
404
405 struct virtualization_resolver : resolvers::virtualization_resolver
406 {
407 protected:
408 virtual string get_hypervisor(collection& facts) override
409 {
410 // The xen fact only resolves if virtualization is xen_privileged.
411 return vm::xen_privileged;
412 }
413
414 virtual string get_cloud_provider(collection& facts) override
415 {
416 return "azure";
417 }
418 };
419
420 struct xen_resolver : resolvers::xen_resolver
421 {
422 protected:
423 virtual string xen_command() override
424 {
425 return "";
426 }
427
428 virtual data collect_data(collection& facts) override
429 {
430 data result;
431 result.domains = { "domain1", "domain2" };
432 return result;
433 }
434 };
435
436 struct zfs_resolver : resolvers::zfs_resolver
437 {
438 protected:
439 virtual string zfs_command() override
440 {
441 return "";
442 }
443
444 virtual data collect_data(collection& facts) override
445 {
446 data result;
447 result.version = 1;
448 result.versions = { "1", "2", "3" };
449 return result;
450 }
451 };
452
453 struct zone_resolver : resolvers::zone_resolver
454 {
455 protected:
456 virtual data collect_data(collection& facts) override
457 {
458 data result;
459 zone z;
460 z.brand = "brand";
461 z.id = "id";
462 z.ip_type = "ip type";
463 z.name = "name";
464 z.path = "path";
465 z.status = "status";
466 z.uuid = "uuid";
467 result.zones.emplace_back(move(z));
468 result.current_zone_name = "name";
469 return result;
470 }
471 };
472
473 struct zpool_resolver : resolvers::zpool_resolver
474 {
475 protected:
476 virtual string zpool_command() override
477 {
478 return "";
479 }
480
481 virtual data collect_data(collection& facts) override
482 {
483 data result;
484 result.version = 1;
485 result.versions = { "1", "2", "3" };
486 result.feature_flags = { "async_destroy", "lz4_compress", "enabled_txg" };
487 return result;
488 }
489 };
490
491 void add_all_facts(collection& facts)
492 {
493 facts.add("env_windows_installdir", make_value<string_value>("C:\\Program Files\\Some\\Path"));
494 facts.add("facterversion", make_value<string_value>("version"));
495 facts.add("aio_agent_version", make_value<string_value>(""));
496 facts.add(make_shared<augeas_resolver>());
497 facts.add(make_shared<disk_resolver>());
498 facts.add(make_shared<dmi_resolver>());
499 facts.add(make_shared<filesystem_resolver>());
500 facts.add(make_shared<fips_resolver>());
501 // TODO: refactor the EC2 resolver to use the "collect_data" pattern
502 facts.add(make_shared<ec2_resolver>());
503 // TODO: refactor the GCE resolver to use the "collect_data" pattern
504 facts.add(make_shared<hypervisors_resolver_test>());
505 facts.add(fact::gce, make_value<map_value>());
506 facts.add(make_shared<identity_resolver>());
507 facts.add(make_shared<kernel_resolver>());
508 facts.add(fact::ldom, make_value<map_value>());
509 facts.add("ldom_domainname", make_value<string_value>("somedomain", true));
510 facts.add(make_shared<load_average_resolver>());
511 facts.add(make_shared<memory_resolver>());
512 facts.add(make_shared<networking_resolver>());
513 facts.add(make_shared<operating_system_resolver>());
514 facts.add(make_shared<resolvers::path_resolver>());
515 facts.add(make_shared<processor_resolver>());
516 facts.add(make_shared<ruby_resolver>());
517 facts.add(make_shared<ssh_resolver>());
518 facts.add(make_shared<system_profiler_resolver>());
519 facts.add(make_shared<timezone_resolver>());
520 facts.add(make_shared<uptime_resolver>());
521 facts.add(make_shared<virtualization_resolver>());
522 facts.add(make_shared<xen_resolver>());
523 facts.add(make_shared<zfs_resolver>());
524 facts.add(make_shared<zone_resolver>());
525 facts.add(make_shared<zpool_resolver>());
526 }
527
528 void validate_attributes(YAML::Node const& node)
529 {
530 REQUIRE(node.IsMap());
531
532 for (auto const& attribute : node) {
533 auto attribute_name = attribute.first.as<string>();
534 CAPTURE(attribute_name);
535 REQUIRE_THAT(attribute_name,
536 Catch::Equals("pattern") ||
537 Catch::Equals("type") ||
538 Catch::Equals("hidden") ||
539 Catch::Equals("description") ||
540 Catch::Equals("resolution") ||
541 Catch::Equals("caveats") ||
542 Catch::Equals("elements") ||
543 Catch::Equals("validate") ||
544 Catch::Equals("blockgroup"));
545 }
546
547 // If pattern is present, it must be a non-empty string
548 auto pattern_attribute = node["pattern"];
549 if (pattern_attribute) {
550 REQUIRE(pattern_attribute.IsScalar());
551 auto pattern = pattern_attribute.as<string>();
552 REQUIRE_FALSE(pattern.empty());
553 }
554
555 // Node must have a type attribute
556 auto type_attribute = node["type"];
557 REQUIRE(type_attribute);
558 REQUIRE(type_attribute.IsScalar());
559 auto type = type_attribute.as<string>();
560 REQUIRE_THAT(type,
561 Catch::Equals("integer") ||
562 Catch::Equals("double") ||
563 Catch::Equals("string") ||
564 Catch::Equals("boolean") ||
565 Catch::Equals("array") ||
566 Catch::Equals("map") ||
567 Catch::Equals("ip") ||
568 Catch::Equals("ip6") ||
569 Catch::Equals("mac"));
570
571 // Check map types
572 auto elements = node["elements"];
573 if (type == "map") {
574 // If the validate attribute is present, it must be true or false
575 auto validate_attribute = node["validate"];
576 string validate = "true";
577 if (validate_attribute) {
578 REQUIRE(validate_attribute.IsScalar());
579 validate = validate_attribute.as<string>();
580 REQUIRE_THAT(validate, AnyOf(Catch::Equals("true"), Catch::Equals("false")));
581 }
582
583 // Validated map values must have elements
584 if (validate == "true") {
585 REQUIRE(elements);
586 REQUIRE(elements.IsMap());
587 }
588 } else {
589 REQUIRE_FALSE(elements);
590
591 // There should not be a validate attribute
592 auto validate_attribute = node["validate"];
593 REQUIRE_FALSE(validate_attribute);
594 }
595
596 // If hidden is present, it must be a boolean
597 auto hidden_attribute = node["hidden"];
598 if (hidden_attribute) {
599 REQUIRE(hidden_attribute.IsScalar());
600 auto hidden = hidden_attribute.as<string>();
601 REQUIRE_THAT(hidden, AnyOf(Catch::Equals("true"), Catch::Equals("false")));
602 }
603
604 // Node must have a description attribute
605 auto description_attribute = node["description"];
606 REQUIRE(description_attribute);
607 REQUIRE(description_attribute.IsScalar());
608 auto description = description_attribute.as<string>();
609 REQUIRE_FALSE(description.empty());
610
611 // If the resolutions is present, it must be a non-empty string
612 auto resolutions_attribute = node["resolutions"];
613 if (resolutions_attribute) {
614 REQUIRE(resolutions_attribute.IsScalar());
615 auto resolutions = resolutions_attribute.as<string>();
616 REQUIRE_FALSE(resolutions.empty());
617 }
618
619 // If the caveats are present, it must be a non-empty string
620 auto caveats_attribute = node["caveats"];
621 if (caveats_attribute) {
622 REQUIRE(caveats_attribute.IsScalar());
623 auto caveats = caveats_attribute.as<string>();
624 REQUIRE_FALSE(caveats.empty());
625 }
626
627 auto blockgroup_attribute = node["blockgroup"];
628 if (blockgroup_attribute) {
629 REQUIRE(blockgroup_attribute.IsScalar());
630 auto blockgroup = blockgroup_attribute.as<string>();
631 REQUIRE_FALSE(blockgroup.empty());
632 }
633
634 // Recurse on elements
635 if (elements) {
636 for (auto const& element : elements) {
637 auto element_name = element.first.as<string>();
638 CAPTURE(element_name);
639 validate_attributes(element.second);
640 }
641 }
642 }
643
644 YAML::Node find_child(YAML::Node const& node, string const& name, set<string>& found)
645 {
646 REQUIRE(node.IsMap());
647
648 for (auto const& child : node) {
649 auto child_name = child.first.as<string>();
650 auto pattern_attribute = child.second["pattern"];
651 if ((pattern_attribute && re_search(name, boost::regex(pattern_attribute.as<string>()))) ||
652 child_name == name) {
653 found.insert(move(child_name));
654 return child.second;
655 }
656 }
657
658 return YAML::Node(YAML::NodeType::Undefined);
659 }
660
661 void validate_fact(YAML::Node const& node, value const* fact_value, bool require_all_elements)
662 {
663 REQUIRE(node.IsMap());
664
665 // Ensure the types match
666 auto type_attribute = node["type"];
667 auto expected_type = type_attribute.as<string>();
668 string type;
669 string_value const* svalue = nullptr;
670 map_value const* map = nullptr;
671 if (dynamic_cast<integer_value const*>(fact_value)) {
672 type = "integer";
673 } else if (dynamic_cast<double_value const*>(fact_value)) {
674 type = "double";
675 } else if ((svalue = dynamic_cast<string_value const*>(fact_value))) {
676 type = "string";
677
678 // Check for special string types; sourced from http://stackoverflow.com/a/17871737
679 static boost::regex ip_pattern("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
680 static boost::regex ip6_pattern("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$");
681 static boost::regex mac_pattern("^(([0-9a-fA-F]){2}\\:){5}([0-9a-fA-F]){2}$");
682
683 if (expected_type == "ip" && re_search(svalue->value(), ip_pattern)) {
684 type = "ip";
685 } else if (expected_type == "ip6" && re_search(svalue->value(), ip6_pattern)) {
686 type = "ip6";
687 } else if (expected_type == "mac" && re_search(svalue->value(), mac_pattern)) {
688 type = "mac";
689 }
690 } else if (dynamic_cast<boolean_value const*>(fact_value)) {
691 type = "boolean";
692 } else if (dynamic_cast<array_value const*>(fact_value)) {
693 type = "array";
694 } else if ((map = dynamic_cast<map_value const*>(fact_value))) {
695 type = "map";
696 } else {
697 FAIL("unexpected fact value type");
698 }
699 REQUIRE(type == expected_type);
700
701 // Ensure the value is hidden according to the schema
702 auto hidden_attribute = node["hidden"];
703 bool expected_hidden = hidden_attribute && hidden_attribute.as<string>() == "true";
704 bool hidden = fact_value->hidden();
705 REQUIRE(hidden == expected_hidden);
706
707 // Recurse on map elements
708 if (map) {
709 auto elements = node["elements"];
710
711 // Validate the map's elements "validate" is unset or true
712 auto validate_attribute = node["validate"];
713 if (!validate_attribute || validate_attribute.as<string>() == "true") {
714 set<string> found;
715 map->each([&](string const& element_name, value const* element_value) {
716 if (!element_value) {
717 return true;
718 }
719 CAPTURE(element_name);
720 auto element = find_child(elements, element_name, found);
721 REQUIRE(element);
722 validate_fact(element, element_value, require_all_elements);
723 return true;
724 });
725
726 // Require all elements were found
727 if (require_all_elements) {
728 REQUIRE(found.size() == elements.size());
729 }
730 }
731 }
732 }
733
734 // TestRail C62454
735 SCENARIO("validating schema") {
736 boost::nowide::ifstream stream(LIBFACTER_TESTS_DIRECTORY "/../schema/facter.yaml");
737
738 YAML::Node schema = YAML::Load(stream);
739 collection_fixture facts;
740
741 WHEN("validating the schema itself") {
742 THEN("all attributes must be valid") {
743 for (auto const& fact : schema) {
744 auto fact_name = fact.first.as<string>();
745 CAPTURE(fact_name);
746 validate_attributes(fact.second);
747 }
748 }
749 }
750 WHEN("validating a fact collection") {
751 THEN("all facts must conform to the schema") {
752 add_all_facts(facts);
753
754 set<string> found;
755
756 facts.each([&](string const& fact_name, value const* fact_value) {
757 if (!fact_value) {
758 return true;
759 }
760 CAPTURE(fact_name);
761 auto fact = find_child(schema, fact_name, found);
762 REQUIRE(fact);
763 validate_fact(fact, fact_value, true);
764 return true;
765 });
766
767 // Require all elements in the schema were found
768 REQUIRE(found.size() == schema.size());
769 }
770 THEN("the current platform's facts must conform to the schema") {
771 facts.add_default_facts(true);
772
773 set<string> found;
774
775 facts.each([&](string const& fact_name, value const* fact_value) {
776 if (!fact_value) {
777 return true;
778 }
779 CAPTURE(fact_name);
780 auto fact = find_child(schema, fact_name, found);
781 REQUIRE(fact);
782 validate_fact(fact, fact_value, false);
783 return true;
784 });
785 }
786 THEN("blocked facts are not resolved") {
787 set<string> blocked_facts;
788 set<string> blockgroups;
789 for (auto const& fact : schema) {
790 auto fact_name = fact.first.as<string>();
791 auto blockgroup = fact.second["blockgroup"];
792 if (blockgroup) {
793 blockgroups.insert(blockgroup.as<string>());
794 blocked_facts.insert(fact_name);
795 }
796 }
797 collection_fixture with_blocked(blockgroups);
798 add_all_facts(with_blocked);
799 with_blocked.resolve_facts();
800 for (auto const& fact : blocked_facts) {
801 CAPTURE(fact);
802 REQUIRE_FALSE(with_blocked.get<map_value>(fact));
803 }
804 }
805 }
806 }
+0
-270
lib/tests/facts/string_value.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <rapidjson/document.h>
3 #include <yaml-cpp/yaml.h>
4 #include <sstream>
5
6 using namespace std;
7 using namespace facter::facts;
8 using namespace rapidjson;
9 using namespace YAML;
10
11 SCENARIO("using a string fact value") {
12 GIVEN("a value to copy") {
13 string s = "hello world";
14 string_value value(s);
15 THEN("the value is copied") {
16 REQUIRE(s == "hello world");
17 REQUIRE(value.value() == "hello world");
18 }
19 }
20 GIVEN("a value to move") {
21 string s = "hello world";
22 string_value value(std::move(s));
23 THEN("the value is moved") {
24 REQUIRE(s.empty());
25 REQUIRE(value.value() == "hello world");
26 }
27 }
28
29 GIVEN("a simple string value") {
30 string_value value("foobar");
31 WHEN("serialized to JSON") {
32 THEN("it should have the same value") {
33 json_value json;
34 json_allocator allocator;
35 value.to_json(allocator, json);
36 REQUIRE(json.IsString());
37 REQUIRE(json.GetString() == string("foobar"));
38 }
39 }
40 WHEN("serialized to YAML") {
41 THEN("it should have the same value") {
42 Emitter emitter;
43 value.write(emitter);
44 REQUIRE(string(emitter.c_str()) == "foobar");
45 }
46 }
47 WHEN("serialized to text with quotes") {
48 THEN("it should be quoted") {
49 ostringstream stream;
50 value.write(stream);
51 REQUIRE(stream.str() == "\"foobar\"");
52 }
53 }
54 WHEN("serialized to text without quotes") {
55 THEN("it should not be quoted") {
56 ostringstream stream;
57 value.write(stream, false);
58 REQUIRE(stream.str() == "foobar");
59 }
60 }
61 }
62
63 GIVEN("an ipv6 address string value ending with ':'") {
64 string_value value("fe80::");
65 WHEN("serialized to JSON") {
66 THEN("it should have the same value") {
67 json_value json;
68 json_allocator allocator;
69 value.to_json(allocator, json);
70 REQUIRE(json.IsString());
71 REQUIRE(json.GetString() == string("fe80::"));
72 }
73 }
74 WHEN("serialized to YAML") {
75 THEN("it should be quoted") {
76 Emitter emitter;
77 value.write(emitter);
78 REQUIRE(string(emitter.c_str()) == "\"fe80::\"");
79 }
80 }
81 WHEN("serialized to text with quotes") {
82 THEN("it should be quoted") {
83 ostringstream stream;
84 value.write(stream);
85 REQUIRE(stream.str() == "\"fe80::\"");
86 }
87 }
88 WHEN("serialized to text without quotes") {
89 THEN("it should not be quoted") {
90 ostringstream stream;
91 value.write(stream, false);
92 REQUIRE(stream.str() == "fe80::");
93 }
94 }
95 }
96
97 GIVEN("an ipv4 address string value") {
98 string_value value("127.254.3.0");
99 WHEN("serialized to JSON") {
100 THEN("it should have the same value") {
101 json_value json;
102 json_allocator allocator;
103 value.to_json(allocator, json);
104 REQUIRE(json.IsString());
105 REQUIRE(json.GetString() == string("127.254.3.0"));
106 }
107 }
108 WHEN("serialized to YAML") {
109 THEN("it should have the same value") {
110 Emitter emitter;
111 value.write(emitter);
112 REQUIRE(string(emitter.c_str()) == "127.254.3.0");
113 }
114 }
115 WHEN("serialized to text with quotes") {
116 THEN("it should be quoted") {
117 ostringstream stream;
118 value.write(stream);
119 REQUIRE(stream.str() == "\"127.254.3.0\"");
120 }
121 }
122 WHEN("serialized to text without quotes") {
123 THEN("it should not be quoted") {
124 ostringstream stream;
125 value.write(stream, false);
126 REQUIRE(stream.str() == "127.254.3.0");
127 }
128 }
129 }
130
131 GIVEN("a ':' prefixed string value") {
132 string_value value("::1");
133 WHEN("serialized to JSON") {
134 THEN("it should have the same value") {
135 json_value json;
136 json_allocator allocator;
137 value.to_json(allocator, json);
138 REQUIRE(json.IsString());
139 REQUIRE(json.GetString() == string("::1"));
140 }
141 }
142 WHEN("serialized to YAML") {
143 THEN("it should be quoted") {
144 Emitter emitter;
145 value.write(emitter);
146 REQUIRE(string(emitter.c_str()) == "\"::1\"");
147 }
148 }
149 WHEN("serialized to text with quotes") {
150 THEN("it should be quoted") {
151 ostringstream stream;
152 value.write(stream);
153 REQUIRE(stream.str() == "\"::1\"");
154 }
155 }
156 WHEN("serialized to text without quotes") {
157 THEN("it should not be quoted") {
158 ostringstream stream;
159 value.write(stream, false);
160 REQUIRE(stream.str() == "::1");
161 }
162 }
163 }
164
165 GIVEN("a valid mac address") {
166 string_value value("00:50:56:55:42:45");
167 WHEN("serialized to JSON") {
168 THEN("it should have the same value") {
169 json_value json;
170 json_allocator allocator;
171 value.to_json(allocator, json);
172 REQUIRE(json.IsString());
173 REQUIRE(json.GetString() == string("00:50:56:55:42:45"));
174 }
175 }
176 WHEN("serialized to YAML") {
177 THEN("it should be quoted") {
178 Emitter emitter;
179 value.write(emitter);
180 REQUIRE(string(emitter.c_str()) == "\"00:50:56:55:42:45\"");
181 }
182 }
183 WHEN("serialized to text with quotes") {
184 THEN("it should be quoted") {
185 ostringstream stream;
186 value.write(stream);
187 REQUIRE(stream.str() == "\"00:50:56:55:42:45\"");
188 }
189 }
190 WHEN("serialized to text without quotes") {
191 THEN("it should not be quoted") {
192 ostringstream stream;
193 value.write(stream, false);
194 REQUIRE(stream.str() == "00:50:56:55:42:45");
195 }
196 }
197 }
198
199 GIVEN("a list of numbers") {
200 string_value value("1,2,3,4,5");
201 WHEN("serialized to JSON") {
202 THEN("it should have the same value") {
203 json_value json;
204 json_allocator allocator;
205 value.to_json(allocator, json);
206 REQUIRE(json.IsString());
207 REQUIRE(json.GetString() == string("1,2,3,4,5"));
208 }
209 }
210 WHEN("serialized to YAML") {
211 THEN("it should be quoted") {
212 Emitter emitter;
213 value.write(emitter);
214 REQUIRE(string(emitter.c_str()) == "\"1,2,3,4,5\"");
215 }
216 }
217 WHEN("serialized to text with quotes") {
218 THEN("it should be quoted") {
219 ostringstream stream;
220 value.write(stream);
221 REQUIRE(stream.str() == "\"1,2,3,4,5\"");
222 }
223 }
224 WHEN("serialized to text without quotes") {
225 THEN("it should not be quoted") {
226 ostringstream stream;
227 value.write(stream, false);
228 REQUIRE(stream.str() == "1,2,3,4,5");
229 }
230 }
231 }
232
233 GIVEN("a boolean value") {
234 for (auto literal : {string("true"), string("false"), string("yes"), string("no")}) {
235 auto value = string_value(literal);
236
237 WHEN("serialized to JSON") {
238 THEN("it should have the same value") {
239 json_value json;
240 json_allocator allocator;
241 value.to_json(allocator, json);
242 REQUIRE(json.IsString());
243 REQUIRE(json.GetString() == literal);
244 }
245 }
246 WHEN("serialized to YAML") {
247 THEN("it should be quoted") {
248 Emitter emitter;
249 value.write(emitter);
250 REQUIRE(string(emitter.c_str()) == "\""+literal+"\"");
251 }
252 }
253 WHEN("serialized to text with quotes") {
254 THEN("it should be quoted") {
255 ostringstream stream;
256 value.write(stream);
257 REQUIRE(stream.str() == "\""+literal+"\"");
258 }
259 }
260 WHEN("serialized to text without quotes") {
261 THEN("it should not be quoted") {
262 ostringstream stream;
263 value.write(stream, false);
264 REQUIRE(stream.str() == literal);
265 }
266 }
267 }
268 }
269 }
+0
-60
lib/tests/facts/windows/collection.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/resolver.hpp>
3 #include <facter/facts/array_value.hpp>
4 #include <facter/facts/map_value.hpp>
5 #include <facter/facts/scalar_value.hpp>
6 #include "../../fixtures.hpp"
7 #include <sstream>
8
9 using namespace std;
10 using namespace facter::facts;
11 using namespace facter::testing;
12
13 SCENARIO("resolving external executable facts into a collection") {
14 collection_fixture facts;
15 REQUIRE(facts.size() == 0u);
16 GIVEN("an absolute path") {
17 facts.add_external_facts({
18 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution",
19 LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell",
20 });
21 THEN("facts should resolve") {
22 REQUIRE(facts.size() == 19u);
23
24 REQUIRE(facts.get<string_value>("exe_fact1"));
25 REQUIRE(facts.get<string_value>("exe_fact2"));
26 REQUIRE_FALSE(facts.get<string_value>("exe_fact3"));
27 REQUIRE(facts.get<string_value>("exe_fact4"));
28
29 REQUIRE(facts.get<string_value>("ps1_fact1"));
30 REQUIRE(facts.get<string_value>("ps1_fact2"));
31 REQUIRE_FALSE(facts.get<string_value>("ps1_fact3"));
32 REQUIRE(facts.get<string_value>("ps1_fact4"));
33
34 REQUIRE(facts.get<string_value>("ps1_json_fact1"));
35 REQUIRE(facts.get<integer_value>("ps1_json_fact2"));
36 REQUIRE(facts.get<boolean_value>("ps1_json_fact3"));
37 REQUIRE(facts.get<array_value>("ps1_json_fact4"));
38 REQUIRE_FALSE(facts.get<boolean_value>("ps1_json_fact5"));
39 REQUIRE(facts.get<map_value>("ps1_json_fact6"));
40
41 REQUIRE(facts.get<string_value>("ps1_yaml_fact1"));
42 REQUIRE(facts.get<integer_value>("ps1_yaml_fact2"));
43 REQUIRE(facts.get<string_value>("ps1_yaml_fact3"));
44 REQUIRE(facts.get<array_value>("ps1_yaml_fact4"));
45 REQUIRE(facts.get<array_value>("ps1_yaml_fact5"));
46 REQUIRE(facts.get<map_value>("ps1_yaml_fact6"));
47 REQUIRE(facts.get<map_value>("ps1_yaml_fact7"));
48 }
49 }
50 GIVEN("a relative path") {
51 test_with_relative_path fixture("foo", "bar.bat", "@echo local_exec_fact=value");
52 facts.add_external_facts({ "foo" });
53 THEN("facts should resolve") {
54 REQUIRE(facts.size() == 1u);
55 REQUIRE(facts.get<string_value>("local_exec_fact"));
56 REQUIRE(facts.get<string_value>("local_exec_fact")->value() == "value");
57 }
58 }
59 }
+0
-38
lib/tests/facts/windows/external_resolvers_factory.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/external_resolvers_factory.hpp>
2 #include "../../fixtures.hpp"
3
4 using namespace std;
5 using namespace facter::facts;
6
7 SCENARIO("checking external file windows resolvers factory") {
8 external_resolvers_factory erf;
9 GIVEN("an executable file") {
10 THEN("the file can be resolved") {
11 REQUIRE(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/facts.bat"));
12 }
13 }
14 GIVEN("a PowerShell file extension") {
15 THEN("the file can be resolved") {
16 REQUIRE(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/powershell/facts.ps1"));
17 }
18 }
19 GIVEN("a non executable file without extension") {
20 THEN("should throw no resolver exception") {
21 REQUIRE_THROWS_AS(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/not_executable"),
22 external::external_fact_no_resolver&);
23 }
24 }
25 GIVEN("a non-executable ruby script") {
26 THEN("the file cannot be resolved") {
27 REQUIRE_THROWS_AS(erf.get_resolver(LIBFACTER_TESTS_DIRECTORY "/fixtures/facts/external/windows/execution/ruby_script.rb"),
28 external::external_fact_no_resolver&);
29 }
30 }
31 GIVEN("a relative path not on PATH") {
32 THEN("the file cannot be resolved") {
33 REQUIRE_THROWS_AS(erf.get_resolver("foo/bar.bat"),
34 external::external_fact_no_resolver&);
35 }
36 }
37 }
+0
-217
lib/tests/facts/windows/networking_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/windows/networking_resolver.hpp>
2 #include <leatherman/windows/windows.hpp>
3 #include <internal/util/windows/wsa.hpp>
4 #include <Ws2tcpip.h>
5
6 using namespace std;
7
8 // Test address manipulation utilities.
9
10 struct networking_utilities : facter::facts::windows::networking_resolver
11 {
12 public:
13 using networking_resolver::create_ipv4_mask;
14 using networking_resolver::create_ipv6_mask;
15 using networking_resolver::mask_ipv4_address;
16 using networking_resolver::mask_ipv6_address;
17
18 template <typename T>
19 std::string address_to_string(T const& addr)
20 {
21 return winsock.address_to_string(const_cast<T&>(addr));
22 }
23
24 template <typename T>
25 std::string address_to_string(T const* addr, T const* mask) { return {}; }
26
27 facter::util::windows::wsa winsock;
28 };
29
30 template <>
31 std::string networking_utilities::address_to_string<sockaddr_in>(sockaddr_in const* addr, sockaddr_in const* mask)
32 {
33 auto masked = mask_ipv4_address(reinterpret_cast<sockaddr const*>(addr), *mask);
34 return address_to_string(masked);
35 }
36
37 template <>
38 std::string networking_utilities::address_to_string<sockaddr_in6>(sockaddr_in6 const* addr, sockaddr_in6 const* mask)
39 {
40 auto masked = mask_ipv6_address(reinterpret_cast<sockaddr const*>(addr), *mask);
41 return address_to_string(masked);
42 }
43
44 static constexpr sockaddr_in make_sockaddr_in(u_char a, u_char b, u_char c, u_char d)
45 { return {AF_INET, 0u, in_addr{a, b, c, d}, {0, 0, 0, 0, 0, 0, 0, 0}}; }
46
47 bool operator== (in_addr const& a, in_addr const& b)
48 { return a.S_un.S_addr == b.S_un.S_addr; }
49
50 bool operator!= (in_addr const& a, in_addr const& b) { return !(a == b); }
51
52 bool operator== (sockaddr_in const& a, sockaddr_in const& b)
53 { return a.sin_family == b.sin_family && a.sin_addr == b.sin_addr; }
54
55 bool operator!= (sockaddr_in const& a, sockaddr_in const& b) { return !(a == b); }
56
57 struct ipv4_case { uint8_t masklen; sockaddr_in addr; string str; };
58 static const ipv4_case ip4_masks[] = {
59 {0u, make_sockaddr_in(0u, 0u, 0u, 0u), "0.0.0.0"},
60 {255u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"},
61 {32u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"},
62 {33u, make_sockaddr_in(255u, 255u, 255u, 255u), "255.255.255.255"},
63 {24u, make_sockaddr_in(255u, 255u, 255u, 0u), "255.255.255.0"},
64 {9u, make_sockaddr_in(255u, 128u, 0u, 0u), "255.128.0.0"},
65 {1u, make_sockaddr_in(128u, 0u, 0u, 0u), "128.0.0.0"},
66 {31u, make_sockaddr_in(255u, 255u, 255u, 254u), "255.255.255.254"}
67 };
68
69 SCENARIO("create IPv4 masks") {
70 networking_utilities util;
71
72 // Test various valid masklen, too large masklen, verify output.
73 for (auto const& item : ip4_masks) {
74 auto mask = util.create_ipv4_mask(item.masklen);
75 REQUIRE(mask == item.addr);
76 }
77
78 // Verify the boolean operator behaves as expected.
79 REQUIRE(ip4_masks[0].addr != ip4_masks[1].addr);
80 REQUIRE(ip4_masks[0].addr != ip4_masks[6].addr);
81 }
82
83 SCENARIO("IPv4 address to string") {
84 networking_utilities util;
85
86 static const pair<sockaddr_in, string> ip4_cases[] = {
87 {make_sockaddr_in(192u, 168u, 0u, 1u), "192.168.0.1"},
88 {make_sockaddr_in(200u, 0u, 154u, 12u), "200.0.154.12"},
89 {make_sockaddr_in(1u, 255u, 128u, 42u), "1.255.128.42"}
90 };
91
92 // Test various valid addresses to string.
93 for (auto const& item : ip4_cases) {
94 REQUIRE(item.second == util.address_to_string(item.first));
95 }
96
97 // Test various mask addresses to string.
98 for (auto const& item : ip4_masks) {
99 REQUIRE(item.str == util.address_to_string(item.addr));
100 }
101 }
102
103 SCENARIO("IPv4 address with mask to string") {
104 networking_utilities util;
105
106 // Test address_to_string with masks applied.
107 auto min = make_sockaddr_in(0u, 0u, 0u, 0u);
108 auto max = make_sockaddr_in(255u, 255u, 255u, 255u);
109 auto zoro = make_sockaddr_in(255u, 255u, 128u, 0u);
110 auto v = make_sockaddr_in(128u, 0u, 0u, 0u);
111
112 auto local = make_sockaddr_in(192u, 168u, 0u, 1u);
113 auto outer = make_sockaddr_in(200u, 0u, 154u, 12u);
114
115 REQUIRE("0.0.0.0" == util.address_to_string(&local, &min));
116 REQUIRE("192.168.0.1" == util.address_to_string(&local, &max));
117 REQUIRE("192.168.0.0" == util.address_to_string(&local, &zoro));
118 REQUIRE("128.0.0.0" == util.address_to_string(&local, &v));
119 REQUIRE("0.0.0.0" == util.address_to_string(&outer, &min));
120 REQUIRE("200.0.154.12" == util.address_to_string(&outer, &max));
121 REQUIRE("200.0.128.0" == util.address_to_string(&outer, &zoro));
122 REQUIRE("128.0.0.0" == util.address_to_string(&outer, &v));
123 }
124
125 static sockaddr_in6 make_sockaddr_in6(array<u_char, 16> x)
126 {
127 sockaddr_in6 addr = {AF_INET6};
128 memcpy(addr.sin6_addr.u.Byte, x.data(), 16*sizeof(u_char));
129 return addr;
130 }
131
132 bool operator== (in6_addr const& a, in6_addr const& b)
133 { return 0 == memcmp(a.u.Word, b.u.Word, 8*sizeof(u_short)); }
134
135 bool operator!= (in6_addr const& a, in6_addr const& b) { return !(a == b); }
136
137 bool operator== (sockaddr_in6 const& a, sockaddr_in6 const& b)
138 { return a.sin6_family == b.sin6_family && a.sin6_addr == b.sin6_addr; }
139
140 bool operator!= (sockaddr_in6 const& a, sockaddr_in6 const& b) { return !(a == b); }
141
142 struct ipv6_case { uint8_t masklen; string str; sockaddr_in6 addr; };
143 static const ipv6_case ip6_masks[] = {
144 {0u, "::", make_sockaddr_in6({})},
145 {1u, "8000::", make_sockaddr_in6({0x80u})},
146 {4u, "f000::", make_sockaddr_in6({0xf0u})},
147 {64u, "ffff:ffff:ffff:ffff::",
148 make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})},
149 {65u, "ffff:ffff:ffff:ffff:8000::",
150 make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0x80u})},
151 {127u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
152 make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xfeu})},
153 {128u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
154 make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})},
155 {129u, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
156 make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu})}
157 };
158
159 SCENARIO("create IPv6 mask") {
160 networking_utilities util;
161
162 // Test various valid masklen, too large masklen, verify output.
163 for (auto const& item : ip6_masks) {
164 auto mask = util.create_ipv6_mask(item.masklen);
165 REQUIRE(item.addr == mask);
166 }
167
168 // Verify the boolean operator behaves as expected.
169 REQUIRE(ip6_masks[0].addr != ip6_masks[1].addr);
170 REQUIRE(ip6_masks[0].addr != ip6_masks[6].addr);
171 }
172
173 SCENARIO("IPv6 address to string") {
174 networking_utilities util;
175
176 static const pair<sockaddr_in6, string> ip6_cases[] = {
177 {make_sockaddr_in6({0u, 0u, 0u, 0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u}), "::ffe9:ffff:ffab:100:0:0"},
178 {make_sockaddr_in6({0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u}), "ff:e9ff:ffff:ab01::"},
179 {make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu}), "fe80:123::4567:89ab:0:0"},
180 {make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu}), "fe80:123::4567:89ab"},
181 {make_sockaddr_in6({0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1u}), "::1"},
182 };
183
184 // Test various valid addresses to string.
185 for (auto const& item : ip6_cases) {
186 REQUIRE(item.second == util.address_to_string(item.first));
187 }
188
189 // Test various mask addresses to string.
190 for (auto const& item : ip6_masks) {
191 REQUIRE(item.str == util.address_to_string(item.addr));
192 }
193 }
194
195 SCENARIO("IPv6 address with mask to string") {
196 networking_utilities util;
197
198 // Test address_to_string with masks applied.
199 auto min = make_sockaddr_in6({});
200 auto max = make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
201 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu});
202 auto zoro = make_sockaddr_in6({0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu});
203 auto v = make_sockaddr_in6({0xf8u});
204
205 auto local = make_sockaddr_in6({0xfeu, 0x80u, 0x01u, 0x23u, 0u, 0u, 0u, 0u, 0x45u, 0x67u, 0x89u, 0xabu});
206 auto outer = make_sockaddr_in6({0u, 0xffu, 0xe9u, 0xffu, 0xffu, 0xffu, 0xabu, 1u});
207
208 REQUIRE("::" == util.address_to_string(&local, &min));
209 REQUIRE("fe80:123::4567:89ab:0:0" == util.address_to_string(&local, &max));
210 REQUIRE("fe80:123::" == util.address_to_string(&local, &zoro));
211 REQUIRE("f800::" == util.address_to_string(&local, &v));
212 REQUIRE("::" == util.address_to_string(&outer, &min));
213 REQUIRE("ff:e9ff:ffff:ab01::" == util.address_to_string(&outer, &max));
214 REQUIRE("ff:e9ff:ffff:ab01::" == util.address_to_string(&outer, &zoro));
215 REQUIRE("::" == util.address_to_string(&outer, &v));
216 }
+0
-18
lib/tests/facts/windows/operating_system_resolver.cc less more
0 #include <catch.hpp>
1 #include <internal/facts/windows/operating_system_resolver.hpp>
2 #include <leatherman/util/environment.hpp>
3 #include <leatherman/windows/windows.hpp>
4 #include <boost/algorithm/string.hpp>
5
6 using namespace std;
7 using namespace leatherman::util;
8
9 SCENARIO("Read SYSTEMROOT OS environment variable") {
10 GIVEN("Windows 32 and 64 bit OS") {
11 string value;
12 REQUIRE(environment::get("SystemRoot", value));
13 string required = "C:\\WINDOWS";
14 boost::algorithm::to_upper(value);
15 REQUIRE(required == value);
16 }
17 }
+0
-2
lib/tests/fixtures/execution/with space/command_with_space.bat less more
0 @echo off
1 echo %1
+0
-12
lib/tests/fixtures/facts/external/json/facts.json less more
0 {
1 "json_fact1": "foo",
2 "json_fact2": 5,
3 "json_fact3": true,
4 "json_fact4": 5.1,
5 "json_fact5": [1, 2, 3],
6 "json_fact6": {
7 "element1" : 1,
8 "element2" : 2
9 },
10 "JSON_fact7" : "bar"
11 }
+0
-1
lib/tests/fixtures/facts/external/json/invalid.json less more
0 !!!this is not valid json!!!
+0
-1
lib/tests/fixtures/facts/external/ordering/bar/foo.yaml less more
0 foo: set in bar/foo.yaml
+0
-1
lib/tests/fixtures/facts/external/ordering/foo/foo.yaml less more
0 foo: set in foo/foo.yaml
+0
-4
lib/tests/fixtures/facts/external/posix/execution/error_message less more
0 #! /usr/bin/env sh
1 echo error message! >&2
2 echo foo=bar
3
+0
-5
lib/tests/fixtures/facts/external/posix/execution/facts less more
0 #! /usr/bin/env sh
1 echo 'exe_fact1=value1'
2 echo 'exe_fact2='
3 echo 'exe_fact3'
4 echo 'EXE_fact4=value2'
+0
-3
lib/tests/fixtures/facts/external/posix/execution/failed less more
0 #! /usr/bin/env sh
1 echo 'this script fails'
2 exit 1
+0
-1
lib/tests/fixtures/facts/external/posix/execution/not_executable less more
0 This file is not executable.
+0
-4
lib/tests/fixtures/facts/external/text/facts.txt less more
0 txt_fact1=value1
1 txt_fact2=
2 not a fact
3 TXT_Fact4=value2
+0
-4
lib/tests/fixtures/facts/external/windows/execution/error_message.bat less more
0 @echo off
1 echo error message! >&2
2 echo foo=bar
3 exit /b 0
+0
-4
lib/tests/fixtures/facts/external/windows/execution/facts.bat less more
0 @echo exe_fact1=value1
1 @echo exe_fact2=
2 @echo exe_fact3
3 @echo EXE_fact4=value2
+0
-2
lib/tests/fixtures/facts/external/windows/execution/failed.cmd less more
0 @echo this script fails
1 exit 1
+0
-1
lib/tests/fixtures/facts/external/windows/execution/not_executable less more
0 This file is not executable.
+0
-1
lib/tests/fixtures/facts/external/windows/execution/ruby_script.rb less more
0 puts "rbver=#{RUBY_VERSION}"
+0
-3
lib/tests/fixtures/facts/external/windows/powershell/error_message.ps1 less more
0 [Console]::Error.WriteLine('error message!')
1 Write 'foo=bar'
2 exit 0
+0
-4
lib/tests/fixtures/facts/external/windows/powershell/facts.ps1 less more
0 echo ps1_fact1=value1
1 echo ps1_fact2=
2 echo ps1_fact3
3 echo PS1_fact4=value2
+0
-2
lib/tests/fixtures/facts/external/windows/powershell/failed.ps1 less more
0 echo this script fails
1 exit 1
+0
-21
lib/tests/fixtures/facts/external/windows/powershell/json.ps1 less more
0 if (Get-Command ConvertTo-JSON -errorAction SilentlyContinue) {
1 @{
2 'PS1_JSON_FACT1' = 'value1'
3 'ps1_json_fact2' = 2
4 'ps1_json_fact3' = $True
5 'ps1_json_fact4' = @('first', 'second')
6 'ps1_json_fact5' = $Null
7 'ps1_json_fact6' = @{ 'a' = 'b'; 'c' = 'd' }
8 } | ConvertTo-JSON -Depth 1 -Compress
9 } else {
10 @'
11 {
12 "PS1_JSON_FACT1": "value1",
13 "ps1_json_fact2": 2,
14 "ps1_json_fact3": true,
15 "ps1_json_fact4": ["first", "second"],
16 "ps1_json_fact5": null,
17 "ps1_json_fact6": { "a": "b", "c": "d" }
18 }
19 '@
20 }
+0
-1
lib/tests/fixtures/facts/external/windows/powershell/not_executable less more
0 This file is not executable.
+0
-18
lib/tests/fixtures/facts/external/windows/powershell/yaml.ps1 less more
0 @'
1 ---
2 PS1_YAML_FACT1: yaml
3 PS1_yaml_fact2: 2
4 ps1_yaml_fact3: |
5 one value
6 but
7 many lines
8 ps1_yaml_fact4:
9 - one
10 - two
11 ps1_yaml_fact5: [first, second, third]
12 ps1_yaml_fact6:
13 red: green
14 orange: blue
15 yellow: purple
16 ps1_yaml_fact7: { key: value }
17 '@
+0
-15
lib/tests/fixtures/facts/external/yaml/facts.yaml less more
0 yaml_fact1: foo
1 yaml_fact2: 5
2 yaml_fact3: true
3 yaml_fact4: 5.1
4 yaml_fact5:
5 - 1
6 - 2
7 - 3
8 yaml_fact6:
9 element1: 1
10 element2: 2
11 YAML_faCt7: bar
12 not_bool: 'true'
13 not_int: '123'
14 not_double: '123.456'
+0
-1
lib/tests/fixtures/facts/external/yaml/invalid.yaml less more
0 !!!this is not valid yaml!!!
+0
-3
lib/tests/fixtures/facts/external/zö/facts.rb less more
0 Facter.add "snowman_fact" do
1 setcode { "olaf" }
2 end
+0
-1
lib/tests/fixtures/facts/external/zö/facts.txt less more
0 snowman_fact=olaf
+0
-3
lib/tests/fixtures/facts/linux/cloud/azure less more
0 first line
1 this has option 245 defined
2 last line
+0
-3
lib/tests/fixtures/facts/linux/cloud/azure-unknown less more
0 first line
1 this has option unknown-245 defined
2 last line
+0
-0
lib/tests/fixtures/facts/linux/cloud/not-azure less more
(Empty file)
+0
-83
lib/tests/fixtures/facts/linux/dmidecode/full.txt less more
0 # dmidecode 2.12
1 SMBIOS 2.5 present.
2 10 structures occupying 450 bytes.
3 Table at 0x000E1000.
4
5 Handle 0x0000, DMI type 0, 20 bytes
6 BIOS Information
7 Vendor: innotek GmbH
8 Version: VirtualBox
9 Release Date: 12/01/2006
10 Address: 0xE0000
11 Runtime Size: 128 kB
12 ROM Size: 128 kB
13 Characteristics:
14 ISA is supported
15 PCI is supported
16 Boot from CD is supported
17 Selectable boot is supported
18 8042 keyboard services are supported (int 9h)
19 CGA/mono video services are supported (int 10h)
20 ACPI is supported
21
22 Handle 0x0001, DMI type 1, 27 bytes
23 System Information
24 Manufacturer: innotek GmbH
25 Product Name: VirtualBox
26 Version: 1.2
27 Serial Number: 0
28 UUID: 735AE71B-8655-4AE2-9CA9-172C1BBEDAB5
29 Wake-up Type: Power Switch
30 SKU Number: Not Specified
31 Family: Virtual Machine
32
33 Handle 0x0008, DMI type 2, 15 bytes
34 Base Board Information
35 Manufacturer: Oracle Corporation
36 Product Name: VirtualBox
37 Version: 1.2
38 Serial Number: 0
39 Asset Tag: Not Specified
40 Features:
41 Board is a hosting board
42 Location In Chassis: Not Specified
43 Chassis Handle: 0x0003
44 Type: Motherboard
45 Contained Object Handles: 0
46
47 Handle 0x0003, DMI type 3, 13 bytes
48 Chassis Information
49 Manufacturer: Oracle Corporation
50 Type: Other
51 Lock: Not Present
52 Version: Not Specified
53 Serial Number: Not Specified
54 Asset Tag: Not Specified
55 Boot-up State: Safe
56 Asset Tag: Not Specified
57 Boot-up State: Safe
58 Power Supply State: Safe
59 Thermal State: Safe
60 Security Status: None
61
62 Handle 0x0007, DMI type 126, 42 bytes
63 Inactive
64
65 Handle 0x0005, DMI type 126, 15 bytes
66 Inactive
67
68 Handle 0x0006, DMI type 126, 28 bytes
69 Inactive
70
71 Handle 0x0002, DMI type 11, 7 bytes
72 OEM Strings
73 String 1: vboxVer_4.3.30
74 String 2: vboxRev_101610
75
76 Handle 0x0008, DMI type 128, 8 bytes
77 OEM-specific Type
78 Header and Data:
79 80 08 08 00 CA 1D 23 00
80
81 Handle 0xFEFF, DMI type 127, 4 bytes
82 End Of Table
+0
-83
lib/tests/fixtures/facts/linux/dmidecode/full_alternative.txt less more
0 # dmidecode 2.12
1 SMBIOS 2.5 present.
2 10 structures occupying 450 bytes.
3 Table at 0x000E1000.
4
5 Handle 0x0000, DMI type 0, 20 bytes
6 BIOS Information
7 Vendor: innotek GmbH
8 Version: VirtualBox
9 Release Date: 12/01/2006
10 Address: 0xE0000
11 Runtime Size: 128 kB
12 ROM Size: 128 kB
13 Characteristics:
14 ISA is supported
15 PCI is supported
16 Boot from CD is supported
17 Selectable boot is supported
18 8042 keyboard services are supported (int 9h)
19 CGA/mono video services are supported (int 10h)
20 ACPI is supported
21
22 Handle 0x0001, DMI type 1, 27 bytes
23 System Information
24 Manufacturer: innotek GmbH
25 Product: VirtualBox
26 Version: 1.2
27 Serial Number: 0
28 UUID: 735AE71B-8655-4AE2-9CA9-172C1BBEDAB5
29 Wake-up Type: Power Switch
30 SKU Number: Not Specified
31 Family: Virtual Machine
32
33 Handle 0x0008, DMI type 2, 15 bytes
34 Base Board Information
35 Manufacturer: Oracle Corporation
36 Product: VirtualBox
37 Version: 1.2
38 Serial Number: 0
39 Asset Tag: Not Specified
40 Features:
41 Board is a hosting board
42 Location In Chassis: Not Specified
43 Chassis Handle: 0x0003
44 Type: Motherboard
45 Contained Object Handles: 0
46
47 Handle 0x0003, DMI type 3, 13 bytes
48 Chassis Information
49 Manufacturer: Oracle Corporation
50 Chassis Type: Other
51 Lock: Not Present
52 Version: Not Specified
53 Serial Number: Not Specified
54 Asset Tag: Not Specified
55 Boot-up State: Safe
56 Asset Tag: Not Specified
57 Boot-up State: Safe
58 Power Supply State: Safe
59 Thermal State: Safe
60 Security Status: None
61
62 Handle 0x0007, DMI type 126, 42 bytes
63 Inactive
64
65 Handle 0x0005, DMI type 126, 15 bytes
66 Inactive
67
68 Handle 0x0006, DMI type 126, 28 bytes
69 Inactive
70
71 Handle 0x0002, DMI type 11, 7 bytes
72 OEM Strings
73 String 1: vboxVer_4.3.30
74 String 2: vboxRev_101610
75
76 Handle 0x0008, DMI type 128, 8 bytes
77 OEM-specific Type
78 Header and Data:
79 80 08 08 00 CA 1D 23 00
80
81 Handle 0xFEFF, DMI type 127, 4 bytes
82 End Of Table
+0
-2
lib/tests/fixtures/facts/linux/dmidecode/none.txt less more
0 # dmidecode 2.12
1 # No SMBIOS nor DMI entry point found, sorry.
+0
-7
lib/tests/fixtures/ruby/100_resolutions.rb less more
0 (1..100).each do
1 Facter.add(:foo) do
2 setcode do
3 'bar'
4 end
5 end
6 end
+0
-7
lib/tests/fixtures/ruby/101_resolutions.rb less more
0 (0..100).each do
1 Facter.add(:foo) do
2 setcode do
3 'bar'
4 end
5 end
6 end
+0
-9
lib/tests/fixtures/ruby/aggregate.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first do
2 ['foo']
3 end
4
5 chunk :second do
6 ['bar']
7 end
8 end
+0
-9
lib/tests/fixtures/ruby/aggregate_invalid_require.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first do
2 ['foo']
3 end
4
5 chunk :second, :require => ['first'] do |first|
6 raise 'nope'
7 end
8 end
+0
-30
lib/tests/fixtures/ruby/aggregate_with_block.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk(:one) do
2 1
3 end
4
5 chunk(:two) do
6 2
7 end
8
9 chunk(:three) do
10 3
11 end
12
13 chunk(:four) do
14 4
15 end
16
17 aggregate do |chunks|
18 raise 'nope' unless chunks.size == 4
19 raise 'nope' unless chunks.has_key? :one
20 raise 'nope' unless chunks.has_key? :two
21 raise 'nope' unless chunks.has_key? :three
22 raise 'nope' unless chunks.has_key? :four
23 sum = 0
24 chunks.each_value do |i|
25 sum += i
26 end
27 sum
28 end
29 end
+0
-9
lib/tests/fixtures/ruby/aggregate_with_cycle.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first, :require => :second do |second|
2 raise 'nope'
3 end
4
5 chunk :second, :require => :first do |first|
6 raise 'nope'
7 end
8 end
+0
-13
lib/tests/fixtures/ruby/aggregate_with_invalid_merge.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first do
2 {
3 'foo' => 'hello',
4 }
5 end
6
7 chunk :second do
8 {
9 'foo' => 'world'
10 }
11 end
12 end
+0
-23
lib/tests/fixtures/ruby/aggregate_with_merge.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first do
2 {
3 'foo' => 'bar',
4 'array' => [1, 2, 3],
5 'hash' => {
6 'jam' => 'cakes',
7 'subarray' => ['hello']
8 }
9 }
10 end
11
12 chunk :second do
13 {
14 'baz' => 'jam',
15 'array' => [4, 5, 6],
16 'hash' => {
17 'foo' => 'bar',
18 'subarray' => ['world']
19 }
20 }
21 end
22 end
+0
-16
lib/tests/fixtures/ruby/aggregate_with_require.rb less more
0 Facter.add(:foo, :type => :aggregate) do
1 chunk :first do
2 ['foo']
3 end
4
5 chunk :second, :require => :first do |first|
6 raise 'nope' unless first == ['foo']
7 ['bar'] + first
8 end
9
10 chunk :third, :require => [:first, :second] do |first, second|
11 raise 'nope' unless first == ['foo']
12 raise 'nope' unless second == ['bar', 'foo']
13 ['baz'] + first + second
14 end
15 end
+0
-6
lib/tests/fixtures/ruby/array_confine.rb less more
0 Facter.add(:foo) do
1 confine 'fact' => [ 'value1', 'value2', 'value3' ]
2 setcode do
3 'bar'
4 end
5 end
+0
-5
lib/tests/fixtures/ruby/array_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 [1, true, false, 'foo', 12.4, [1], { :foo => 'bar' }]
3 end
4 end
+0
-19
lib/tests/fixtures/ruby/bad_command.rb less more
0 Facter.add(:foo) do
1 setcode 'not_a_valid_command'
2 end
3
4 Facter.add(:foo) do
5 setcode do
6 Facter::Core::Execution.exec('not_a_valid_command')
7 end
8 end
9
10 Facter.add(:foo) do
11 setcode do
12 begin
13 Facter::Core::Execution.execute('not_a_valid_command')
14 'bar'
15 rescue Facter::Core::Execution::ExecutionFailure => ex
16 end
17 end
18 end
+0
-2
lib/tests/fixtures/ruby/bad_syntax.rb less more
0 Facter.foo(:add) do
1 end
+0
-5
lib/tests/fixtures/ruby/bignum_fact_value.rb less more
0 Facter.add('bignum_fact') do
1 setcode do
2 12345678901
3 end
4 end
+0
-9
lib/tests/fixtures/ruby/block_confine.rb less more
0 Facter.add(:foo) do
1 confine :fact1 do |value|
2 value == 'value1'
3 end
4
5 setcode do
6 'bar'
7 end
8 end
+0
-9
lib/tests/fixtures/ruby/block_false_confine.rb less more
0 Facter.add(:foo) do
1 confine do
2 false
3 end
4
5 setcode do
6 'bar'
7 end
8 end
+0
-9
lib/tests/fixtures/ruby/block_nil_confine.rb less more
0 Facter.add(:foo) do
1 confine do
2 nil
3 end
4
5 setcode do
6 'bar'
7 end
8 end
+0
-9
lib/tests/fixtures/ruby/block_true_confine.rb less more
0 Facter.add(:foo) do
1 confine do
2 true
3 end
4
5 setcode do
6 'bar'
7 end
8 end
+0
-6
lib/tests/fixtures/ruby/boolean_false_confine.rb less more
0 Facter.add(:foo) do
1 confine 'fact' => false
2 setcode do
3 'bar'
4 end
5 end
+0
-5
lib/tests/fixtures/ruby/boolean_false_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 false
3 end
4 end
+0
-6
lib/tests/fixtures/ruby/boolean_true_confine.rb less more
0 Facter.add(:foo) do
1 confine 'fact' => true
2 setcode do
3 'bar'
4 end
5 end
+0
-5
lib/tests/fixtures/ruby/boolean_true_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 true
3 end
4 end
+0
-5
lib/tests/fixtures/ruby/command_with_space.rb less more
0 Facter.add('foo') do
1 setcode do
2 Facter::Util::Resolution.exec('command_with_space.bat bar')
3 end
4 end
+0
-22
lib/tests/fixtures/ruby/confine_missing_fact.rb less more
0 Facter.add(:foo) do
1 confine :not_a_fact => 'foo'
2 setcode do
3 'bar'
4 end
5 end
6
7 Facter.add(:foo) do
8 confine :kernel => 'Linux', :not_a_fact => 'foo'
9 setcode do
10 'bar'
11 end
12 end
13
14 Facter.add(:foo) do
15 confine :not_a_fact do |value|
16 true
17 end
18 setcode do
19 'bar'
20 end
21 end
+0
-20
lib/tests/fixtures/ruby/confine_weight.rb less more
0 Facter.add(:foo) do
1 confine :fact2 => 'value2'
2 setcode do
3 'value1'
4 end
5 end
6
7 Facter.add(:foo) do
8 confine :fact1 => 'value1', :fact2 => 'value2', :fact3 => 'value3'
9 setcode do
10 'value2'
11 end
12 end
13
14 Facter.add(:foo) do
15 confine :fact2 => 'value2', :fact3 => 'value3'
16 setcode do
17 'value3'
18 end
19 end
+0
-15
lib/tests/fixtures/ruby/custom_dir/expect_network_init.rb less more
0 require 'net/http'
1 Facter.add('sometest') do
2 setcode do
3 begin
4 uri = URI("http://localhost:42000")
5 if (Net::HTTP.get_response(uri))
6 'Yay'
7 else
8 'Nay'
9 end
10 rescue => e
11 e.message
12 end
13 end
14 end
+0
-7
lib/tests/fixtures/ruby/cycle.rb less more
0 Facter.add('foo') do
1 confine 'bar' => 'baz'
2 end
3
4 Facter.add('bar') do
5 confine 'foo' => 'baz'
6 end
+0
-7
lib/tests/fixtures/ruby/debug.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter.debug 'message1'
3 Facter.debug 'message2'
4 nil
5 end
6 end
+0
-14
lib/tests/fixtures/ruby/debugging.rb less more
0 begin
1 # Log without debugging
2 Facter.debugging false
3 Facter.debug 'nope'
4
5 # Can be set
6 Facter.debugging true
7 raise 'nope' unless Facter.debugging?
8
9 # Log with debugging
10 Facter.debug 'yep'
11 ensure
12 Facter.debugging false
13 end
+0
-9
lib/tests/fixtures/ruby/debugonce.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter.debugonce 'unique debug1'
3 Facter.debugonce 'unique debug1'
4 Facter.debugonce 'unique debug2'
5 Facter.debugonce 'unique debug2'
6 nil
7 end
8 end
+0
-13
lib/tests/fixtures/ruby/define_aggregate_fact.rb less more
0 Facter.define_fact(:foo) do
1 raise 'nope' unless name == 'foo'
2
3 define_resolution(nil, :type => :aggregate) do
4 chunk :first do
5 ['foo']
6 end
7
8 chunk :second do
9 ['bar']
10 end
11 end
12 end
+0
-9
lib/tests/fixtures/ruby/define_fact.rb less more
0 Facter.define_fact(:foo) do
1 raise 'nope' unless name == 'foo'
2
3 define_resolution(nil) do
4 setcode do
5 'bar'
6 end
7 end
8 end
+0
-5
lib/tests/fixtures/ruby/double_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 12.34
3 end
4 end
+0
-3
lib/tests/fixtures/ruby/empty_command.rb less more
0 Facter.add(:foo) do
1 setcode ''
2 end
+0
-1
lib/tests/fixtures/ruby/empty_fact.rb less more
0 Facter.add(:foo)
+0
-1
lib/tests/fixtures/ruby/empty_fact_with_value.rb less more
0 Facter.add(:foo, :value => { :int => 1, :bool_true => true, :bool_false => false, :double => 12.34, :string => 'foo', :array => [1, 2, 3] })
+0
-3
lib/tests/fixtures/ruby/empty_setcode_command.rb less more
0 Facter.add(:foo) do
1 setcode 'echo >&2'
2 end
+0
-7
lib/tests/fixtures/ruby/exec.rb less more
0 Facter.add(:foo) do
1 setcode do
2 result = Facter::Core::Execution.exec('echo bar baz')
3 raise 'nope' unless result == Facter::Util::Resolution.exec('echo bar baz')
4 result
5 end
6 end
+0
-17
lib/tests/fixtures/ruby/execute_on_fail_raise.rb less more
0 # Should raise by default
1 begin
2 Facter::Core::Execution.execute('the_most_interesting_command_in_the_world')
3 raise 'did not raise'
4 rescue Facter::Core::Execution::ExecutionFailure
5 end
6
7 # Should raise if given an option hash that does not contain :on_fail
8 begin
9 Facter::Core::Execution.execute('the_most_interesting_command_in_the_world', {})
10 raise 'did not raise'
11 rescue Facter::Core::Execution::ExecutionFailure
12 end
13
14 # Should raise if directly given the option
15 Facter::Core::Execution.execute('the_most_interesting_command_in_the_world', :on_fail => :raise)
16 raise 'did not raise'
+0
-5
lib/tests/fixtures/ruby/execute_on_fail_value.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter::Core::Execution.execute('the_most_interesting_command_in_the_world', :on_fail => 'default')
3 end
4 end
+0
-1
lib/tests/fixtures/ruby/execute_timeout.rb less more
0 Facter::Core::Execution.execute("#{RbConfig.ruby} -e 'sleep 15'", :timeout => 1)
+0
-5
lib/tests/fixtures/ruby/execution_failure.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter::Core::Execution.exec('echo bar && false')
3 end
4 end
+0
-5
lib/tests/fixtures/ruby/existing_aggregate_resolution.rb less more
0 Facter.add(:foo, :name => :bar, :type => :aggregate) do
1 end
2
3 Facter.add(:foo, :name => :bar) do
4 end
+0
-5
lib/tests/fixtures/ruby/existing_simple_resolution.rb less more
0 Facter.add(:foo, :name => :bar) do
1 end
2
3 Facter.add(:foo, :name => :bar, :type => :aggregate) do
4 end
+0
-7
lib/tests/fixtures/ruby/fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 raise 'nope' unless Facter.fact('not_a_fact').nil?
3 bar = Facter.fact('bar')
4 bar.value unless bar.nil?
5 end
6 end
+0
-7
lib/tests/fixtures/ruby/facter.rb less more
0 require 'facter'
1
2 Facter.add(:foo) do
3 setcode do
4 'bar'
5 end
6 end
+0
-17
lib/tests/fixtures/ruby/facterversion.rb less more
0 Facter.add(:facterversion) do
1 setcode do
2 # This tests a custom fact that attempts to override a built-in fact
3 # but does not resolve to a value; the built-in fact should
4 # not be overridden
5 nil
6 end
7 end
8
9 Facter.add(:facterversion) do
10 setcode do
11 # This tests a custom fact that attempts to override a built-in fact
12 # but does not set a weight higher than 0; the built-in fact should not
13 # be overridden
14 'overridden'
15 end
16 end
+0
-5
lib/tests/fixtures/ruby/hash_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 { :int => 1, :bool_true => true, :bool_false => false, :double => 12.34, :string => 'foo', :array => [1, 2, 3] }
3 end
4 end
+0
-5
lib/tests/fixtures/ruby/hash_with_non_string_key.rb less more
0 Facter.add(:foo) do
1 setcode do
2 { foo: 'bar' }
3 end
4 end
+0
-5
lib/tests/fixtures/ruby/integer_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 1234
3 end
4 end
+0
-21
lib/tests/fixtures/ruby/log_exception.rb less more
0 Facter.add(:foo) do
1 setcode do
2 begin
3 raise "first"
4 rescue Exception => ex
5 Facter.log_exception ex
6 end
7
8 begin
9 raise "second"
10 rescue Exception => ex
11 Facter.log_exception ex, :default
12 end
13
14 begin
15 raise "nope"
16 rescue Exception => ex
17 Facter.log_exception ex, 'third'
18 end
19 end
20 end
+0
-7
lib/tests/fixtures/ruby/lookup.rb less more
0 Facter.add(:foo) do
1 setcode do
2 raise 'nope' unless Facter['not_a_fact'].nil?
3 bar = Facter['bar']
4 bar.value unless bar.nil?
5 end
6 end
+0
-6
lib/tests/fixtures/ruby/multi_confine.rb less more
0 Facter.add(:foo) do
1 confine 'Fact1' => 'Value1', 'Fact2' => 'Value2', 'Fact3' => 'Value3'
2 setcode do
3 'bar'
4 end
5 end
+0
-13
lib/tests/fixtures/ruby/named_resolution.rb less more
0 Facter.add(:foo, :name => 'bar') do
1 raise 'nope' unless name == 'bar'
2 setcode do
3 'value1'
4 end
5 end
6
7 Facter.add(:foo, :name => 'bar') do
8 raise 'nope' unless name == 'bar'
9 setcode do
10 'value2'
11 end
12 end
+0
-5
lib/tests/fixtures/ruby/negative_number.rb less more
0 Facter.add(:foo) do
1 setcode do
2 -101
3 end
4 end
+0
-5
lib/tests/fixtures/ruby/nil_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 nil
3 end
4 end
+0
-21
lib/tests/fixtures/ruby/nonexistent_command.rb less more
0 Facter.add(:first) do
1 setcode do
2 next 'fail' unless Facter::Core::Execution.exec("does_not_exist || echo fail").nil?
3 'pass'
4 end
5 end
6
7 Facter.add(:second) do
8 setcode 'does_not_exist || echo fail'
9 end
10
11 Facter.add(:third) do
12 setcode do
13 begin
14 Facter::Core::Execution.execute("does_not_exist || echo fail")
15 rescue Facter::Core::Execution::ExecutionFailure
16 next 'pass'
17 end
18 'fail'
19 end
20 end
+0
-16
lib/tests/fixtures/ruby/on_message.rb less more
0 def test_message(level, message)
1 Facter.on_message do |lvl, msg|
2 raise 'nope' unless level == lvl
3 raise 'nope' unless message = msg
4 end
5
6 begin
7 Facter.debug message if level == :debug
8 Facter.warn message if level == :warn
9 rescue Exception => ex
10 Facter.on_message
11 end
12 end
13
14 test_message(:debug, "debug message")
15 test_message(:warn, "warning message")
+0
-6
lib/tests/fixtures/ruby/range_confine.rb less more
0 Facter.add(:foo) do
1 confine :fact => (3..5)
2 setcode do
3 'bar'
4 end
5 end
+0
-6
lib/tests/fixtures/ruby/regexp_confine.rb less more
0 Facter.add(:foo) do
1 confine :fact => /foo/
2 setcode do
3 'bar'
4 end
5 end
+0
-6
lib/tests/fixtures/ruby/ruby.rb less more
0 Facter.add(:ruby) do
1 has_weight 1
2 setcode do
3 'override'
4 end
5 end
+0
-11
lib/tests/fixtures/ruby/simple.rb less more
0 Facter.add(:foo) do
1 setcode do
2 'bar'
3 end
4 end
5
6 Facter.add(:foo) do
7 setcode do
8 'baz'
9 end
10 end
+0
-7
lib/tests/fixtures/ruby/simple_command.rb less more
0 Facter.add(:foo) do
1 setcode 'echo bar baz'
2 end
3
4 Facter.add(:foo) do
5 setcode 'echo baz'
6 end
+0
-6
lib/tests/fixtures/ruby/simple_confine.rb less more
0 Facter.add(:foo) do
1 confine 'SomeFact' => 'SomeValue'
2 setcode do
3 'bar'
4 end
5 end
+0
-5
lib/tests/fixtures/ruby/simple_resolution.rb less more
0 Facter.add(:foo, :type => :simple) do
1 setcode do
2 'bar'
3 end
4 end
+0
-7
lib/tests/fixtures/ruby/single_allocation.rb less more
0 Facter.add(:foo) do
1 setcode do
2 # Ensure that the same object is returned from Facter.value for built-in facts
3 Facter.value(:facterversion) &&
4 Facter.value(:facterversion).object_id == Facter.value(:facterversion).object_id
5 end
6 end
+0
-9
lib/tests/fixtures/ruby/stderr_output.rb less more
0 Facter.add(:first) do
1 setcode do
2 Facter::Core::Execution.exec("echo foo 1>&2 && echo bar")
3 end
4 end
5
6 Facter.add(:second) do
7 setcode 'echo foo 1>&2 && echo bar'
8 end
+0
-5
lib/tests/fixtures/ruby/string_fact.rb less more
0 Facter.add(:foo) do
1 setcode do
2 'hello world'
3 end
4 end
+0
-10
lib/tests/fixtures/ruby/timeout.rb less more
0 # We should get a warning for using the timeout option
1 Facter.add(:timeout, :name => 'bar', :timeout => 1000) do
2 # And another warning for using timeout=
3 self.timeout = 10
4 end
5
6 # Try again to ensure only one warning each
7 Facter.add(:timeout, :name => 'bar', :timeout => 100) do
8 self.timeout = 1
9 end
+0
-22
lib/tests/fixtures/ruby/trace.rb less more
0 begin
1 # Log without tracing
2 Facter.trace false
3 begin
4 raise 'first'
5 rescue Exception => ex
6 Facter.log_exception ex
7 end
8
9 # Can be set
10 Facter.trace true
11 raise 'nope' unless Facter.trace?
12
13 # Log with tracing
14 begin
15 raise 'second'
16 rescue Exception => ex
17 Facter.log_exception ex
18 end
19 ensure
20 Facter.trace false
21 end
+0
-6
lib/tests/fixtures/ruby/uniᐁdir/customfacts™.rb less more
0 # encoding: utf-8
1 Facter.add('somefact™') do
2 setcode do
3 'other™'
4 end
5 end
+0
-7
lib/tests/fixtures/ruby/uses_exit_code.rb less more
0 Facter.add(:foo) do
1 setcode do
2 cmd = Gem.win_platform? ? 'cmd /k "exit 99"' : 'sh -c "exit 99"'
3 Facter::Core::Execution.execute(cmd);
4 $?.exitstatus
5 end
6 end
+0
-6
lib/tests/fixtures/ruby/value.rb less more
0 Facter.add(:foo) do
1 setcode do
2 raise 'nope' unless Facter.value('not_a_fact').nil?
3 Facter.value('bar')
4 end
5 end
+0
-2
lib/tests/fixtures/ruby/version.rb less more
0 raise 'nope' unless Facter.version == Facter::FACTERVERSION
1 Facter.debug Facter.version
+0
-7
lib/tests/fixtures/ruby/warn.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter.warn 'message1'
3 Facter.warn 'message2'
4 nil
5 end
6 end
+0
-9
lib/tests/fixtures/ruby/warnonce.rb less more
0 Facter.add(:foo) do
1 setcode do
2 Facter.warnonce 'unique warning1'
3 Facter.warnonce 'unique warning1'
4 Facter.warnonce 'unique warning2'
5 Facter.warnonce 'unique warning2'
6 nil
7 end
8 end
+0
-20
lib/tests/fixtures/ruby/weight.rb less more
0 Facter.add(:foo) do
1 has_weight 100
2 setcode do
3 'value1'
4 end
5 end
6
7 Facter.add(:foo) do
8 has_weight 1000
9 setcode do
10 'value2'
11 end
12 end
13
14 Facter.add(:foo) do
15 has_weight 1
16 setcode do
17 'value3'
18 end
19 end
+0
-17
lib/tests/fixtures/ruby/weight_option.rb less more
0 Facter.add(:foo, :weight => 100) do
1 setcode do
2 'value1'
3 end
4 end
5
6 Facter.add(:foo, :weight => 1000) do
7 setcode do
8 'value2'
9 end
10 end
11
12 Facter.add(:foo, :weight => 1) do
13 setcode do
14 'value3'
15 end
16 end
+0
-9
lib/tests/fixtures/ruby/which.rb less more
0 Facter.add(:foo) do
1 setcode do
2 raise 'nope' unless Facter::Core::Execution.which('not_a_command').nil?
3 raise 'nope' if Facter::Core::Execution.which('sh').nil? && Facter::Core::Execution.which('cmd.exe').nil?
4 raise 'nope' if (Facter::Core::Execution.which('sh') != Facter::Util::Resolution.which('sh')) ||
5 (Facter::Core::Execution.which('cmd.exe') != Facter::Util::Resolution.which('cmd.exe'))
6 'bar'
7 end
8 end
+0
-7
lib/tests/fixtures/ruby/windows/ole.rb less more
0 Facter.add(:foo) do
1 setcode do
2 require 'win32ole'
3 context = WIN32OLE.new('WbemScripting.SWbemNamedValueSet')
4 'bar' unless context.nil?
5 end
6 end
+0
-63
lib/tests/fixtures.cc less more
0 #include "fixtures.hpp"
1 #include <boost/filesystem.hpp>
2 #include <boost/nowide/fstream.hpp>
3 #include <iostream>
4 #include <sstream>
5
6 #pragma GCC diagnostic push
7 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
8 #pragma GCC diagnostic ignored "-Wunused-variable"
9 #include <boost/thread/thread.hpp>
10 #include <boost/chrono/duration.hpp>
11 #pragma GCC diagnostic pop
12
13 using namespace std;
14 using namespace boost::filesystem;
15
16 namespace facter { namespace testing {
17
18 bool load_fixture(string const& name, string& data)
19 {
20 string path = string(LIBFACTER_TESTS_DIRECTORY) + "/fixtures/" + name;
21 boost::nowide::ifstream in(path.c_str(), ios_base::in | ios_base::binary);
22 if (!in) {
23 return false;
24 }
25 ostringstream buffer;
26 buffer << in.rdbuf();
27 data = buffer.str();
28 return true;
29 }
30
31 test_with_relative_path::test_with_relative_path(string const& dirname, string const& filename, string const& contents)
32 {
33 path dir(dirname);
34 if (exists(dir)) {
35 throw runtime_error(dir.string() + " already exists");
36 }
37 if (!create_directory(dir)) {
38 throw runtime_error(dir.string() + " could not be created");
39 }
40 _dir = dir.string();
41
42 path exec = dir / filename;
43 {
44 boost::nowide::ofstream exec_file(exec.string().c_str());
45 exec_file << contents << endl;
46 }
47 permissions(exec, add_perms | owner_exe | group_exe);
48 }
49
50 test_with_relative_path::~test_with_relative_path()
51 {
52 if (!_dir.empty()) {
53 remove_all(_dir);
54 // Wait for at most 5 seconds to ensure the directory is destroyed.
55 int count = 50;
56 while (exists(_dir) && --count > 0) {
57 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
58 }
59 }
60 }
61
62 }} // namespace facter::testing
+0
-26
lib/tests/fixtures.hpp.in less more
0 #pragma once
1
2 #include <string>
3 #include <functional>
4 #include "collection_fixture.hpp"
5
6 #define JAVA_EXECUTABLE "@Java_JAVA_EXECUTABLE@"
7 #define BINARY_DIRECTORY "@CMAKE_BINARY_DIR@"
8 #define LIBFACTER_TESTS_DIRECTORY "@CMAKE_CURRENT_LIST_DIR@"
9
10 namespace facter { namespace testing {
11
12 bool load_fixture(std::string const& name, std::string& data);
13
14 // Creates a local directory and adds a file <filename> to it containing <contents>.
15 // When destroyed, it deletes the directory.
16 struct test_with_relative_path
17 {
18 test_with_relative_path(std::string const& dirname, std::string const& filename, std::string const& contents);
19 ~test_with_relative_path();
20
21 private:
22 std::string _dir;
23 };
24
25 }} // namespace facter::testing
+0
-65
lib/tests/java/facter.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/collection.hpp>
2 #include <leatherman/execution/execution.hpp>
3 #include <leatherman/util/environment.hpp>
4 #include <boost/filesystem.hpp>
5 #include "../fixtures.hpp"
6
7 using namespace std;
8 using namespace facter::facts;
9 using namespace leatherman::execution;
10 using namespace leatherman::util;
11 using namespace boost::filesystem;
12 using namespace facter::testing;
13
14 SCENARIO("using libfacter from Java") {
15 collection_fixture facts;
16 facts.add_default_facts(true);
17
18 path jar_path = path(BINARY_DIRECTORY) / "lib" / "facter.jar";
19
20 CAPTURE(JAVA_EXECUTABLE);
21 CAPTURE(BINARY_DIRECTORY);
22 CAPTURE(jar_path);
23
24 GIVEN("the os fact") {
25 try {
26 auto exec = execute(
27 JAVA_EXECUTABLE,
28 {
29 "-jar",
30 jar_path.string(),
31 "os"
32 },
33 {
34 { "FACTERDIR", BINARY_DIRECTORY }
35 },
36 0,
37 {
38 execution_options::trim_output,
39 execution_options::merge_environment,
40 execution_options::throw_on_failure
41 });
42 CAPTURE(exec.output);
43 CAPTURE(exec.error);
44 THEN("the value should match") {
45 REQUIRE(exec.success);
46 ostringstream ss;
47 auto value = facts["os"];
48 REQUIRE(value);
49 value->write(ss);
50 REQUIRE(exec.output == ss.str());
51 }
52 } catch (child_exit_exception const& ex) {
53 CAPTURE(ex.output());
54 CAPTURE(ex.error());
55 CAPTURE(ex.status_code());
56 FAIL("exception from child process.");
57 } catch (child_signal_exception const& ex) {
58 CAPTURE(ex.output());
59 CAPTURE(ex.error());
60 CAPTURE(ex.signal());
61 FAIL("signal from child process.");
62 }
63 }
64 }
+0
-29
lib/tests/log_capture.cc less more
0 #include "log_capture.hpp"
1 #include <boost/nowide/iostream.hpp>
2
3 using namespace std;
4 using namespace facter::logging;
5
6 namespace facter { namespace testing {
7
8 log_capture::log_capture(facter::logging::level level)
9 {
10 // Setup logging for capturing
11 setup_logging(_stream);
12 set_level(level);
13 }
14
15 log_capture::~log_capture()
16 {
17 // Cleanup
18 setup_logging(boost::nowide::cout);
19 set_level(level::none);
20 clear_logged_errors();
21 }
22
23 string log_capture::result() const
24 {
25 return _stream.str();
26 }
27
28 }} // namespace facter::testing
+0
-35
lib/tests/log_capture.hpp less more
0 #pragma once
1
2 #include <sstream>
3 #include <string>
4 #include <facter/logging/logging.hpp>
5
6 namespace facter { namespace testing {
7
8 /**
9 * Utility class for capturing facter log output.
10 */
11 struct log_capture
12 {
13 /**
14 * Constructs the log capture and starts capturing log output.
15 * @param level The log level to capture.
16 */
17 explicit log_capture(facter::logging::level level);
18
19 /**
20 * Destructs the log capture and stops capturing log output.
21 */
22 ~log_capture();
23
24 /**
25 * Gets the captured log.
26 * @return Returns the captured log as a single string.
27 */
28 std::string result() const;
29
30 private:
31 std::ostringstream _stream;
32 };
33
34 }} // namespace facter::testing
+0
-69
lib/tests/logging/logging.cc less more
0 #include <catch.hpp>
1 #include <facter/logging/logging.hpp>
2 #include <leatherman/util/regex.hpp>
3 #include "../log_capture.hpp"
4
5 using namespace std;
6 using namespace facter::logging;
7 using namespace leatherman::util;
8 using namespace facter::testing;
9
10 SCENARIO("logging with a TRACE level") {
11 log_capture capture(level::trace);
12 REQUIRE(is_enabled(level::trace));
13 log(level::trace, "testing %1% %2% %3%", 1, "2", 3.0);
14 auto output = capture.result();
15 CAPTURE(output);
16 REQUIRE(re_search(output, boost::regex("TRACE puppetlabs\\.facter - testing 1 2 3$")));
17 REQUIRE_FALSE(error_logged());
18 }
19
20 SCENARIO("logging with a DEBUG level") {
21 log_capture capture(level::debug);
22 REQUIRE(is_enabled(level::debug));
23 log(level::debug, "testing %1% %2% %3%", 1, "2", 3.0);
24 auto output = capture.result();
25 CAPTURE(output);
26 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - testing 1 2 3$")));
27 REQUIRE_FALSE(error_logged());
28 }
29
30 SCENARIO("logging with an INFO level") {
31 log_capture capture(level::info);
32 REQUIRE(is_enabled(level::info));
33 log(level::info, "testing %1% %2% %3%", 1, "2", 3.0);
34 auto output = capture.result();
35 CAPTURE(output);
36 REQUIRE(re_search(output, boost::regex("INFO puppetlabs\\.facter - testing 1 2 3$")));
37 REQUIRE_FALSE(error_logged());
38 }
39
40 SCENARIO("logging with a WARNING level") {
41 log_capture capture(level::warning);
42 REQUIRE(is_enabled(level::warning));
43 log(level::warning, "testing %1% %2% %3%", 1, "2", 3.0);
44 auto output = capture.result();
45 CAPTURE(output);
46 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - testing 1 2 3")));
47 REQUIRE_FALSE(error_logged());
48 }
49
50 SCENARIO("logging with an ERROR level") {
51 log_capture capture(level::error);
52 REQUIRE(is_enabled(level::error));
53 log(level::error, "testing %1% %2% %3%", 1, "2", 3.0);
54 auto output = capture.result();
55 CAPTURE(output);
56 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - testing 1 2 3$")));
57 REQUIRE(error_logged());
58 }
59
60 SCENARIO("logging with a FATAL level") {
61 log_capture capture(level::fatal);
62 REQUIRE(is_enabled(level::fatal));
63 log(level::fatal, "testing %1% %2% %3%", 1, "2", 3.0);
64 auto output = capture.result();
65 CAPTURE(output);
66 REQUIRE(re_search(output, boost::regex("FATAL puppetlabs\\.facter - testing 1 2 3$")));
67 REQUIRE(error_logged());
68 }
+0
-20
lib/tests/main.cc less more
0 #define CATCH_CONFIG_RUNNER
1 #include <catch.hpp>
2 #include <facter/ruby/ruby.hpp>
3 #include <facter/logging/logging.hpp>
4 #include <boost/nowide/iostream.hpp>
5
6 using namespace std;
7 using namespace facter::logging;
8
9 int main(int argc, char **argv)
10 {
11 // Disable logging for tests
12 setup_logging(boost::nowide::cout);
13 set_level(level::none);
14
15 // Before running tests, initialize Ruby
16 facter::ruby::initialize();
17
18 return Catch::Session().run( argc, argv );
19 }
+0
-44
lib/tests/mock_server.cc less more
0 #include <cstdlib>
1 #include <iostream>
2 #include <utility>
3 #include "mock_server.hpp"
4
5 namespace facter {
6
7 static void session(tcp::socket sock) {
8 try {
9 for (;;) {
10 char data[1024];
11
12 boost::system::error_code error;
13 sock.read_some(boost::asio::buffer(data), error);
14 if (error == boost::asio::error::eof) {
15 break; // Connection closed cleanly by peer.
16 } else if (error) {
17 throw boost::system::system_error(error); // Some other error.
18 }
19
20 std::string response = "HTTP/1.1 301 Moved Permanently\nContent-length: 0\nLocation: https://puppet.com/\nConnection: close\n\n";
21 boost::asio::write(sock, boost::asio::buffer(response));
22 }
23 } catch (std::exception& e) {
24 std::cerr << "Exception in thread: " << e.what() << "\n";
25 }
26 }
27
28 mock_server::mock_server(int port) :
29 acceptor_(io_service_, tcp::endpoint(tcp::v4(), port)),
30 socket_(io_service_)
31 {
32 acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
33 if (!ec) session(std::move(socket_));
34 });
35 thread_ = boost::thread([this]() {io_service_.run_one();});
36 }
37
38 mock_server::~mock_server()
39 {
40 thread_.join();
41 }
42
43 } // namespace facter
+0
-25
lib/tests/mock_server.hpp less more
0 #pragma once
1
2 #pragma GCC diagnostic push
3 #pragma GCC diagnostic ignored "-Wunused-variable"
4 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
5 #include <boost/asio.hpp>
6 #include <boost/thread.hpp>
7 #pragma GCC diagnostic pop
8
9 namespace facter {
10
11 using boost::asio::ip::tcp;
12
13 class mock_server {
14 public:
15 mock_server(int port);
16 ~mock_server();
17 private:
18 boost::asio::io_service io_service_;
19 tcp::acceptor acceptor_;
20 tcp::socket socket_;
21 boost::thread thread_;
22 };
23
24 } // namespace facter
+0
-634
lib/tests/ruby/ruby.cc less more
0 #include <catch.hpp>
1 #include <facter/version.h>
2 #include <facter/facts/scalar_value.hpp>
3 #include <internal/ruby/ruby_value.hpp>
4 #include <leatherman/util/regex.hpp>
5 #include <leatherman/util/scoped_env.hpp>
6 #include <leatherman/ruby/api.hpp>
7 #include "./ruby_helper.hpp"
8 #include "../collection_fixture.hpp"
9 #include "../log_capture.hpp"
10
11 using namespace std;
12 using namespace facter::facts;
13 using namespace facter::ruby;
14 using namespace facter::logging;
15 using namespace facter::testing;
16 using namespace leatherman::util;
17 using namespace leatherman::ruby;
18
19 SCENARIO("custom facts written in Ruby") {
20 collection_fixture facts;
21 REQUIRE(facts.size() == 0u);
22
23 // Setup ruby
24 auto& ruby = api::instance();
25 REQUIRE(ruby.initialized());
26 ruby.include_stack_trace(true);
27
28 GIVEN("a fact that resolves to nil") {
29 REQUIRE(load_custom_fact("nil_fact.rb", facts));
30 THEN("the value should not be in the collection") {
31 REQUIRE_FALSE(facts["foo"]);
32 }
33 }
34 GIVEN("a fact that resolves to non-nil") {
35 REQUIRE(load_custom_fact("simple.rb", facts));
36 THEN("the value should be in the collection") {
37 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
38 }
39 }
40 GIVEN("a fact with a simple resolution") {
41 REQUIRE(load_custom_fact("simple_resolution.rb", facts));
42 THEN("the value should be in the collection") {
43 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
44 }
45 }
46 GIVEN("a fact without any resolutions") {
47 WHEN("the fact has no explicit value") {
48 REQUIRE(load_custom_fact("empty_fact.rb", facts));
49 THEN("the value should not be in the collection") {
50 REQUIRE_FALSE(facts["foo"]);
51 }
52 }
53 WHEN("the fact has an explicit value") {
54 REQUIRE(load_custom_fact("empty_fact_with_value.rb", facts));
55 THEN("the value should be in the collection") {
56 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "{\n int => 1,\n bool_true => true,\n bool_false => false,\n double => 12.34,\n string => \"foo\",\n array => [\n 1,\n 2,\n 3\n ]\n}");
57 }
58 }
59 }
60 GIVEN("a fact with an empty command") {
61 log_capture capture(level::error);
62 REQUIRE_FALSE(load_custom_fact("empty_command.rb", facts));
63 THEN("an error is logged") {
64 auto output = capture.result();
65 CAPTURE(output);
66 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* expected a non-empty String for first argument")));
67 }
68 }
69 GIVEN("a fact with a command") {
70 REQUIRE(load_custom_fact("simple_command.rb", facts));
71 THEN("the value should be in the collection") {
72 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar baz\"");
73 }
74 }
75 GIVEN("a fact with a bad command") {
76 THEN("the value should not be in the collection") {
77 REQUIRE_FALSE(facts["foo"]);
78 }
79 }
80 GIVEN("a fact with unicode characters in the path and name") {
81 REQUIRE(load_custom_fact("uni\u1401dir/customfacts\u2122.rb", facts));
82 THEN("the value should be in the collection") {
83 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("somefact\u2122")) == "\"other\u2122\"");
84 }
85 }
86 GIVEN("a fact with a confine") {
87 WHEN("the confine is met") {
88 facts.add("somefact", make_value<string_value>("SomeValue"));
89 REQUIRE(load_custom_fact("simple_confine.rb", facts));
90 THEN("the value should be in the collection") {
91 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
92 }
93 }
94 WHEN("the confine is not met") {
95 REQUIRE(load_custom_fact("simple_confine.rb", facts));
96 THEN("the value should not be in the collection") {
97 REQUIRE_FALSE(facts["foo"]);
98 }
99 }
100 WHEN("the multiple confines are present and one is not met") {
101 facts.add("kernel", make_value<string_value>("linux"));
102 REQUIRE(load_custom_fact("confine_missing_fact.rb", facts));
103 THEN("the value should not be in the collection") {
104 REQUIRE_FALSE(facts["foo"]);
105 }
106 }
107 WHEN("multiple confines are met") {
108 facts.add("fact1", make_value<string_value>("VALUE1"));
109 facts.add("fact2", make_value<string_value>("Value2"));
110 facts.add("fact3", make_value<string_value>("value3"));
111 REQUIRE(load_custom_fact("multi_confine.rb", facts));
112 THEN("the value should be in the collection") {
113 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
114 }
115 }
116 WHEN("none of the multiple confines are met") {
117 REQUIRE(load_custom_fact("multi_confine.rb", facts));
118 THEN("the value should not be in the collection") {
119 REQUIRE_FALSE(facts["foo"]);
120 }
121 }
122 WHEN("the confine is a block that returns nil") {
123 REQUIRE(load_custom_fact("block_nil_confine.rb", facts));
124 THEN("the value should not be in the collection") {
125 REQUIRE_FALSE(facts["foo"]);
126 }
127 }
128 WHEN("the confine is a block that evaluates to false") {
129 REQUIRE(load_custom_fact("block_confine.rb", facts));
130 THEN("the value should not be in the collection") {
131 REQUIRE_FALSE(facts["foo"]);
132 }
133 }
134 WHEN("the confine is a block that simply returns false") {
135 REQUIRE(load_custom_fact("block_false_confine.rb", facts));
136 THEN("the value should not be in the collection") {
137 REQUIRE_FALSE(facts["foo"]);
138 }
139 }
140 WHEN("the confine is a block that evaluates to true") {
141 facts.add("fact1", make_value<string_value>("value1"));
142 REQUIRE(load_custom_fact("block_confine.rb", facts));
143 THEN("the value should be in the collection") {
144 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
145 }
146 }
147 WHEN("the confine is a block that simply returns true") {
148 REQUIRE(load_custom_fact("block_true_confine.rb", facts));
149 THEN("the value should be in the collection") {
150 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
151 }
152 }
153 WHEN("the confine is an array and the value is not in the array") {
154 facts.add("fact", make_value<string_value>("foo"));
155 REQUIRE(load_custom_fact("array_confine.rb", facts));
156 THEN("the value should not be in the collection") {
157 REQUIRE_FALSE(facts["foo"]);
158 }
159 }
160 WHEN("the confine is an array and the value is in the array") {
161 facts.add("fact", make_value<string_value>("value3"));
162 REQUIRE(load_custom_fact("array_confine.rb", facts));
163 THEN("the value should be in the collection") {
164 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
165 }
166 }
167 WHEN("the confine is a regular expression that evaluates to true") {
168 facts.add("fact", make_value<string_value>("foo"));
169 REQUIRE(load_custom_fact("regexp_confine.rb", facts));
170 THEN("the value should be in the collection") {
171 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
172 }
173 }
174 WHEN("the confine is a regular expression that evaluates to false") {
175 facts.add("fact", make_value<string_value>("baz"));
176 REQUIRE(load_custom_fact("regexp_confine.rb", facts));
177 THEN("the value should not be in the collection") {
178 REQUIRE_FALSE(facts["foo"]);
179 }
180 }
181 WHEN("the confine is a range that evaluates to true") {
182 facts.add("fact", make_value<integer_value>(4));
183 REQUIRE(load_custom_fact("range_confine.rb", facts));
184 THEN("the value should be in the collection") {
185 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
186 }
187 }
188 WHEN("the confine is a range that evaluates to false") {
189 facts.add("fact", make_value<integer_value>(10));
190 REQUIRE(load_custom_fact("range_confine.rb", facts));
191 THEN("the value should not be in the collection") {
192 REQUIRE_FALSE(facts["foo"]);
193 }
194 }
195 WHEN("the confine evaluates to true") {
196 facts.add("fact", make_value<boolean_value>(true));
197 REQUIRE(load_custom_fact("boolean_true_confine.rb", facts));
198 THEN("the value should be in the collection") {
199 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
200 }
201 }
202 WHEN("the confine evaluates to false") {
203 facts.add("fact", make_value<boolean_value>(true));
204 REQUIRE(load_custom_fact("boolean_false_confine.rb", facts));
205 THEN("the value should not be in the collection") {
206 REQUIRE_FALSE(facts["foo"]);
207 }
208 }
209 THEN("resolution with the most confines wins") {
210 facts.add("fact1", make_value<string_value>("value1"));
211 facts.add("fact2", make_value<string_value>("value2"));
212 facts.add("fact3", make_value<string_value>("value3"));
213 REQUIRE(load_custom_fact("confine_weight.rb", facts));
214 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"value2\"");
215 }
216 }
217 GIVEN("a file with a syntax error") {
218 log_capture capture(level::error);
219 REQUIRE_FALSE(load_custom_fact("bad_syntax.rb", facts));
220 THEN("an error is logged") {
221 auto output = capture.result();
222 CAPTURE(output);
223 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* undefined method `foo' for Facter:Module")));
224 }
225 }
226 GIVEN("a fact with weighted resolutions") {
227 REQUIRE(load_custom_fact("weight.rb", facts));
228 THEN("the resolution with the highest weight wins") {
229 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"value2\"");
230 }
231 }
232 GIVEN("a fact with weight options") {
233 REQUIRE(load_custom_fact("weight_option.rb", facts));
234 THEN("the resolution with the highest weight wins") {
235 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"value2\"");
236 }
237 }
238 GIVEN("a fact that resolves to a string value") {
239 REQUIRE(load_custom_fact("string_fact.rb", facts));
240 THEN("the value is a string") {
241 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"hello world\"");
242 }
243 }
244 GIVEN("a fact that resolves to an integer value") {
245 REQUIRE(load_custom_fact("integer_fact.rb", facts));
246 THEN("the value is an integer") {
247 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "1234");
248 }
249 }
250 GIVEN("a fact that resolves to a true value") {
251 REQUIRE(load_custom_fact("boolean_true_fact.rb", facts));
252 THEN("the value is true") {
253 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "true");
254 }
255 }
256 GIVEN("a fact that resolves to a false value") {
257 REQUIRE(load_custom_fact("boolean_false_fact.rb", facts));
258 THEN("the value is false") {
259 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "false");
260 }
261 }
262 GIVEN("a fact that resolves to a double value") {
263 REQUIRE(load_custom_fact("double_fact.rb", facts));
264 THEN("the value is a double") {
265 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "12.34");
266 }
267 }
268 GIVEN("a fact that resolves to an array value") {
269 REQUIRE(load_custom_fact("array_fact.rb", facts));
270 THEN("the value is an array") {
271 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "[\n 1,\n true,\n false,\n \"foo\",\n 12.4,\n [\n 1\n ],\n {\n foo => \"bar\"\n }\n]");
272 }
273 THEN("the members of the fact can be queried") {
274 REQUIRE(ruby_value_to_string(facts.query<ruby_value>("foo.5.0")) == "1");
275 }
276 }
277 GIVEN("a fact that resolves to a hash value") {
278 REQUIRE(load_custom_fact("hash_fact.rb", facts));
279 THEN("the value is a hash") {
280 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "{\n int => 1,\n bool_true => true,\n bool_false => false,\n double => 12.34,\n string => \"foo\",\n array => [\n 1,\n 2,\n 3\n ]\n}");
281 }
282 THEN("the members of the fact can be queried") {
283 REQUIRE(ruby_value_to_string(facts.query<ruby_value>("foo.array.1")) == "2");
284 }
285 }
286 GIVEN("a fact that resolves using Facter.value") {
287 facts.add("bar", make_value<string_value>("baz"));
288 REQUIRE(load_custom_fact("value.rb", facts));
289 THEN("the value should match") {
290 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"baz\"");
291 }
292 }
293 GIVEN("a fact that resolves using Facter.fact") {
294 facts.add("bar", make_value<string_value>("baz"));
295 REQUIRE(load_custom_fact("fact.rb", facts));
296 THEN("the value should match") {
297 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"baz\"");
298 }
299 }
300 GIVEN("a fact that resolves using Facter[]") {
301 facts.add("bar", make_value<string_value>("baz"));
302 REQUIRE(load_custom_fact("lookup.rb", facts));
303 THEN("the value should match") {
304 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"baz\"");
305 }
306 }
307 GIVEN("a fact that resolves when using Facter::Core::Execution#which") {
308 REQUIRE(load_custom_fact("which.rb", facts));
309 THEN("the value should match") {
310 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
311 }
312 }
313 GIVEN("a fact that logs debug messages") {
314 log_capture capture(level::debug);
315 REQUIRE(load_custom_fact("debug.rb", facts));
316 THEN("the messages should be logged") {
317 auto output = capture.result();
318 CAPTURE(output);
319 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - message1")));
320 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - message2")));
321 }
322 }
323 GIVEN("a fact that logs debug messages only once") {
324 log_capture capture(level::debug);
325 REQUIRE(load_custom_fact("debugonce.rb", facts));
326 THEN("the messages should be logged") {
327 auto output = capture.result();
328 CAPTURE(output);
329 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - unique debug1")));
330 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - unique debug2")));
331 }
332 }
333 GIVEN("a fact that logs warning messages") {
334 log_capture capture(level::warning);
335 REQUIRE(load_custom_fact("warn.rb", facts));
336 THEN("the messages should be logged") {
337 auto output = capture.result();
338 CAPTURE(output);
339 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - message1")));
340 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - message2")));
341 }
342 }
343 GIVEN("a fact that logs warning messages only once") {
344 log_capture capture(level::warning);
345 REQUIRE(load_custom_fact("warnonce.rb", facts));
346 THEN("the messages should be logged") {
347 auto output = capture.result();
348 CAPTURE(output);
349 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - unique warning1")));
350 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - unique warning2")));
351 }
352 }
353 GIVEN("a fact that logs an exception") {
354 log_capture capture(level::error);
355 REQUIRE(load_custom_fact("log_exception.rb", facts));
356 THEN("an error should be logged") {
357 auto output = capture.result();
358 CAPTURE(output);
359 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - first")));
360 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - second")));
361 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - third")));
362 }
363 }
364 GIVEN("a fact with a named resolution") {
365 REQUIRE(load_custom_fact("named_resolution.rb", facts));
366 THEN("adding a resolution with the same name overrides the existing resolution") {
367 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"value2\"");
368 }
369 }
370 GIVEN("a fact added with define_fact and define_resolution") {
371 REQUIRE(load_custom_fact("define_fact.rb", facts));
372 THEN("the value should be in the collection") {
373 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
374 }
375 }
376 GIVEN("a fact with a dependency cycle") {
377 log_capture capture(level::error);
378 REQUIRE(load_custom_fact("cycle.rb", facts));
379 THEN("an error is logged") {
380 auto output = capture.result();
381 CAPTURE(output);
382 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* cycle detected while requesting value of fact \"bar\"")));
383 }
384 }
385 GIVEN("an aggregate resolution with array chunks") {
386 REQUIRE(load_custom_fact("aggregate.rb", facts));
387 THEN("the arrays are appended") {
388 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "[\n \"foo\",\n \"bar\"\n]");
389 }
390 }
391 GIVEN("an aggregate resolution with required chunks") {
392 REQUIRE(load_custom_fact("aggregate_with_require.rb", facts));
393 THEN("the arrays are appended in dependency order") {
394 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "[\n \"foo\",\n \"bar\",\n \"foo\",\n \"baz\",\n \"foo\",\n \"bar\",\n \"foo\"\n]");
395 }
396 }
397 GIVEN("an aggregate resolution with an invalid require") {
398 log_capture capture(level::error);
399 REQUIRE_FALSE(load_custom_fact("aggregate_invalid_require.rb", facts));
400 THEN("the arrays are appended in dependency order") {
401 auto output = capture.result();
402 CAPTURE(output);
403 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* expected a Symbol or Array of Symbol for require option")));
404 }
405 }
406 GIVEN("an aggregate resolution with a custom aggregator") {
407 REQUIRE(load_custom_fact("aggregate_with_block.rb", facts));
408 THEN("the value should be what was returned from the block") {
409 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "10");
410 }
411 }
412 GIVEN("an aggregate resolution with hashes to merge") {
413 REQUIRE(load_custom_fact("aggregate_with_merge.rb", facts));
414 THEN("the value should be a deep merge of the hashes") {
415 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "{\n foo => \"bar\",\n array => [\n 1,\n 2,\n 3,\n 4,\n 5,\n 6\n ],\n hash => {\n jam => \"cakes\",\n subarray => [\n \"hello\",\n \"world\"\n ],\n foo => \"bar\"\n },\n baz => \"jam\"\n}");
416 }
417 }
418 GIVEN("an aggregate resolution with an invalid require") {
419 log_capture capture(level::error);
420 REQUIRE(load_custom_fact("aggregate_with_invalid_merge.rb", facts));
421 THEN("an error is logged") {
422 auto output = capture.result();
423 CAPTURE(output);
424 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* cannot merge \"hello\":String and \"world\":String")));
425 }
426 }
427 GIVEN("an aggregate resolution with a cycle") {
428 log_capture capture(level::error);
429 REQUIRE(load_custom_fact("aggregate_with_cycle.rb", facts));
430 THEN("an error is logged") {
431 auto output = capture.result();
432 CAPTURE(output);
433 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs.facter - .* chunk dependency cycle detected")));
434 }
435 }
436 GIVEN("a fact with a defined aggregate resolution") {
437 REQUIRE(load_custom_fact("define_aggregate_fact.rb", facts));
438 THEN("value should be in the collection") {
439 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "[\n \"foo\",\n \"bar\"\n]");
440 }
441 }
442 GIVEN("an aggregate resolution when a simple resolution already exists") {
443 log_capture capture(level::error);
444 REQUIRE_FALSE(load_custom_fact("existing_simple_resolution.rb", facts));
445 THEN("an error is logged") {
446 auto output = capture.result();
447 CAPTURE(output);
448 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* cannot define an aggregate resolution with name \"bar\": a simple resolution with the same name already exists")));
449 }
450 }
451 GIVEN("a simple resolution when an aggregate resolution already exists") {
452 log_capture capture(level::error);
453 REQUIRE_FALSE(load_custom_fact("existing_aggregate_resolution.rb", facts));
454 THEN("an error is logged") {
455 auto output = capture.result();
456 CAPTURE(output);
457 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* cannot define a simple resolution with name \"bar\": an aggregate resolution with the same name already exists")));
458 }
459 }
460 GIVEN("a custom fact that logs the facter version") {
461 log_capture capture(level::debug);
462 REQUIRE(load_custom_fact("version.rb", facts));
463 THEN("the expected version is logged") {
464 auto output = capture.result();
465 CAPTURE(output);
466 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - " + (boost::format("%1%\\.%2%\\.%3%") % LIBFACTER_VERSION_MAJOR % LIBFACTER_VERSION_MINOR % LIBFACTER_VERSION_PATCH).str())));
467 }
468 }
469 GIVEN("a fact resolution that uses Facter::Core::Execution#exec") {
470 REQUIRE(load_custom_fact("exec.rb", facts));
471 THEN("value should be in the collection") {
472 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar baz\"");
473 }
474 }
475 GIVEN("Facter::Core::Execution#execute with on_fail => :raise") {
476 log_capture capture(level::error);
477 REQUIRE_FALSE(load_custom_fact("execute_on_fail_raise.rb", facts));
478 THEN("an error is logged") {
479 auto output = capture.result();
480 CAPTURE(output);
481 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* execution of command \"the_most_interesting_command_in_the_world\" failed")));
482 }
483 }
484 GIVEN("a fact resolution that uses Facter::Core::Execution#execute with a default value") {
485 REQUIRE(load_custom_fact("execute_on_fail_value.rb", facts));
486 THEN("value should be in the collection") {
487 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"default\"");
488 }
489 }
490 GIVEN("a fact resolution that uses Facter::Core::Execution#execute with a timeout") {
491 log_capture capture(level::error);
492 REQUIRE_FALSE(load_custom_fact("execute_timeout.rb", facts));
493 THEN("an error is logged") {
494 auto output = capture.result();
495 CAPTURE(output);
496 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* command timed out after 1 seconds")));
497 }
498 }
499 GIVEN("a fact that uses timeout") {
500 log_capture capture(level::warning);
501 REQUIRE(load_custom_fact("timeout.rb", facts));
502 THEN("a warning is logged") {
503 auto output = capture.result();
504 CAPTURE(output);
505 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - timeout option is not supported for custom facts and will be ignored.$")));
506 REQUIRE(re_search(output, boost::regex("WARN puppetlabs\\.facter - timeout= is not supported for custom facts and will be ignored.$")));
507 }
508 }
509 GIVEN("a fact that uses Facter#trace to enable backtraces") {
510 log_capture capture(level::error);
511 REQUIRE(load_custom_fact("trace.rb", facts));
512 THEN("backtraces are logged") {
513 auto output = capture.result();
514 CAPTURE(output);
515 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - first")));
516 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - second\\nbacktrace:")));
517 }
518 }
519 GIVEN("a fact that uses Facter#debugging to enable debug messages") {
520 log_capture capture(level::debug);
521 REQUIRE(load_custom_fact("debugging.rb", facts));
522 THEN("debug message is logged") {
523 auto output = capture.result();
524 CAPTURE(output);
525 REQUIRE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - yep")));
526 REQUIRE_FALSE(re_search(output, boost::regex("DEBUG puppetlabs\\.facter - nope")));
527 }
528 }
529 GIVEN("a custom on_message block") {
530 log_capture capture(level::debug);
531 REQUIRE(load_custom_fact("on_message.rb", facts));
532 THEN("no messages are logged") {
533 REQUIRE_FALSE(re_search(capture.result(), boost::regex("debug message")));
534 }
535 }
536 GIVEN("a custom fact with a higher weight than a built-in fact") {
537 REQUIRE(load_custom_fact("ruby.rb", facts));
538 THEN("the custom fact wins") {
539 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("ruby")) == "\"override\"");
540 }
541 }
542 GIVEN("a custom fact with the same weight as a built-in fact") {
543 REQUIRE(load_custom_fact("facterversion.rb", facts));
544 THEN("the built-in fact wins") {
545 REQUIRE(ruby_value_to_string(facts["facterversion"]) == "\"" LIBFACTER_VERSION "\"");
546 }
547 }
548 GIVEN("a fact value from the environment") {
549 scoped_env var("FACTER_RuBy", "from environment!");
550 REQUIRE(load_custom_fact("ruby.rb", facts));
551 THEN("the value from the environment wins") {
552 REQUIRE(ruby_value_to_string(facts["ruby"]) == "\"from environment!\"");
553 }
554 }
555 GIVEN("a hash value with non-string keys") {
556 REQUIRE(load_custom_fact("hash_with_non_string_key.rb", facts));
557 THEN("the keys are converted to strings") {
558 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "{\n foo => \"bar\"\n}");
559 }
560 }
561 GIVEN("a fact that requires facter") {
562 REQUIRE(load_custom_fact("facter.rb", facts));
563 THEN("the require succeeds") {
564 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
565 }
566 }
567 GIVEN("a fact that has 100 resolutions") {
568 REQUIRE(load_custom_fact("100_resolutions.rb", facts));
569 THEN("the fact evaluates") {
570 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
571 }
572 }
573 GIVEN("a fact that has 101 resolutions") {
574 log_capture capture(level::error);
575 REQUIRE_FALSE(load_custom_fact("101_resolutions.rb", facts));
576 THEN("an error is logged") {
577 auto output = capture.result();
578 CAPTURE(output);
579 REQUIRE(re_search(output, boost::regex("ERROR puppetlabs\\.facter - .* fact \"foo\" already has the maximum number of resolutions allowed \\(100\\)")));
580 }
581 }
582 GIVEN("a fact that runs a command outputting to stderr") {
583 REQUIRE(load_custom_fact("stderr_output.rb", facts));
584 THEN("the values should only contain stdout output") {
585 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("first")) == "\"bar\"");
586 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("second")) == "\"bar\"");
587 }
588 }
589 GIVEN("a fact that runs a setcode command that returns no output") {
590 REQUIRE(load_custom_fact("empty_setcode_command.rb", facts));
591 THEN("the fact should not resolve") {
592 REQUIRE_FALSE(facts["foo"]);
593 }
594 }
595 GIVEN("a fact that runs executes nonexistent commands") {
596 REQUIRE(load_custom_fact("nonexistent_command.rb", facts));
597 THEN("the fact should not resolve") {
598 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("first")) == "\"pass\"");
599 REQUIRE_FALSE(facts["second"]);
600 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("third")) == "\"pass\"");
601 }
602 }
603 GIVEN("a fact that executes a command that returns non-zero") {
604 REQUIRE(load_custom_fact("execution_failure.rb", facts));
605 THEN("the fact value should be the command's output") {
606 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
607 }
608 }
609 GIVEN("a built-in fact requested multiple times") {
610 REQUIRE(load_custom_fact("single_allocation.rb", facts));
611 THEN("the value should be properly cached between Facter.value calls") {
612 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "true");
613 }
614 }
615 GIVEN("a fact that returns a negative number") {
616 REQUIRE(load_custom_fact("negative_number.rb", facts));
617 THEN("the value should be output as a signed value") {
618 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "-101");
619 }
620 }
621 GIVEN("a fact that returns the exit code of its command") {
622 REQUIRE(load_custom_fact("uses_exit_code.rb", facts));
623 THEN("the value should be the exit code") {
624 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "99");
625 }
626 }
627 GIVEN("a fact that returns a number larger than 32-bits") {
628 REQUIRE(load_custom_fact("bignum_fact_value.rb", facts));
629 THEN("the value should be output correctly") {
630 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("bignum_fact")) == "12345678901");
631 }
632 }
633 }
+0
-35
lib/tests/ruby/ruby_dirfacts.cc less more
0 #include <catch.hpp>
1 #include <facter/facts/scalar_value.hpp>
2 #include <facter/ruby/ruby.hpp>
3 #include <internal/ruby/ruby_value.hpp>
4 #include <leatherman/ruby/api.hpp>
5 #include "../fixtures.hpp"
6 #include "../collection_fixture.hpp"
7 #include "./ruby_helper.hpp"
8
9 #include "../mock_server.hpp"
10
11 using namespace std;
12 using namespace facter::ruby;
13 using namespace facter::testing;
14 using namespace leatherman::ruby;
15
16 SCENARIO("directories of custom facts written in Ruby") {
17 collection_fixture facts;
18 REQUIRE(facts.size() == 0u);
19
20 // Setup ruby
21 auto& ruby = api::instance();
22 REQUIRE(ruby.initialized());
23 ruby.include_stack_trace(true);
24
25 string fixtures = LIBFACTER_TESTS_DIRECTORY "/fixtures/ruby/";
26
27 GIVEN("a fact that performs network activity") {
28 facter::mock_server m(42000);
29 load_custom_facts(facts, vector<string>{fixtures+"custom_dir"});
30 THEN("the network location should resolve") {
31 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("sometest")) == "\"Yay\"");
32 }
33 }
34 }
+0
-41
lib/tests/ruby/ruby_helper.cc less more
0 #include "ruby_helper.hpp"
1 #include <leatherman/logging/logging.hpp>
2 #include <leatherman/ruby/api.hpp>
3 #include <internal/ruby/module.hpp>
4 #include "../fixtures.hpp"
5
6 using namespace std;
7 using namespace facter::ruby;
8 using namespace facter::facts;
9 using namespace leatherman::ruby;
10
11 bool load_custom_fact(string const& filename, collection& facts)
12 {
13 auto& ruby = api::instance();
14
15 module mod(facts);
16
17 string file = LIBFACTER_TESTS_DIRECTORY "/fixtures/ruby/" + filename;
18 VALUE result = ruby.rescue([&]() {
19 // Do not construct C++ objects in a rescue callback
20 // C++ stack unwinding will not take place if a Ruby exception is thrown!
21 ruby.rb_load(ruby.utf8_value(file), 0);
22 return ruby.true_value();
23 }, [&](VALUE ex) {
24 LOG_ERROR("error while resolving custom facts in {1}: {2}", file, ruby.exception_to_string(ex));
25 return ruby.false_value();
26 });
27
28 mod.resolve_facts();
29
30 return ruby.is_true(result);
31 }
32
33 string ruby_value_to_string(value const* value)
34 {
35 ostringstream ss;
36 if (value) {
37 value->write(ss);
38 }
39 return ss.str();
40 }
+0
-8
lib/tests/ruby/ruby_helper.hpp less more
0 #pragma once
1 #include <facter/facts/collection.hpp>
2 #include <facter/facts/value.hpp>
3 #include <string>
4
5 bool load_custom_fact(std::string const& filename, facter::facts::collection& facts);
6
7 std::string ruby_value_to_string(facter::facts::value const* value);
+0
-53
lib/tests/ruby/windows/ruby.cc less more
0 #include <catch.hpp>
1 #include <leatherman/ruby/api.hpp>
2 #include <leatherman/util/scoped_env.hpp>
3 #include <leatherman/util/environment.hpp>
4 #include <internal/ruby/ruby_value.hpp>
5 #include "../ruby_helper.hpp"
6 #include "../../collection_fixture.hpp"
7 #include "../../fixtures.hpp"
8
9 using namespace std;
10 using namespace facter::testing;
11 using namespace facter::ruby;
12 using namespace leatherman::ruby;
13 using namespace leatherman::util;
14
15 SCENARIO("Windows custom facts written in Ruby") {
16 collection_fixture facts;
17 REQUIRE(facts.size() == 0u);
18
19 // Setup ruby
20 auto& ruby = api::instance();
21 REQUIRE(ruby.initialized());
22 ruby.include_stack_trace(true);
23
24 GIVEN("a fact that loads win32ole") {
25 REQUIRE(load_custom_fact("windows/ole.rb", facts));
26 THEN("the value should be in the collection") {
27 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
28 }
29 }
30 }
31
32 SCENARIO("Run command with space in path") {
33 collection_fixture facts;
34 REQUIRE(facts.size() == 0u);
35
36 // Setup ruby
37 auto& ruby = api::instance();
38 REQUIRE(ruby.initialized());
39 ruby.include_stack_trace(true);
40
41 GIVEN("a directory with a space on the PATH") {
42 string path;
43 environment::get("PATH", path);
44 scoped_env var("PATH", path + environment::get_path_separator() + LIBFACTER_TESTS_DIRECTORY "/fixtures/execution/with space");
45 environment::reload_search_paths();
46 REQUIRE(load_custom_fact("command_with_space.rb", facts));
47 THEN("the command should execute successfully") {
48 REQUIRE(ruby_value_to_string(facts.get<ruby_value>("foo")) == "\"bar\"");
49 }
50 }
51 environment::reload_search_paths();
52 }
+0
-10
lib/tests/util/bsd/scoped_ifaddrs.cc less more
0 #include <catch.hpp>
1 #include <internal/util/bsd/scoped_ifaddrs.hpp>
2
3 using namespace std;
4 using namespace facter::util::bsd;
5
6 SCENARIO("constructing a scoped_ifaddrs") {
7 scoped_ifaddrs addrs;
8 REQUIRE(static_cast<ifaddrs*>(addrs));
9 }
+0
-11
lib/tests/util/posix/scoped_addrinfo.cc less more
0 #include <catch.hpp>
1 #include <internal/util/posix/scoped_addrinfo.hpp>
2
3 using namespace std;
4 using namespace facter::util::posix;
5
6 SCENARIO("constructing a scoped_addrinfo") {
7 scoped_addrinfo info("localhost");
8 REQUIRE(info.result() == 0);
9 REQUIRE(static_cast<addrinfo*>(info));
10 }
+0
-21
lib/tests/util/posix/scoped_descriptor.cc less more
0 #include <catch.hpp>
1 #include <internal/util/posix/scoped_descriptor.hpp>
2 #include <sys/socket.h>
3 #include <fcntl.h>
4
5 using namespace std;
6 using namespace facter::util::posix;
7
8 SCENARIO("constructing a POSIX scoped descriptor") {
9 int sock = socket(AF_INET, SOCK_DGRAM, 0);
10
11 {
12 scoped_descriptor scoped(sock);
13 }
14
15 // This check could theoretically fail if the OS reassigns the file
16 // descriptor between the above destructor call and this line
17 // This likely will not happen during testing.
18 REQUIRE(fcntl(sock, F_GETFD) == -1);
19 REQUIRE(errno == EBADF);
20 }
+0
-12
lib/tests/util/scoped_bio.cc less more
0 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1 #include <catch.hpp>
2 #include <internal/util/scoped_bio.hpp>
3 #include <openssl/evp.h>
4
5 using namespace std;
6 using namespace facter::util;
7
8 SCENARIO("constructing a scoped_bio") {
9 scoped_bio b64((BIO_f_base64()));
10 REQUIRE(static_cast<BIO*>(b64));
11 }
+0
-289
lib/tests/util/string.cc less more
0 #include <catch.hpp>
1 #include <facter/util/string.hpp>
2
3 using namespace std;
4 using namespace facter::util;
5
6 SCENARIO("converting bytes to hex strings") {
7 uint8_t buffer[] = { 0xBA, 0xAD, 0xF0, 0x0D };
8 WHEN("specifying uppercase") {
9 THEN("hex characters should be uppercase") {
10 REQUIRE(to_hex(buffer, sizeof(buffer), true) == "BAADF00D");
11 }
12 }
13 WHEN("specifying the default lowercase") {
14 THEN("hex characters should be lowercase") {
15 REQUIRE(to_hex(buffer, sizeof(buffer)) == "baadf00d");
16 }
17 }
18 GIVEN("a null buffer") {
19 THEN("an empty string should be returned") {
20 REQUIRE(to_hex(nullptr, 0) == "");
21 }
22 }
23 GIVEN("an empty buffer") {
24 THEN("an empty string should be returned") {
25 REQUIRE(to_hex(buffer, 0) == "");
26 }
27 }
28 }
29
30 SCENARIO("converting bytes to SI unit strings") {
31 GIVEN("zero bytes") {
32 THEN("the string should show 0 bytes") {
33 REQUIRE(si_string(0) == "0 bytes");
34 }
35 }
36 GIVEN("less than 1 KiB") {
37 WHEN("not close to 1 KiB") {
38 THEN("the string should be in bytes") {
39 REQUIRE(si_string(100) == "100 bytes");
40 }
41 }
42 WHEN("close to 1 KiB") {
43 THEN("the string should be in bytes") {
44 REQUIRE(si_string(1023) == "1023 bytes");
45 }
46 }
47 }
48 GIVEN("exactly 1 KiB") {
49 THEN("the string should be in KiB") {
50 REQUIRE(si_string(1024) == "1.00 KiB");
51 }
52 }
53 GIVEN("less than 1 MiB") {
54 WHEN("not close to 1 MiB") {
55 THEN("the string should be in KiB") {
56 REQUIRE(si_string(4097) == "4.00 KiB");
57 }
58 }
59 WHEN("almost 1 MiB") {
60 THEN("the string should be in MiB") {
61 REQUIRE(si_string((1024ull * 1024ull) - 1) == "1.00 MiB");
62 }
63 }
64 }
65 GIVEN("exactly 1 MiB") {
66 THEN("the string should be in MiB") {
67 REQUIRE(si_string(1024ull * 1024ull) == "1.00 MiB");
68 }
69 }
70 GIVEN("less than 1 GiB") {
71 WHEN("not close to 1 GiB") {
72 THEN("the string should be in MiB") {
73 REQUIRE(si_string(10ull * 1024ull * 1023ull) == "9.99 MiB");
74 }
75 }
76 WHEN("almost 1 GiB") {
77 THEN("the string should be in GiB") {
78 REQUIRE(si_string((1024ull * 1024ull * 1024ull) - 1) == "1.00 GiB");
79 }
80 }
81 }
82 GIVEN("exactly 1 GiB") {
83 THEN("the string should be in GiB") {
84 REQUIRE(si_string(1024ull * 1024ull * 1024ull) == "1.00 GiB");
85 }
86 }
87 GIVEN("less than 1 TiB") {
88 WHEN("not close to 1 TiB") {
89 THEN("the string should be in GiB") {
90 REQUIRE(si_string(12ull * 1024ull * 1024ull * 1023ull) == "11.99 GiB");
91 }
92 }
93 WHEN("almost 1 TiB") {
94 THEN("the string should be in TiB") {
95 REQUIRE(si_string((1024ull * 1024ull * 1024ull * 1024ull) - 1) == "1.00 TiB");
96 }
97 }
98 }
99 GIVEN("exactly 1 TiB") {
100 THEN("the string should be in TiB") {
101 REQUIRE(si_string(1024ull * 1024ull * 1024ull * 1024ull) == "1.00 TiB");
102 }
103 }
104 GIVEN("less than 1 PiB") {
105 WHEN("not close to 1 PiB") {
106 THEN("the string should be in TiB") {
107 REQUIRE(si_string(50ull * 1024ull * 1024ull * 1024ull * 1023ull) == "49.95 TiB");
108 }
109 }
110 WHEN("almost 1 PiB") {
111 THEN("the string should be in PiB") {
112 REQUIRE(si_string((1024ull * 1024ull * 1024ull * 1024ull * 1024ull) - 1) == "1.00 PiB");
113 }
114 }
115 }
116 GIVEN("exactly 1 PiB") {
117 THEN("the string should be in PiB") {
118 REQUIRE(si_string(1024ull * 1024ull * 1024ull * 1024ull * 1024ull) == "1.00 PiB");
119 }
120 }
121 GIVEN("less than 1 EiB") {
122 WHEN("not close to 1 EiB") {
123 THEN("the string should be in PiB") {
124 REQUIRE(si_string(100ull * 1024ull * 1024ull * 1024ull * 1024ull * 1023ull) == "99.90 PiB");
125 }
126 }
127 WHEN("almost 1 EiB") {
128 THEN("the string should be in EiB") {
129 REQUIRE(si_string((1024ull * 1024ull * 1024ull * 1024ull * 1024ull * 1024ull) - 1) == "1.00 EiB");
130 }
131 }
132 }
133 GIVEN("exactly 1 EiB") {
134 THEN("the string should be in PiB") {
135 REQUIRE(si_string(1024ull * 1024ull * 1024ull * 1024ull * 1024ull * 1024ull) == "1.00 EiB");
136 }
137 }
138 GIVEN("the unsigned maximum 64-bit value") {
139 THEN("the string should be in EiB") {
140 REQUIRE(si_string(numeric_limits<uint64_t>::max()) == "16.00 EiB");
141 }
142 }
143 }
144
145 SCENARIO("converting percentages to strings") {
146 GIVEN("any value out of zero") {
147 THEN("it should be 100%") {
148 REQUIRE(percentage(0, 0) == "100%");
149 REQUIRE(percentage(10000, 0) == "100%");
150 }
151 }
152 GIVEN("zero out of any value") {
153 THEN("it should be 0%") {
154 REQUIRE(percentage(0, 10) == "0%");
155 REQUIRE(percentage(0, 100) == "0%");
156 }
157 }
158 GIVEN("more than the maximum") {
159 THEN("it should be 100%") {
160 REQUIRE(percentage(1000, 100) == "100%");
161 }
162 }
163 GIVEN("small percentages") {
164 THEN("it should round to the nearest hundred of a percent") {
165 REQUIRE(percentage(1, 100) == "1.00%");
166 REQUIRE(percentage(11, 1000) == "1.10%");
167 REQUIRE(percentage(111, 10000) == "1.11%");
168 REQUIRE(percentage(1140000000ul, 50000000000ul) == "2.28%");
169 REQUIRE(percentage(1000, 10000) == "10.00%");
170 }
171 }
172 GIVEN("large percentages") {
173 THEN("it should round to the nearest hundred of a percent, but never 100%") {
174 REQUIRE(percentage(414906340801ul, 560007030104ul) == "74.09%");
175 REQUIRE(percentage(99984, 100000) == "99.98%");
176 REQUIRE(percentage(999899, 1000000) == "99.99%");
177 REQUIRE(percentage(99999, 100000) == "99.99%");
178 }
179 }
180 GIVEN("the maximum value") {
181 WHEN("off by one") {
182 THEN("it should not be 100%") {
183 REQUIRE(percentage(numeric_limits<uint64_t>::max() - 1, numeric_limits<uint64_t>::max()) == "99.99%");
184 }
185 }
186 WHEN("both are maximum") {
187 THEN("it should be 100%") {
188 REQUIRE(percentage(numeric_limits<uint64_t>::max(), numeric_limits<uint64_t>::max()) == "100%");
189 }
190 }
191 }
192 }
193
194 SCENARIO("converting frequencies to strings") {
195 GIVEN("a frequency of 0") {
196 THEN("it should report in Hz") {
197 REQUIRE(frequency(0) == "0 Hz");
198 }
199 }
200 GIVEN("a frequency of less than 1 kHz") {
201 WHEN("not close to 1 kHz") {
202 THEN("it should be in Hz") {
203 REQUIRE(frequency(100) == "100 Hz");
204 }
205 }
206 WHEN("close to 1 kHz") {
207 THEN("it should be in Hz") {
208 REQUIRE(frequency(999) == "999 Hz");
209 }
210 }
211 }
212 GIVEN("exactly 1 kHz") {
213 THEN("it should be in kHz") {
214 REQUIRE(frequency(1000) == "1.00 kHz");
215 }
216 }
217 GIVEN("a frequency of less than 1 MHz") {
218 WHEN("not close to 1 MHz") {
219 THEN("it should be in kHz") {
220 REQUIRE(frequency(1000 * 999) == "999.00 kHz");
221 }
222 }
223 WHEN("close to 1 MHz") {
224 THEN("it should be in MHz") {
225 REQUIRE(frequency((1000 * 1000) - 1) == "1.00 MHz");
226 }
227 }
228 }
229 GIVEN("exactly 1 MHz") {
230 THEN("it should be in MHz") {
231 REQUIRE(frequency(1000 * 1000) == "1.00 MHz");
232 }
233 }
234 GIVEN("a frequency of less than 1 GHz") {
235 WHEN("not close to 1 GHz") {
236 THEN("it should be in MHz") {
237 REQUIRE(frequency(1000 * 1000 * 999) == "999.00 MHz");
238 }
239 }
240 WHEN("close to 1 GHz") {
241 THEN("it should be in GHz") {
242 REQUIRE(frequency((1000 * 1000 * 1000) - 1) == "1.00 GHz");
243 }
244 }
245 }
246 GIVEN("exactly 1 GHz") {
247 THEN("it should be in GHz") {
248 REQUIRE(frequency(1000 * 1000 * 1000) == "1.00 GHz");
249 }
250 }
251 GIVEN("a frequency of less than 1 THz") {
252 WHEN("not close to 1 THz") {
253 THEN("it should be in GHz") {
254 REQUIRE(frequency(1000ull * 1000 * 1000 * 999) == "999.00 GHz");
255 }
256 }
257 WHEN("close to 1 THz") {
258 THEN("it should be in THz") {
259 REQUIRE(frequency((1000ull * 1000 * 1000 * 1000) - 1) == "1.00 THz");
260 }
261 }
262 }
263 GIVEN("exactly 1 THz") {
264 THEN("it should be in THz") {
265 REQUIRE(frequency(1000ull * 1000 * 1000 * 1000) == "1.00 THz");
266 }
267 }
268 GIVEN("the maximum value") {
269 THEN("it should be in Hz") {
270 REQUIRE(frequency(numeric_limits<int64_t>::max()) == "9223372036854775807 Hz");
271 }
272 }
273 }
274
275 SCENARIO("converting strings to integers") {
276 GIVEN("a string that is a valid integer") {
277 THEN("it should be converted to its integer representation") {
278 auto oint = maybe_stoi("12");
279 REQUIRE(oint.is_initialized());
280 REQUIRE(oint.get_value_or(0) == 12);
281 }
282 }
283 GIVEN("a string that is not a valid integer") {
284 THEN("nothing should be returned") {
285 REQUIRE_FALSE(maybe_stoi("foo").is_initialized());
286 }
287 }
288 }
+0
-28
lib/version.h.in less more
0 ///
1 /// @file
2 /// Declares macros for the facter library version.
3 ///
4 #pragma once
5
6 ///
7 /// The facter library major version.
8 ///
9 #define LIBFACTER_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
10 ///
11 /// The facter library minor version.
12 ///
13 #define LIBFACTER_VERSION_MINOR @PROJECT_VERSION_MINOR@
14 ///
15 /// The facter library patch version.
16 ///
17 #define LIBFACTER_VERSION_PATCH @PROJECT_VERSION_PATCH@
18
19 ///
20 /// The facter library version as a string (without commit SHA1).
21 ///
22 #define LIBFACTER_VERSION "@PROJECT_VERSION@"
23
24 ///
25 /// The facter library version as a string (with commit SHA1).
26 ///
27 #define LIBFACTER_VERSION_WITH_COMMIT "@PROJECT_VERSION@@LIBFACTER_COMMIT@"
+0
-7
locales/CMakeLists.txt less more
0 if (NOT WIN32)
1 gettext_templates(${CMAKE_CURRENT_SOURCE_DIR} ${ALL_SOURCES})
2 gettext_compile(${CMAKE_CURRENT_SOURCE_DIR} share/locale)
3 SET_DIRECTORY_PROPERTIES(PROPERTIES CLEAN_NO_CUSTOM TRUE)
4 export_var(GETTEXT_ENABLED)
5 endif()
6
+0
-1734
locales/FACTER.pot less more
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR Puppet <docs@puppet.com>
2 # This file is distributed under the same license as the FACTER package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 #
5 #, fuzzy
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: FACTER 3.14.10\n"
9 "Report-Msgid-Bugs-To: docs@puppet.com\n"
10 "POT-Creation-Date: \n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language: \n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
18
19 #: exe/facter.cc
20 msgid ""
21 "Synopsis\n"
22 "========\n"
23 "\n"
24 "Collect and display facts about the system.\n"
25 "\n"
26 "Usage\n"
27 "=====\n"
28 "\n"
29 " facter [options] [query] [query] [...]\n"
30 "\n"
31 "Options\n"
32 "=======\n"
33 "\n"
34 "%1%\n"
35 "Description\n"
36 "===========\n"
37 "\n"
38 "Collect and display facts about the current system. The library behind\n"
39 "facter is easy to extend, making facter an easy way to collect information\n"
40 "about a system.\n"
41 "\n"
42 "If no queries are given, then all facts will be returned.\n"
43 "\n"
44 "Example Queries\n"
45 "===============\n"
46 "\n"
47 " facter kernel\n"
48 " facter networking.ip\n"
49 " facter processors.models.0\n"
50 "\n"
51 "Config File\n"
52 "===========\n"
53 "\n"
54 "Contains settings for configuring external and custom fact directories,\n"
55 "setting command line options, and blocking and caching facts.\n"
56 "Loaded by default from %2%.\n"
57 "See man page, README, or docs for more details."
58 msgstr ""
59
60 #. log
61 #: exe/facter.cc
62 msgid "executed with command line: %1%."
63 msgstr ""
64
65 #. log
66 #: exe/facter.cc
67 msgid "resolving all facts."
68 msgstr ""
69
70 #. log
71 #: exe/facter.cc
72 msgid "requested queries: %1%."
73 msgstr ""
74
75 #: exe/facter.cc
76 msgid "Enable color output."
77 msgstr ""
78
79 #: exe/facter.cc
80 msgid "The location of the config file."
81 msgstr ""
82
83 #: exe/facter.cc
84 msgid "A directory to use for custom facts."
85 msgstr ""
86
87 #: exe/facter.cc
88 msgid "Enable debug output."
89 msgstr ""
90
91 #: exe/facter.cc
92 msgid "A directory to use for external facts."
93 msgstr ""
94
95 #: exe/facter.cc
96 msgid "Print this help message."
97 msgstr ""
98
99 #: exe/facter.cc
100 msgid "Output in JSON format."
101 msgstr ""
102
103 #: exe/facter.cc
104 msgid "List the names of all blockable fact groups."
105 msgstr ""
106
107 #: exe/facter.cc
108 msgid "List the names of all cacheable fact groups."
109 msgstr ""
110
111 #: exe/facter.cc
112 msgid ""
113 "Set logging level.\n"
114 "Supported levels are: none, trace, debug, info, warn, error, and fatal."
115 msgstr ""
116
117 #: exe/facter.cc
118 msgid "Disable fact blocking."
119 msgstr ""
120
121 #: exe/facter.cc
122 msgid "Disable loading and refreshing facts from the cache"
123 msgstr ""
124
125 #: exe/facter.cc
126 msgid "Disable color output."
127 msgstr ""
128
129 #: exe/facter.cc
130 msgid "Disable custom facts."
131 msgstr ""
132
133 #: exe/facter.cc
134 msgid "Disable external facts."
135 msgstr ""
136
137 #: exe/facter.cc
138 msgid "Disable loading Ruby, facts requiring Ruby, and custom facts."
139 msgstr ""
140
141 #: exe/facter.cc
142 msgid ""
143 "Load the Puppet libraries, thus allowing Facter to load Puppet-specific "
144 "facts."
145 msgstr ""
146
147 #: exe/facter.cc
148 msgid "Show legacy facts when querying all facts."
149 msgstr ""
150
151 #: exe/facter.cc
152 msgid "Enable backtraces for custom facts."
153 msgstr ""
154
155 #: exe/facter.cc
156 msgid "Enable verbose (info) output."
157 msgstr ""
158
159 #: exe/facter.cc
160 msgid "Print the version and exit."
161 msgstr ""
162
163 #: exe/facter.cc
164 msgid "Output in YAML format."
165 msgstr ""
166
167 #: exe/facter.cc
168 msgid "Enable more aggressive error reporting."
169 msgstr ""
170
171 #: exe/facter.cc
172 msgid "color and no-color options conflict: please specify only one."
173 msgstr ""
174
175 #: exe/facter.cc
176 msgid "json and yaml options conflict: please specify only one."
177 msgstr ""
178
179 #: exe/facter.cc
180 msgid ""
181 "no-external-facts and external-dir options conflict: please specify only one."
182 msgstr ""
183
184 #: exe/facter.cc
185 msgid ""
186 "no-custom-facts and custom-dir options conflict: please specify only one."
187 msgstr ""
188
189 #: exe/facter.cc
190 msgid ""
191 "debug, verbose, and log-level options conflict: please specify only one."
192 msgstr ""
193
194 #: exe/facter.cc
195 msgid "no-ruby and custom-dir options conflict: please specify only one."
196 msgstr ""
197
198 #: exe/facter.cc
199 msgid "puppet and no-custom-facts options conflict: please specify only one."
200 msgstr ""
201
202 #: exe/facter.cc
203 msgid "puppet and no-ruby options conflict: please specify only one."
204 msgstr ""
205
206 #: exe/facter.cc
207 msgid "error: %1%"
208 msgstr ""
209
210 #. log
211 #: exe/facter.cc
212 msgid "Environment variable INSIDE_FACTER is set to 'true'"
213 msgstr ""
214
215 #. log
216 #: exe/facter.cc
217 msgid ""
218 "Facter was called recursively, skipping external facts. Add '--no-external-"
219 "facts' to silence this warning"
220 msgstr ""
221
222 #: exe/facter.cc
223 msgid "failed to initialize logging system due to a locale error: %1%"
224 msgstr ""
225
226 #. log
227 #: exe/facter.cc
228 msgid "unhandled exception: %1%"
229 msgstr ""
230
231 #. warning
232 #: lib/inc/internal/util/agent.hpp
233 msgid "{1} not found at configured location {2}, using PATH instead"
234 msgstr ""
235
236 #. warning
237 #: lib/inc/internal/util/agent.hpp
238 msgid ""
239 "{1} not found at configured location {2}, using PATH instead, parameter "
240 "expand is {3}"
241 msgstr ""
242
243 #: lib/inc/internal/util/aix/odm.hpp
244 msgid "failed to retrieve ODM error message"
245 msgstr ""
246
247 #: lib/inc/internal/util/aix/odm.hpp
248 msgid ""
249 "Tried to construct an iterator with valid data but no owner. Naughty naughty."
250 msgstr ""
251
252 #: lib/inc/internal/util/aix/odm.hpp
253 msgid "Cannot iterate over the same ODM class concurrently"
254 msgstr ""
255
256 #. debug
257 #: lib/inc/internal/util/aix/vmount.hpp
258 msgid "Required space for mountpoints is {1}"
259 msgstr ""
260
261 #. debug
262 #: lib/inc/internal/util/aix/vmount.hpp
263 msgid "Got {1} mountpoints"
264 msgstr ""
265
266 #. debug
267 #: lib/src/facts/aix/disk_resolver.cc
268 msgid "got a disk type: {1}"
269 msgstr ""
270
271 #: lib/src/facts/aix/disk_resolver.cc lib/src/facts/aix/processor_resolver.cc
272 msgid "PdDvLn=%1%"
273 msgstr ""
274
275 #. debug
276 #: lib/src/facts/aix/disk_resolver.cc
277 msgid "got a disk: {1}"
278 msgstr ""
279
280 #: lib/src/facts/aix/disk_resolver.cc
281 msgid "/dev/%1%"
282 msgstr ""
283
284 #. debug
285 #: lib/src/facts/aix/disk_resolver.cc
286 msgid ""
287 "Could not open device %1% for reading: %2% (%3%). Disk facts will not be "
288 "populated for this device"
289 msgstr ""
290
291 #. debug
292 #: lib/src/facts/aix/disk_resolver.cc
293 msgid ""
294 "Ioctl IOCINFO failed for device %1%: %2% (%3%). Disk facts will not be "
295 "populated for this device"
296 msgstr ""
297
298 #. warning
299 #: lib/src/facts/aix/disk_resolver.cc
300 msgid ""
301 "Expected a Disk or SCSI disk device, got device code '{1}'. This is probably "
302 "a Facter bug. Please report it, and include this error message."
303 msgstr ""
304
305 #. warning
306 #: lib/src/facts/aix/filesystem_resolver.cc
307 msgid "Could not get fs data for {1}: {2}"
308 msgstr ""
309
310 #: lib/src/facts/aix/filesystem_resolver.cc
311 #: lib/src/facts/aix/processor_resolver.cc
312 msgid "name=%1%"
313 msgstr ""
314
315 #. warning
316 #: lib/src/facts/aix/filesystem_resolver.cc
317 msgid "Could not get info for partition '{1}' from the LVM subsystem"
318 msgstr ""
319
320 #. warning
321 #: lib/src/facts/aix/filesystem_resolver.cc
322 msgid "querylv returned success but we got a null LV. WTF?"
323 msgstr ""
324
325 #: lib/src/facts/aix/load_average_resolver.cc
326 msgid "failed to retrieve the load averages"
327 msgstr ""
328
329 #. debug
330 #: lib/src/facts/aix/memory_resolver.cc
331 msgid "cannot use device {1}: error is {2}"
332 msgstr ""
333
334 #: lib/src/facts/aix/networking_resolver.cc
335 msgid "getkerninfo call was unsuccessful"
336 msgstr ""
337
338 #. warning
339 #: lib/src/facts/aix/networking_resolver.cc
340 msgid ""
341 "got mixed address families for interface {1}, can't map them to a single "
342 "binding."
343 msgstr ""
344
345 #. info
346 #: lib/src/facts/aix/networking_resolver.cc
347 msgid "got ipv4 addresses for interface {1}"
348 msgstr ""
349
350 #. info
351 #: lib/src/facts/aix/networking_resolver.cc
352 msgid "got ipv6 addresses for interface {1}"
353 msgstr ""
354
355 #. info
356 #: lib/src/facts/aix/networking_resolver.cc
357 msgid "skipping unknown address family {1} for interface {2}"
358 msgstr ""
359
360 #. info
361 #: lib/src/facts/aix/networking_resolver.cc
362 msgid "somehow didn't get an address family for interface {1}"
363 msgstr ""
364
365 #. info
366 #: lib/src/facts/aix/networking_resolver.cc
367 msgid "got an unknown RT_IFLIST message: {1}"
368 msgstr ""
369
370 #. debug
371 #: lib/src/facts/aix/nim_resolver.cc
372 msgid "Could not read `/etc/niminfo`"
373 msgstr ""
374
375 #: lib/src/facts/aix/operating_system_resolver.cc
376 msgid "name = %1% AND attribute = %2%"
377 msgstr ""
378
379 #. debug
380 #: lib/src/facts/aix/operating_system_resolver.cc
381 msgid "Could not get a value from the ODM for {1}'s '{2}' attribute."
382 msgstr ""
383
384 #: lib/src/facts/aix/operating_system_resolver.cc
385 msgid "name = %1%"
386 msgstr ""
387
388 #. debug
389 #: lib/src/facts/aix/operating_system_resolver.cc
390 msgid ""
391 "Could not get a value from the ODM for {1}'s '{2}' attribute: There is no "
392 "CuDv entry for {1}."
393 msgstr ""
394
395 #: lib/src/facts/aix/operating_system_resolver.cc
396 msgid "uniquetype = %1% AND attribute = %2%"
397 msgstr ""
398
399 #. debug
400 #: lib/src/facts/aix/operating_system_resolver.cc
401 msgid ""
402 "Could not get a value from the ODM for {1}'s '{2}' attribute: There is no "
403 "PdAt entry for {1} with {2}."
404 msgstr ""
405
406 #. debug
407 #: lib/src/facts/aix/operating_system_resolver.cc
408 msgid ""
409 "Could not get a value for the OS architecture. Your machine does not have "
410 "any processors!"
411 msgstr ""
412
413 #. debug
414 #: lib/src/facts/aix/processor_resolver.cc
415 msgid "got a processor type: {1}"
416 msgstr ""
417
418 #. debug
419 #: lib/src/facts/aix/processor_resolver.cc
420 msgid "got a processor: {1}"
421 msgstr ""
422
423 #. debug
424 #: lib/src/facts/aix/processor_resolver.cc
425 msgid "got attribute {1}={2} for processor {3}"
426 msgstr ""
427
428 #. info
429 #: lib/src/facts/aix/processor_resolver.cc
430 msgid "don't know what to do with processor attribute {1}"
431 msgstr ""
432
433 #. warning
434 #: lib/src/facts/aix/processor_resolver.cc
435 msgid ""
436 "mismatched processor frequencies found; facter will only report one of them"
437 msgstr ""
438
439 #. warning
440 #: lib/src/facts/aix/serial_number_resolver.cc
441 msgid ""
442 "Could not retrieve serial number: sys0 systemid did not match the expected "
443 "format"
444 msgstr ""
445
446 #. debug
447 #: lib/src/facts/array_value.cc
448 msgid "null value cannot be added to array."
449 msgstr ""
450
451 #. error
452 #: lib/src/facts/bsd/filesystem_resolver.cc
453 msgid "getfsstat failed: {1} ({2}): file system facts are unavailable."
454 msgstr ""
455
456 #. warning
457 #: lib/src/facts/bsd/networking_resolver.cc
458 msgid "getifaddrs failed: {1} ({2}): interface information is unavailable."
459 msgstr ""
460
461 #. debug
462 #: lib/src/facts/bsd/networking_resolver.cc
463 #: lib/src/facts/solaris/networking_resolver.cc
464 msgid "got primary interface: \"{1}\""
465 msgstr ""
466
467 #. debug
468 #: lib/src/facts/bsd/networking_resolver.cc
469 #: lib/src/facts/linux/virtualization_resolver.cc
470 msgid "searching \"{1}\" for dhclient lease files."
471 msgstr ""
472
473 #. debug
474 #: lib/src/facts/bsd/networking_resolver.cc
475 msgid "reading \"{1}\" for dhclient lease information."
476 msgstr ""
477
478 #. debug
479 #: lib/src/facts/bsd/networking_resolver.cc
480 msgid "searching \"{1}\" for NetworkManager internal lease files"
481 msgstr ""
482
483 #. debug
484 #: lib/src/facts/bsd/networking_resolver.cc
485 msgid "reading \"{1}\" for NetworkManager lease information."
486 msgstr ""
487
488 #. debug
489 #: lib/src/facts/bsd/networking_resolver.cc
490 msgid "searching \"{1}\" for systemd-networkd DHCP lease files"
491 msgstr ""
492
493 #. debug
494 #: lib/src/facts/bsd/networking_resolver.cc
495 msgid "searching \"{1}\" for systemd-networkd DHCP lease information"
496 msgstr ""
497
498 #. debug
499 #: lib/src/facts/cache.cc
500 msgid "Deleting unused cache file {1}"
501 msgstr ""
502
503 #. debug
504 #: lib/src/facts/cache.cc
505 msgid "cache file for {1} facts contained invalid JSON, refreshing"
506 msgstr ""
507
508 #. debug
509 #: lib/src/facts/cache.cc
510 msgid "cache file for {1} facts was missing, refreshing"
511 msgstr ""
512
513 #. debug
514 #: lib/src/facts/cache.cc
515 msgid "loading cached values for {1} facts"
516 msgstr ""
517
518 #. debug
519 #: lib/src/facts/cache.cc
520 msgid "caching values for {1} facts"
521 msgstr ""
522
523 #. debug
524 #: lib/src/facts/cache.cc
525 msgid "Loading cached custom facts from file \"{1}\""
526 msgstr ""
527
528 #. debug
529 #: lib/src/facts/cache.cc
530 msgid "Custom facts cache file contained invalid JSON, refreshing"
531 msgstr ""
532
533 #. debug
534 #: lib/src/facts/cache.cc
535 msgid "Custom facts cache file expired/missing. Refreshing"
536 msgstr ""
537
538 #. debug
539 #: lib/src/facts/cache.cc
540 msgid "Saving cached custom facts to {1}"
541 msgstr ""
542
543 #. debug
544 #: lib/src/facts/collection.cc
545 msgid ""
546 "fact \"{1}\" resolved to null and the existing value of {2} will be removed."
547 msgstr ""
548
549 #. debug
550 #: lib/src/facts/collection.cc
551 msgid "new value for fact \"{1}\" ignored, because it's a lower weight"
552 msgstr ""
553
554 #. debug
555 #: lib/src/facts/collection.cc
556 msgid "fact \"{1}\" has changed from {2} to {3}."
557 msgstr ""
558
559 #. debug
560 #: lib/src/facts/collection.cc
561 msgid "fact \"{1}\" resolved to null and will not be added."
562 msgstr ""
563
564 #. debug
565 #: lib/src/facts/collection.cc
566 msgid "fact \"{1}\" has resolved to {2}."
567 msgstr ""
568
569 #. warning
570 #. debug
571 #: lib/src/facts/collection.cc
572 msgid "skipping external facts for \"{1}\": {2}"
573 msgstr ""
574
575 #. debug
576 #: lib/src/facts/collection.cc
577 msgid "searching {1} for external facts."
578 msgstr ""
579
580 #. debug
581 #: lib/src/facts/collection.cc
582 msgid "skipping file \"{1}\": {2}"
583 msgstr ""
584
585 #. debug
586 #: lib/src/facts/collection.cc
587 msgid "no external facts were found."
588 msgstr ""
589
590 #. error
591 #: lib/src/facts/collection.cc
592 msgid ""
593 "Caching is enabled for group \"{1}\" while there are at least two external "
594 "facts files with the same filename. To fix this either remove \"{1}\" from "
595 "cached groups or rename one of the files:\n"
596 "\"{2}\"\n"
597 "\"{3}\" "
598 msgstr ""
599
600 #. error
601 #: lib/src/facts/collection.cc
602 msgid "error while processing \"{1}\" for external facts: {2}"
603 msgstr ""
604
605 #. debug
606 #: lib/src/facts/collection.cc
607 msgid ""
608 "setting fact \"{1}\" based on the value of environment variable \"{2}\"."
609 msgstr ""
610
611 #. debug
612 #: lib/src/facts/collection.cc
613 msgid "blocking collection of {1} facts."
614 msgstr ""
615
616 #. debug
617 #: lib/src/facts/collection.cc
618 msgid "{1} resolver cannot be blocked."
619 msgstr ""
620
621 #. debug
622 #: lib/src/facts/collection.cc
623 msgid "resolving {1} facts."
624 msgstr ""
625
626 #. warning
627 #: lib/src/facts/collection.cc
628 msgid "exception resolving {1} facts, some facts will not be available: {2}"
629 msgstr ""
630
631 #. debug
632 #: lib/src/facts/collection.cc
633 msgid "cannot lookup an element with \"{1}\" from Ruby fact"
634 msgstr ""
635
636 #. debug
637 #: lib/src/facts/collection.cc
638 msgid "cannot lookup a hash element with \"{1}\": element does not exist."
639 msgstr ""
640
641 #. debug
642 #: lib/src/facts/collection.cc lib/src/ruby/ruby.cc
643 msgid ""
644 "cannot lookup an array element with \"{1}\": expected an integral value."
645 msgstr ""
646
647 #. debug
648 #: lib/src/facts/collection.cc lib/src/ruby/ruby.cc
649 msgid ""
650 "cannot lookup an array element with \"{1}\": expected a non-negative value."
651 msgstr ""
652
653 #. debug
654 #: lib/src/facts/collection.cc lib/src/ruby/ruby.cc
655 msgid "cannot lookup an array element with \"{1}\": the array is empty."
656 msgstr ""
657
658 #. debug
659 #: lib/src/facts/collection.cc lib/src/ruby/ruby.cc
660 msgid ""
661 "cannot lookup an array element with \"{1}\": expected an integral value "
662 "between 0 and {2} (inclusive)."
663 msgstr ""
664
665 #. debug
666 #: lib/src/facts/external/execution_resolver.cc
667 msgid "resolving facts from executable file \"{1}\"."
668 msgstr ""
669
670 #. debug
671 #: lib/src/facts/external/execution_resolver.cc
672 #: lib/src/facts/external/windows/powershell_resolver.cc
673 msgid "Could not parse executable fact output as YAML or JSON ({1})"
674 msgstr ""
675
676 #. debug
677 #: lib/src/facts/external/execution_resolver.cc
678 #: lib/src/facts/external/text_resolver.cc
679 #: lib/src/facts/external/windows/powershell_resolver.cc
680 msgid "ignoring line in output: {1}"
681 msgstr ""
682
683 #. warning
684 #: lib/src/facts/external/execution_resolver.cc
685 #: lib/src/facts/external/windows/powershell_resolver.cc
686 msgid "external fact file \"{1}\" had output on stderr: {2}"
687 msgstr ""
688
689 #. debug
690 #: lib/src/facts/external/execution_resolver.cc
691 msgid "completed resolving facts from executable file \"{1}\"."
692 msgstr ""
693
694 #: lib/src/facts/external/json_resolver.cc
695 #: lib/src/facts/resolvers/gce_resolver.cc
696 msgid "expected non-empty key in object."
697 msgstr ""
698
699 #: lib/src/facts/external/json_resolver.cc
700 #: lib/src/facts/resolvers/gce_resolver.cc
701 msgid "expected document to contain an object."
702 msgstr ""
703
704 #. debug
705 #: lib/src/facts/external/json_resolver.cc
706 msgid "resolving facts from JSON file \"{1}\"."
707 msgstr ""
708
709 #: lib/src/facts/external/json_resolver.cc
710 #: lib/src/facts/external/text_resolver.cc
711 #: lib/src/facts/external/yaml_resolver.cc
712 msgid "file could not be opened."
713 msgstr ""
714
715 #. debug
716 #: lib/src/facts/external/json_resolver.cc
717 msgid "completed resolving facts from JSON file \"{1}\"."
718 msgstr ""
719
720 #. debug
721 #: lib/src/facts/external/text_resolver.cc
722 msgid "resolving facts from text file \"{1}\"."
723 msgstr ""
724
725 #. debug
726 #: lib/src/facts/external/text_resolver.cc
727 msgid "completed resolving facts from text file \"{1}\"."
728 msgstr ""
729
730 #. debug
731 #: lib/src/facts/external/windows/powershell_resolver.cc
732 msgid "resolving facts from powershell script \"{1}\"."
733 msgstr ""
734
735 #. debug
736 #: lib/src/facts/external/windows/powershell_resolver.cc
737 msgid "completed resolving facts from powershell script \"{1}\"."
738 msgstr ""
739
740 #. debug
741 #: lib/src/facts/external/yaml_resolver.cc
742 msgid "resolving facts from YAML file \"{1}\"."
743 msgstr ""
744
745 #. debug
746 #: lib/src/facts/external/yaml_resolver.cc
747 msgid "completed resolving facts from YAML file \"{1}\"."
748 msgstr ""
749
750 #. debug
751 #: lib/src/facts/freebsd/dmi_resolver.cc
752 msgid "kenv lookup for {1}"
753 msgstr ""
754
755 #. info
756 #: lib/src/facts/freebsd/dmi_resolver.cc
757 msgid "kenv lookup for {1} failed: {2} ({3})"
758 msgstr ""
759
760 #. debug
761 #: lib/src/facts/freebsd/memory_resolver.cc
762 msgid "sysctlnametomib() failed"
763 msgstr ""
764
765 #. debug
766 #: lib/src/facts/freebsd/memory_resolver.cc
767 msgid "xswdev version mismatch"
768 msgstr ""
769
770 #. warning
771 #: lib/src/facts/freebsd/networking_resolver.cc
772 #: lib/src/facts/linux/networking_resolver.cc
773 #: lib/src/facts/openbsd/networking_resolver.cc
774 msgid ""
775 "socket failed: {1} ({2}): interface MTU fact is unavailable for interface "
776 "{3}."
777 msgstr ""
778
779 #. warning
780 #: lib/src/facts/freebsd/networking_resolver.cc
781 #: lib/src/facts/linux/networking_resolver.cc
782 #: lib/src/facts/openbsd/networking_resolver.cc
783 msgid ""
784 "ioctl failed: {1} ({2}): interface MTU fact is unavailable for interface {3}."
785 msgstr ""
786
787 #. debug
788 #: lib/src/facts/freebsd/processor_resolver.cc
789 #: lib/src/facts/openbsd/processor_resolver.cc
790 msgid ""
791 "sysctl hw.ncpu failed: {1} ({2}): logical processor count is unavailable."
792 msgstr ""
793
794 #. debug
795 #: lib/src/facts/freebsd/processor_resolver.cc
796 #: lib/src/facts/openbsd/processor_resolver.cc
797 msgid "sysctl hw.model failed: {1} ({2}): processor models are unavailable."
798 msgstr ""
799
800 #. debug
801 #: lib/src/facts/freebsd/processor_resolver.cc
802 #: lib/src/facts/openbsd/processor_resolver.cc
803 msgid "sysctl hw.cpuspeed failed: {1} ({2}): processor speed is unavailable."
804 msgstr ""
805
806 #. debug
807 #: lib/src/facts/glib/load_average_resolver.cc
808 msgid "failed to retrieve load averages: {1} ({2})."
809 msgstr ""
810
811 #. debug
812 #: lib/src/facts/linux/disk_resolver.cc
813 msgid "{1}: {2}: disk facts are unavailable."
814 msgstr ""
815
816 #. debug
817 #: lib/src/facts/linux/disk_resolver.cc
818 msgid "size of disk {1} is invalid: size information is unavailable."
819 msgstr ""
820
821 #. debug
822 #: lib/src/facts/linux/dmi_resolver.cc
823 msgid ""
824 "/sys/class/dmi cannot be accessed: using dmidecode to query DMI information."
825 msgstr ""
826
827 #. debug
828 #: lib/src/facts/linux/dmi_resolver.cc
829 msgid "{1}: {2}."
830 msgstr ""
831
832 #. debug
833 #: lib/src/facts/linux/dmi_resolver.cc
834 msgid "{1}: file could not be read."
835 msgstr ""
836
837 #. error
838 #: lib/src/facts/linux/filesystem_resolver.cc
839 msgid "setmntent failed: {1} ({2}): mountpoints are unavailable."
840 msgstr ""
841
842 #. debug
843 #: lib/src/facts/linux/filesystem_resolver.cc
844 msgid "blkid_probe_all failed: partition attributes are not available."
845 msgstr ""
846
847 #. debug
848 #: lib/src/facts/linux/filesystem_resolver.cc
849 msgid "blkid_get_cache failed: partition attributes are not available."
850 msgstr ""
851
852 #. debug
853 #: lib/src/facts/linux/filesystem_resolver.cc
854 msgid ""
855 "facter was built without libblkid support: partition attributes are not "
856 "available."
857 msgstr ""
858
859 #. debug
860 #: lib/src/facts/linux/filesystem_resolver.cc
861 msgid "blkid_get_dev failed: partition attributes are unavailable for '{1}'."
862 msgstr ""
863
864 #. debug
865 #: lib/src/facts/linux/filesystem_resolver.cc
866 msgid ""
867 "cannot determine size of partition '{1}': '{2}' is not an integral value."
868 msgstr ""
869
870 #. debug
871 #: lib/src/facts/linux/networking_resolver.cc
872 msgid ""
873 "Could not find the 'ip' command. Network bindings will not be populated from "
874 "routing table"
875 msgstr ""
876
877 #. debug
878 #: lib/src/facts/linux/networking_resolver.cc
879 msgid ""
880 "Could not find the 'ip' command. Physical macaddress for bonded interfaces "
881 "will be incorrect."
882 msgstr ""
883
884 #. debug
885 #: lib/src/facts/linux/virtualization_resolver.cc
886 msgid "reading \"{1}\" for dhclient lease azure information."
887 msgstr ""
888
889 #. debug
890 #: lib/src/facts/linux/virtualization_resolver.cc
891 msgid "found azure option in \"{1}\" lease file."
892 msgstr ""
893
894 #. debug
895 #: lib/src/facts/map_value.cc
896 msgid "null value cannot be added to map."
897 msgstr ""
898
899 #. debug
900 #: lib/src/facts/openbsd/dmi_resolver.cc
901 msgid "sysctl_lookup failed: {1} ({2})."
902 msgstr ""
903
904 #. debug
905 #: lib/src/facts/openbsd/memory_resolver.cc
906 #: lib/src/facts/osx/memory_resolver.cc
907 msgid "sysctl failed: {1} ({2}): system page size is unknown."
908 msgstr ""
909
910 #. debug
911 #: lib/src/facts/openbsd/memory_resolver.cc
912 msgid "sysctl uvmexp failed: {1} ({2}): free memory is not available."
913 msgstr ""
914
915 #. debug
916 #: lib/src/facts/openbsd/memory_resolver.cc
917 msgid "swapctl: SWAP_STATS failed: {1} ({2})"
918 msgstr ""
919
920 #. debug
921 #: lib/src/facts/openbsd/memory_resolver.cc
922 msgid "sysctl failed: {1} ({2}): encrypted swap fact not available."
923 msgstr ""
924
925 #. debug
926 #: lib/src/facts/osx/dmi_resolver.cc
927 msgid "sysctl failed: {1} ({2}): DMI facts are unavailable."
928 msgstr ""
929
930 #. debug
931 #: lib/src/facts/osx/memory_resolver.cc
932 msgid "sysctl failed: {1} ({2}): total memory size is not available."
933 msgstr ""
934
935 #. debug
936 #: lib/src/facts/osx/memory_resolver.cc
937 msgid "host_statistics64 failed: {1} ({2}): free memory size is not available."
938 msgstr ""
939
940 #. debug
941 #: lib/src/facts/osx/memory_resolver.cc
942 msgid "sysctl failed: {1} ({2}): swap data is not available."
943 msgstr ""
944
945 #. debug
946 #: lib/src/facts/osx/processor_resolver.cc
947 msgid "sysctlbyname failed: logical processor count is unavailable."
948 msgstr ""
949
950 #. debug
951 #: lib/src/facts/osx/processor_resolver.cc
952 msgid ""
953 "sysctlbyname failed: {1} ({2}): physical processor count is unavailable."
954 msgstr ""
955
956 #. debug
957 #: lib/src/facts/osx/processor_resolver.cc
958 msgid "sysctlbyname failed: {1} ({2}): processor models are unavailable."
959 msgstr ""
960
961 #. debug
962 #: lib/src/facts/osx/processor_resolver.cc
963 msgid "sysctlbyname failed: {1} ({2}): processor speed is unavailable."
964 msgstr ""
965
966 #: lib/src/facts/posix/external_resolvers_factory.cc
967 #: lib/src/facts/windows/external_resolvers_factory.cc
968 msgid "No resolver for external facts file {1}"
969 msgstr ""
970
971 #. warning
972 #: lib/src/facts/posix/identity_resolver.cc
973 msgid "getpwuid_r failed: {1} ({2})"
974 msgstr ""
975
976 #. warning
977 #: lib/src/facts/posix/identity_resolver.cc
978 msgid "effective uid {1} does not have a passwd entry."
979 msgstr ""
980
981 #. warning
982 #: lib/src/facts/posix/identity_resolver.cc
983 msgid "getgrgid_r failed: {1} ({2})"
984 msgstr ""
985
986 #. warning
987 #: lib/src/facts/posix/identity_resolver.cc
988 msgid "effective gid {1} does not have a group entry."
989 msgstr ""
990
991 #. warning
992 #: lib/src/facts/posix/kernel_resolver.cc
993 #: lib/src/facts/solaris/kernel_resolver.cc
994 msgid "uname failed: {1} ({2}): kernel facts are unavailable."
995 msgstr ""
996
997 #. warning
998 #: lib/src/facts/posix/networking_resolver.cc
999 msgid "gethostname failed: {1} ({2}): hostname is unavailable."
1000 msgstr ""
1001
1002 #. debug
1003 #: lib/src/facts/posix/networking_resolver.cc
1004 msgid "using the FQDN returned by gethostname: {1}."
1005 msgstr ""
1006
1007 #. warning
1008 #: lib/src/facts/posix/networking_resolver.cc
1009 msgid ""
1010 "getaddrinfo failed: {1} ({2}): hostname may not be externally resolvable."
1011 msgstr ""
1012
1013 #. debug
1014 #: lib/src/facts/posix/networking_resolver.cc
1015 msgid ""
1016 "hostname \"{1}\" could not be resolved: hostname may not be externally "
1017 "resolvable."
1018 msgstr ""
1019
1020 #. debug
1021 #: lib/src/facts/posix/operatingsystem_resolver.cc
1022 msgid "uname failed: {1} ({2}): OS hardware is unavailable."
1023 msgstr ""
1024
1025 #. warning
1026 #: lib/src/facts/posix/timezone_resolver.cc
1027 msgid "localtime_r failed: timezone is unavailable."
1028 msgstr ""
1029
1030 #. warning
1031 #: lib/src/facts/posix/timezone_resolver.cc
1032 msgid "strftime failed: timezone is unavailable."
1033 msgstr ""
1034
1035 #: lib/src/facts/posix/uptime_resolver.cc
1036 msgid "Attempting to calculate the uptime from the utmpx file"
1037 msgstr ""
1038
1039 #: lib/src/facts/posix/uptime_resolver.cc
1040 msgid "Could not calculate the uptime from the utmpx file"
1041 msgstr ""
1042
1043 #. debug
1044 #: lib/src/facts/posix/xen_resolver.cc
1045 msgid "failure executing {1}: {2}"
1046 msgstr ""
1047
1048 #. debug
1049 #: lib/src/facts/resolvers/ec2_resolver.cc
1050 #: lib/src/facts/resolvers/gce_resolver.cc
1051 msgid "request for {1} returned a status code of {2}."
1052 msgstr ""
1053
1054 #. info
1055 #: lib/src/facts/resolvers/ec2_resolver.cc
1056 msgid "EC2 facts are unavailable: facter was built without libcurl support."
1057 msgstr ""
1058
1059 #. debug
1060 #: lib/src/facts/resolvers/ec2_resolver.cc
1061 msgid "EC2 facts are unavailable: not running under an EC2 instance."
1062 msgstr ""
1063
1064 #. debug
1065 #: lib/src/facts/resolvers/ec2_resolver.cc
1066 msgid "querying EC2 instance metadata at {1}."
1067 msgstr ""
1068
1069 #. debug
1070 #: lib/src/facts/resolvers/ec2_resolver.cc
1071 msgid ""
1072 "EC2 facts are unavailable: not running under an EC2 instance or EC2 is not "
1073 "responding in a timely manner."
1074 msgstr ""
1075
1076 #. error
1077 #: lib/src/facts/resolvers/ec2_resolver.cc
1078 msgid "EC2 metadata request failed: {1}"
1079 msgstr ""
1080
1081 #. debug
1082 #: lib/src/facts/resolvers/ec2_resolver.cc
1083 msgid "querying EC2 instance user data at {1}."
1084 msgstr ""
1085
1086 #. error
1087 #: lib/src/facts/resolvers/ec2_resolver.cc
1088 msgid "EC2 user data request failed: {1}"
1089 msgstr ""
1090
1091 #. debug
1092 #: lib/src/facts/resolvers/gce_resolver.cc
1093 msgid "not running under a GCE instance."
1094 msgstr ""
1095
1096 #. info
1097 #: lib/src/facts/resolvers/gce_resolver.cc
1098 msgid "GCE facts are unavailable: facter was built without libcurl support."
1099 msgstr ""
1100
1101 #. debug
1102 #: lib/src/facts/resolvers/gce_resolver.cc
1103 msgid "querying GCE metadata."
1104 msgstr ""
1105
1106 #. error
1107 #: lib/src/facts/resolvers/gce_resolver.cc
1108 msgid "failed to parse GCE metadata: {1}."
1109 msgstr ""
1110
1111 #. error
1112 #: lib/src/facts/resolvers/gce_resolver.cc
1113 msgid "GCE metadata request failed: {1}"
1114 msgstr ""
1115
1116 #. debug
1117 #: lib/src/facts/resolvers/networking_resolver.cc
1118 msgid ""
1119 "no primary interface found: using the first interface with an assigned "
1120 "address as the primary interface."
1121 msgstr ""
1122
1123 #: lib/src/facts/resolvers/networking_resolver.cc
1124 #, c-format
1125 msgid "%02x:%02x:%02x:%02x:%02x:%02x"
1126 msgstr ""
1127
1128 #: lib/src/facts/resolvers/networking_resolver.cc
1129 #, c-format
1130 msgid ""
1131 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
1132 "%02x:%02x:%02x:%02x:%02x"
1133 msgstr ""
1134
1135 #. error
1136 #: lib/src/facts/resolvers/ruby_resolver.cc
1137 msgid "error while resolving ruby {1} fact: {2}"
1138 msgstr ""
1139
1140 #. debug
1141 #: lib/src/facts/resolvers/ssh_resolver.cc
1142 msgid "{1} could not be located."
1143 msgstr ""
1144
1145 #. debug
1146 #: lib/src/facts/resolvers/ssh_resolver.cc
1147 msgid "{1} could not be read."
1148 msgstr ""
1149
1150 #. debug
1151 #: lib/src/facts/resolvers/ssh_resolver.cc
1152 msgid "unexpected contents for {1}."
1153 msgstr ""
1154
1155 #. debug
1156 #: lib/src/facts/resolvers/ssh_resolver.cc
1157 msgid "failed to decode SSH key \"{1}\"."
1158 msgstr ""
1159
1160 #: lib/src/facts/resolvers/ssh_resolver.cc
1161 msgid "SSHFP %1% 1 %2%"
1162 msgstr ""
1163
1164 #: lib/src/facts/resolvers/ssh_resolver.cc
1165 msgid "SSHFP %1% 2 %2%"
1166 msgstr ""
1167
1168 #. info
1169 #: lib/src/facts/resolvers/ssh_resolver.cc
1170 msgid ""
1171 "facter was built without OpenSSL support: SSH fingerprint information is "
1172 "unavailable."
1173 msgstr ""
1174
1175 #: lib/src/facts/resolvers/uptime_resolver.cc
1176 #, c-format
1177 msgid "%d:%02d hours"
1178 msgstr ""
1179
1180 #: lib/src/facts/resolvers/uptime_resolver.cc
1181 #, c-format
1182 msgid "%d days"
1183 msgstr ""
1184
1185 #. debug
1186 #: lib/src/facts/solaris/disk_resolver.cc
1187 msgid "disk information is unavailable: {1}."
1188 msgstr ""
1189
1190 #. error
1191 #: lib/src/facts/solaris/filesystem_resolver.cc
1192 msgid "fopen of /etc/mnttab failed: {1} ({2}): mountpoint data is unavailable."
1193 msgstr ""
1194
1195 #. debug
1196 #: lib/src/facts/solaris/memory_resolver.cc
1197 msgid "failed to read memory facts from kstat api: {1}."
1198 msgstr ""
1199
1200 #. debug
1201 #: lib/src/facts/solaris/memory_resolver.cc
1202 msgid "swapctl failed: {1} ({2}): swap information is unavailable"
1203 msgstr ""
1204
1205 #. debug
1206 #: lib/src/facts/solaris/memory_resolver.cc
1207 msgid "swapctl with SC_LIST failed: {1} ({2}): swap information is unavailable"
1208 msgstr ""
1209
1210 #. debug
1211 #: lib/src/facts/solaris/networking_resolver.cc
1212 msgid "socket failed {1} ({2}): interface information is unavailable."
1213 msgstr ""
1214
1215 #. debug
1216 #: lib/src/facts/solaris/networking_resolver.cc
1217 msgid ""
1218 "ioctl with SIOCGLIFNUM failed: {1} ({2}): interface information is "
1219 "unavailable."
1220 msgstr ""
1221
1222 #. debug
1223 #: lib/src/facts/solaris/networking_resolver.cc
1224 msgid ""
1225 "ioctl with SIOCGLIFCONF failed: {1} ({2}): interface information is "
1226 "unavailable."
1227 msgstr ""
1228
1229 #. debug
1230 #: lib/src/facts/solaris/networking_resolver.cc
1231 msgid ""
1232 "socket failed: {1} ({2}): netmask and network for interface {3} are "
1233 "unavailable."
1234 msgstr ""
1235
1236 #. debug
1237 #: lib/src/facts/solaris/networking_resolver.cc
1238 msgid ""
1239 "ioctl with SIOCGLIFNETMASK failed: {1} ({2}): netmask and network for "
1240 "interface {3} are unavailable."
1241 msgstr ""
1242
1243 #. debug
1244 #: lib/src/facts/solaris/networking_resolver.cc
1245 msgid ""
1246 "socket failed: {1} ({2}): link level address for interface {3} is "
1247 "unavailable."
1248 msgstr ""
1249
1250 #. debug
1251 #: lib/src/facts/solaris/networking_resolver.cc
1252 msgid ""
1253 "ioctl with SIOCGARP failed: {1} ({2}): link level address for {3} is "
1254 "unavailable."
1255 msgstr ""
1256
1257 #. debug
1258 #: lib/src/facts/solaris/networking_resolver.cc
1259 msgid "socket failed: {1} ({2}): MTU for interface {3} is unavailable."
1260 msgstr ""
1261
1262 #. debug
1263 #: lib/src/facts/solaris/networking_resolver.cc
1264 msgid ""
1265 "ioctl with SIOCGLIFMTU failed: {1} ({2}): MTU for interface {3} is "
1266 "unavailable."
1267 msgstr ""
1268
1269 #. debug
1270 #: lib/src/facts/solaris/processor_resolver.cc
1271 msgid "failed to read processor data entry: {1}."
1272 msgstr ""
1273
1274 #. debug
1275 #: lib/src/facts/solaris/processor_resolver.cc
1276 msgid "failed to read processor data from kstat api: {1}."
1277 msgstr ""
1278
1279 #. warning
1280 #: lib/src/facts/windows/collection.cc
1281 msgid "external facts unavailable, %1%"
1282 msgstr ""
1283
1284 #. debug
1285 #: lib/src/facts/windows/collection.cc
1286 msgid "HOME environment variable not set, external facts unavailable"
1287 msgstr ""
1288
1289 #. error
1290 #: lib/src/facts/windows/collection.cc
1291 msgid "failed adding platform facts that require WMI: {1}"
1292 msgstr ""
1293
1294 #. debug
1295 #: lib/src/facts/windows/dmi_resolver.cc
1296 msgid "WMI query returned no results for {1} with values {2} and {3}."
1297 msgstr ""
1298
1299 #. debug
1300 #: lib/src/facts/windows/fips_resolver.cc
1301 msgid "failure getting fips_mode: {1}"
1302 msgstr ""
1303
1304 #. debug
1305 #: lib/src/facts/windows/identity_resolver.cc
1306 msgid "failure resolving identity facts: {1}"
1307 msgstr ""
1308
1309 #. debug
1310 #: lib/src/facts/windows/kernel_resolver.cc
1311 msgid "failed to get the OS version information from RtlGetVersion"
1312 msgstr ""
1313
1314 #: lib/src/facts/windows/kernel_resolver.cc
1315 msgid "%1%.%2%.%3%"
1316 msgstr ""
1317
1318 #. debug
1319 #: lib/src/facts/windows/memory_resolver.cc
1320 msgid "resolving memory facts failed: {1}"
1321 msgstr ""
1322
1323 #. debug
1324 #: lib/src/facts/windows/networking_resolver.cc
1325 msgid "failure resolving hostname: {1}"
1326 msgstr ""
1327
1328 #. debug
1329 #: lib/src/facts/windows/networking_resolver.cc
1330 msgid "failed creating IPv4 mask of length {1}"
1331 msgstr ""
1332
1333 #. debug
1334 #: lib/src/facts/windows/networking_resolver.cc
1335 msgid "failed creating IPv6 mask with component of length {1}"
1336 msgstr ""
1337
1338 #. debug
1339 #: lib/src/facts/windows/networking_resolver.cc
1340 msgid "failure getting networking::domain fact: {1}"
1341 msgstr ""
1342
1343 #. debug
1344 #: lib/src/facts/windows/networking_resolver.cc
1345 msgid "failure resolving networking facts: {1}"
1346 msgstr ""
1347
1348 #. debug
1349 #: lib/src/facts/windows/networking_resolver.cc
1350 msgid "failed to retrieve dhcp v4 server address for {1}: {2}"
1351 msgstr ""
1352
1353 #. debug
1354 #: lib/src/facts/windows/networking_resolver.cc
1355 msgid "failed to retrieve ip{1} address for {2}: {3}"
1356 msgstr ""
1357
1358 #. debug
1359 #: lib/src/facts/windows/operating_system_resolver.cc
1360 msgid "Could not determine if we are running in WOW64: {1}"
1361 msgstr ""
1362
1363 #. debug
1364 #: lib/src/facts/windows/operating_system_resolver.cc
1365 msgid "error finding SYSTEMROOT: {1}"
1366 msgstr ""
1367
1368 #. debug
1369 #: lib/src/facts/windows/operating_system_resolver.cc
1370 msgid "failure getting ReleaseId: {1}"
1371 msgstr ""
1372
1373 #. debug
1374 #: lib/src/facts/windows/operating_system_resolver.cc
1375 msgid "failure getting EditionID: {1}"
1376 msgstr ""
1377
1378 #. debug
1379 #: lib/src/facts/windows/operating_system_resolver.cc
1380 msgid "failure getting InstallationType: {1}"
1381 msgstr ""
1382
1383 #. debug
1384 #: lib/src/facts/windows/operating_system_resolver.cc
1385 msgid "failure getting ProductName: {1}"
1386 msgstr ""
1387
1388 #. debug
1389 #: lib/src/facts/windows/operating_system_resolver.cc
1390 msgid ""
1391 "Could not resolve the OS release and OS major version facts from the kernel "
1392 "version fact"
1393 msgstr ""
1394
1395 #. debug
1396 #: lib/src/facts/windows/processor_resolver.cc
1397 msgid "WMI processor Name, Architecture query returned no results."
1398 msgstr ""
1399
1400 #. debug
1401 #: lib/src/facts/windows/processor_resolver.cc
1402 msgid "Unable to determine processor type: unknown architecture"
1403 msgstr ""
1404
1405 #. debug
1406 #: lib/src/facts/windows/ssh_resolver.cc
1407 msgid "error finding programdata: {1}"
1408 msgstr ""
1409
1410 #. warning
1411 #: lib/src/facts/windows/timezone_resolver.cc
1412 msgid "localtime failed: timezone is unavailable: {1} ({2})"
1413 msgstr ""
1414
1415 #. warning
1416 #: lib/src/facts/windows/timezone_resolver.cc
1417 msgid "wcsftime failed: timezone is unavailable: {1} ({2})"
1418 msgstr ""
1419
1420 #. log
1421 #: lib/src/logging/logging.cc
1422 msgid "locale environment variables were bad; continuing with LANG=C LC_ALL=C"
1423 msgstr ""
1424
1425 #. log
1426 #: lib/src/logging/logging.cc
1427 msgid ""
1428 "Could not initialize locale, even with LC_* variables cleared. Continuing "
1429 "without localization support"
1430 msgstr ""
1431
1432 #: lib/src/logging/logging.cc
1433 msgid ""
1434 "could not initialize logging, even with locale variables reset to LANG=C "
1435 "LC_ALL=C: {1}"
1436 msgstr ""
1437
1438 #: lib/src/ruby/aggregate_resolution.cc
1439 msgid "expected chunk name to be a Symbol"
1440 msgstr ""
1441
1442 #: lib/src/ruby/aggregate_resolution.cc lib/src/ruby/resolution.cc
1443 #: lib/src/ruby/simple_resolution.cc
1444 msgid "a block must be provided"
1445 msgstr ""
1446
1447 #: lib/src/ruby/aggregate_resolution.cc lib/src/ruby/fact.cc
1448 msgid "expected a Symbol for options key"
1449 msgstr ""
1450
1451 #: lib/src/ruby/aggregate_resolution.cc
1452 msgid "expected a Symbol or Array of Symbol for require option"
1453 msgstr ""
1454
1455 #: lib/src/ruby/aggregate_resolution.cc lib/src/ruby/fact.cc
1456 msgid "unexpected option {1}"
1457 msgstr ""
1458
1459 #: lib/src/ruby/aggregate_resolution.cc lib/src/ruby/fact.cc
1460 #: lib/src/ruby/module.cc
1461 msgid "wrong number of arguments ({1} for 2)"
1462 msgstr ""
1463
1464 #: lib/src/ruby/aggregate_resolution.cc
1465 msgid "wrong number of arguments ({1} for 3)"
1466 msgstr ""
1467
1468 #: lib/src/ruby/aggregate_resolution.cc
1469 msgid "cannot merge {1}:{2} and {3}:{4}"
1470 msgstr ""
1471
1472 #: lib/src/ruby/chunk.cc
1473 msgid "chunk dependency cycle detected"
1474 msgstr ""
1475
1476 #: lib/src/ruby/fact.cc
1477 msgid "cycle detected while requesting value of fact \"{1}\""
1478 msgstr ""
1479
1480 #. error
1481 #: lib/src/ruby/fact.cc
1482 msgid "error while resolving custom fact \"{1}\": {2}"
1483 msgstr ""
1484
1485 #: lib/src/ruby/fact.cc
1486 msgid "expected resolution name to be a String"
1487 msgstr ""
1488
1489 #: lib/src/ruby/fact.cc
1490 msgid "expected resolution name to be a Symbol or String"
1491 msgstr ""
1492
1493 #: lib/src/ruby/fact.cc
1494 msgid "expected a Hash for the options"
1495 msgstr ""
1496
1497 #: lib/src/ruby/fact.cc
1498 msgid "expected a Symbol for type option"
1499 msgstr ""
1500
1501 #: lib/src/ruby/fact.cc
1502 msgid "expected simple or aggregate for resolution type but was given {1}"
1503 msgstr ""
1504
1505 #. warning
1506 #: lib/src/ruby/fact.cc
1507 msgid "timeout option is not supported for custom facts and will be ignored."
1508 msgstr ""
1509
1510 #: lib/src/ruby/fact.cc
1511 msgid ""
1512 "fact \"{1}\" already has the maximum number of resolutions allowed ({2})."
1513 msgstr ""
1514
1515 #: lib/src/ruby/fact.cc
1516 msgid ""
1517 "cannot define an aggregate resolution with name \"{1}\": a simple resolution "
1518 "with the same name already exists"
1519 msgstr ""
1520
1521 #: lib/src/ruby/fact.cc
1522 msgid ""
1523 "cannot define a simple resolution with name \"{1}\": an aggregate resolution "
1524 "with the same name already exists"
1525 msgstr ""
1526
1527 #: lib/src/ruby/fact.cc lib/src/ruby/module.cc
1528 msgid "expected a String or Symbol for fact name"
1529 msgstr ""
1530
1531 #. warning
1532 #: lib/src/ruby/module.cc lib/src/ruby/ruby.cc
1533 msgid "{1}: facts requiring Ruby will not be resolved."
1534 msgstr ""
1535
1536 #: lib/src/ruby/module.cc
1537 msgid "could not initialize facter due to a locale error: {1}"
1538 msgstr ""
1539
1540 #: lib/src/ruby/module.cc
1541 msgid "Ruby API is not initialized."
1542 msgstr ""
1543
1544 #. warning
1545 #: lib/src/ruby/module.cc
1546 msgid "{1}: Ruby cleanup ended prematurely"
1547 msgstr ""
1548
1549 #. debug
1550 #: lib/src/ruby/module.cc
1551 msgid "loading all custom facts."
1552 msgstr ""
1553
1554 #. debug
1555 #: lib/src/ruby/module.cc
1556 msgid "loading custom fact directories from config file"
1557 msgstr ""
1558
1559 #. debug
1560 #: lib/src/ruby/module.cc
1561 msgid "searching for custom facts in {1}."
1562 msgstr ""
1563
1564 #: lib/src/ruby/module.cc
1565 msgid "loading external fact directories from config file"
1566 msgstr ""
1567
1568 #. error
1569 #: lib/src/ruby/module.cc
1570 msgid "{1} uncaught exception: {2}"
1571 msgstr ""
1572
1573 #: lib/src/ruby/module.cc
1574 msgid "unexpected self value {1}"
1575 msgstr ""
1576
1577 #. debug
1578 #: lib/src/ruby/module.cc
1579 msgid ""
1580 "Facter was compiled with a Leatherman version that cannot expand shell "
1581 "builtins. Falling back to the default behavior."
1582 msgstr ""
1583
1584 #: lib/src/ruby/module.cc
1585 msgid "Cause: {1}"
1586 msgstr ""
1587
1588 #: lib/src/ruby/module.cc
1589 msgid "execution of command \"{1}\" failed: command not found."
1590 msgstr ""
1591
1592 #: lib/src/ruby/module.cc
1593 msgid "execution of command \"{1}\" failed."
1594 msgstr ""
1595
1596 #. debug
1597 #: lib/src/ruby/module.cc
1598 msgid "searching for custom fact \"{1}\"."
1599 msgstr ""
1600
1601 #. debug
1602 #: lib/src/ruby/module.cc
1603 msgid "searching for {1} in {2}."
1604 msgstr ""
1605
1606 #. debug
1607 #: lib/src/ruby/module.cc
1608 msgid "custom fact \"{1}\" was not found."
1609 msgstr ""
1610
1611 #. info
1612 #: lib/src/ruby/module.cc
1613 msgid "loading custom facts from {1}."
1614 msgstr ""
1615
1616 #. error
1617 #: lib/src/ruby/module.cc
1618 msgid "error while resolving custom facts in {1}: {2}"
1619 msgstr ""
1620
1621 #: lib/src/ruby/module.cc
1622 msgid "invalid log level specified."
1623 msgstr ""
1624
1625 #: lib/src/ruby/resolution.cc
1626 msgid "a block is unexpected when passing a Hash"
1627 msgstr ""
1628
1629 #: lib/src/ruby/resolution.cc
1630 msgid "expected a String or Symbol for confine key"
1631 msgstr ""
1632
1633 #: lib/src/ruby/resolution.cc
1634 msgid "expected argument to be a String, Symbol, or Hash"
1635 msgstr ""
1636
1637 #: lib/src/ruby/resolution.cc lib/src/ruby/simple_resolution.cc
1638 msgid "wrong number of arguments ({1} for 1)"
1639 msgstr ""
1640
1641 #. warning
1642 #: lib/src/ruby/resolution.cc
1643 msgid "timeout= is not supported for custom facts and will be ignored."
1644 msgstr ""
1645
1646 #. debug
1647 #: lib/src/ruby/ruby.cc
1648 msgid "Redirecting ruby's stdout to stderr"
1649 msgstr ""
1650
1651 #. debug
1652 #: lib/src/ruby/ruby.cc
1653 msgid "Restoring Ruby's stdout"
1654 msgstr ""
1655
1656 #. warning
1657 #: lib/src/ruby/ruby.cc
1658 msgid "Could not load puppet; some facts may be unavailable: {1}"
1659 msgstr ""
1660
1661 #. debug
1662 #: lib/src/ruby/ruby.cc
1663 msgid "cannot lookup element \"{1}\": container is not an array or hash"
1664 msgstr ""
1665
1666 #: lib/src/ruby/simple_resolution.cc
1667 msgid "expected a non-empty String for first argument"
1668 msgstr ""
1669
1670 #: lib/src/ruby/simple_resolution.cc
1671 msgid "a block is unexpected when passing a String"
1672 msgstr ""
1673
1674 #: lib/src/util/freebsd/geom.cc
1675 msgid "Skipping config {1} because it has a null value"
1676 msgstr ""
1677
1678 #: lib/src/util/freebsd/geom.cc
1679 msgid "Unable to get GEOM tree"
1680 msgstr ""
1681
1682 #: lib/src/util/freebsd/geom.cc
1683 msgid "The GEOM class \"{1}\" was not found"
1684 msgstr ""
1685
1686 #: lib/src/util/posix/utmpx_file.cc
1687 msgid "only one utmpx_file instance can exist at a time!"
1688 msgstr ""
1689
1690 #: lib/src/util/posix/utmpx_file.cc
1691 msgid "Reading the utmpx file ..."
1692 msgstr ""
1693
1694 #: lib/src/util/solaris/k_stat.cc
1695 msgid "kstat_open failed"
1696 msgstr ""
1697
1698 #: lib/src/util/solaris/k_stat.cc
1699 msgid "kstat_read failed: {1} ({2})"
1700 msgstr ""
1701
1702 #: lib/src/util/solaris/k_stat.cc
1703 msgid "kstat_data_lookup failed for {1}"
1704 msgstr ""
1705
1706 #: lib/src/util/solaris/k_stat.cc
1707 msgid "invalid datatype {1} {2}"
1708 msgstr ""
1709
1710 #: lib/src/util/windows/wsa.cc
1711 msgid "%1% (%2%)"
1712 msgstr ""
1713
1714 #. debug
1715 #: lib/src/util/windows/wsa.cc
1716 msgid "initializing Winsock"
1717 msgstr ""
1718
1719 #: lib/src/util/windows/wsa.cc
1720 msgid "WSAStartup failed with error"
1721 msgstr ""
1722
1723 #: lib/src/util/windows/wsa.cc
1724 msgid "could not find a usable version of Winsock.dll"
1725 msgstr ""
1726
1727 #: lib/src/util/windows/wsa.cc
1728 msgid "address to string translation failed"
1729 msgstr ""
1730
1731 #: lib/src/util/windows/wsa.cc
1732 msgid "string to address translation failed"
1733 msgstr ""
00 .\" generated with Ronn/v0.7.3
11 .\" http://github.com/rtomayko/ronn/tree/0.7.3
22 .
3 .TH "FACTER" "8" "April 2015" "" ""
3 .TH "FACTER" "8" "July 2021" "Puppet, Inc." "Facter manual"
44 .
55 .SH "NAME"
6 \fBfacter\fR \- Gather system information
6 \fBfacter\fR
77 .
88 .SH "SYNOPSIS"
9 Collect and display facts about the system\.
10 .
11 .SH "USAGE"
12 .
13 .nf
14
15 facter [\-\-color] [\-\-config] [\-\-custom\-dir DIR] [\-d|\-\-debug] [\-\-external\-dir DIR] [\-\-help]
16 [\-j|\-\-json] [\-\-list\-block\-groups] [\-\-list\-cache\-groups] [\-l|\-\-log\-level LEVEL (=warn)]
17 [\-\-no\-block] [\-\-no\-cache] [\-\-no\-color] [\-\-no\-custom\-facts] [\-\-no\-external\-facts]
18 [\-\-trace] [\-\-verbose] [\-v|\-\-version] [\-y|\-\-yaml] [\-\-strict] [fact] [fact] [\.\.\.]
19 .
20 .fi
9 facter [options] [query] [query] [\.\.\.]
2110 .
2211 .SH "DESCRIPTION"
2312 Collect and display facts about the current system\. The library behind Facter is easy to extend, making Facter an easy way to collect information about a system\.
2615 If no queries are given, then all facts will be returned\.
2716 .
2817 .P
29 Many of the command line options can also be set via the HOCON config file. This file can also be used to block or cache certain fact groups\.
18 Many of the command line options can also be set via the HOCON config file\. This file can also be used to block or cache certain fact groups\.
3019 .
3120 .SH "OPTIONS"
3221 .
33 .nf
34 \fB\-\-color\fR Enable color output\.
35 \fB\-\-config\fR A custom location for the config file\.
36 \fB\-\-custom-dir\fR arg A directory to use for custom facts\.
37 \fB\-d, [ \-\-debug ]\fR Enable debug output\.
38 \fB\-\-external-dir\fR arg A directory to use for external facts\.
39 \fB\-\-help\fR Print help and usage information\.
40 \fB\-j, [ \-\-json ]\fR Output facts in JSON format\.
41 \fB\-\-list\-block\-groups\fR List the names of all blockable fact groups\.
42 \fB\-\-list\-cache\-groups\fR List the names of all cacheable fact groups\.
43 \fB\-\-show-legacy\fR Show legacy facts when querying all facts\.
44 \fB\-l, [ \-\-log-level ]\fR arg (=warn) Set logging level\.
45 Supported levels are: none, trace, debug,
46 info, warn, error, and fatal\.
47 \fB\-\-no\-block\fR Disable fact blocking\.
48 \fB\-\-no\-cache\fR Disable fact caching\.
49 \fB\-\-no-color\fR Disable color output\.
50 \fB\-\-no-custom-fact\fR Disable custom facts\.
51 \fB\-\-no-external-facts\fR Disable external facts\.
52 \fB\-\-no-ruby\fR Disable loading Ruby, facts requiring Ruby, and custom facts\.
53 \fB\-\-trace\fR Enable backtraces for custom facts\.
54 \fB\-\-verbose\fR Enable verbose (info) output\.
55 \fB\-v, [ \-\-version ]\fR Print the version and exit\.
56 \fB\-y, [ \-\-yaml ]\fR Output facts in YAML format\.
57 .
58 .fi
22 .TP
23 \fB\-\-color\fR:
24 .
25 .IP
26 Enable color output\.
27 .
28 .TP
29 \fB\-\-no\-color\fR:
30 .
31 .IP
32 Disable color output\.
33 .
34 .TP
35 \fB\-c\fR, \fB\-\-config\fR:
36 .
37 .IP
38 The location of the config file\.
39 .
40 .TP
41 \fB\-\-custom\-dir\fR:
42 .
43 .IP
44 A directory to use for custom facts\.
45 .
46 .TP
47 \fB\-d\fR, \fB\-\-debug\fR:
48 .
49 .IP
50 Enable debug output\.
51 .
52 .TP
53 \fB\-\-external\-dir\fR:
54 .
55 .IP
56 A directory to use for external facts\.
57 .
58 .TP
59 \fB\-\-hocon\fR:
60 .
61 .IP
62 Output in Hocon format\.
63 .
64 .TP
65 \fB\-j\fR, \fB\-\-json\fR:
66 .
67 .IP
68 Output in JSON format\.
69 .
70 .TP
71 \fB\-l\fR, \fB\-\-log\-level\fR:
72 .
73 .IP
74 Set logging level\. Supported levels are: none, trace, debug, info, warn, error, and fatal\.
75 .
76 .TP
77 \fB\-\-no\-block\fR:
78 .
79 .IP
80 Disable fact blocking\.
81 .
82 .TP
83 \fB\-\-no\-cache\fR:
84 .
85 .IP
86 Disable loading and refreshing facts from the cache
87 .
88 .TP
89 \fB\-\-no\-custom\-facts\fR:
90 .
91 .IP
92 Disable custom facts\.
93 .
94 .TP
95 \fB\-\-no\-external\-facts\fR:
96 .
97 .IP
98 Disable external facts\.
99 .
100 .TP
101 \fB\-\-no\-ruby\fR:
102 .
103 .IP
104 Disable loading Ruby, facts requiring Ruby, and custom facts\.
105 .
106 .TP
107 \fB\-\-trace\fR:
108 .
109 .IP
110 Enable backtraces for custom facts\.
111 .
112 .TP
113 \fB\-\-verbose\fR:
114 .
115 .IP
116 Enable verbose (info) output\.
117 .
118 .TP
119 \fB\-\-show\-legacy\fR:
120 .
121 .IP
122 Show legacy facts when querying all facts\.
123 .
124 .TP
125 \fB\-y\fR, \fB\-\-yaml\fR:
126 .
127 .IP
128 Output in YAML format\.
129 .
130 .TP
131 \fB\-\-strict\fR:
132 .
133 .IP
134 Enable more aggressive error reporting\.
135 .
136 .TP
137 \fB\-t\fR, \fB\-\-timing\fR:
138 .
139 .IP
140 Show how much time it took to resolve each fact
141 .
142 .TP
143 \fB\-\-sequential\fR:
144 .
145 .IP
146 Resolve facts sequentially
147 .
148 .TP
149 \fB\-p\fR, \fB\-\-puppet\fR:
150 .
151 .IP
152 Load the Puppet libraries, thus allowing Facter to load Puppet\-specific facts\.
153 .
154 .TP
155 \fB\-\-version, \-v\fR:
156 .
157 .IP
158 Print the version
159 .
160 .TP
161 \fB\-\-list\-block\-groups\fR:
162 .
163 .IP
164 List block groups
165 .
166 .TP
167 \fB\-\-list\-cache\-groups\fR:
168 .
169 .IP
170 List cache groups
171 .
172 .TP
173 \fB\-\-help, \-h\fR:
174 .
175 .IP
176 Help for all arguments
59177 .
60178 .SH "FILES"
61 .
62 .nf
63 \fI/etc/puppetlabs/facter/facter.conf\fR
64 .RS
65 A HOCON config file that can be used to specify directories for custom and external facts, set various command line options, and specify facts to block. See example below for details, or visit the GitHub README.
66 .
67 .SH "EXAMPLE"
179 \fI/etc/puppetlabs/facter/facter\.conf\fR
180 .
181 .P
182 A HOCON config file that can be used to specify directories for custom and external facts, set various command line options, and specify facts to block\. See example below for details, or visit the GitHub README \fIhttps://github\.com/puppetlabs/puppetlabs\-hocon#overview\fR\.
183 .
184 .SH "EXAMPLES"
68185 Display all facts:
69186 .
70187 .IP "" 4
75192 disks => {
76193 sda => {
77194 model => "Virtual disk",
78 size => "8.00 GiB",
195 size => "8\.00 GiB",
79196 size_bytes => 8589934592,
80197 vendor => "ExampleVendor"
81198 }
84201 bios => {
85202 release_date => "06/23/2013",
86203 vendor => "Example Vendor",
87 version => "6.00"
204 version => "6\.00"
88205 }
89206 }
90207 [\.\.\.]
105222 count => 2,
106223 isa => "x86_64",
107224 models => [
108 "Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz",
109 "Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz"
225 "Intel(R) Xeon(R) CPU E5\-2680 v2 @ 2\.80GHz",
226 "Intel(R) Xeon(R) CPU E5\-2680 v2 @ 2\.80GHz"
110227 ],
111228 physicalcount => 2
112229 }
121238 .IP "" 4
122239 .
123240 .nf
124 $ facter processors.isa
241
242 $ facter processors\.isa
125243 x86_64
126244 .
127245 .fi
129247 .IP "" 0
130248 .
131249 .P
132 Display a single legacy fact. Note that non-structured facts existing in previous versions of Facter are still available,
133 but are not displayed by default due to redundancy with newer structured facts:
134 .
135 .IP "" 4
136 .
137 .nf
250 Display a single legacy fact\. Note that non\-structured facts existing in previous versions of Facter are still available, but are not displayed by default due to redundancy with newer structured facts:
251 .
252 .IP "" 4
253 .
254 .nf
255
138256 $ facter processorcount
139257 2
140258 .
149267 .
150268 .nf
151269
152 $ facter \-\-json os.name os.release.major processors.isa
270 $ facter \-\-json os\.name os\.release\.major processors\.isa
153271 {
154 "os.name": "Ubuntu",
155 "os.release.major": "14.04",
156 "processors.isa": "x86_64"
157 }
158 .
159 .fi
160 .
161 .IP "" 0
162 .
163 .P
164 An example config file.
272 "os\.name": "Ubuntu",
273 "os\.release\.major": "14\.04",
274 "processors\.isa": "x86_64"
275 }
276 .
277 .fi
278 .
279 .IP "" 0
280 .
281 .P
282 An example config file\.
165283 .
166284 .IP "" 4
167285 .
169287
170288 # always loaded (CLI and as Ruby module)
171289 global : {
172 external-dir : "~/external/facts",
173 custom-dir : [
290 external\-dir : "~/external/facts",
291 custom\-dir : [
174292 "~/custom/facts",
175 "~/custom/facts/more-facts"
293 "~/custom/facts/more\-facts"
176294 ],
177 no-external-facts : false,
178 no-custom-facts : false,
179 no-ruby : false
295 no\-external\-facts : false,
296 no\-custom\-facts : false,
297 no\-ruby : false
180298 }
181299 # loaded when running from the command line
182300 cli : {
183301 debug : false,
184302 trace : true,
185303 verbose : false,
186 log-level : "info"
187 }
188 # always loaded, fact-sepcific configuration
304 log\-level : "info"
305 }
306 # always loaded, fact\-specific configuration
189307 facts : {
190 # for valid blocklist entries, use --list-block-groups
308 # for valid blocklist entries, use \-\-list\-block\-groups
191309 blocklist : [ "file system", "EC2" ],
192 # for valid time-to-live entries, use --list-cache-groups
310 # for valid time\-to\-live entries, use \-\-list\-cache\-groups
193311 ttls : [ { "timezone" : 30 days } ]
194312 }
195313 .
196314 .fi
197315 .
198316 .IP "" 0
199 .SH "AUTHOR"
200 Luke Kanies
201 .
202 .SH "COPYRIGHT"
203 Copyright (c) 2011\-2015 Puppet Labs, Inc Licensed under the Apache 2\.0 license
204 .
317
+0
-4754
scripts/cpplint.py less more
0 #!/usr/bin/python
1 #
2 # Copyright (c) 2009 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Does google-lint on c++ files.
31
32 The goal of this script is to identify places in the code that *may*
33 be in non-compliance with google style. It does not attempt to fix
34 up these problems -- the point is to educate. It does also not
35 attempt to find all problems, or to ensure that everything it does
36 find is legitimately a problem.
37
38 In particular, we can get very confused by /* and // inside strings!
39 We do a small hack, which is to ignore //'s with "'s after them on the
40 same line, but it is far from perfect (in either direction).
41 """
42
43 import codecs
44 import copy
45 import getopt
46 import math # for log
47 import os
48 import re
49 import sre_compile
50 import string
51 import sys
52 import unicodedata
53
54
55 _USAGE = """
56 Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
57 [--counting=total|toplevel|detailed] [--root=subdir]
58 [--linelength=digits]
59 <file> [file] ...
60
61 The style guidelines this tries to follow are those in
62 http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
63
64 Every problem is given a confidence score from 1-5, with 5 meaning we are
65 certain of the problem, and 1 meaning it could be a legitimate construct.
66 This will miss some errors, and is not a substitute for a code review.
67
68 To suppress false-positive errors of a certain category, add a
69 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
70 suppresses errors of all categories on that line.
71
72 The files passed in will be linted; at least one file must be provided.
73 Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
74 extensions with the --extensions flag.
75
76 Flags:
77
78 output=vs7
79 By default, the output is formatted to ease emacs parsing. Visual Studio
80 compatible output (vs7) may also be used. Other formats are unsupported.
81
82 verbose=#
83 Specify a number 0-5 to restrict errors to certain verbosity levels.
84
85 filter=-x,+y,...
86 Specify a comma-separated list of category-filters to apply: only
87 error messages whose category names pass the filters will be printed.
88 (Category names are printed with the message and look like
89 "[whitespace/indent]".) Filters are evaluated left to right.
90 "-FOO" and "FOO" means "do not print categories that start with FOO".
91 "+FOO" means "do print categories that start with FOO".
92
93 Examples: --filter=-whitespace,+whitespace/braces
94 --filter=whitespace,runtime/printf,+runtime/printf_format
95 --filter=-,+build/include_what_you_use
96
97 To see a list of all the categories used in cpplint, pass no arg:
98 --filter=
99
100 counting=total|toplevel|detailed
101 The total number of errors found is always printed. If
102 'toplevel' is provided, then the count of errors in each of
103 the top-level categories like 'build' and 'whitespace' will
104 also be printed. If 'detailed' is provided, then a count
105 is provided for each category like 'build/class'.
106
107 root=subdir
108 The root directory used for deriving header guard CPP variable.
109 By default, the header guard CPP variable is calculated as the relative
110 path to the directory that contains .git, .hg, or .svn. When this flag
111 is specified, the relative path is calculated from the specified
112 directory. If the specified directory does not exist, this flag is
113 ignored.
114
115 Examples:
116 Assuing that src/.git exists, the header guard CPP variables for
117 src/chrome/browser/ui/browser.h are:
118
119 No flag => CHROME_BROWSER_UI_BROWSER_H_
120 --root=chrome => BROWSER_UI_BROWSER_H_
121 --root=chrome/browser => UI_BROWSER_H_
122
123 linelength=digits
124 This is the allowed line length for the project. The default value is
125 80 characters.
126
127 Examples:
128 --linelength=120
129
130 extensions=extension,extension,...
131 The allowed file extensions that cpplint will check
132
133 Examples:
134 --extensions=hpp,cpp
135 """
136
137 # We categorize each error message we print. Here are the categories.
138 # We want an explicit list so we can list them all in cpplint --filter=.
139 # If you add a new error message with a new category, add it to the list
140 # here! cpplint_unittest.py should tell you if you forget to do this.
141 _ERROR_CATEGORIES = [
142 'build/class',
143 'build/deprecated',
144 'build/endif_comment',
145 'build/explicit_make_pair',
146 'build/forward_decl',
147 'build/header_guard',
148 'build/include',
149 'build/include_alpha',
150 'build/include_order',
151 'build/include_what_you_use',
152 'build/namespaces',
153 'build/printf_format',
154 'build/storage_class',
155 'legal/copyright',
156 'readability/alt_tokens',
157 'readability/braces',
158 'readability/casting',
159 'readability/check',
160 'readability/constructors',
161 'readability/fn_size',
162 'readability/function',
163 'readability/multiline_comment',
164 'readability/multiline_string',
165 'readability/namespace',
166 'readability/nolint',
167 'readability/nul',
168 'readability/streams',
169 'readability/todo',
170 'readability/utf8',
171 'runtime/arrays',
172 'runtime/casting',
173 'runtime/explicit',
174 'runtime/int',
175 'runtime/init',
176 'runtime/invalid_increment',
177 'runtime/member_string_references',
178 'runtime/memset',
179 'runtime/operator',
180 'runtime/printf',
181 'runtime/printf_format',
182 'runtime/references',
183 'runtime/string',
184 'runtime/threadsafe_fn',
185 'runtime/vlog',
186 'whitespace/blank_line',
187 'whitespace/braces',
188 'whitespace/comma',
189 'whitespace/comments',
190 'whitespace/empty_conditional_body',
191 'whitespace/empty_loop_body',
192 'whitespace/end_of_line',
193 'whitespace/ending_newline',
194 'whitespace/forcolon',
195 'whitespace/indent',
196 'whitespace/line_length',
197 'whitespace/newline',
198 'whitespace/operators',
199 'whitespace/parens',
200 'whitespace/semicolon',
201 'whitespace/tab',
202 'whitespace/todo'
203 ]
204
205 # The default state of the category filter. This is overrided by the --filter=
206 # flag. By default all errors are on, so only add here categories that should be
207 # off by default (i.e., categories that must be enabled by the --filter= flags).
208 # All entries here should start with a '-' or '+', as in the --filter= flag.
209 _DEFAULT_FILTERS = ['-build/include_alpha']
210
211 # We used to check for high-bit characters, but after much discussion we
212 # decided those were OK, as long as they were in UTF-8 and didn't represent
213 # hard-coded international strings, which belong in a separate i18n file.
214
215
216 # C++ headers
217 _CPP_HEADERS = frozenset([
218 # Legacy
219 'algobase.h',
220 'algo.h',
221 'alloc.h',
222 'builtinbuf.h',
223 'bvector.h',
224 'complex.h',
225 'defalloc.h',
226 'deque.h',
227 'editbuf.h',
228 'fstream.h',
229 'function.h',
230 'hash_map',
231 'hash_map.h',
232 'hash_set',
233 'hash_set.h',
234 'hashtable.h',
235 'heap.h',
236 'indstream.h',
237 'iomanip.h',
238 'iostream.h',
239 'istream.h',
240 'iterator.h',
241 'list.h',
242 'map.h',
243 'multimap.h',
244 'multiset.h',
245 'ostream.h',
246 'pair.h',
247 'parsestream.h',
248 'pfstream.h',
249 'procbuf.h',
250 'pthread_alloc',
251 'pthread_alloc.h',
252 'rope',
253 'rope.h',
254 'ropeimpl.h',
255 'set.h',
256 'slist',
257 'slist.h',
258 'stack.h',
259 'stdiostream.h',
260 'stl_alloc.h',
261 'stl_relops.h',
262 'streambuf.h',
263 'stream.h',
264 'strfile.h',
265 'strstream.h',
266 'tempbuf.h',
267 'tree.h',
268 'type_traits.h',
269 'vector.h',
270 # 17.6.1.2 C++ library headers
271 'algorithm',
272 'array',
273 'atomic',
274 'bitset',
275 'chrono',
276 'codecvt',
277 'complex',
278 'condition_variable',
279 'deque',
280 'exception',
281 'forward_list',
282 'fstream',
283 'functional',
284 'future',
285 'initializer_list',
286 'iomanip',
287 'ios',
288 'iosfwd',
289 'iostream',
290 'istream',
291 'iterator',
292 'limits',
293 'list',
294 'locale',
295 'map',
296 'memory',
297 'mutex',
298 'new',
299 'numeric',
300 'ostream',
301 'queue',
302 'random',
303 'ratio',
304 'regex',
305 'set',
306 'sstream',
307 'stack',
308 'stdexcept',
309 'streambuf',
310 'string',
311 'strstream',
312 'system_error',
313 'thread',
314 'tuple',
315 'typeindex',
316 'typeinfo',
317 'type_traits',
318 'unordered_map',
319 'unordered_set',
320 'utility',
321 'valarray',
322 'vector',
323 # 17.6.1.2 C++ headers for C library facilities
324 'cassert',
325 'ccomplex',
326 'cctype',
327 'cerrno',
328 'cfenv',
329 'cfloat',
330 'cinttypes',
331 'ciso646',
332 'climits',
333 'clocale',
334 'cmath',
335 'csetjmp',
336 'csignal',
337 'cstdalign',
338 'cstdarg',
339 'cstdbool',
340 'cstddef',
341 'cstdint',
342 'cstdio',
343 'cstdlib',
344 'cstring',
345 'ctgmath',
346 'ctime',
347 'cuchar',
348 'cwchar',
349 'cwctype',
350 ])
351
352 # Assertion macros. These are defined in base/logging.h and
353 # testing/base/gunit.h. Note that the _M versions need to come first
354 # for substring matching to work.
355 _CHECK_MACROS = [
356 'DCHECK', 'CHECK',
357 'EXPECT_TRUE_M', 'EXPECT_TRUE',
358 'ASSERT_TRUE_M', 'ASSERT_TRUE',
359 'EXPECT_FALSE_M', 'EXPECT_FALSE',
360 'ASSERT_FALSE_M', 'ASSERT_FALSE',
361 ]
362
363 # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
364 _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
365
366 for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
367 ('>=', 'GE'), ('>', 'GT'),
368 ('<=', 'LE'), ('<', 'LT')]:
369 _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
370 _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
371 _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
372 _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
373 _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement
374 _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement
375
376 for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
377 ('>=', 'LT'), ('>', 'LE'),
378 ('<=', 'GT'), ('<', 'GE')]:
379 _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
380 _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
381 _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement
382 _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement
383
384 # Alternative tokens and their replacements. For full list, see section 2.5
385 # Alternative tokens [lex.digraph] in the C++ standard.
386 #
387 # Digraphs (such as '%:') are not included here since it's a mess to
388 # match those on a word boundary.
389 _ALT_TOKEN_REPLACEMENT = {
390 'and': '&&',
391 'bitor': '|',
392 'or': '||',
393 'xor': '^',
394 'compl': '~',
395 'bitand': '&',
396 'and_eq': '&=',
397 'or_eq': '|=',
398 'xor_eq': '^=',
399 'not': '!',
400 'not_eq': '!='
401 }
402
403 # Compile regular expression that matches all the above keywords. The "[ =()]"
404 # bit is meant to avoid matching these keywords outside of boolean expressions.
405 #
406 # False positives include C-style multi-line comments and multi-line strings
407 # but those have always been troublesome for cpplint.
408 _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
409 r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
410
411
412 # These constants define types of headers for use with
413 # _IncludeState.CheckNextIncludeOrder().
414 _C_SYS_HEADER = 1
415 _CPP_SYS_HEADER = 2
416 _LIKELY_MY_HEADER = 3
417 _POSSIBLE_MY_HEADER = 4
418 _OTHER_HEADER = 5
419
420 # These constants define the current inline assembly state
421 _NO_ASM = 0 # Outside of inline assembly block
422 _INSIDE_ASM = 1 # Inside inline assembly block
423 _END_ASM = 2 # Last line of inline assembly block
424 _BLOCK_ASM = 3 # The whole block is an inline assembly block
425
426 # Match start of assembly blocks
427 _MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
428 r'(?:\s+(volatile|__volatile__))?'
429 r'\s*[{(]')
430
431
432 _regexp_compile_cache = {}
433
434 # Finds occurrences of NOLINT or NOLINT(...).
435 _RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?')
436
437 # {str, set(int)}: a map from error categories to sets of linenumbers
438 # on which those errors are expected and should be suppressed.
439 _error_suppressions = {}
440
441 # The root directory used for deriving header guard CPP variable.
442 # This is set by --root flag.
443 _root = None
444
445 # The allowed line length of files.
446 # This is set by --linelength flag.
447 _line_length = 80
448
449 # The allowed extensions for file names
450 # This is set by --extensions flag.
451 _valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
452
453 def ParseNolintSuppressions(filename, raw_line, linenum, error):
454 """Updates the global list of error-suppressions.
455
456 Parses any NOLINT comments on the current line, updating the global
457 error_suppressions store. Reports an error if the NOLINT comment
458 was malformed.
459
460 Args:
461 filename: str, the name of the input file.
462 raw_line: str, the line of input text, with comments.
463 linenum: int, the number of the current line.
464 error: function, an error handler.
465 """
466 # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*).
467 matched = _RE_SUPPRESSION.search(raw_line)
468 if matched:
469 category = matched.group(1)
470 if category in (None, '(*)'): # => "suppress all"
471 _error_suppressions.setdefault(None, set()).add(linenum)
472 else:
473 if category.startswith('(') and category.endswith(')'):
474 category = category[1:-1]
475 if category in _ERROR_CATEGORIES:
476 _error_suppressions.setdefault(category, set()).add(linenum)
477 else:
478 error(filename, linenum, 'readability/nolint', 5,
479 'Unknown NOLINT error category: %s' % category)
480
481
482 def ResetNolintSuppressions():
483 "Resets the set of NOLINT suppressions to empty."
484 _error_suppressions.clear()
485
486
487 def IsErrorSuppressedByNolint(category, linenum):
488 """Returns true if the specified error category is suppressed on this line.
489
490 Consults the global error_suppressions map populated by
491 ParseNolintSuppressions/ResetNolintSuppressions.
492
493 Args:
494 category: str, the category of the error.
495 linenum: int, the current line number.
496 Returns:
497 bool, True iff the error should be suppressed due to a NOLINT comment.
498 """
499 return (linenum in _error_suppressions.get(category, set()) or
500 linenum in _error_suppressions.get(None, set()))
501
502 def Match(pattern, s):
503 """Matches the string with the pattern, caching the compiled regexp."""
504 # The regexp compilation caching is inlined in both Match and Search for
505 # performance reasons; factoring it out into a separate function turns out
506 # to be noticeably expensive.
507 if pattern not in _regexp_compile_cache:
508 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
509 return _regexp_compile_cache[pattern].match(s)
510
511
512 def ReplaceAll(pattern, rep, s):
513 """Replaces instances of pattern in a string with a replacement.
514
515 The compiled regex is kept in a cache shared by Match and Search.
516
517 Args:
518 pattern: regex pattern
519 rep: replacement text
520 s: search string
521
522 Returns:
523 string with replacements made (or original string if no replacements)
524 """
525 if pattern not in _regexp_compile_cache:
526 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
527 return _regexp_compile_cache[pattern].sub(rep, s)
528
529
530 def Search(pattern, s):
531 """Searches the string for the pattern, caching the compiled regexp."""
532 if pattern not in _regexp_compile_cache:
533 _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
534 return _regexp_compile_cache[pattern].search(s)
535
536
537 class _IncludeState(dict):
538 """Tracks line numbers for includes, and the order in which includes appear.
539
540 As a dict, an _IncludeState object serves as a mapping between include
541 filename and line number on which that file was included.
542
543 Call CheckNextIncludeOrder() once for each header in the file, passing
544 in the type constants defined above. Calls in an illegal order will
545 raise an _IncludeError with an appropriate error message.
546
547 """
548 # self._section will move monotonically through this set. If it ever
549 # needs to move backwards, CheckNextIncludeOrder will raise an error.
550 _INITIAL_SECTION = 0
551 _MY_H_SECTION = 1
552 _C_SECTION = 2
553 _CPP_SECTION = 3
554 _OTHER_H_SECTION = 4
555
556 _TYPE_NAMES = {
557 _C_SYS_HEADER: 'C system header',
558 _CPP_SYS_HEADER: 'C++ system header',
559 _LIKELY_MY_HEADER: 'header this file implements',
560 _POSSIBLE_MY_HEADER: 'header this file may implement',
561 _OTHER_HEADER: 'other header',
562 }
563 _SECTION_NAMES = {
564 _INITIAL_SECTION: "... nothing. (This can't be an error.)",
565 _MY_H_SECTION: 'a header this file implements',
566 _C_SECTION: 'C system header',
567 _CPP_SECTION: 'C++ system header',
568 _OTHER_H_SECTION: 'other header',
569 }
570
571 def __init__(self):
572 dict.__init__(self)
573 self.ResetSection()
574
575 def ResetSection(self):
576 # The name of the current section.
577 self._section = self._INITIAL_SECTION
578 # The path of last found header.
579 self._last_header = ''
580
581 def SetLastHeader(self, header_path):
582 self._last_header = header_path
583
584 def CanonicalizeAlphabeticalOrder(self, header_path):
585 """Returns a path canonicalized for alphabetical comparison.
586
587 - replaces "-" with "_" so they both cmp the same.
588 - removes '-inl' since we don't require them to be after the main header.
589 - lowercase everything, just in case.
590
591 Args:
592 header_path: Path to be canonicalized.
593
594 Returns:
595 Canonicalized path.
596 """
597 return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
598
599 def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
600 """Check if a header is in alphabetical order with the previous header.
601
602 Args:
603 clean_lines: A CleansedLines instance containing the file.
604 linenum: The number of the line to check.
605 header_path: Canonicalized header to be checked.
606
607 Returns:
608 Returns true if the header is in alphabetical order.
609 """
610 # If previous section is different from current section, _last_header will
611 # be reset to empty string, so it's always less than current header.
612 #
613 # If previous line was a blank line, assume that the headers are
614 # intentionally sorted the way they are.
615 if (self._last_header > header_path and
616 not Match(r'^\s*$', clean_lines.elided[linenum - 1])):
617 return False
618 return True
619
620 def CheckNextIncludeOrder(self, header_type):
621 """Returns a non-empty error message if the next header is out of order.
622
623 This function also updates the internal state to be ready to check
624 the next include.
625
626 Args:
627 header_type: One of the _XXX_HEADER constants defined above.
628
629 Returns:
630 The empty string if the header is in the right order, or an
631 error message describing what's wrong.
632
633 """
634 error_message = ('Found %s after %s' %
635 (self._TYPE_NAMES[header_type],
636 self._SECTION_NAMES[self._section]))
637
638 last_section = self._section
639
640 if header_type == _C_SYS_HEADER:
641 if self._section <= self._C_SECTION:
642 self._section = self._C_SECTION
643 else:
644 self._last_header = ''
645 return error_message
646 elif header_type == _CPP_SYS_HEADER:
647 if self._section <= self._CPP_SECTION:
648 self._section = self._CPP_SECTION
649 else:
650 self._last_header = ''
651 return error_message
652 elif header_type == _LIKELY_MY_HEADER:
653 if self._section <= self._MY_H_SECTION:
654 self._section = self._MY_H_SECTION
655 else:
656 self._section = self._OTHER_H_SECTION
657 elif header_type == _POSSIBLE_MY_HEADER:
658 if self._section <= self._MY_H_SECTION:
659 self._section = self._MY_H_SECTION
660 else:
661 # This will always be the fallback because we're not sure
662 # enough that the header is associated with this file.
663 self._section = self._OTHER_H_SECTION
664 else:
665 assert header_type == _OTHER_HEADER
666 self._section = self._OTHER_H_SECTION
667
668 if last_section != self._section:
669 self._last_header = ''
670
671 return ''
672
673
674 class _CppLintState(object):
675 """Maintains module-wide state.."""
676
677 def __init__(self):
678 self.verbose_level = 1 # global setting.
679 self.error_count = 0 # global count of reported errors
680 # filters to apply when emitting error messages
681 self.filters = _DEFAULT_FILTERS[:]
682 self.counting = 'total' # In what way are we counting errors?
683 self.errors_by_category = {} # string to int dict storing error counts
684
685 # output format:
686 # "emacs" - format that emacs can parse (default)
687 # "vs7" - format that Microsoft Visual Studio 7 can parse
688 self.output_format = 'emacs'
689
690 def SetOutputFormat(self, output_format):
691 """Sets the output format for errors."""
692 self.output_format = output_format
693
694 def SetVerboseLevel(self, level):
695 """Sets the module's verbosity, and returns the previous setting."""
696 last_verbose_level = self.verbose_level
697 self.verbose_level = level
698 return last_verbose_level
699
700 def SetCountingStyle(self, counting_style):
701 """Sets the module's counting options."""
702 self.counting = counting_style
703
704 def SetFilters(self, filters):
705 """Sets the error-message filters.
706
707 These filters are applied when deciding whether to emit a given
708 error message.
709
710 Args:
711 filters: A string of comma-separated filters (eg "+whitespace/indent").
712 Each filter should start with + or -; else we die.
713
714 Raises:
715 ValueError: The comma-separated filters did not all start with '+' or '-'.
716 E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
717 """
718 # Default filters always have less priority than the flag ones.
719 self.filters = _DEFAULT_FILTERS[:]
720 for filt in filters.split(','):
721 clean_filt = filt.strip()
722 if clean_filt:
723 self.filters.append(clean_filt)
724 for filt in self.filters:
725 if not (filt.startswith('+') or filt.startswith('-')):
726 raise ValueError('Every filter in --filters must start with + or -'
727 ' (%s does not)' % filt)
728
729 def ResetErrorCounts(self):
730 """Sets the module's error statistic back to zero."""
731 self.error_count = 0
732 self.errors_by_category = {}
733
734 def IncrementErrorCount(self, category):
735 """Bumps the module's error statistic."""
736 self.error_count += 1
737 if self.counting in ('toplevel', 'detailed'):
738 if self.counting != 'detailed':
739 category = category.split('/')[0]
740 if category not in self.errors_by_category:
741 self.errors_by_category[category] = 0
742 self.errors_by_category[category] += 1
743
744 def PrintErrorCounts(self):
745 """Print a summary of errors by category, and the total."""
746 for category, count in self.errors_by_category.iteritems():
747 sys.stderr.write('Category \'%s\' errors found: %d\n' %
748 (category, count))
749 sys.stderr.write('Total errors found: %d\n' % self.error_count)
750
751 _cpplint_state = _CppLintState()
752
753
754 def _OutputFormat():
755 """Gets the module's output format."""
756 return _cpplint_state.output_format
757
758
759 def _SetOutputFormat(output_format):
760 """Sets the module's output format."""
761 _cpplint_state.SetOutputFormat(output_format)
762
763
764 def _VerboseLevel():
765 """Returns the module's verbosity setting."""
766 return _cpplint_state.verbose_level
767
768
769 def _SetVerboseLevel(level):
770 """Sets the module's verbosity, and returns the previous setting."""
771 return _cpplint_state.SetVerboseLevel(level)
772
773
774 def _SetCountingStyle(level):
775 """Sets the module's counting options."""
776 _cpplint_state.SetCountingStyle(level)
777
778
779 def _Filters():
780 """Returns the module's list of output filters, as a list."""
781 return _cpplint_state.filters
782
783
784 def _SetFilters(filters):
785 """Sets the module's error-message filters.
786
787 These filters are applied when deciding whether to emit a given
788 error message.
789
790 Args:
791 filters: A string of comma-separated filters (eg "whitespace/indent").
792 Each filter should start with + or -; else we die.
793 """
794 _cpplint_state.SetFilters(filters)
795
796
797 class _FunctionState(object):
798 """Tracks current function name and the number of lines in its body."""
799
800 _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
801 _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
802
803 def __init__(self):
804 self.in_a_function = False
805 self.lines_in_function = 0
806 self.current_function = ''
807
808 def Begin(self, function_name):
809 """Start analyzing function body.
810
811 Args:
812 function_name: The name of the function being tracked.
813 """
814 self.in_a_function = True
815 self.lines_in_function = 0
816 self.current_function = function_name
817
818 def Count(self):
819 """Count line in current function body."""
820 if self.in_a_function:
821 self.lines_in_function += 1
822
823 def Check(self, error, filename, linenum):
824 """Report if too many lines in function body.
825
826 Args:
827 error: The function to call with any errors found.
828 filename: The name of the current file.
829 linenum: The number of the line to check.
830 """
831 if Match(r'T(EST|est)', self.current_function):
832 base_trigger = self._TEST_TRIGGER
833 else:
834 base_trigger = self._NORMAL_TRIGGER
835 trigger = base_trigger * 2**_VerboseLevel()
836
837 if self.lines_in_function > trigger:
838 error_level = int(math.log(self.lines_in_function / base_trigger, 2))
839 # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
840 if error_level > 5:
841 error_level = 5
842 error(filename, linenum, 'readability/fn_size', error_level,
843 'Small and focused functions are preferred:'
844 ' %s has %d non-comment lines'
845 ' (error triggered by exceeding %d lines).' % (
846 self.current_function, self.lines_in_function, trigger))
847
848 def End(self):
849 """Stop analyzing function body."""
850 self.in_a_function = False
851
852
853 class _IncludeError(Exception):
854 """Indicates a problem with the include order in a file."""
855 pass
856
857
858 class FileInfo:
859 """Provides utility functions for filenames.
860
861 FileInfo provides easy access to the components of a file's path
862 relative to the project root.
863 """
864
865 def __init__(self, filename):
866 self._filename = filename
867
868 def FullName(self):
869 """Make Windows paths like Unix."""
870 return os.path.abspath(self._filename).replace('\\', '/')
871
872 def RepositoryName(self):
873 """FullName after removing the local path to the repository.
874
875 If we have a real absolute path name here we can try to do something smart:
876 detecting the root of the checkout and truncating /path/to/checkout from
877 the name so that we get header guards that don't include things like
878 "C:\Documents and Settings\..." or "/home/username/..." in them and thus
879 people on different computers who have checked the source out to different
880 locations won't see bogus errors.
881 """
882 fullname = self.FullName()
883
884 if os.path.exists(fullname):
885 project_dir = os.path.dirname(fullname)
886
887 if os.path.exists(os.path.join(project_dir, ".svn")):
888 # If there's a .svn file in the current directory, we recursively look
889 # up the directory tree for the top of the SVN checkout
890 root_dir = project_dir
891 one_up_dir = os.path.dirname(root_dir)
892 while os.path.exists(os.path.join(one_up_dir, ".svn")):
893 root_dir = os.path.dirname(root_dir)
894 one_up_dir = os.path.dirname(one_up_dir)
895
896 prefix = os.path.commonprefix([root_dir, project_dir])
897 return fullname[len(prefix) + 1:]
898
899 # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
900 # searching up from the current path.
901 root_dir = os.path.dirname(fullname)
902 while (root_dir != os.path.dirname(root_dir) and
903 not os.path.exists(os.path.join(root_dir, ".git")) and
904 not os.path.exists(os.path.join(root_dir, ".hg")) and
905 not os.path.exists(os.path.join(root_dir, ".svn"))):
906 root_dir = os.path.dirname(root_dir)
907
908 if (os.path.exists(os.path.join(root_dir, ".git")) or
909 os.path.exists(os.path.join(root_dir, ".hg")) or
910 os.path.exists(os.path.join(root_dir, ".svn"))):
911 prefix = os.path.commonprefix([root_dir, project_dir])
912 return fullname[len(prefix) + 1:]
913
914 # Don't know what to do; header guard warnings may be wrong...
915 return fullname
916
917 def Split(self):
918 """Splits the file into the directory, basename, and extension.
919
920 For 'chrome/browser/browser.cc', Split() would
921 return ('chrome/browser', 'browser', '.cc')
922
923 Returns:
924 A tuple of (directory, basename, extension).
925 """
926
927 googlename = self.RepositoryName()
928 project, rest = os.path.split(googlename)
929 return (project,) + os.path.splitext(rest)
930
931 def BaseName(self):
932 """File base name - text after the final slash, before the final period."""
933 return self.Split()[1]
934
935 def Extension(self):
936 """File extension - text following the final period."""
937 return self.Split()[2]
938
939 def NoExtension(self):
940 """File has no source file extension."""
941 return '/'.join(self.Split()[0:2])
942
943 def IsSource(self):
944 """File has a source file extension."""
945 return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
946
947
948 def _ShouldPrintError(category, confidence, linenum):
949 """If confidence >= verbose, category passes filter and is not suppressed."""
950
951 # There are three ways we might decide not to print an error message:
952 # a "NOLINT(category)" comment appears in the source,
953 # the verbosity level isn't high enough, or the filters filter it out.
954 if IsErrorSuppressedByNolint(category, linenum):
955 return False
956 if confidence < _cpplint_state.verbose_level:
957 return False
958
959 is_filtered = False
960 for one_filter in _Filters():
961 if one_filter.startswith('-'):
962 if category.startswith(one_filter[1:]):
963 is_filtered = True
964 elif one_filter.startswith('+'):
965 if category.startswith(one_filter[1:]):
966 is_filtered = False
967 else:
968 assert False # should have been checked for in SetFilter.
969 if is_filtered:
970 return False
971
972 return True
973
974
975 def Error(filename, linenum, category, confidence, message):
976 """Logs the fact we've found a lint error.
977
978 We log where the error was found, and also our confidence in the error,
979 that is, how certain we are this is a legitimate style regression, and
980 not a misidentification or a use that's sometimes justified.
981
982 False positives can be suppressed by the use of
983 "cpplint(category)" comments on the offending line. These are
984 parsed into _error_suppressions.
985
986 Args:
987 filename: The name of the file containing the error.
988 linenum: The number of the line containing the error.
989 category: A string used to describe the "category" this bug
990 falls under: "whitespace", say, or "runtime". Categories
991 may have a hierarchy separated by slashes: "whitespace/indent".
992 confidence: A number from 1-5 representing a confidence score for
993 the error, with 5 meaning that we are certain of the problem,
994 and 1 meaning that it could be a legitimate construct.
995 message: The error message.
996 """
997 if _ShouldPrintError(category, confidence, linenum):
998 _cpplint_state.IncrementErrorCount(category)
999 if _cpplint_state.output_format == 'vs7':
1000 sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
1001 filename, linenum, message, category, confidence))
1002 elif _cpplint_state.output_format == 'eclipse':
1003 sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
1004 filename, linenum, message, category, confidence))
1005 else:
1006 sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
1007 filename, linenum, message, category, confidence))
1008
1009
1010 # Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
1011 _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
1012 r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
1013 # Matches strings. Escape codes should already be removed by ESCAPES.
1014 _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"')
1015 # Matches characters. Escape codes should already be removed by ESCAPES.
1016 _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'")
1017 # Matches multi-line C++ comments.
1018 # This RE is a little bit more complicated than one might expect, because we
1019 # have to take care of space removals tools so we can handle comments inside
1020 # statements better.
1021 # The current rule is: We only clear spaces from both sides when we're at the
1022 # end of the line. Otherwise, we try to remove spaces from the right side,
1023 # if this doesn't work we try on left side but only if there's a non-character
1024 # on the right.
1025 _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
1026 r"""(\s*/\*.*\*/\s*$|
1027 /\*.*\*/\s+|
1028 \s+/\*.*\*/(?=\W)|
1029 /\*.*\*/)""", re.VERBOSE)
1030
1031
1032 def IsCppString(line):
1033 """Does line terminate so, that the next symbol is in string constant.
1034
1035 This function does not consider single-line nor multi-line comments.
1036
1037 Args:
1038 line: is a partial line of code starting from the 0..n.
1039
1040 Returns:
1041 True, if next character appended to 'line' is inside a
1042 string constant.
1043 """
1044
1045 line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"
1046 return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
1047
1048
1049 def CleanseRawStrings(raw_lines):
1050 """Removes C++11 raw strings from lines.
1051
1052 Before:
1053 static const char kData[] = R"(
1054 multi-line string
1055 )";
1056
1057 After:
1058 static const char kData[] = ""
1059 (replaced by blank line)
1060 "";
1061
1062 Args:
1063 raw_lines: list of raw lines.
1064
1065 Returns:
1066 list of lines with C++11 raw strings replaced by empty strings.
1067 """
1068
1069 delimiter = None
1070 lines_without_raw_strings = []
1071 for line in raw_lines:
1072 if delimiter:
1073 # Inside a raw string, look for the end
1074 end = line.find(delimiter)
1075 if end >= 0:
1076 # Found the end of the string, match leading space for this
1077 # line and resume copying the original lines, and also insert
1078 # a "" on the last line.
1079 leading_space = Match(r'^(\s*)\S', line)
1080 line = leading_space.group(1) + '""' + line[end + len(delimiter):]
1081 delimiter = None
1082 else:
1083 # Haven't found the end yet, append a blank line.
1084 line = ''
1085
1086 else:
1087 # Look for beginning of a raw string.
1088 # See 2.14.15 [lex.string] for syntax.
1089 matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
1090 if matched:
1091 delimiter = ')' + matched.group(2) + '"'
1092
1093 end = matched.group(3).find(delimiter)
1094 if end >= 0:
1095 # Raw string ended on same line
1096 line = (matched.group(1) + '""' +
1097 matched.group(3)[end + len(delimiter):])
1098 delimiter = None
1099 else:
1100 # Start of a multi-line raw string
1101 line = matched.group(1) + '""'
1102
1103 lines_without_raw_strings.append(line)
1104
1105 # TODO(unknown): if delimiter is not None here, we might want to
1106 # emit a warning for unterminated string.
1107 return lines_without_raw_strings
1108
1109
1110 def FindNextMultiLineCommentStart(lines, lineix):
1111 """Find the beginning marker for a multiline comment."""
1112 while lineix < len(lines):
1113 if lines[lineix].strip().startswith('/*'):
1114 # Only return this marker if the comment goes beyond this line
1115 if lines[lineix].strip().find('*/', 2) < 0:
1116 return lineix
1117 lineix += 1
1118 return len(lines)
1119
1120
1121 def FindNextMultiLineCommentEnd(lines, lineix):
1122 """We are inside a comment, find the end marker."""
1123 while lineix < len(lines):
1124 if lines[lineix].strip().endswith('*/'):
1125 return lineix
1126 lineix += 1
1127 return len(lines)
1128
1129
1130 def RemoveMultiLineCommentsFromRange(lines, begin, end):
1131 """Clears a range of lines for multi-line comments."""
1132 # Having // dummy comments makes the lines non-empty, so we will not get
1133 # unnecessary blank line warnings later in the code.
1134 for i in range(begin, end):
1135 lines[i] = '// dummy'
1136
1137
1138 def RemoveMultiLineComments(filename, lines, error):
1139 """Removes multiline (c-style) comments from lines."""
1140 lineix = 0
1141 while lineix < len(lines):
1142 lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
1143 if lineix_begin >= len(lines):
1144 return
1145 lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
1146 if lineix_end >= len(lines):
1147 error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
1148 'Could not find end of multi-line comment')
1149 return
1150 RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
1151 lineix = lineix_end + 1
1152
1153
1154 def CleanseComments(line):
1155 """Removes //-comments and single-line C-style /* */ comments.
1156
1157 Args:
1158 line: A line of C++ source.
1159
1160 Returns:
1161 The line with single-line comments removed.
1162 """
1163 commentpos = line.find('//')
1164 if commentpos != -1 and not IsCppString(line[:commentpos]):
1165 line = line[:commentpos].rstrip()
1166 # get rid of /* ... */
1167 return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
1168
1169
1170 class CleansedLines(object):
1171 """Holds 3 copies of all lines with different preprocessing applied to them.
1172
1173 1) elided member contains lines without strings and comments,
1174 2) lines member contains lines without comments, and
1175 3) raw_lines member contains all the lines without processing.
1176 All these three members are of <type 'list'>, and of the same length.
1177 """
1178
1179 def __init__(self, lines):
1180 self.elided = []
1181 self.lines = []
1182 self.raw_lines = lines
1183 self.num_lines = len(lines)
1184 self.lines_without_raw_strings = CleanseRawStrings(lines)
1185 for linenum in range(len(self.lines_without_raw_strings)):
1186 self.lines.append(CleanseComments(
1187 self.lines_without_raw_strings[linenum]))
1188 elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
1189 self.elided.append(CleanseComments(elided))
1190
1191 def NumLines(self):
1192 """Returns the number of lines represented."""
1193 return self.num_lines
1194
1195 @staticmethod
1196 def _CollapseStrings(elided):
1197 """Collapses strings and chars on a line to simple "" or '' blocks.
1198
1199 We nix strings first so we're not fooled by text like '"http://"'
1200
1201 Args:
1202 elided: The line being processed.
1203
1204 Returns:
1205 The line with collapsed strings.
1206 """
1207 if not _RE_PATTERN_INCLUDE.match(elided):
1208 # Remove escaped characters first to make quote/single quote collapsing
1209 # basic. Things that look like escaped characters shouldn't occur
1210 # outside of strings and chars.
1211 elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
1212 elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided)
1213 elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided)
1214 return elided
1215
1216
1217 def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
1218 """Find the position just after the matching endchar.
1219
1220 Args:
1221 line: a CleansedLines line.
1222 startpos: start searching at this position.
1223 depth: nesting level at startpos.
1224 startchar: expression opening character.
1225 endchar: expression closing character.
1226
1227 Returns:
1228 On finding matching endchar: (index just after matching endchar, 0)
1229 Otherwise: (-1, new depth at end of this line)
1230 """
1231 for i in xrange(startpos, len(line)):
1232 if line[i] == startchar:
1233 depth += 1
1234 elif line[i] == endchar:
1235 depth -= 1
1236 if depth == 0:
1237 return (i + 1, 0)
1238 return (-1, depth)
1239
1240
1241 def CloseExpression(clean_lines, linenum, pos):
1242 """If input points to ( or { or [ or <, finds the position that closes it.
1243
1244 If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
1245 linenum/pos that correspond to the closing of the expression.
1246
1247 Args:
1248 clean_lines: A CleansedLines instance containing the file.
1249 linenum: The number of the line to check.
1250 pos: A position on the line.
1251
1252 Returns:
1253 A tuple (line, linenum, pos) pointer *past* the closing brace, or
1254 (line, len(lines), -1) if we never find a close. Note we ignore
1255 strings and comments when matching; and the line we return is the
1256 'cleansed' line at linenum.
1257 """
1258
1259 line = clean_lines.elided[linenum]
1260 startchar = line[pos]
1261 if startchar not in '({[<':
1262 return (line, clean_lines.NumLines(), -1)
1263 if startchar == '(': endchar = ')'
1264 if startchar == '[': endchar = ']'
1265 if startchar == '{': endchar = '}'
1266 if startchar == '<': endchar = '>'
1267
1268 # Check first line
1269 (end_pos, num_open) = FindEndOfExpressionInLine(
1270 line, pos, 0, startchar, endchar)
1271 if end_pos > -1:
1272 return (line, linenum, end_pos)
1273
1274 # Continue scanning forward
1275 while linenum < clean_lines.NumLines() - 1:
1276 linenum += 1
1277 line = clean_lines.elided[linenum]
1278 (end_pos, num_open) = FindEndOfExpressionInLine(
1279 line, 0, num_open, startchar, endchar)
1280 if end_pos > -1:
1281 return (line, linenum, end_pos)
1282
1283 # Did not find endchar before end of file, give up
1284 return (line, clean_lines.NumLines(), -1)
1285
1286
1287 def FindStartOfExpressionInLine(line, endpos, depth, startchar, endchar):
1288 """Find position at the matching startchar.
1289
1290 This is almost the reverse of FindEndOfExpressionInLine, but note
1291 that the input position and returned position differs by 1.
1292
1293 Args:
1294 line: a CleansedLines line.
1295 endpos: start searching at this position.
1296 depth: nesting level at endpos.
1297 startchar: expression opening character.
1298 endchar: expression closing character.
1299
1300 Returns:
1301 On finding matching startchar: (index at matching startchar, 0)
1302 Otherwise: (-1, new depth at beginning of this line)
1303 """
1304 for i in xrange(endpos, -1, -1):
1305 if line[i] == endchar:
1306 depth += 1
1307 elif line[i] == startchar:
1308 depth -= 1
1309 if depth == 0:
1310 return (i, 0)
1311 return (-1, depth)
1312
1313
1314 def ReverseCloseExpression(clean_lines, linenum, pos):
1315 """If input points to ) or } or ] or >, finds the position that opens it.
1316
1317 If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
1318 linenum/pos that correspond to the opening of the expression.
1319
1320 Args:
1321 clean_lines: A CleansedLines instance containing the file.
1322 linenum: The number of the line to check.
1323 pos: A position on the line.
1324
1325 Returns:
1326 A tuple (line, linenum, pos) pointer *at* the opening brace, or
1327 (line, 0, -1) if we never find the matching opening brace. Note
1328 we ignore strings and comments when matching; and the line we
1329 return is the 'cleansed' line at linenum.
1330 """
1331 line = clean_lines.elided[linenum]
1332 endchar = line[pos]
1333 if endchar not in ')}]>':
1334 return (line, 0, -1)
1335 if endchar == ')': startchar = '('
1336 if endchar == ']': startchar = '['
1337 if endchar == '}': startchar = '{'
1338 if endchar == '>': startchar = '<'
1339
1340 # Check last line
1341 (start_pos, num_open) = FindStartOfExpressionInLine(
1342 line, pos, 0, startchar, endchar)
1343 if start_pos > -1:
1344 return (line, linenum, start_pos)
1345
1346 # Continue scanning backward
1347 while linenum > 0:
1348 linenum -= 1
1349 line = clean_lines.elided[linenum]
1350 (start_pos, num_open) = FindStartOfExpressionInLine(
1351 line, len(line) - 1, num_open, startchar, endchar)
1352 if start_pos > -1:
1353 return (line, linenum, start_pos)
1354
1355 # Did not find startchar before beginning of file, give up
1356 return (line, 0, -1)
1357
1358
1359 def CheckForCopyright(filename, lines, error):
1360 """Logs an error if no Copyright message appears at the top of the file."""
1361
1362 # We'll say it should occur by line 10. Don't forget there's a
1363 # dummy line at the front.
1364 for line in xrange(1, min(len(lines), 11)):
1365 if re.search(r'Copyright', lines[line], re.I): break
1366 else: # means no copyright line was found
1367 error(filename, 0, 'legal/copyright', 5,
1368 'No copyright message found. '
1369 'You should have a line: "Copyright [year] <Copyright Owner>"')
1370
1371
1372 def GetHeaderGuardCPPVariable(filename):
1373 """Returns the CPP variable that should be used as a header guard.
1374
1375 Args:
1376 filename: The name of a C++ header file.
1377
1378 Returns:
1379 The CPP variable that should be used as a header guard in the
1380 named file.
1381
1382 """
1383
1384 # Restores original filename in case that cpplint is invoked from Emacs's
1385 # flymake.
1386 filename = re.sub(r'_flymake\.h$', '.h', filename)
1387 filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
1388
1389 fileinfo = FileInfo(filename)
1390 file_path_from_root = fileinfo.RepositoryName()
1391 if _root:
1392 file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root)
1393 return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_'
1394
1395
1396 def CheckForHeaderGuard(filename, lines, error):
1397 """Checks that the file contains a header guard.
1398
1399 Logs an error if no #ifndef header guard is present. For other
1400 headers, checks that the full pathname is used.
1401
1402 Args:
1403 filename: The name of the C++ header file.
1404 lines: An array of strings, each representing a line of the file.
1405 error: The function to call with any errors found.
1406 """
1407
1408 cppvar = GetHeaderGuardCPPVariable(filename)
1409
1410 ifndef = None
1411 ifndef_linenum = 0
1412 define = None
1413 endif = None
1414 endif_linenum = 0
1415 for linenum, line in enumerate(lines):
1416 linesplit = line.split()
1417 if len(linesplit) >= 2:
1418 # find the first occurrence of #ifndef and #define, save arg
1419 if not ifndef and linesplit[0] == '#ifndef':
1420 # set ifndef to the header guard presented on the #ifndef line.
1421 ifndef = linesplit[1]
1422 ifndef_linenum = linenum
1423 if not define and linesplit[0] == '#define':
1424 define = linesplit[1]
1425 # find the last occurrence of #endif, save entire line
1426 if line.startswith('#endif'):
1427 endif = line
1428 endif_linenum = linenum
1429
1430 if not ifndef:
1431 error(filename, 0, 'build/header_guard', 5,
1432 'No #ifndef header guard found, suggested CPP variable is: %s' %
1433 cppvar)
1434 return
1435
1436 if not define:
1437 error(filename, 0, 'build/header_guard', 5,
1438 'No #define header guard found, suggested CPP variable is: %s' %
1439 cppvar)
1440 return
1441
1442 # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
1443 # for backward compatibility.
1444 if ifndef != cppvar:
1445 error_level = 0
1446 if ifndef != cppvar + '_':
1447 error_level = 5
1448
1449 ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
1450 error)
1451 error(filename, ifndef_linenum, 'build/header_guard', error_level,
1452 '#ifndef header guard has wrong style, please use: %s' % cppvar)
1453
1454 if define != ifndef:
1455 error(filename, 0, 'build/header_guard', 5,
1456 '#ifndef and #define don\'t match, suggested CPP variable is: %s' %
1457 cppvar)
1458 return
1459
1460 if endif != ('#endif // %s' % cppvar):
1461 error_level = 0
1462 if endif != ('#endif // %s' % (cppvar + '_')):
1463 error_level = 5
1464
1465 ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
1466 error)
1467 error(filename, endif_linenum, 'build/header_guard', error_level,
1468 '#endif line should be "#endif // %s"' % cppvar)
1469
1470
1471 def CheckForBadCharacters(filename, lines, error):
1472 """Logs an error for each line containing bad characters.
1473
1474 Two kinds of bad characters:
1475
1476 1. Unicode replacement characters: These indicate that either the file
1477 contained invalid UTF-8 (likely) or Unicode replacement characters (which
1478 it shouldn't). Note that it's possible for this to throw off line
1479 numbering if the invalid UTF-8 occurred adjacent to a newline.
1480
1481 2. NUL bytes. These are problematic for some tools.
1482
1483 Args:
1484 filename: The name of the current file.
1485 lines: An array of strings, each representing a line of the file.
1486 error: The function to call with any errors found.
1487 """
1488 for linenum, line in enumerate(lines):
1489 if u'\ufffd' in line:
1490 error(filename, linenum, 'readability/utf8', 5,
1491 'Line contains invalid UTF-8 (or Unicode replacement character).')
1492 if '\0' in line:
1493 error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
1494
1495
1496 def CheckForNewlineAtEOF(filename, lines, error):
1497 """Logs an error if there is no newline char at the end of the file.
1498
1499 Args:
1500 filename: The name of the current file.
1501 lines: An array of strings, each representing a line of the file.
1502 error: The function to call with any errors found.
1503 """
1504
1505 # The array lines() was created by adding two newlines to the
1506 # original file (go figure), then splitting on \n.
1507 # To verify that the file ends in \n, we just have to make sure the
1508 # last-but-two element of lines() exists and is empty.
1509 if len(lines) < 3 or lines[-2]:
1510 error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
1511 'Could not find a newline character at the end of the file.')
1512
1513
1514 def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
1515 """Logs an error if we see /* ... */ or "..." that extend past one line.
1516
1517 /* ... */ comments are legit inside macros, for one line.
1518 Otherwise, we prefer // comments, so it's ok to warn about the
1519 other. Likewise, it's ok for strings to extend across multiple
1520 lines, as long as a line continuation character (backslash)
1521 terminates each line. Although not currently prohibited by the C++
1522 style guide, it's ugly and unnecessary. We don't do well with either
1523 in this lint program, so we warn about both.
1524
1525 Args:
1526 filename: The name of the current file.
1527 clean_lines: A CleansedLines instance containing the file.
1528 linenum: The number of the line to check.
1529 error: The function to call with any errors found.
1530 """
1531 line = clean_lines.elided[linenum]
1532
1533 # Remove all \\ (escaped backslashes) from the line. They are OK, and the
1534 # second (escaped) slash may trigger later \" detection erroneously.
1535 line = line.replace('\\\\', '')
1536
1537 if line.count('/*') > line.count('*/'):
1538 error(filename, linenum, 'readability/multiline_comment', 5,
1539 'Complex multi-line /*...*/-style comment found. '
1540 'Lint may give bogus warnings. '
1541 'Consider replacing these with //-style comments, '
1542 'with #if 0...#endif, '
1543 'or with more clearly structured multi-line comments.')
1544
1545 if (line.count('"') - line.count('\\"')) % 2:
1546 error(filename, linenum, 'readability/multiline_string', 5,
1547 'Multi-line string ("...") found. This lint script doesn\'t '
1548 'do well with such strings, and may give bogus warnings. '
1549 'Use C++11 raw strings or concatenation instead.')
1550
1551
1552 threading_list = (
1553 ('asctime(', 'asctime_r('),
1554 ('ctime(', 'ctime_r('),
1555 ('getgrgid(', 'getgrgid_r('),
1556 ('getgrnam(', 'getgrnam_r('),
1557 ('getlogin(', 'getlogin_r('),
1558 ('getpwnam(', 'getpwnam_r('),
1559 ('getpwuid(', 'getpwuid_r('),
1560 ('gmtime(', 'gmtime_r('),
1561 ('localtime(', 'localtime_r('),
1562 ('rand(', 'rand_r('),
1563 ('strtok(', 'strtok_r('),
1564 ('ttyname(', 'ttyname_r('),
1565 )
1566
1567
1568 def CheckPosixThreading(filename, clean_lines, linenum, error):
1569 """Checks for calls to thread-unsafe functions.
1570
1571 Much code has been originally written without consideration of
1572 multi-threading. Also, engineers are relying on their old experience;
1573 they have learned posix before threading extensions were added. These
1574 tests guide the engineers to use thread-safe functions (when using
1575 posix directly).
1576
1577 Args:
1578 filename: The name of the current file.
1579 clean_lines: A CleansedLines instance containing the file.
1580 linenum: The number of the line to check.
1581 error: The function to call with any errors found.
1582 """
1583 line = clean_lines.elided[linenum]
1584 for single_thread_function, multithread_safe_function in threading_list:
1585 ix = line.find(single_thread_function)
1586 # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
1587 if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and
1588 line[ix - 1] not in ('_', '.', '>'))):
1589 error(filename, linenum, 'runtime/threadsafe_fn', 2,
1590 'Consider using ' + multithread_safe_function +
1591 '...) instead of ' + single_thread_function +
1592 '...) for improved thread safety.')
1593
1594
1595 def CheckVlogArguments(filename, clean_lines, linenum, error):
1596 """Checks that VLOG() is only used for defining a logging level.
1597
1598 For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
1599 VLOG(FATAL) are not.
1600
1601 Args:
1602 filename: The name of the current file.
1603 clean_lines: A CleansedLines instance containing the file.
1604 linenum: The number of the line to check.
1605 error: The function to call with any errors found.
1606 """
1607 line = clean_lines.elided[linenum]
1608 if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
1609 error(filename, linenum, 'runtime/vlog', 5,
1610 'VLOG() should be used with numeric verbosity level. '
1611 'Use LOG() if you want symbolic severity levels.')
1612
1613
1614 # Matches invalid increment: *count++, which moves pointer instead of
1615 # incrementing a value.
1616 _RE_PATTERN_INVALID_INCREMENT = re.compile(
1617 r'^\s*\*\w+(\+\+|--);')
1618
1619
1620 def CheckInvalidIncrement(filename, clean_lines, linenum, error):
1621 """Checks for invalid increment *count++.
1622
1623 For example following function:
1624 void increment_counter(int* count) {
1625 *count++;
1626 }
1627 is invalid, because it effectively does count++, moving pointer, and should
1628 be replaced with ++*count, (*count)++ or *count += 1.
1629
1630 Args:
1631 filename: The name of the current file.
1632 clean_lines: A CleansedLines instance containing the file.
1633 linenum: The number of the line to check.
1634 error: The function to call with any errors found.
1635 """
1636 line = clean_lines.elided[linenum]
1637 if _RE_PATTERN_INVALID_INCREMENT.match(line):
1638 error(filename, linenum, 'runtime/invalid_increment', 5,
1639 'Changing pointer instead of value (or unused value of operator*).')
1640
1641
1642 class _BlockInfo(object):
1643 """Stores information about a generic block of code."""
1644
1645 def __init__(self, seen_open_brace):
1646 self.seen_open_brace = seen_open_brace
1647 self.open_parentheses = 0
1648 self.inline_asm = _NO_ASM
1649
1650 def CheckBegin(self, filename, clean_lines, linenum, error):
1651 """Run checks that applies to text up to the opening brace.
1652
1653 This is mostly for checking the text after the class identifier
1654 and the "{", usually where the base class is specified. For other
1655 blocks, there isn't much to check, so we always pass.
1656
1657 Args:
1658 filename: The name of the current file.
1659 clean_lines: A CleansedLines instance containing the file.
1660 linenum: The number of the line to check.
1661 error: The function to call with any errors found.
1662 """
1663 pass
1664
1665 def CheckEnd(self, filename, clean_lines, linenum, error):
1666 """Run checks that applies to text after the closing brace.
1667
1668 This is mostly used for checking end of namespace comments.
1669
1670 Args:
1671 filename: The name of the current file.
1672 clean_lines: A CleansedLines instance containing the file.
1673 linenum: The number of the line to check.
1674 error: The function to call with any errors found.
1675 """
1676 pass
1677
1678
1679 class _ClassInfo(_BlockInfo):
1680 """Stores information about a class."""
1681
1682 def __init__(self, name, class_or_struct, clean_lines, linenum):
1683 _BlockInfo.__init__(self, False)
1684 self.name = name
1685 self.starting_linenum = linenum
1686 self.is_derived = False
1687 if class_or_struct == 'struct':
1688 self.access = 'public'
1689 self.is_struct = True
1690 else:
1691 self.access = 'private'
1692 self.is_struct = False
1693
1694 # Remember initial indentation level for this class. Using raw_lines here
1695 # instead of elided to account for leading comments.
1696 initial_indent = Match(r'^( *)\S', clean_lines.raw_lines[linenum])
1697 if initial_indent:
1698 self.class_indent = len(initial_indent.group(1))
1699 else:
1700 self.class_indent = 0
1701
1702 # Try to find the end of the class. This will be confused by things like:
1703 # class A {
1704 # } *x = { ...
1705 #
1706 # But it's still good enough for CheckSectionSpacing.
1707 self.last_line = 0
1708 depth = 0
1709 for i in range(linenum, clean_lines.NumLines()):
1710 line = clean_lines.elided[i]
1711 depth += line.count('{') - line.count('}')
1712 if not depth:
1713 self.last_line = i
1714 break
1715
1716 def CheckBegin(self, filename, clean_lines, linenum, error):
1717 # Look for a bare ':'
1718 if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
1719 self.is_derived = True
1720
1721 def CheckEnd(self, filename, clean_lines, linenum, error):
1722 # Check that closing brace is aligned with beginning of the class.
1723 # Only do this if the closing brace is indented by only whitespaces.
1724 # This means we will not check single-line class definitions.
1725 indent = Match(r'^( *)\}', clean_lines.elided[linenum])
1726 if indent and len(indent.group(1)) != self.class_indent:
1727 if self.is_struct:
1728 parent = 'struct ' + self.name
1729 else:
1730 parent = 'class ' + self.name
1731 error(filename, linenum, 'whitespace/indent', 3,
1732 'Closing brace should be aligned with beginning of %s' % parent)
1733
1734
1735 class _NamespaceInfo(_BlockInfo):
1736 """Stores information about a namespace."""
1737
1738 def __init__(self, name, linenum):
1739 _BlockInfo.__init__(self, False)
1740 self.name = name or ''
1741 self.starting_linenum = linenum
1742
1743 def CheckEnd(self, filename, clean_lines, linenum, error):
1744 """Check end of namespace comments."""
1745 line = clean_lines.raw_lines[linenum]
1746
1747 # Check how many lines is enclosed in this namespace. Don't issue
1748 # warning for missing namespace comments if there aren't enough
1749 # lines. However, do apply checks if there is already an end of
1750 # namespace comment and it's incorrect.
1751 #
1752 # TODO(unknown): We always want to check end of namespace comments
1753 # if a namespace is large, but sometimes we also want to apply the
1754 # check if a short namespace contained nontrivial things (something
1755 # other than forward declarations). There is currently no logic on
1756 # deciding what these nontrivial things are, so this check is
1757 # triggered by namespace size only, which works most of the time.
1758 if (linenum - self.starting_linenum < 10
1759 and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)):
1760 return
1761
1762 # Look for matching comment at end of namespace.
1763 #
1764 # Note that we accept C style "/* */" comments for terminating
1765 # namespaces, so that code that terminate namespaces inside
1766 # preprocessor macros can be cpplint clean.
1767 #
1768 # We also accept stuff like "// end of namespace <name>." with the
1769 # period at the end.
1770 #
1771 # Besides these, we don't accept anything else, otherwise we might
1772 # get false negatives when existing comment is a substring of the
1773 # expected namespace.
1774 if self.name:
1775 # Named namespace
1776 if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) +
1777 r'[\*/\.\\\s]*$'),
1778 line):
1779 error(filename, linenum, 'readability/namespace', 5,
1780 'Namespace should be terminated with "// namespace %s"' %
1781 self.name)
1782 else:
1783 # Anonymous namespace
1784 if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):
1785 error(filename, linenum, 'readability/namespace', 5,
1786 'Namespace should be terminated with "// namespace"')
1787
1788
1789 class _PreprocessorInfo(object):
1790 """Stores checkpoints of nesting stacks when #if/#else is seen."""
1791
1792 def __init__(self, stack_before_if):
1793 # The entire nesting stack before #if
1794 self.stack_before_if = stack_before_if
1795
1796 # The entire nesting stack up to #else
1797 self.stack_before_else = []
1798
1799 # Whether we have already seen #else or #elif
1800 self.seen_else = False
1801
1802
1803 class _NestingState(object):
1804 """Holds states related to parsing braces."""
1805
1806 def __init__(self):
1807 # Stack for tracking all braces. An object is pushed whenever we
1808 # see a "{", and popped when we see a "}". Only 3 types of
1809 # objects are possible:
1810 # - _ClassInfo: a class or struct.
1811 # - _NamespaceInfo: a namespace.
1812 # - _BlockInfo: some other type of block.
1813 self.stack = []
1814
1815 # Stack of _PreprocessorInfo objects.
1816 self.pp_stack = []
1817
1818 def SeenOpenBrace(self):
1819 """Check if we have seen the opening brace for the innermost block.
1820
1821 Returns:
1822 True if we have seen the opening brace, False if the innermost
1823 block is still expecting an opening brace.
1824 """
1825 return (not self.stack) or self.stack[-1].seen_open_brace
1826
1827 def InNamespaceBody(self):
1828 """Check if we are currently one level inside a namespace body.
1829
1830 Returns:
1831 True if top of the stack is a namespace block, False otherwise.
1832 """
1833 return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
1834
1835 def UpdatePreprocessor(self, line):
1836 """Update preprocessor stack.
1837
1838 We need to handle preprocessors due to classes like this:
1839 #ifdef SWIG
1840 struct ResultDetailsPageElementExtensionPoint {
1841 #else
1842 struct ResultDetailsPageElementExtensionPoint : public Extension {
1843 #endif
1844
1845 We make the following assumptions (good enough for most files):
1846 - Preprocessor condition evaluates to true from #if up to first
1847 #else/#elif/#endif.
1848
1849 - Preprocessor condition evaluates to false from #else/#elif up
1850 to #endif. We still perform lint checks on these lines, but
1851 these do not affect nesting stack.
1852
1853 Args:
1854 line: current line to check.
1855 """
1856 if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
1857 # Beginning of #if block, save the nesting stack here. The saved
1858 # stack will allow us to restore the parsing state in the #else case.
1859 self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
1860 elif Match(r'^\s*#\s*(else|elif)\b', line):
1861 # Beginning of #else block
1862 if self.pp_stack:
1863 if not self.pp_stack[-1].seen_else:
1864 # This is the first #else or #elif block. Remember the
1865 # whole nesting stack up to this point. This is what we
1866 # keep after the #endif.
1867 self.pp_stack[-1].seen_else = True
1868 self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
1869
1870 # Restore the stack to how it was before the #if
1871 self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
1872 else:
1873 # TODO(unknown): unexpected #else, issue warning?
1874 pass
1875 elif Match(r'^\s*#\s*endif\b', line):
1876 # End of #if or #else blocks.
1877 if self.pp_stack:
1878 # If we saw an #else, we will need to restore the nesting
1879 # stack to its former state before the #else, otherwise we
1880 # will just continue from where we left off.
1881 if self.pp_stack[-1].seen_else:
1882 # Here we can just use a shallow copy since we are the last
1883 # reference to it.
1884 self.stack = self.pp_stack[-1].stack_before_else
1885 # Drop the corresponding #if
1886 self.pp_stack.pop()
1887 else:
1888 # TODO(unknown): unexpected #endif, issue warning?
1889 pass
1890
1891 def Update(self, filename, clean_lines, linenum, error):
1892 """Update nesting state with current line.
1893
1894 Args:
1895 filename: The name of the current file.
1896 clean_lines: A CleansedLines instance containing the file.
1897 linenum: The number of the line to check.
1898 error: The function to call with any errors found.
1899 """
1900 line = clean_lines.elided[linenum]
1901
1902 # Update pp_stack first
1903 self.UpdatePreprocessor(line)
1904
1905 # Count parentheses. This is to avoid adding struct arguments to
1906 # the nesting stack.
1907 if self.stack:
1908 inner_block = self.stack[-1]
1909 depth_change = line.count('(') - line.count(')')
1910 inner_block.open_parentheses += depth_change
1911
1912 # Also check if we are starting or ending an inline assembly block.
1913 if inner_block.inline_asm in (_NO_ASM, _END_ASM):
1914 if (depth_change != 0 and
1915 inner_block.open_parentheses == 1 and
1916 _MATCH_ASM.match(line)):
1917 # Enter assembly block
1918 inner_block.inline_asm = _INSIDE_ASM
1919 else:
1920 # Not entering assembly block. If previous line was _END_ASM,
1921 # we will now shift to _NO_ASM state.
1922 inner_block.inline_asm = _NO_ASM
1923 elif (inner_block.inline_asm == _INSIDE_ASM and
1924 inner_block.open_parentheses == 0):
1925 # Exit assembly block
1926 inner_block.inline_asm = _END_ASM
1927
1928 # Consume namespace declaration at the beginning of the line. Do
1929 # this in a loop so that we catch same line declarations like this:
1930 # namespace proto2 { namespace bridge { class MessageSet; } }
1931 while True:
1932 # Match start of namespace. The "\b\s*" below catches namespace
1933 # declarations even if it weren't followed by a whitespace, this
1934 # is so that we don't confuse our namespace checker. The
1935 # missing spaces will be flagged by CheckSpacing.
1936 namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)
1937 if not namespace_decl_match:
1938 break
1939
1940 new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)
1941 self.stack.append(new_namespace)
1942
1943 line = namespace_decl_match.group(2)
1944 if line.find('{') != -1:
1945 new_namespace.seen_open_brace = True
1946 line = line[line.find('{') + 1:]
1947
1948 # Look for a class declaration in whatever is left of the line
1949 # after parsing namespaces. The regexp accounts for decorated classes
1950 # such as in:
1951 # class LOCKABLE API Object {
1952 # };
1953 #
1954 # Templates with class arguments may confuse the parser, for example:
1955 # template <class T
1956 # class Comparator = less<T>,
1957 # class Vector = vector<T> >
1958 # class HeapQueue {
1959 #
1960 # Because this parser has no nesting state about templates, by the
1961 # time it saw "class Comparator", it may think that it's a new class.
1962 # Nested templates have a similar problem:
1963 # template <
1964 # typename ExportedType,
1965 # typename TupleType,
1966 # template <typename, typename> class ImplTemplate>
1967 #
1968 # To avoid these cases, we ignore classes that are followed by '=' or '>'
1969 class_decl_match = Match(
1970 r'\s*(template\s*<[\w\s<>,:]*>\s*)?'
1971 r'(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)'
1972 r'(([^=>]|<[^<>]*>|<[^<>]*<[^<>]*>\s*>)*)$', line)
1973 if (class_decl_match and
1974 (not self.stack or self.stack[-1].open_parentheses == 0)):
1975 self.stack.append(_ClassInfo(
1976 class_decl_match.group(4), class_decl_match.group(2),
1977 clean_lines, linenum))
1978 line = class_decl_match.group(5)
1979
1980 # If we have not yet seen the opening brace for the innermost block,
1981 # run checks here.
1982 if not self.SeenOpenBrace():
1983 self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
1984
1985 # Update access control if we are inside a class/struct
1986 if self.stack and isinstance(self.stack[-1], _ClassInfo):
1987 classinfo = self.stack[-1]
1988 access_match = Match(
1989 r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
1990 r':(?:[^:]|$)',
1991 line)
1992 if access_match:
1993 classinfo.access = access_match.group(2)
1994
1995 # Check that access keywords are indented +1 space. Skip this
1996 # check if the keywords are not preceded by whitespaces.
1997 indent = access_match.group(1)
1998 if (len(indent) != classinfo.class_indent + 1 and
1999 Match(r'^\s*$', indent)):
2000 if classinfo.is_struct:
2001 parent = 'struct ' + classinfo.name
2002 else:
2003 parent = 'class ' + classinfo.name
2004 slots = ''
2005 if access_match.group(3):
2006 slots = access_match.group(3)
2007 error(filename, linenum, 'whitespace/indent', 3,
2008 '%s%s: should be indented +1 space inside %s' % (
2009 access_match.group(2), slots, parent))
2010
2011 # Consume braces or semicolons from what's left of the line
2012 while True:
2013 # Match first brace, semicolon, or closed parenthesis.
2014 matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)
2015 if not matched:
2016 break
2017
2018 token = matched.group(1)
2019 if token == '{':
2020 # If namespace or class hasn't seen a opening brace yet, mark
2021 # namespace/class head as complete. Push a new block onto the
2022 # stack otherwise.
2023 if not self.SeenOpenBrace():
2024 self.stack[-1].seen_open_brace = True
2025 else:
2026 self.stack.append(_BlockInfo(True))
2027 if _MATCH_ASM.match(line):
2028 self.stack[-1].inline_asm = _BLOCK_ASM
2029 elif token == ';' or token == ')':
2030 # If we haven't seen an opening brace yet, but we already saw
2031 # a semicolon, this is probably a forward declaration. Pop
2032 # the stack for these.
2033 #
2034 # Similarly, if we haven't seen an opening brace yet, but we
2035 # already saw a closing parenthesis, then these are probably
2036 # function arguments with extra "class" or "struct" keywords.
2037 # Also pop these stack for these.
2038 if not self.SeenOpenBrace():
2039 self.stack.pop()
2040 else: # token == '}'
2041 # Perform end of block checks and pop the stack.
2042 if self.stack:
2043 self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
2044 self.stack.pop()
2045 line = matched.group(2)
2046
2047 def InnermostClass(self):
2048 """Get class info on the top of the stack.
2049
2050 Returns:
2051 A _ClassInfo object if we are inside a class, or None otherwise.
2052 """
2053 for i in range(len(self.stack), 0, -1):
2054 classinfo = self.stack[i - 1]
2055 if isinstance(classinfo, _ClassInfo):
2056 return classinfo
2057 return None
2058
2059 def CheckCompletedBlocks(self, filename, error):
2060 """Checks that all classes and namespaces have been completely parsed.
2061
2062 Call this when all lines in a file have been processed.
2063 Args:
2064 filename: The name of the current file.
2065 error: The function to call with any errors found.
2066 """
2067 # Note: This test can result in false positives if #ifdef constructs
2068 # get in the way of brace matching. See the testBuildClass test in
2069 # cpplint_unittest.py for an example of this.
2070 for obj in self.stack:
2071 if isinstance(obj, _ClassInfo):
2072 error(filename, obj.starting_linenum, 'build/class', 5,
2073 'Failed to find complete declaration of class %s' %
2074 obj.name)
2075 elif isinstance(obj, _NamespaceInfo):
2076 error(filename, obj.starting_linenum, 'build/namespaces', 5,
2077 'Failed to find complete declaration of namespace %s' %
2078 obj.name)
2079
2080
2081 def CheckForNonStandardConstructs(filename, clean_lines, linenum,
2082 nesting_state, error):
2083 r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
2084
2085 Complain about several constructs which gcc-2 accepts, but which are
2086 not standard C++. Warning about these in lint is one way to ease the
2087 transition to new compilers.
2088 - put storage class first (e.g. "static const" instead of "const static").
2089 - "%lld" instead of %qd" in printf-type functions.
2090 - "%1$d" is non-standard in printf-type functions.
2091 - "\%" is an undefined character escape sequence.
2092 - text after #endif is not allowed.
2093 - invalid inner-style forward declaration.
2094 - >? and <? operators, and their >?= and <?= cousins.
2095
2096 Additionally, check for constructor/destructor style violations and reference
2097 members, as it is very convenient to do so while checking for
2098 gcc-2 compliance.
2099
2100 Args:
2101 filename: The name of the current file.
2102 clean_lines: A CleansedLines instance containing the file.
2103 linenum: The number of the line to check.
2104 nesting_state: A _NestingState instance which maintains information about
2105 the current stack of nested blocks being parsed.
2106 error: A callable to which errors are reported, which takes 4 arguments:
2107 filename, line number, error level, and message
2108 """
2109
2110 # Remove comments from the line, but leave in strings for now.
2111 line = clean_lines.lines[linenum]
2112
2113 if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
2114 error(filename, linenum, 'runtime/printf_format', 3,
2115 '%q in format strings is deprecated. Use %ll instead.')
2116
2117 if Search(r'printf\s*\(.*".*%\d+\$', line):
2118 error(filename, linenum, 'runtime/printf_format', 2,
2119 '%N$ formats are unconventional. Try rewriting to avoid them.')
2120
2121 # Remove escaped backslashes before looking for undefined escapes.
2122 line = line.replace('\\\\', '')
2123
2124 if Search(r'("|\').*\\(%|\[|\(|{)', line):
2125 error(filename, linenum, 'build/printf_format', 3,
2126 '%, [, (, and { are undefined character escapes. Unescape them.')
2127
2128 # For the rest, work with both comments and strings removed.
2129 line = clean_lines.elided[linenum]
2130
2131 if Search(r'\b(const|volatile|void|char|short|int|long'
2132 r'|float|double|signed|unsigned'
2133 r'|schar|u?int8|u?int16|u?int32|u?int64)'
2134 r'\s+(register|static|extern|typedef)\b',
2135 line):
2136 error(filename, linenum, 'build/storage_class', 5,
2137 'Storage class (static, extern, typedef, etc) should be first.')
2138
2139 if Match(r'\s*#\s*endif\s*[^/\s]+', line):
2140 error(filename, linenum, 'build/endif_comment', 5,
2141 'Uncommented text after #endif is non-standard. Use a comment.')
2142
2143 if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
2144 error(filename, linenum, 'build/forward_decl', 5,
2145 'Inner-style forward declarations are invalid. Remove this line.')
2146
2147 if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
2148 line):
2149 error(filename, linenum, 'build/deprecated', 3,
2150 '>? and <? (max and min) operators are non-standard and deprecated.')
2151
2152 if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
2153 # TODO(unknown): Could it be expanded safely to arbitrary references,
2154 # without triggering too many false positives? The first
2155 # attempt triggered 5 warnings for mostly benign code in the regtest, hence
2156 # the restriction.
2157 # Here's the original regexp, for the reference:
2158 # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'
2159 # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'
2160 error(filename, linenum, 'runtime/member_string_references', 2,
2161 'const string& members are dangerous. It is much better to use '
2162 'alternatives, such as pointers or simple constants.')
2163
2164 # Everything else in this function operates on class declarations.
2165 # Return early if the top of the nesting stack is not a class, or if
2166 # the class head is not completed yet.
2167 classinfo = nesting_state.InnermostClass()
2168 if not classinfo or not classinfo.seen_open_brace:
2169 return
2170
2171 # The class may have been declared with namespace or classname qualifiers.
2172 # The constructor and destructor will not have those qualifiers.
2173 base_classname = classinfo.name.split('::')[-1]
2174
2175 # Look for single-argument constructors that aren't marked explicit.
2176 # Technically a valid construct, but against style.
2177 args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)'
2178 % re.escape(base_classname),
2179 line)
2180 if (args and
2181 args.group(1) != 'void' and
2182 not args.group(1).startswith('std::initializer_list<') and
2183 not Match(r'(const\s+)?%s(\s+const)?\s*(?:<\w+>\s*)?&'
2184 % re.escape(base_classname), args.group(1).strip())):
2185 error(filename, linenum, 'runtime/explicit', 5,
2186 'Single-argument constructors should be marked explicit.')
2187
2188
2189 def CheckSpacingForFunctionCall(filename, line, linenum, error):
2190 """Checks for the correctness of various spacing around function calls.
2191
2192 Args:
2193 filename: The name of the current file.
2194 line: The text of the line to check.
2195 linenum: The number of the line to check.
2196 error: The function to call with any errors found.
2197 """
2198
2199 # Since function calls often occur inside if/for/while/switch
2200 # expressions - which have their own, more liberal conventions - we
2201 # first see if we should be looking inside such an expression for a
2202 # function call, to which we can apply more strict standards.
2203 fncall = line # if there's no control flow construct, look at whole line
2204 for pattern in (r'\bif\s*\((.*)\)\s*{',
2205 r'\bfor\s*\((.*)\)\s*{',
2206 r'\bwhile\s*\((.*)\)\s*[{;]',
2207 r'\bswitch\s*\((.*)\)\s*{'):
2208 match = Search(pattern, line)
2209 if match:
2210 fncall = match.group(1) # look inside the parens for function calls
2211 break
2212
2213 # Except in if/for/while/switch, there should never be space
2214 # immediately inside parens (eg "f( 3, 4 )"). We make an exception
2215 # for nested parens ( (a+b) + c ). Likewise, there should never be
2216 # a space before a ( when it's a function argument. I assume it's a
2217 # function argument when the char before the whitespace is legal in
2218 # a function name (alnum + _) and we're not starting a macro. Also ignore
2219 # pointers and references to arrays and functions coz they're too tricky:
2220 # we use a very simple way to recognize these:
2221 # " (something)(maybe-something)" or
2222 # " (something)(maybe-something," or
2223 # " (something)[something]"
2224 # Note that we assume the contents of [] to be short enough that
2225 # they'll never need to wrap.
2226 if ( # Ignore control structures.
2227 not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
2228 fncall) and
2229 # Ignore pointers/references to functions.
2230 not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
2231 # Ignore pointers/references to arrays.
2232 not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
2233 if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call
2234 error(filename, linenum, 'whitespace/parens', 4,
2235 'Extra space after ( in function call')
2236 elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):
2237 error(filename, linenum, 'whitespace/parens', 2,
2238 'Extra space after (')
2239 if (Search(r'\w\s+\(', fncall) and
2240 not Search(r'#\s*define|typedef', fncall) and
2241 not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)):
2242 error(filename, linenum, 'whitespace/parens', 4,
2243 'Extra space before ( in function call')
2244 # If the ) is followed only by a newline or a { + newline, assume it's
2245 # part of a control statement (if/while/etc), and don't complain
2246 if Search(r'[^)]\s+\)\s*[^{\s]', fncall):
2247 # If the closing parenthesis is preceded by only whitespaces,
2248 # try to give a more descriptive error message.
2249 if Search(r'^\s+\)', fncall):
2250 error(filename, linenum, 'whitespace/parens', 2,
2251 'Closing ) should be moved to the previous line')
2252 else:
2253 error(filename, linenum, 'whitespace/parens', 2,
2254 'Extra space before )')
2255
2256
2257 def IsBlankLine(line):
2258 """Returns true if the given line is blank.
2259
2260 We consider a line to be blank if the line is empty or consists of
2261 only white spaces.
2262
2263 Args:
2264 line: A line of a string.
2265
2266 Returns:
2267 True, if the given line is blank.
2268 """
2269 return not line or line.isspace()
2270
2271
2272 def CheckForFunctionLengths(filename, clean_lines, linenum,
2273 function_state, error):
2274 """Reports for long function bodies.
2275
2276 For an overview why this is done, see:
2277 http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
2278
2279 Uses a simplistic algorithm assuming other style guidelines
2280 (especially spacing) are followed.
2281 Only checks unindented functions, so class members are unchecked.
2282 Trivial bodies are unchecked, so constructors with huge initializer lists
2283 may be missed.
2284 Blank/comment lines are not counted so as to avoid encouraging the removal
2285 of vertical space and comments just to get through a lint check.
2286 NOLINT *on the last line of a function* disables this check.
2287
2288 Args:
2289 filename: The name of the current file.
2290 clean_lines: A CleansedLines instance containing the file.
2291 linenum: The number of the line to check.
2292 function_state: Current function name and lines in body so far.
2293 error: The function to call with any errors found.
2294 """
2295 lines = clean_lines.lines
2296 line = lines[linenum]
2297 raw = clean_lines.raw_lines
2298 raw_line = raw[linenum]
2299 joined_line = ''
2300
2301 starting_func = False
2302 regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ...
2303 match_result = Match(regexp, line)
2304 if match_result:
2305 # If the name is all caps and underscores, figure it's a macro and
2306 # ignore it, unless it's TEST or TEST_F.
2307 function_name = match_result.group(1).split()[-1]
2308 if function_name == 'TEST' or function_name == 'TEST_F' or (
2309 not Match(r'[A-Z_]+$', function_name)):
2310 starting_func = True
2311
2312 if starting_func:
2313 body_found = False
2314 for start_linenum in xrange(linenum, clean_lines.NumLines()):
2315 start_line = lines[start_linenum]
2316 joined_line += ' ' + start_line.lstrip()
2317 if Search(r'(;|})', start_line): # Declarations and trivial functions
2318 body_found = True
2319 break # ... ignore
2320 elif Search(r'{', start_line):
2321 body_found = True
2322 function = Search(r'((\w|:)*)\(', line).group(1)
2323 if Match(r'TEST', function): # Handle TEST... macros
2324 parameter_regexp = Search(r'(\(.*\))', joined_line)
2325 if parameter_regexp: # Ignore bad syntax
2326 function += parameter_regexp.group(1)
2327 else:
2328 function += '()'
2329 function_state.Begin(function)
2330 break
2331 if not body_found:
2332 # No body for the function (or evidence of a non-function) was found.
2333 error(filename, linenum, 'readability/fn_size', 5,
2334 'Lint failed to find start of function body.')
2335 elif Match(r'^\}\s*$', line): # function end
2336 function_state.Check(error, filename, linenum)
2337 function_state.End()
2338 elif not Match(r'^\s*$', line):
2339 function_state.Count() # Count non-blank/non-comment lines.
2340
2341
2342 _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')
2343
2344
2345 def CheckComment(comment, filename, linenum, error):
2346 """Checks for common mistakes in TODO comments.
2347
2348 Args:
2349 comment: The text of the comment from the line in question.
2350 filename: The name of the current file.
2351 linenum: The number of the line to check.
2352 error: The function to call with any errors found.
2353 """
2354 match = _RE_PATTERN_TODO.match(comment)
2355 if match:
2356 # One whitespace is correct; zero whitespace is handled elsewhere.
2357 leading_whitespace = match.group(1)
2358 if len(leading_whitespace) > 1:
2359 error(filename, linenum, 'whitespace/todo', 2,
2360 'Too many spaces before TODO')
2361
2362 username = match.group(2)
2363 if not username:
2364 error(filename, linenum, 'readability/todo', 2,
2365 'Missing username in TODO; it should look like '
2366 '"// TODO(my_username): Stuff."')
2367
2368 middle_whitespace = match.group(3)
2369 # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
2370 if middle_whitespace != ' ' and middle_whitespace != '':
2371 error(filename, linenum, 'whitespace/todo', 2,
2372 'TODO(my_username) should be followed by a space')
2373
2374 def CheckAccess(filename, clean_lines, linenum, nesting_state, error):
2375 """Checks for improper use of DISALLOW* macros.
2376
2377 Args:
2378 filename: The name of the current file.
2379 clean_lines: A CleansedLines instance containing the file.
2380 linenum: The number of the line to check.
2381 nesting_state: A _NestingState instance which maintains information about
2382 the current stack of nested blocks being parsed.
2383 error: The function to call with any errors found.
2384 """
2385 line = clean_lines.elided[linenum] # get rid of comments and strings
2386
2387 matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|'
2388 r'DISALLOW_EVIL_CONSTRUCTORS|'
2389 r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line)
2390 if not matched:
2391 return
2392 if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo):
2393 if nesting_state.stack[-1].access != 'private':
2394 error(filename, linenum, 'readability/constructors', 3,
2395 '%s must be in the private: section' % matched.group(1))
2396
2397 else:
2398 # Found DISALLOW* macro outside a class declaration, or perhaps it
2399 # was used inside a function when it should have been part of the
2400 # class declaration. We could issue a warning here, but it
2401 # probably resulted in a compiler error already.
2402 pass
2403
2404
2405 def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix):
2406 """Find the corresponding > to close a template.
2407
2408 Args:
2409 clean_lines: A CleansedLines instance containing the file.
2410 linenum: Current line number.
2411 init_suffix: Remainder of the current line after the initial <.
2412
2413 Returns:
2414 True if a matching bracket exists.
2415 """
2416 line = init_suffix
2417 nesting_stack = ['<']
2418 while True:
2419 # Find the next operator that can tell us whether < is used as an
2420 # opening bracket or as a less-than operator. We only want to
2421 # warn on the latter case.
2422 #
2423 # We could also check all other operators and terminate the search
2424 # early, e.g. if we got something like this "a<b+c", the "<" is
2425 # most likely a less-than operator, but then we will get false
2426 # positives for default arguments and other template expressions.
2427 match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line)
2428 if match:
2429 # Found an operator, update nesting stack
2430 operator = match.group(1)
2431 line = match.group(2)
2432
2433 if nesting_stack[-1] == '<':
2434 # Expecting closing angle bracket
2435 if operator in ('<', '(', '['):
2436 nesting_stack.append(operator)
2437 elif operator == '>':
2438 nesting_stack.pop()
2439 if not nesting_stack:
2440 # Found matching angle bracket
2441 return True
2442 elif operator == ',':
2443 # Got a comma after a bracket, this is most likely a template
2444 # argument. We have not seen a closing angle bracket yet, but
2445 # it's probably a few lines later if we look for it, so just
2446 # return early here.
2447 return True
2448 else:
2449 # Got some other operator.
2450 return False
2451
2452 else:
2453 # Expecting closing parenthesis or closing bracket
2454 if operator in ('<', '(', '['):
2455 nesting_stack.append(operator)
2456 elif operator in (')', ']'):
2457 # We don't bother checking for matching () or []. If we got
2458 # something like (] or [), it would have been a syntax error.
2459 nesting_stack.pop()
2460
2461 else:
2462 # Scan the next line
2463 linenum += 1
2464 if linenum >= len(clean_lines.elided):
2465 break
2466 line = clean_lines.elided[linenum]
2467
2468 # Exhausted all remaining lines and still no matching angle bracket.
2469 # Most likely the input was incomplete, otherwise we should have
2470 # seen a semicolon and returned early.
2471 return True
2472
2473
2474 def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix):
2475 """Find the corresponding < that started a template.
2476
2477 Args:
2478 clean_lines: A CleansedLines instance containing the file.
2479 linenum: Current line number.
2480 init_prefix: Part of the current line before the initial >.
2481
2482 Returns:
2483 True if a matching bracket exists.
2484 """
2485 line = init_prefix
2486 nesting_stack = ['>']
2487 while True:
2488 # Find the previous operator
2489 match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line)
2490 if match:
2491 # Found an operator, update nesting stack
2492 operator = match.group(2)
2493 line = match.group(1)
2494
2495 if nesting_stack[-1] == '>':
2496 # Expecting opening angle bracket
2497 if operator in ('>', ')', ']'):
2498 nesting_stack.append(operator)
2499 elif operator == '<':
2500 nesting_stack.pop()
2501 if not nesting_stack:
2502 # Found matching angle bracket
2503 return True
2504 elif operator == ',':
2505 # Got a comma before a bracket, this is most likely a
2506 # template argument. The opening angle bracket is probably
2507 # there if we look for it, so just return early here.
2508 return True
2509 else:
2510 # Got some other operator.
2511 return False
2512
2513 else:
2514 # Expecting opening parenthesis or opening bracket
2515 if operator in ('>', ')', ']'):
2516 nesting_stack.append(operator)
2517 elif operator in ('(', '['):
2518 nesting_stack.pop()
2519
2520 else:
2521 # Scan the previous line
2522 linenum -= 1
2523 if linenum < 0:
2524 break
2525 line = clean_lines.elided[linenum]
2526
2527 # Exhausted all earlier lines and still no matching angle bracket.
2528 return False
2529
2530
2531 def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
2532 """Checks for the correctness of various spacing issues in the code.
2533
2534 Things we check for: spaces around operators, spaces after
2535 if/for/while/switch, no spaces around parens in function calls, two
2536 spaces between code and comment, don't start a block with a blank
2537 line, don't end a function with a blank line, don't add a blank line
2538 after public/protected/private, don't have too many blank lines in a row.
2539
2540 Args:
2541 filename: The name of the current file.
2542 clean_lines: A CleansedLines instance containing the file.
2543 linenum: The number of the line to check.
2544 nesting_state: A _NestingState instance which maintains information about
2545 the current stack of nested blocks being parsed.
2546 error: The function to call with any errors found.
2547 """
2548
2549 # Don't use "elided" lines here, otherwise we can't check commented lines.
2550 # Don't want to use "raw" either, because we don't want to check inside C++11
2551 # raw strings,
2552 raw = clean_lines.lines_without_raw_strings
2553 line = raw[linenum]
2554
2555 # Before nixing comments, check if the line is blank for no good
2556 # reason. This includes the first line after a block is opened, and
2557 # blank lines at the end of a function (ie, right before a line like '}'
2558 #
2559 # Skip all the blank line checks if we are immediately inside a
2560 # namespace body. In other words, don't issue blank line warnings
2561 # for this block:
2562 # namespace {
2563 #
2564 # }
2565 #
2566 # A warning about missing end of namespace comments will be issued instead.
2567 if IsBlankLine(line) and not nesting_state.InNamespaceBody():
2568 elided = clean_lines.elided
2569 prev_line = elided[linenum - 1]
2570 prevbrace = prev_line.rfind('{')
2571 # TODO(unknown): Don't complain if line before blank line, and line after,
2572 # both start with alnums and are indented the same amount.
2573 # This ignores whitespace at the start of a namespace block
2574 # because those are not usually indented.
2575 if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
2576 # OK, we have a blank line at the start of a code block. Before we
2577 # complain, we check if it is an exception to the rule: The previous
2578 # non-empty line has the parameters of a function header that are indented
2579 # 4 spaces (because they did not fit in a 80 column line when placed on
2580 # the same line as the function name). We also check for the case where
2581 # the previous line is indented 6 spaces, which may happen when the
2582 # initializers of a constructor do not fit into a 80 column line.
2583 exception = False
2584 if Match(r' {6}\w', prev_line): # Initializer list?
2585 # We are looking for the opening column of initializer list, which
2586 # should be indented 4 spaces to cause 6 space indentation afterwards.
2587 search_position = linenum-2
2588 while (search_position >= 0
2589 and Match(r' {6}\w', elided[search_position])):
2590 search_position -= 1
2591 exception = (search_position >= 0
2592 and elided[search_position][:5] == ' :')
2593 else:
2594 # Search for the function arguments or an initializer list. We use a
2595 # simple heuristic here: If the line is indented 4 spaces; and we have a
2596 # closing paren, without the opening paren, followed by an opening brace
2597 # or colon (for initializer lists) we assume that it is the last line of
2598 # a function header. If we have a colon indented 4 spaces, it is an
2599 # initializer list.
2600 exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
2601 prev_line)
2602 or Match(r' {4}:', prev_line))
2603
2604 if not exception:
2605 error(filename, linenum, 'whitespace/blank_line', 2,
2606 'Redundant blank line at the start of a code block '
2607 'should be deleted.')
2608 # Ignore blank lines at the end of a block in a long if-else
2609 # chain, like this:
2610 # if (condition1) {
2611 # // Something followed by a blank line
2612 #
2613 # } else if (condition2) {
2614 # // Something else
2615 # }
2616 if linenum + 1 < clean_lines.NumLines():
2617 next_line = raw[linenum + 1]
2618 if (next_line
2619 and Match(r'\s*}', next_line)
2620 and next_line.find('} else ') == -1):
2621 error(filename, linenum, 'whitespace/blank_line', 3,
2622 'Redundant blank line at the end of a code block '
2623 'should be deleted.')
2624
2625 matched = Match(r'\s*(public|protected|private):', prev_line)
2626 if matched:
2627 error(filename, linenum, 'whitespace/blank_line', 3,
2628 'Do not leave a blank line after "%s:"' % matched.group(1))
2629
2630 # Next, we complain if there's a comment too near the text
2631 commentpos = line.find('//')
2632 if commentpos != -1:
2633 # Check if the // may be in quotes. If so, ignore it
2634 # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
2635 if (line.count('"', 0, commentpos) -
2636 line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
2637 # Allow one space for new scopes, two spaces otherwise:
2638 if (not Match(r'^\s*{ //', line) and
2639 ((commentpos >= 1 and
2640 line[commentpos-1] not in string.whitespace) or
2641 (commentpos >= 2 and
2642 line[commentpos-2] not in string.whitespace))):
2643 error(filename, linenum, 'whitespace/comments', 2,
2644 'At least two spaces is best between code and comments')
2645 # There should always be a space between the // and the comment
2646 commentend = commentpos + 2
2647 if commentend < len(line) and not line[commentend] == ' ':
2648 # but some lines are exceptions -- e.g. if they're big
2649 # comment delimiters like:
2650 # //----------------------------------------------------------
2651 # or are an empty C++ style Doxygen comment, like:
2652 # ///
2653 # or C++ style Doxygen comments placed after the variable:
2654 # ///< Header comment
2655 # //!< Header comment
2656 # or they begin with multiple slashes followed by a space:
2657 # //////// Header comment
2658 match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or
2659 Search(r'^/$', line[commentend:]) or
2660 Search(r'^!< ', line[commentend:]) or
2661 Search(r'^/< ', line[commentend:]) or
2662 Search(r'^/+ ', line[commentend:]))
2663 if not match:
2664 error(filename, linenum, 'whitespace/comments', 4,
2665 'Should have a space between // and comment')
2666 CheckComment(line[commentpos:], filename, linenum, error)
2667
2668 line = clean_lines.elided[linenum] # get rid of comments and strings
2669
2670 # Don't try to do spacing checks for operator methods
2671 line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line)
2672
2673 # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".
2674 # Otherwise not. Note we only check for non-spaces on *both* sides;
2675 # sometimes people put non-spaces on one side when aligning ='s among
2676 # many lines (not that this is behavior that I approve of...)
2677 if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line):
2678 error(filename, linenum, 'whitespace/operators', 4,
2679 'Missing spaces around =')
2680
2681 # It's ok not to have spaces around binary operators like + - * /, but if
2682 # there's too little whitespace, we get concerned. It's hard to tell,
2683 # though, so we punt on this one for now. TODO.
2684
2685 # You should always have whitespace around binary operators.
2686 #
2687 # Check <= and >= first to avoid false positives with < and >, then
2688 # check non-include lines for spacing around < and >.
2689 match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line)
2690 if match:
2691 error(filename, linenum, 'whitespace/operators', 3,
2692 'Missing spaces around %s' % match.group(1))
2693 # We allow no-spaces around << when used like this: 10<<20, but
2694 # not otherwise (particularly, not when used as streams)
2695 # Also ignore using ns::operator<<;
2696 match = Search(r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line)
2697 if (match and
2698 not (match.group(1).isdigit() and match.group(2).isdigit()) and
2699 not (match.group(1) == 'operator' and match.group(2) == ';')):
2700 error(filename, linenum, 'whitespace/operators', 3,
2701 'Missing spaces around <<')
2702 elif not Match(r'#.*include', line):
2703 # Avoid false positives on ->
2704 reduced_line = line.replace('->', '')
2705
2706 # Look for < that is not surrounded by spaces. This is only
2707 # triggered if both sides are missing spaces, even though
2708 # technically should should flag if at least one side is missing a
2709 # space. This is done to avoid some false positives with shifts.
2710 match = Search(r'[^\s<]<([^\s=<].*)', reduced_line)
2711 if (match and
2712 not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))):
2713 error(filename, linenum, 'whitespace/operators', 3,
2714 'Missing spaces around <')
2715
2716 # Look for > that is not surrounded by spaces. Similar to the
2717 # above, we only trigger if both sides are missing spaces to avoid
2718 # false positives with shifts.
2719 match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line)
2720 if (match and
2721 not FindPreviousMatchingAngleBracket(clean_lines, linenum,
2722 match.group(1))):
2723 error(filename, linenum, 'whitespace/operators', 3,
2724 'Missing spaces around >')
2725
2726 # We allow no-spaces around >> for almost anything. This is because
2727 # C++11 allows ">>" to close nested templates, which accounts for
2728 # most cases when ">>" is not followed by a space.
2729 #
2730 # We still warn on ">>" followed by alpha character, because that is
2731 # likely due to ">>" being used for right shifts, e.g.:
2732 # value >> alpha
2733 #
2734 # When ">>" is used to close templates, the alphanumeric letter that
2735 # follows would be part of an identifier, and there should still be
2736 # a space separating the template type and the identifier.
2737 # type<type<type>> alpha
2738 match = Search(r'>>[a-zA-Z_]', line)
2739 if match:
2740 error(filename, linenum, 'whitespace/operators', 3,
2741 'Missing spaces around >>')
2742
2743 # There shouldn't be space around unary operators
2744 match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
2745 if match:
2746 error(filename, linenum, 'whitespace/operators', 4,
2747 'Extra space for operator %s' % match.group(1))
2748
2749 # A pet peeve of mine: no spaces after an if, while, switch, or for
2750 match = Search(r' (if\(|for\(|while\(|switch\()', line)
2751 if match:
2752 error(filename, linenum, 'whitespace/parens', 5,
2753 'Missing space before ( in %s' % match.group(1))
2754
2755 # For if/for/while/switch, the left and right parens should be
2756 # consistent about how many spaces are inside the parens, and
2757 # there should either be zero or one spaces inside the parens.
2758 # We don't want: "if ( foo)" or "if ( foo )".
2759 # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
2760 match = Search(r'\b(if|for|while|switch)\s*'
2761 r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
2762 line)
2763 if match:
2764 if len(match.group(2)) != len(match.group(4)):
2765 if not (match.group(3) == ';' and
2766 len(match.group(2)) == 1 + len(match.group(4)) or
2767 not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
2768 error(filename, linenum, 'whitespace/parens', 5,
2769 'Mismatching spaces inside () in %s' % match.group(1))
2770 if len(match.group(2)) not in [0, 1]:
2771 error(filename, linenum, 'whitespace/parens', 5,
2772 'Should have zero or one spaces inside ( and ) in %s' %
2773 match.group(1))
2774
2775 # You should always have a space after a comma (either as fn arg or operator)
2776 #
2777 # This does not apply when the non-space character following the
2778 # comma is another comma, since the only time when that happens is
2779 # for empty macro arguments.
2780 #
2781 # We run this check in two passes: first pass on elided lines to
2782 # verify that lines contain missing whitespaces, second pass on raw
2783 # lines to confirm that those missing whitespaces are not due to
2784 # elided comments.
2785 if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
2786 error(filename, linenum, 'whitespace/comma', 3,
2787 'Missing space after ,')
2788
2789 # You should always have a space after a semicolon
2790 # except for few corner cases
2791 # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
2792 # space after ;
2793 if Search(r';[^\s};\\)/]', line):
2794 error(filename, linenum, 'whitespace/semicolon', 3,
2795 'Missing space after ;')
2796
2797 # Next we will look for issues with function calls.
2798 CheckSpacingForFunctionCall(filename, line, linenum, error)
2799
2800 # Except after an opening paren, or after another opening brace (in case of
2801 # an initializer list, for instance), you should have spaces before your
2802 # braces. And since you should never have braces at the beginning of a line,
2803 # this is an easy test.
2804 match = Match(r'^(.*[^ ({]){', line)
2805 if match:
2806 # Try a bit harder to check for brace initialization. This
2807 # happens in one of the following forms:
2808 # Constructor() : initializer_list_{} { ... }
2809 # Constructor{}.MemberFunction()
2810 # Type variable{};
2811 # FunctionCall(type{}, ...);
2812 # LastArgument(..., type{});
2813 # LOG(INFO) << type{} << " ...";
2814 # map_of_type[{...}] = ...;
2815 #
2816 # We check for the character following the closing brace, and
2817 # silence the warning if it's one of those listed above, i.e.
2818 # "{.;,)<]".
2819 #
2820 # To account for nested initializer list, we allow any number of
2821 # closing braces up to "{;,)<". We can't simply silence the
2822 # warning on first sight of closing brace, because that would
2823 # cause false negatives for things that are not initializer lists.
2824 # Silence this: But not this:
2825 # Outer{ if (...) {
2826 # Inner{...} if (...){ // Missing space before {
2827 # }; }
2828 #
2829 # There is a false negative with this approach if people inserted
2830 # spurious semicolons, e.g. "if (cond){};", but we will catch the
2831 # spurious semicolon with a separate check.
2832 (endline, endlinenum, endpos) = CloseExpression(
2833 clean_lines, linenum, len(match.group(1)))
2834 trailing_text = ''
2835 if endpos > -1:
2836 trailing_text = endline[endpos:]
2837 for offset in xrange(endlinenum + 1,
2838 min(endlinenum + 3, clean_lines.NumLines() - 1)):
2839 trailing_text += clean_lines.elided[offset]
2840 if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text):
2841 error(filename, linenum, 'whitespace/braces', 5,
2842 'Missing space before {')
2843
2844 # Make sure '} else {' has spaces.
2845 if Search(r'}else', line):
2846 error(filename, linenum, 'whitespace/braces', 5,
2847 'Missing space before else')
2848
2849 # You shouldn't have spaces before your brackets, except maybe after
2850 # 'delete []' or 'new char * []'.
2851 if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line):
2852 error(filename, linenum, 'whitespace/braces', 5,
2853 'Extra space before [')
2854
2855 # You shouldn't have a space before a semicolon at the end of the line.
2856 # There's a special case for "for" since the style guide allows space before
2857 # the semicolon there.
2858 if Search(r':\s*;\s*$', line):
2859 error(filename, linenum, 'whitespace/semicolon', 5,
2860 'Semicolon defining empty statement. Use {} instead.')
2861 elif Search(r'^\s*;\s*$', line):
2862 error(filename, linenum, 'whitespace/semicolon', 5,
2863 'Line contains only semicolon. If this should be an empty statement, '
2864 'use {} instead.')
2865 elif (Search(r'\s+;\s*$', line) and
2866 not Search(r'\bfor\b', line)):
2867 error(filename, linenum, 'whitespace/semicolon', 5,
2868 'Extra space before last semicolon. If this should be an empty '
2869 'statement, use {} instead.')
2870
2871 # In range-based for, we wanted spaces before and after the colon, but
2872 # not around "::" tokens that might appear.
2873 if (Search('for *\(.*[^:]:[^: ]', line) or
2874 Search('for *\(.*[^: ]:[^:]', line)):
2875 error(filename, linenum, 'whitespace/forcolon', 2,
2876 'Missing space around colon in range-based for loop')
2877
2878
2879 def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
2880 """Checks for additional blank line issues related to sections.
2881
2882 Currently the only thing checked here is blank line before protected/private.
2883
2884 Args:
2885 filename: The name of the current file.
2886 clean_lines: A CleansedLines instance containing the file.
2887 class_info: A _ClassInfo objects.
2888 linenum: The number of the line to check.
2889 error: The function to call with any errors found.
2890 """
2891 # Skip checks if the class is small, where small means 25 lines or less.
2892 # 25 lines seems like a good cutoff since that's the usual height of
2893 # terminals, and any class that can't fit in one screen can't really
2894 # be considered "small".
2895 #
2896 # Also skip checks if we are on the first line. This accounts for
2897 # classes that look like
2898 # class Foo { public: ... };
2899 #
2900 # If we didn't find the end of the class, last_line would be zero,
2901 # and the check will be skipped by the first condition.
2902 if (class_info.last_line - class_info.starting_linenum <= 24 or
2903 linenum <= class_info.starting_linenum):
2904 return
2905
2906 matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])
2907 if matched:
2908 # Issue warning if the line before public/protected/private was
2909 # not a blank line, but don't do this if the previous line contains
2910 # "class" or "struct". This can happen two ways:
2911 # - We are at the beginning of the class.
2912 # - We are forward-declaring an inner class that is semantically
2913 # private, but needed to be public for implementation reasons.
2914 # Also ignores cases where the previous line ends with a backslash as can be
2915 # common when defining classes in C macros.
2916 prev_line = clean_lines.lines[linenum - 1]
2917 if (not IsBlankLine(prev_line) and
2918 not Search(r'\b(class|struct)\b', prev_line) and
2919 not Search(r'\\$', prev_line)):
2920 # Try a bit harder to find the beginning of the class. This is to
2921 # account for multi-line base-specifier lists, e.g.:
2922 # class Derived
2923 # : public Base {
2924 end_class_head = class_info.starting_linenum
2925 for i in range(class_info.starting_linenum, linenum):
2926 if Search(r'\{\s*$', clean_lines.lines[i]):
2927 end_class_head = i
2928 break
2929 if end_class_head < linenum - 1:
2930 error(filename, linenum, 'whitespace/blank_line', 3,
2931 '"%s:" should be preceded by a blank line' % matched.group(1))
2932
2933
2934 def GetPreviousNonBlankLine(clean_lines, linenum):
2935 """Return the most recent non-blank line and its line number.
2936
2937 Args:
2938 clean_lines: A CleansedLines instance containing the file contents.
2939 linenum: The number of the line to check.
2940
2941 Returns:
2942 A tuple with two elements. The first element is the contents of the last
2943 non-blank line before the current line, or the empty string if this is the
2944 first non-blank line. The second is the line number of that line, or -1
2945 if this is the first non-blank line.
2946 """
2947
2948 prevlinenum = linenum - 1
2949 while prevlinenum >= 0:
2950 prevline = clean_lines.elided[prevlinenum]
2951 if not IsBlankLine(prevline): # if not a blank line...
2952 return (prevline, prevlinenum)
2953 prevlinenum -= 1
2954 return ('', -1)
2955
2956
2957 def CheckBraces(filename, clean_lines, linenum, error):
2958 """Looks for misplaced braces (e.g. at the end of line).
2959
2960 Args:
2961 filename: The name of the current file.
2962 clean_lines: A CleansedLines instance containing the file.
2963 linenum: The number of the line to check.
2964 error: The function to call with any errors found.
2965 """
2966
2967 line = clean_lines.elided[linenum] # get rid of comments and strings
2968
2969 if Match(r'\s*{\s*$', line):
2970 # We allow an open brace to start a line in the case where someone is using
2971 # braces in a block to explicitly create a new scope, which is commonly used
2972 # to control the lifetime of stack-allocated variables. Braces are also
2973 # used for brace initializers inside function calls. We don't detect this
2974 # perfectly: we just don't complain if the last non-whitespace character on
2975 # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
2976 # previous line starts a preprocessor block.
2977 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
2978 if (not Search(r'[,;:}{(]\s*$', prevline) and
2979 not Match(r'\s*#', prevline)):
2980 error(filename, linenum, 'whitespace/braces', 4,
2981 '{ should almost always be at the end of the previous line')
2982
2983 # An else clause should be on the same line as the preceding closing brace.
2984 if Match(r'\s*else\s*', line):
2985 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
2986 if Match(r'\s*}\s*$', prevline):
2987 error(filename, linenum, 'whitespace/newline', 4,
2988 'An else should appear on the same line as the preceding }')
2989
2990 # If braces come on one side of an else, they should be on both.
2991 # However, we have to worry about "else if" that spans multiple lines!
2992 if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
2993 if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if
2994 # find the ( after the if
2995 pos = line.find('else if')
2996 pos = line.find('(', pos)
2997 if pos > 0:
2998 (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
2999 if endline[endpos:].find('{') == -1: # must be brace after if
3000 error(filename, linenum, 'readability/braces', 5,
3001 'If an else has a brace on one side, it should have it on both')
3002 else: # common case: else not followed by a multi-line if
3003 error(filename, linenum, 'readability/braces', 5,
3004 'If an else has a brace on one side, it should have it on both')
3005
3006 # Likewise, an else should never have the else clause on the same line
3007 if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
3008 error(filename, linenum, 'whitespace/newline', 4,
3009 'Else clause should never be on same line as else (use 2 lines)')
3010
3011 # In the same way, a do/while should never be on one line
3012 if Match(r'\s*do [^\s{]', line):
3013 error(filename, linenum, 'whitespace/newline', 4,
3014 'do/while clauses should not be on a single line')
3015
3016 # Block bodies should not be followed by a semicolon. Due to C++11
3017 # brace initialization, there are more places where semicolons are
3018 # required than not, so we use a whitelist approach to check these
3019 # rather than a blacklist. These are the places where "};" should
3020 # be replaced by just "}":
3021 # 1. Some flavor of block following closing parenthesis:
3022 # for (;;) {};
3023 # while (...) {};
3024 # switch (...) {};
3025 # Function(...) {};
3026 # if (...) {};
3027 # if (...) else if (...) {};
3028 #
3029 # 2. else block:
3030 # if (...) else {};
3031 #
3032 # 3. const member function:
3033 # Function(...) const {};
3034 #
3035 # 4. Block following some statement:
3036 # x = 42;
3037 # {};
3038 #
3039 # 5. Block at the beginning of a function:
3040 # Function(...) {
3041 # {};
3042 # }
3043 #
3044 # Note that naively checking for the preceding "{" will also match
3045 # braces inside multi-dimensional arrays, but this is fine since
3046 # that expression will not contain semicolons.
3047 #
3048 # 6. Block following another block:
3049 # while (true) {}
3050 # {};
3051 #
3052 # 7. End of namespaces:
3053 # namespace {};
3054 #
3055 # These semicolons seems far more common than other kinds of
3056 # redundant semicolons, possibly due to people converting classes
3057 # to namespaces. For now we do not warn for this case.
3058 #
3059 # Try matching case 1 first.
3060 match = Match(r'^(.*\)\s*)\{', line)
3061 if match:
3062 # Matched closing parenthesis (case 1). Check the token before the
3063 # matching opening parenthesis, and don't warn if it looks like a
3064 # macro. This avoids these false positives:
3065 # - macro that defines a base class
3066 # - multi-line macro that defines a base class
3067 # - macro that defines the whole class-head
3068 #
3069 # But we still issue warnings for macros that we know are safe to
3070 # warn, specifically:
3071 # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
3072 # - TYPED_TEST
3073 # - INTERFACE_DEF
3074 # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
3075 #
3076 # We implement a whitelist of safe macros instead of a blacklist of
3077 # unsafe macros, even though the latter appears less frequently in
3078 # google code and would have been easier to implement. This is because
3079 # the downside for getting the whitelist wrong means some extra
3080 # semicolons, while the downside for getting the blacklist wrong
3081 # would result in compile errors.
3082 #
3083 # In addition to macros, we also don't want to warn on compound
3084 # literals.
3085 closing_brace_pos = match.group(1).rfind(')')
3086 opening_parenthesis = ReverseCloseExpression(
3087 clean_lines, linenum, closing_brace_pos)
3088 if opening_parenthesis[2] > -1:
3089 line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
3090 macro = Search(r'\b([A-Z_]+)\s*$', line_prefix)
3091 if ((macro and
3092 macro.group(1) not in (
3093 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
3094 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
3095 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
3096 Search(r'\s+=\s*$', line_prefix)):
3097 match = None
3098
3099 else:
3100 # Try matching cases 2-3.
3101 match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
3102 if not match:
3103 # Try matching cases 4-6. These are always matched on separate lines.
3104 #
3105 # Note that we can't simply concatenate the previous line to the
3106 # current line and do a single match, otherwise we may output
3107 # duplicate warnings for the blank line case:
3108 # if (cond) {
3109 # // blank line
3110 # }
3111 prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
3112 if prevline and Search(r'[;{}]\s*$', prevline):
3113 match = Match(r'^(\s*)\{', line)
3114
3115 # Check matching closing brace
3116 if match:
3117 (endline, endlinenum, endpos) = CloseExpression(
3118 clean_lines, linenum, len(match.group(1)))
3119 if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
3120 # Current {} pair is eligible for semicolon check, and we have found
3121 # the redundant semicolon, output warning here.
3122 #
3123 # Note: because we are scanning forward for opening braces, and
3124 # outputting warnings for the matching closing brace, if there are
3125 # nested blocks with trailing semicolons, we will get the error
3126 # messages in reversed order.
3127 error(filename, endlinenum, 'readability/braces', 4,
3128 "You don't need a ; after a }")
3129
3130
3131 def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
3132 """Look for empty loop/conditional body with only a single semicolon.
3133
3134 Args:
3135 filename: The name of the current file.
3136 clean_lines: A CleansedLines instance containing the file.
3137 linenum: The number of the line to check.
3138 error: The function to call with any errors found.
3139 """
3140
3141 # Search for loop keywords at the beginning of the line. Because only
3142 # whitespaces are allowed before the keywords, this will also ignore most
3143 # do-while-loops, since those lines should start with closing brace.
3144 #
3145 # We also check "if" blocks here, since an empty conditional block
3146 # is likely an error.
3147 line = clean_lines.elided[linenum]
3148 matched = Match(r'\s*(for|while|if)\s*\(', line)
3149 if matched:
3150 # Find the end of the conditional expression
3151 (end_line, end_linenum, end_pos) = CloseExpression(
3152 clean_lines, linenum, line.find('('))
3153
3154 # Output warning if what follows the condition expression is a semicolon.
3155 # No warning for all other cases, including whitespace or newline, since we
3156 # have a separate check for semicolons preceded by whitespace.
3157 if end_pos >= 0 and Match(r';', end_line[end_pos:]):
3158 if matched.group(1) == 'if':
3159 error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
3160 'Empty conditional bodies should use {}')
3161 else:
3162 error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
3163 'Empty loop bodies should use {} or continue')
3164
3165
3166 def CheckCheck(filename, clean_lines, linenum, error):
3167 """Checks the use of CHECK and EXPECT macros.
3168
3169 Args:
3170 filename: The name of the current file.
3171 clean_lines: A CleansedLines instance containing the file.
3172 linenum: The number of the line to check.
3173 error: The function to call with any errors found.
3174 """
3175
3176 # Decide the set of replacement macros that should be suggested
3177 lines = clean_lines.elided
3178 check_macro = None
3179 start_pos = -1
3180 for macro in _CHECK_MACROS:
3181 i = lines[linenum].find(macro)
3182 if i >= 0:
3183 check_macro = macro
3184
3185 # Find opening parenthesis. Do a regular expression match here
3186 # to make sure that we are matching the expected CHECK macro, as
3187 # opposed to some other macro that happens to contain the CHECK
3188 # substring.
3189 matched = Match(r'^(.*\b' + check_macro + r'\s*)\(', lines[linenum])
3190 if not matched:
3191 continue
3192 start_pos = len(matched.group(1))
3193 break
3194 if not check_macro or start_pos < 0:
3195 # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT'
3196 return
3197
3198 # Find end of the boolean expression by matching parentheses
3199 (last_line, end_line, end_pos) = CloseExpression(
3200 clean_lines, linenum, start_pos)
3201 if end_pos < 0:
3202 return
3203 if linenum == end_line:
3204 expression = lines[linenum][start_pos + 1:end_pos - 1]
3205 else:
3206 expression = lines[linenum][start_pos + 1:]
3207 for i in xrange(linenum + 1, end_line):
3208 expression += lines[i]
3209 expression += last_line[0:end_pos - 1]
3210
3211 # Parse expression so that we can take parentheses into account.
3212 # This avoids false positives for inputs like "CHECK((a < 4) == b)",
3213 # which is not replaceable by CHECK_LE.
3214 lhs = ''
3215 rhs = ''
3216 operator = None
3217 while expression:
3218 matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'
3219 r'==|!=|>=|>|<=|<|\()(.*)$', expression)
3220 if matched:
3221 token = matched.group(1)
3222 if token == '(':
3223 # Parenthesized operand
3224 expression = matched.group(2)
3225 (end, _) = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
3226 if end < 0:
3227 return # Unmatched parenthesis
3228 lhs += '(' + expression[0:end]
3229 expression = expression[end:]
3230 elif token in ('&&', '||'):
3231 # Logical and/or operators. This means the expression
3232 # contains more than one term, for example:
3233 # CHECK(42 < a && a < b);
3234 #
3235 # These are not replaceable with CHECK_LE, so bail out early.
3236 return
3237 elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):
3238 # Non-relational operator
3239 lhs += token
3240 expression = matched.group(2)
3241 else:
3242 # Relational operator
3243 operator = token
3244 rhs = matched.group(2)
3245 break
3246 else:
3247 # Unparenthesized operand. Instead of appending to lhs one character
3248 # at a time, we do another regular expression match to consume several
3249 # characters at once if possible. Trivial benchmark shows that this
3250 # is more efficient when the operands are longer than a single
3251 # character, which is generally the case.
3252 matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)
3253 if not matched:
3254 matched = Match(r'^(\s*\S)(.*)$', expression)
3255 if not matched:
3256 break
3257 lhs += matched.group(1)
3258 expression = matched.group(2)
3259
3260 # Only apply checks if we got all parts of the boolean expression
3261 if not (lhs and operator and rhs):
3262 return
3263
3264 # Check that rhs do not contain logical operators. We already know
3265 # that lhs is fine since the loop above parses out && and ||.
3266 if rhs.find('&&') > -1 or rhs.find('||') > -1:
3267 return
3268
3269 # At least one of the operands must be a constant literal. This is
3270 # to avoid suggesting replacements for unprintable things like
3271 # CHECK(variable != iterator)
3272 #
3273 # The following pattern matches decimal, hex integers, strings, and
3274 # characters (in that order).
3275 lhs = lhs.strip()
3276 rhs = rhs.strip()
3277 match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'
3278 if Match(match_constant, lhs) or Match(match_constant, rhs):
3279 # Note: since we know both lhs and rhs, we can provide a more
3280 # descriptive error message like:
3281 # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42)
3282 # Instead of:
3283 # Consider using CHECK_EQ instead of CHECK(a == b)
3284 #
3285 # We are still keeping the less descriptive message because if lhs
3286 # or rhs gets long, the error message might become unreadable.
3287 error(filename, linenum, 'readability/check', 2,
3288 'Consider using %s instead of %s(a %s b)' % (
3289 _CHECK_REPLACEMENT[check_macro][operator],
3290 check_macro, operator))
3291
3292
3293 def CheckAltTokens(filename, clean_lines, linenum, error):
3294 """Check alternative keywords being used in boolean expressions.
3295
3296 Args:
3297 filename: The name of the current file.
3298 clean_lines: A CleansedLines instance containing the file.
3299 linenum: The number of the line to check.
3300 error: The function to call with any errors found.
3301 """
3302 line = clean_lines.elided[linenum]
3303
3304 # Avoid preprocessor lines
3305 if Match(r'^\s*#', line):
3306 return
3307
3308 # Last ditch effort to avoid multi-line comments. This will not help
3309 # if the comment started before the current line or ended after the
3310 # current line, but it catches most of the false positives. At least,
3311 # it provides a way to workaround this warning for people who use
3312 # multi-line comments in preprocessor macros.
3313 #
3314 # TODO(unknown): remove this once cpplint has better support for
3315 # multi-line comments.
3316 if line.find('/*') >= 0 or line.find('*/') >= 0:
3317 return
3318
3319 for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
3320 error(filename, linenum, 'readability/alt_tokens', 2,
3321 'Use operator %s instead of %s' % (
3322 _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))
3323
3324
3325 def GetLineWidth(line):
3326 """Determines the width of the line in column positions.
3327
3328 Args:
3329 line: A string, which may be a Unicode string.
3330
3331 Returns:
3332 The width of the line in column positions, accounting for Unicode
3333 combining characters and wide characters.
3334 """
3335 if isinstance(line, unicode):
3336 width = 0
3337 for uc in unicodedata.normalize('NFC', line):
3338 if unicodedata.east_asian_width(uc) in ('W', 'F'):
3339 width += 2
3340 elif not unicodedata.combining(uc):
3341 width += 1
3342 return width
3343 else:
3344 return len(line)
3345
3346
3347 def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
3348 error):
3349 """Checks rules from the 'C++ style rules' section of cppguide.html.
3350
3351 Most of these rules are hard to test (naming, comment style), but we
3352 do what we can. In particular we check for 2-space indents, line lengths,
3353 tab usage, spaces inside code, etc.
3354
3355 Args:
3356 filename: The name of the current file.
3357 clean_lines: A CleansedLines instance containing the file.
3358 linenum: The number of the line to check.
3359 file_extension: The extension (without the dot) of the filename.
3360 nesting_state: A _NestingState instance which maintains information about
3361 the current stack of nested blocks being parsed.
3362 error: The function to call with any errors found.
3363 """
3364
3365 # Don't use "elided" lines here, otherwise we can't check commented lines.
3366 # Don't want to use "raw" either, because we don't want to check inside C++11
3367 # raw strings,
3368 raw_lines = clean_lines.lines_without_raw_strings
3369 line = raw_lines[linenum]
3370
3371 if line.find('\t') != -1:
3372 error(filename, linenum, 'whitespace/tab', 1,
3373 'Tab found; better to use spaces')
3374
3375 # One or three blank spaces at the beginning of the line is weird; it's
3376 # hard to reconcile that with 2-space indents.
3377 # NOTE: here are the conditions rob pike used for his tests. Mine aren't
3378 # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces
3379 # if(RLENGTH > 20) complain = 0;
3380 # if(match($0, " +(error|private|public|protected):")) complain = 0;
3381 # if(match(prev, "&& *$")) complain = 0;
3382 # if(match(prev, "\\|\\| *$")) complain = 0;
3383 # if(match(prev, "[\",=><] *$")) complain = 0;
3384 # if(match($0, " <<")) complain = 0;
3385 # if(match(prev, " +for \\(")) complain = 0;
3386 # if(prevodd && match(prevprev, " +for \\(")) complain = 0;
3387 initial_spaces = 0
3388 cleansed_line = clean_lines.elided[linenum]
3389 while initial_spaces < len(line) and line[initial_spaces] == ' ':
3390 initial_spaces += 1
3391 if line and line[-1].isspace():
3392 error(filename, linenum, 'whitespace/end_of_line', 4,
3393 'Line ends in whitespace. Consider deleting these extra spaces.')
3394 # There are certain situations we allow one space, notably for section labels
3395 elif ((initial_spaces == 1 or initial_spaces == 3) and
3396 not Match(r'\s*\w+\s*:\s*$', cleansed_line)):
3397 error(filename, linenum, 'whitespace/indent', 3,
3398 'Weird number of spaces at line-start. '
3399 'Are you using a 2-space indent?')
3400
3401 # Check if the line is a header guard.
3402 is_header_guard = False
3403 if file_extension in ['h', 'hpp']:
3404 cppvar = GetHeaderGuardCPPVariable(filename)
3405 if (line.startswith('#ifndef %s' % cppvar) or
3406 line.startswith('#define %s' % cppvar) or
3407 line.startswith('#endif // %s' % cppvar)):
3408 is_header_guard = True
3409 # #include lines and header guards can be long, since there's no clean way to
3410 # split them.
3411 #
3412 # URLs can be long too. It's possible to split these, but it makes them
3413 # harder to cut&paste.
3414 #
3415 # The "$Id:...$" comment may also get very long without it being the
3416 # developers fault.
3417 if (not line.startswith('#include') and not is_header_guard and
3418 not Match(r'^\s*//.*http(s?)://\S*$', line) and
3419 not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
3420 line_width = GetLineWidth(line)
3421 extended_length = int((_line_length * 1.25))
3422 if line_width > extended_length:
3423 error(filename, linenum, 'whitespace/line_length', 4,
3424 'Lines should very rarely be longer than %i characters' %
3425 extended_length)
3426 elif line_width > _line_length:
3427 error(filename, linenum, 'whitespace/line_length', 2,
3428 'Lines should be <= %i characters long' % _line_length)
3429
3430 if (cleansed_line.count(';') > 1 and
3431 # for loops are allowed two ;'s (and may run over two lines).
3432 cleansed_line.find('for') == -1 and
3433 (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
3434 GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
3435 # It's ok to have many commands in a switch case that fits in 1 line
3436 not ((cleansed_line.find('case ') != -1 or
3437 cleansed_line.find('default:') != -1) and
3438 cleansed_line.find('break;') != -1)):
3439 error(filename, linenum, 'whitespace/newline', 0,
3440 'More than one command on the same line')
3441
3442 # Some more style checks
3443 CheckBraces(filename, clean_lines, linenum, error)
3444 CheckEmptyBlockBody(filename, clean_lines, linenum, error)
3445 CheckAccess(filename, clean_lines, linenum, nesting_state, error)
3446 CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
3447 CheckCheck(filename, clean_lines, linenum, error)
3448 CheckAltTokens(filename, clean_lines, linenum, error)
3449 classinfo = nesting_state.InnermostClass()
3450 if classinfo:
3451 CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)
3452
3453
3454 _RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"')
3455 _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
3456 # Matches the first component of a filename delimited by -s and _s. That is:
3457 # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
3458 # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'
3459 # _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'
3460 # _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'
3461 _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
3462
3463
3464 def _DropCommonSuffixes(filename):
3465 """Drops common suffixes like _test.cc or -inl.h from filename.
3466
3467 For example:
3468 >>> _DropCommonSuffixes('foo/foo-inl.h')
3469 'foo/foo'
3470 >>> _DropCommonSuffixes('foo/bar/foo.cc')
3471 'foo/bar/foo'
3472 >>> _DropCommonSuffixes('foo/foo_internal.h')
3473 'foo/foo'
3474 >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')
3475 'foo/foo_unusualinternal'
3476
3477 Args:
3478 filename: The input filename.
3479
3480 Returns:
3481 The filename with the common suffix removed.
3482 """
3483 for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
3484 'inl.h', 'impl.h', 'internal.h'):
3485 if (filename.endswith(suffix) and len(filename) > len(suffix) and
3486 filename[-len(suffix) - 1] in ('-', '_')):
3487 return filename[:-len(suffix) - 1]
3488 return os.path.splitext(filename)[0]
3489
3490
3491 def _IsTestFilename(filename):
3492 """Determines if the given filename has a suffix that identifies it as a test.
3493
3494 Args:
3495 filename: The input filename.
3496
3497 Returns:
3498 True if 'filename' looks like a test, False otherwise.
3499 """
3500 if (filename.endswith('_test.cc') or
3501 filename.endswith('_unittest.cc') or
3502 filename.endswith('_regtest.cc')):
3503 return True
3504 else:
3505 return False
3506
3507
3508 def _ClassifyInclude(fileinfo, include, is_system):
3509 """Figures out what kind of header 'include' is.
3510
3511 Args:
3512 fileinfo: The current file cpplint is running over. A FileInfo instance.
3513 include: The path to a #included file.
3514 is_system: True if the #include used <> rather than "".
3515
3516 Returns:
3517 One of the _XXX_HEADER constants.
3518
3519 For example:
3520 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)
3521 _C_SYS_HEADER
3522 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
3523 _CPP_SYS_HEADER
3524 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
3525 _LIKELY_MY_HEADER
3526 >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
3527 ... 'bar/foo_other_ext.h', False)
3528 _POSSIBLE_MY_HEADER
3529 >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)
3530 _OTHER_HEADER
3531 """
3532 # This is a list of all standard c++ header files, except
3533 # those already checked for above.
3534 is_cpp_h = include in _CPP_HEADERS
3535
3536 if is_system:
3537 if is_cpp_h:
3538 return _CPP_SYS_HEADER
3539 else:
3540 return _C_SYS_HEADER
3541
3542 # If the target file and the include we're checking share a
3543 # basename when we drop common extensions, and the include
3544 # lives in . , then it's likely to be owned by the target file.
3545 target_dir, target_base = (
3546 os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))
3547 include_dir, include_base = os.path.split(_DropCommonSuffixes(include))
3548 if target_base == include_base and (
3549 include_dir == target_dir or
3550 include_dir == os.path.normpath(target_dir + '/../public')):
3551 return _LIKELY_MY_HEADER
3552
3553 # If the target and include share some initial basename
3554 # component, it's possible the target is implementing the
3555 # include, so it's allowed to be first, but we'll never
3556 # complain if it's not there.
3557 target_first_component = _RE_FIRST_COMPONENT.match(target_base)
3558 include_first_component = _RE_FIRST_COMPONENT.match(include_base)
3559 if (target_first_component and include_first_component and
3560 target_first_component.group(0) ==
3561 include_first_component.group(0)):
3562 return _POSSIBLE_MY_HEADER
3563
3564 return _OTHER_HEADER
3565
3566
3567
3568 def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
3569 """Check rules that are applicable to #include lines.
3570
3571 Strings on #include lines are NOT removed from elided line, to make
3572 certain tasks easier. However, to prevent false positives, checks
3573 applicable to #include lines in CheckLanguage must be put here.
3574
3575 Args:
3576 filename: The name of the current file.
3577 clean_lines: A CleansedLines instance containing the file.
3578 linenum: The number of the line to check.
3579 include_state: An _IncludeState instance in which the headers are inserted.
3580 error: The function to call with any errors found.
3581 """
3582 fileinfo = FileInfo(filename)
3583
3584 line = clean_lines.lines[linenum]
3585
3586 # "include" should use the new style "foo/bar.h" instead of just "bar.h"
3587 if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line):
3588 error(filename, linenum, 'build/include', 4,
3589 'Include the directory when naming .h files')
3590
3591 # we shouldn't include a file more than once. actually, there are a
3592 # handful of instances where doing so is okay, but in general it's
3593 # not.
3594 match = _RE_PATTERN_INCLUDE.search(line)
3595 if match:
3596 include = match.group(2)
3597 is_system = (match.group(1) == '<')
3598 if include in include_state:
3599 error(filename, linenum, 'build/include', 4,
3600 '"%s" already included at %s:%s' %
3601 (include, filename, include_state[include]))
3602 else:
3603 include_state[include] = linenum
3604
3605 # We want to ensure that headers appear in the right order:
3606 # 1) for foo.cc, foo.h (preferred location)
3607 # 2) c system files
3608 # 3) cpp system files
3609 # 4) for foo.cc, foo.h (deprecated location)
3610 # 5) other google headers
3611 #
3612 # We classify each include statement as one of those 5 types
3613 # using a number of techniques. The include_state object keeps
3614 # track of the highest type seen, and complains if we see a
3615 # lower type after that.
3616 error_message = include_state.CheckNextIncludeOrder(
3617 _ClassifyInclude(fileinfo, include, is_system))
3618 if error_message:
3619 error(filename, linenum, 'build/include_order', 4,
3620 '%s. Should be: %s.h, c system, c++ system, other.' %
3621 (error_message, fileinfo.BaseName()))
3622 canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
3623 if not include_state.IsInAlphabeticalOrder(
3624 clean_lines, linenum, canonical_include):
3625 error(filename, linenum, 'build/include_alpha', 4,
3626 'Include "%s" not in alphabetical order' % include)
3627 include_state.SetLastHeader(canonical_include)
3628
3629 # Look for any of the stream classes that are part of standard C++.
3630 match = _RE_PATTERN_INCLUDE.match(line)
3631 if match:
3632 include = match.group(2)
3633 if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include):
3634 # Many unit tests use cout, so we exempt them.
3635 if not _IsTestFilename(filename):
3636 error(filename, linenum, 'readability/streams', 3,
3637 'Streams are highly discouraged.')
3638
3639
3640 def _GetTextInside(text, start_pattern):
3641 r"""Retrieves all the text between matching open and close parentheses.
3642
3643 Given a string of lines and a regular expression string, retrieve all the text
3644 following the expression and between opening punctuation symbols like
3645 (, [, or {, and the matching close-punctuation symbol. This properly nested
3646 occurrences of the punctuations, so for the text like
3647 printf(a(), b(c()));
3648 a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
3649 start_pattern must match string having an open punctuation symbol at the end.
3650
3651 Args:
3652 text: The lines to extract text. Its comments and strings must be elided.
3653 It can be single line and can span multiple lines.
3654 start_pattern: The regexp string indicating where to start extracting
3655 the text.
3656 Returns:
3657 The extracted text.
3658 None if either the opening string or ending punctuation could not be found.
3659 """
3660 # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably
3661 # rewritten to use _GetTextInside (and use inferior regexp matching today).
3662
3663 # Give opening punctuations to get the matching close-punctuations.
3664 matching_punctuation = {'(': ')', '{': '}', '[': ']'}
3665 closing_punctuation = set(matching_punctuation.itervalues())
3666
3667 # Find the position to start extracting text.
3668 match = re.search(start_pattern, text, re.M)
3669 if not match: # start_pattern not found in text.
3670 return None
3671 start_position = match.end(0)
3672
3673 assert start_position > 0, (
3674 'start_pattern must ends with an opening punctuation.')
3675 assert text[start_position - 1] in matching_punctuation, (
3676 'start_pattern must ends with an opening punctuation.')
3677 # Stack of closing punctuations we expect to have in text after position.
3678 punctuation_stack = [matching_punctuation[text[start_position - 1]]]
3679 position = start_position
3680 while punctuation_stack and position < len(text):
3681 if text[position] == punctuation_stack[-1]:
3682 punctuation_stack.pop()
3683 elif text[position] in closing_punctuation:
3684 # A closing punctuation without matching opening punctuations.
3685 return None
3686 elif text[position] in matching_punctuation:
3687 punctuation_stack.append(matching_punctuation[text[position]])
3688 position += 1
3689 if punctuation_stack:
3690 # Opening punctuations left without matching close-punctuations.
3691 return None
3692 # punctuations match.
3693 return text[start_position:position - 1]
3694
3695
3696 # Patterns for matching call-by-reference parameters.
3697 #
3698 # Supports nested templates up to 2 levels deep using this messy pattern:
3699 # < (?: < (?: < [^<>]*
3700 # >
3701 # | [^<>] )*
3702 # >
3703 # | [^<>] )*
3704 # >
3705 _RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
3706 _RE_PATTERN_TYPE = (
3707 r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
3708 r'(?:\w|'
3709 r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
3710 r'::)+')
3711 # A call-by-reference parameter ends with '& identifier'.
3712 _RE_PATTERN_REF_PARAM = re.compile(
3713 r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
3714 r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
3715 # A call-by-const-reference parameter either ends with 'const& identifier'
3716 # or looks like 'const type& identifier' when 'type' is atomic.
3717 _RE_PATTERN_CONST_REF_PARAM = (
3718 r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
3719 r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
3720
3721
3722 def CheckLanguage(filename, clean_lines, linenum, file_extension,
3723 include_state, nesting_state, error):
3724 """Checks rules from the 'C++ language rules' section of cppguide.html.
3725
3726 Some of these rules are hard to test (function overloading, using
3727 uint32 inappropriately), but we do the best we can.
3728
3729 Args:
3730 filename: The name of the current file.
3731 clean_lines: A CleansedLines instance containing the file.
3732 linenum: The number of the line to check.
3733 file_extension: The extension (without the dot) of the filename.
3734 include_state: An _IncludeState instance in which the headers are inserted.
3735 nesting_state: A _NestingState instance which maintains information about
3736 the current stack of nested blocks being parsed.
3737 error: The function to call with any errors found.
3738 """
3739 # If the line is empty or consists of entirely a comment, no need to
3740 # check it.
3741 line = clean_lines.elided[linenum]
3742 if not line:
3743 return
3744
3745 match = _RE_PATTERN_INCLUDE.search(line)
3746 if match:
3747 CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
3748 return
3749
3750 # Reset include state across preprocessor directives. This is meant
3751 # to silence warnings for conditional includes.
3752 if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line):
3753 include_state.ResetSection()
3754
3755 # Make Windows paths like Unix.
3756 fullname = os.path.abspath(filename).replace('\\', '/')
3757
3758 # TODO(unknown): figure out if they're using default arguments in fn proto.
3759
3760 # Check to see if they're using an conversion function cast.
3761 # I just try to capture the most common basic types, though there are more.
3762 # Parameterless conversion functions, such as bool(), are allowed as they are
3763 # probably a member operator declaration or default constructor.
3764 match = Search(
3765 r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there
3766 r'(int|float|double|bool|char|int32|uint32|int64|uint64)'
3767 r'(\([^)].*)', line)
3768 if match:
3769 matched_new = match.group(1)
3770 matched_type = match.group(2)
3771 matched_funcptr = match.group(3)
3772
3773 # gMock methods are defined using some variant of MOCK_METHODx(name, type)
3774 # where type may be float(), int(string), etc. Without context they are
3775 # virtually indistinguishable from int(x) casts. Likewise, gMock's
3776 # MockCallback takes a template parameter of the form return_type(arg_type),
3777 # which looks much like the cast we're trying to detect.
3778 #
3779 # std::function<> wrapper has a similar problem.
3780 #
3781 # Return types for function pointers also look like casts if they
3782 # don't have an extra space.
3783 if (matched_new is None and # If new operator, then this isn't a cast
3784 not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
3785 Search(r'\bMockCallback<.*>', line) or
3786 Search(r'\bfunction<.*>', line)) and
3787 not (matched_funcptr and
3788 Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',
3789 matched_funcptr))):
3790 # Try a bit harder to catch gmock lines: the only place where
3791 # something looks like an old-style cast is where we declare the
3792 # return type of the mocked method, and the only time when we
3793 # are missing context is if MOCK_METHOD was split across
3794 # multiple lines. The missing MOCK_METHOD is usually one or two
3795 # lines back, so scan back one or two lines.
3796 #
3797 # It's not possible for gmock macros to appear in the first 2
3798 # lines, since the class head + section name takes up 2 lines.
3799 if (linenum < 2 or
3800 not (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',
3801 clean_lines.elided[linenum - 1]) or
3802 Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',
3803 clean_lines.elided[linenum - 2]))):
3804 error(filename, linenum, 'readability/casting', 4,
3805 'Using deprecated casting style. '
3806 'Use static_cast<%s>(...) instead' %
3807 matched_type)
3808
3809 CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
3810 'static_cast',
3811 r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
3812
3813 # This doesn't catch all cases. Consider (const char * const)"hello".
3814 #
3815 # (char *) "foo" should always be a const_cast (reinterpret_cast won't
3816 # compile).
3817 if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
3818 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error):
3819 pass
3820 else:
3821 # Check pointer casts for other than string constants
3822 CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum],
3823 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error)
3824
3825 # In addition, we look for people taking the address of a cast. This
3826 # is dangerous -- casts can assign to temporaries, so the pointer doesn't
3827 # point where you think.
3828 match = Search(
3829 r'(?:&\(([^)]+)\)[\w(])|'
3830 r'(?:&(static|dynamic|down|reinterpret)_cast\b)', line)
3831 if match and match.group(1) != '*':
3832 error(filename, linenum, 'runtime/casting', 4,
3833 ('Are you taking an address of a cast? '
3834 'This is dangerous: could be a temp var. '
3835 'Take the address before doing the cast, rather than after'))
3836
3837 # Create an extended_line, which is the concatenation of the current and
3838 # next lines, for more effective checking of code that may span more than one
3839 # line.
3840 if linenum + 1 < clean_lines.NumLines():
3841 extended_line = line + clean_lines.elided[linenum + 1]
3842 else:
3843 extended_line = line
3844
3845 # Check for people declaring static/global STL strings at the top level.
3846 # This is dangerous because the C++ language does not guarantee that
3847 # globals with constructors are initialized before the first access.
3848 match = Match(
3849 r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)',
3850 line)
3851 # Make sure it's not a function.
3852 # Function template specialization looks like: "string foo<Type>(...".
3853 # Class template definitions look like: "string Foo<Type>::Method(...".
3854 #
3855 # Also ignore things that look like operators. These are matched separately
3856 # because operator names cross non-word boundaries. If we change the pattern
3857 # above, we would decrease the accuracy of matching identifiers.
3858 if (match and
3859 not Search(r'\boperator\W', line) and
3860 not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3))):
3861 error(filename, linenum, 'runtime/string', 4,
3862 'For a static/global string constant, use a C style string instead: '
3863 '"%schar %s[]".' %
3864 (match.group(1), match.group(2)))
3865
3866 if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line):
3867 error(filename, linenum, 'runtime/init', 4,
3868 'You seem to be initializing a member variable with itself.')
3869
3870 if file_extension in ['h', 'hpp']:
3871 # TODO(unknown): check that 1-arg constructors are explicit.
3872 # How to tell it's a constructor?
3873 # (handled in CheckForNonStandardConstructs for now)
3874 # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS
3875 # (level 1 error)
3876 pass
3877
3878 # Check if people are using the verboten C basic types. The only exception
3879 # we regularly allow is "unsigned short port" for port.
3880 if Search(r'\bshort port\b', line):
3881 if not Search(r'\bunsigned short port\b', line):
3882 error(filename, linenum, 'runtime/int', 4,
3883 'Use "unsigned short" for ports, not "short"')
3884 else:
3885 match = Search(r'\b(short|long(?! +double)|long long)\b', line)
3886 if match:
3887 error(filename, linenum, 'runtime/int', 4,
3888 'Use int16/int64/etc, rather than the C type %s' % match.group(1))
3889
3890 # When snprintf is used, the second argument shouldn't be a literal.
3891 match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
3892 if match and match.group(2) != '0':
3893 # If 2nd arg is zero, snprintf is used to calculate size.
3894 error(filename, linenum, 'runtime/printf', 3,
3895 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
3896 'to snprintf.' % (match.group(1), match.group(2)))
3897
3898 # Check if some verboten C functions are being used.
3899 if Search(r'\bsprintf\b', line):
3900 error(filename, linenum, 'runtime/printf', 5,
3901 'Never use sprintf. Use snprintf instead.')
3902 match = Search(r'\b(strcpy|strcat)\b', line)
3903 if match:
3904 error(filename, linenum, 'runtime/printf', 4,
3905 'Almost always, snprintf is better than %s' % match.group(1))
3906
3907 # Check if some verboten operator overloading is going on
3908 # TODO(unknown): catch out-of-line unary operator&:
3909 # class X {};
3910 # int operator&(const X& x) { return 42; } // unary operator&
3911 # The trick is it's hard to tell apart from binary operator&:
3912 # class Y { int operator&(const Y& x) { return 23; } }; // binary operator&
3913 if Search(r'\boperator\s*&\s*\(\s*\)', line):
3914 error(filename, linenum, 'runtime/operator', 4,
3915 'Unary operator& is dangerous. Do not use it.')
3916
3917 # Check for suspicious usage of "if" like
3918 # } if (a == b) {
3919 if Search(r'\}\s*if\s*\(', line):
3920 error(filename, linenum, 'readability/braces', 4,
3921 'Did you mean "else if"? If not, start a new line for "if".')
3922
3923 # Check for potential format string bugs like printf(foo).
3924 # We constrain the pattern not to pick things like DocidForPrintf(foo).
3925 # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())
3926 # TODO(sugawarayu): Catch the following case. Need to change the calling
3927 # convention of the whole function to process multiple line to handle it.
3928 # printf(
3929 # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line);
3930 printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')
3931 if printf_args:
3932 match = Match(r'([\w.\->()]+)$', printf_args)
3933 if match and match.group(1) != '__VA_ARGS__':
3934 function_name = re.search(r'\b((?:string)?printf)\s*\(',
3935 line, re.I).group(1)
3936 error(filename, linenum, 'runtime/printf', 4,
3937 'Potential format string bug. Do %s("%%s", %s) instead.'
3938 % (function_name, match.group(1)))
3939
3940 # Check for potential memset bugs like memset(buf, sizeof(buf), 0).
3941 match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
3942 if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):
3943 error(filename, linenum, 'runtime/memset', 4,
3944 'Did you mean "memset(%s, 0, %s)"?'
3945 % (match.group(1), match.group(2)))
3946
3947 if Search(r'\busing namespace\b', line):
3948 error(filename, linenum, 'build/namespaces', 5,
3949 'Do not use namespace using-directives. '
3950 'Use using-declarations instead.')
3951
3952 # Detect variable-length arrays.
3953 match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
3954 if (match and match.group(2) != 'return' and match.group(2) != 'delete' and
3955 match.group(3).find(']') == -1):
3956 # Split the size using space and arithmetic operators as delimiters.
3957 # If any of the resulting tokens are not compile time constants then
3958 # report the error.
3959 tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))
3960 is_const = True
3961 skip_next = False
3962 for tok in tokens:
3963 if skip_next:
3964 skip_next = False
3965 continue
3966
3967 if Search(r'sizeof\(.+\)', tok): continue
3968 if Search(r'arraysize\(\w+\)', tok): continue
3969
3970 tok = tok.lstrip('(')
3971 tok = tok.rstrip(')')
3972 if not tok: continue
3973 if Match(r'\d+', tok): continue
3974 if Match(r'0[xX][0-9a-fA-F]+', tok): continue
3975 if Match(r'k[A-Z0-9]\w*', tok): continue
3976 if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
3977 if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
3978 # A catch all for tricky sizeof cases, including 'sizeof expression',
3979 # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
3980 # requires skipping the next token because we split on ' ' and '*'.
3981 if tok.startswith('sizeof'):
3982 skip_next = True
3983 continue
3984 is_const = False
3985 break
3986 if not is_const:
3987 error(filename, linenum, 'runtime/arrays', 1,
3988 'Do not use variable-length arrays. Use an appropriately named '
3989 "('k' followed by CamelCase) compile-time constant for the size.")
3990
3991 # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or
3992 # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing
3993 # in the class declaration.
3994 match = Match(
3995 (r'\s*'
3996 r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))'
3997 r'\(.*\);$'),
3998 line)
3999 if match and linenum + 1 < clean_lines.NumLines():
4000 next_line = clean_lines.elided[linenum + 1]
4001 # We allow some, but not all, declarations of variables to be present
4002 # in the statement that defines the class. The [\w\*,\s]* fragment of
4003 # the regular expression below allows users to declare instances of
4004 # the class or pointers to instances, but not less common types such
4005 # as function pointers or arrays. It's a tradeoff between allowing
4006 # reasonable code and avoiding trying to parse more C++ using regexps.
4007 if not Search(r'^\s*}[\w\*,\s]*;', next_line):
4008 error(filename, linenum, 'readability/constructors', 3,
4009 match.group(1) + ' should be the last thing in the class')
4010
4011 # Check for use of unnamed namespaces in header files. Registration
4012 # macros are typically OK, so we allow use of "namespace {" on lines
4013 # that end with backslashes.
4014 if (file_extension in ['h', 'hpp']
4015 and Search(r'\bnamespace\s*{', line)
4016 and line[-1] != '\\'):
4017 error(filename, linenum, 'build/namespaces', 4,
4018 'Do not use unnamed namespaces in header files. See '
4019 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
4020 ' for more information.')
4021
4022 def CheckForNonConstReference(filename, clean_lines, linenum,
4023 nesting_state, error):
4024 """Check for non-const references.
4025
4026 Separate from CheckLanguage since it scans backwards from current
4027 line, instead of scanning forward.
4028
4029 Args:
4030 filename: The name of the current file.
4031 clean_lines: A CleansedLines instance containing the file.
4032 linenum: The number of the line to check.
4033 nesting_state: A _NestingState instance which maintains information about
4034 the current stack of nested blocks being parsed.
4035 error: The function to call with any errors found.
4036 """
4037 # Do nothing if there is no '&' on current line.
4038 line = clean_lines.elided[linenum]
4039 if '&' not in line:
4040 return
4041
4042 # Long type names may be broken across multiple lines, usually in one
4043 # of these forms:
4044 # LongType
4045 # ::LongTypeContinued &identifier
4046 # LongType::
4047 # LongTypeContinued &identifier
4048 # LongType<
4049 # ...>::LongTypeContinued &identifier
4050 #
4051 # If we detected a type split across two lines, join the previous
4052 # line to current line so that we can match const references
4053 # accordingly.
4054 #
4055 # Note that this only scans back one line, since scanning back
4056 # arbitrary number of lines would be expensive. If you have a type
4057 # that spans more than 2 lines, please use a typedef.
4058 if linenum > 1:
4059 previous = None
4060 if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
4061 # previous_line\n + ::current_line
4062 previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
4063 clean_lines.elided[linenum - 1])
4064 elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
4065 # previous_line::\n + current_line
4066 previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
4067 clean_lines.elided[linenum - 1])
4068 if previous:
4069 line = previous.group(1) + line.lstrip()
4070 else:
4071 # Check for templated parameter that is split across multiple lines
4072 endpos = line.rfind('>')
4073 if endpos > -1:
4074 (_, startline, startpos) = ReverseCloseExpression(
4075 clean_lines, linenum, endpos)
4076 if startpos > -1 and startline < linenum:
4077 # Found the matching < on an earlier line, collect all
4078 # pieces up to current line.
4079 line = ''
4080 for i in xrange(startline, linenum + 1):
4081 line += clean_lines.elided[i].strip()
4082
4083 # Check for non-const references in function parameters. A single '&' may
4084 # found in the following places:
4085 # inside expression: binary & for bitwise AND
4086 # inside expression: unary & for taking the address of something
4087 # inside declarators: reference parameter
4088 # We will exclude the first two cases by checking that we are not inside a
4089 # function body, including one that was just introduced by a trailing '{'.
4090 # TODO(unknwon): Doesn't account for preprocessor directives.
4091 # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].
4092 check_params = False
4093 if not nesting_state.stack:
4094 check_params = True # top level
4095 elif (isinstance(nesting_state.stack[-1], _ClassInfo) or
4096 isinstance(nesting_state.stack[-1], _NamespaceInfo)):
4097 check_params = True # within class or namespace
4098 elif Match(r'.*{\s*$', line):
4099 if (len(nesting_state.stack) == 1 or
4100 isinstance(nesting_state.stack[-2], _ClassInfo) or
4101 isinstance(nesting_state.stack[-2], _NamespaceInfo)):
4102 check_params = True # just opened global/class/namespace block
4103 # We allow non-const references in a few standard places, like functions
4104 # called "swap()" or iostream operators like "<<" or ">>". Do not check
4105 # those function parameters.
4106 #
4107 # We also accept & in static_assert, which looks like a function but
4108 # it's actually a declaration expression.
4109 whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
4110 r'operator\s*[<>][<>]|'
4111 r'static_assert|COMPILE_ASSERT'
4112 r')\s*\(')
4113 if Search(whitelisted_functions, line):
4114 check_params = False
4115 elif not Search(r'\S+\([^)]*$', line):
4116 # Don't see a whitelisted function on this line. Actually we
4117 # didn't see any function name on this line, so this is likely a
4118 # multi-line parameter list. Try a bit harder to catch this case.
4119 for i in xrange(2):
4120 if (linenum > i and
4121 Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
4122 check_params = False
4123 break
4124
4125 if check_params:
4126 decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
4127 for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
4128 if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
4129 error(filename, linenum, 'runtime/references', 2,
4130 'Is this a non-const reference? '
4131 'If so, make const or use a pointer: ' +
4132 ReplaceAll(' *<', '<', parameter))
4133
4134
4135 def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
4136 error):
4137 """Checks for a C-style cast by looking for the pattern.
4138
4139 Args:
4140 filename: The name of the current file.
4141 linenum: The number of the line to check.
4142 line: The line of code to check.
4143 raw_line: The raw line of code to check, with comments.
4144 cast_type: The string for the C++ cast to recommend. This is either
4145 reinterpret_cast, static_cast, or const_cast, depending.
4146 pattern: The regular expression used to find C-style casts.
4147 error: The function to call with any errors found.
4148
4149 Returns:
4150 True if an error was emitted.
4151 False otherwise.
4152 """
4153 match = Search(pattern, line)
4154 if not match:
4155 return False
4156
4157 # Exclude lines with sizeof, since sizeof looks like a cast.
4158 sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1])
4159 if sizeof_match:
4160 return False
4161
4162 # operator++(int) and operator--(int)
4163 if (line[0:match.start(1) - 1].endswith(' operator++') or
4164 line[0:match.start(1) - 1].endswith(' operator--')):
4165 return False
4166
4167 # A single unnamed argument for a function tends to look like old
4168 # style cast. If we see those, don't issue warnings for deprecated
4169 # casts, instead issue warnings for unnamed arguments where
4170 # appropriate.
4171 #
4172 # These are things that we want warnings for, since the style guide
4173 # explicitly require all parameters to be named:
4174 # Function(int);
4175 # Function(int) {
4176 # ConstMember(int) const;
4177 # ConstMember(int) const {
4178 # ExceptionMember(int) throw (...);
4179 # ExceptionMember(int) throw (...) {
4180 # PureVirtual(int) = 0;
4181 #
4182 # These are functions of some sort, where the compiler would be fine
4183 # if they had named parameters, but people often omit those
4184 # identifiers to reduce clutter:
4185 # (FunctionPointer)(int);
4186 # (FunctionPointer)(int) = value;
4187 # Function((function_pointer_arg)(int))
4188 # <TemplateArgument(int)>;
4189 # <(FunctionPointerTemplateArgument)(int)>;
4190 remainder = line[match.end(0):]
4191 if Match(r'^\s*(?:;|const\b|throw\b|=|>|\{|\))', remainder):
4192 # Looks like an unnamed parameter.
4193
4194 # Don't warn on any kind of template arguments.
4195 if Match(r'^\s*>', remainder):
4196 return False
4197
4198 # Don't warn on assignments to function pointers, but keep warnings for
4199 # unnamed parameters to pure virtual functions. Note that this pattern
4200 # will also pass on assignments of "0" to function pointers, but the
4201 # preferred values for those would be "nullptr" or "NULL".
4202 matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder)
4203 if matched_zero and matched_zero.group(1) != '0':
4204 return False
4205
4206 # Don't warn on function pointer declarations. For this we need
4207 # to check what came before the "(type)" string.
4208 if Match(r'.*\)\s*$', line[0:match.start(0)]):
4209 return False
4210
4211 # Don't warn if the parameter is named with block comments, e.g.:
4212 # Function(int /*unused_param*/);
4213 if '/*' in raw_line:
4214 return False
4215
4216 # Passed all filters, issue warning here.
4217 error(filename, linenum, 'readability/function', 3,
4218 'All parameters should be named in a function')
4219 return True
4220
4221 # At this point, all that should be left is actual casts.
4222 error(filename, linenum, 'readability/casting', 4,
4223 'Using C-style cast. Use %s<%s>(...) instead' %
4224 (cast_type, match.group(1)))
4225
4226 return True
4227
4228
4229 _HEADERS_CONTAINING_TEMPLATES = (
4230 ('<deque>', ('deque',)),
4231 ('<functional>', ('unary_function', 'binary_function',
4232 'plus', 'minus', 'multiplies', 'divides', 'modulus',
4233 'negate',
4234 'equal_to', 'not_equal_to', 'greater', 'less',
4235 'greater_equal', 'less_equal',
4236 'logical_and', 'logical_or', 'logical_not',
4237 'unary_negate', 'not1', 'binary_negate', 'not2',
4238 'bind1st', 'bind2nd',
4239 'pointer_to_unary_function',
4240 'pointer_to_binary_function',
4241 'ptr_fun',
4242 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
4243 'mem_fun_ref_t',
4244 'const_mem_fun_t', 'const_mem_fun1_t',
4245 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
4246 'mem_fun_ref',
4247 )),
4248 ('<limits>', ('numeric_limits',)),
4249 ('<list>', ('list',)),
4250 ('<map>', ('map', 'multimap',)),
4251 ('<memory>', ('allocator',)),
4252 ('<queue>', ('queue', 'priority_queue',)),
4253 ('<set>', ('set', 'multiset',)),
4254 ('<stack>', ('stack',)),
4255 ('<string>', ('char_traits', 'basic_string',)),
4256 ('<utility>', ('pair',)),
4257 ('<vector>', ('vector',)),
4258
4259 # gcc extensions.
4260 # Note: std::hash is their hash, ::hash is our hash
4261 ('<hash_map>', ('hash_map', 'hash_multimap',)),
4262 ('<hash_set>', ('hash_set', 'hash_multiset',)),
4263 ('<slist>', ('slist',)),
4264 )
4265
4266 _RE_PATTERN_STRING = re.compile(r'\bstring\b')
4267
4268 _re_pattern_algorithm_header = []
4269 for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap',
4270 'transform'):
4271 # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
4272 # type::max().
4273 _re_pattern_algorithm_header.append(
4274 (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
4275 _template,
4276 '<algorithm>'))
4277
4278 _re_pattern_templates = []
4279 for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
4280 for _template in _templates:
4281 _re_pattern_templates.append(
4282 (re.compile(r'(\<|\b)' + _template + r'\s*\<'),
4283 _template + '<>',
4284 _header))
4285
4286
4287 def FilesBelongToSameModule(filename_cc, filename_h):
4288 """Check if these two filenames belong to the same module.
4289
4290 The concept of a 'module' here is a as follows:
4291 foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
4292 same 'module' if they are in the same directory.
4293 some/path/public/xyzzy and some/path/internal/xyzzy are also considered
4294 to belong to the same module here.
4295
4296 If the filename_cc contains a longer path than the filename_h, for example,
4297 '/absolute/path/to/base/sysinfo.cc', and this file would include
4298 'base/sysinfo.h', this function also produces the prefix needed to open the
4299 header. This is used by the caller of this function to more robustly open the
4300 header file. We don't have access to the real include paths in this context,
4301 so we need this guesswork here.
4302
4303 Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
4304 according to this implementation. Because of this, this function gives
4305 some false positives. This should be sufficiently rare in practice.
4306
4307 Args:
4308 filename_cc: is the path for the .cc file
4309 filename_h: is the path for the header path
4310
4311 Returns:
4312 Tuple with a bool and a string:
4313 bool: True if filename_cc and filename_h belong to the same module.
4314 string: the additional prefix needed to open the header file.
4315 """
4316
4317 if not filename_cc.endswith('.cc'):
4318 return (False, '')
4319 filename_cc = filename_cc[:-len('.cc')]
4320 if filename_cc.endswith('_unittest'):
4321 filename_cc = filename_cc[:-len('_unittest')]
4322 elif filename_cc.endswith('_test'):
4323 filename_cc = filename_cc[:-len('_test')]
4324 filename_cc = filename_cc.replace('/public/', '/')
4325 filename_cc = filename_cc.replace('/internal/', '/')
4326
4327 if not filename_h.endswith('.h'):
4328 return (False, '')
4329 filename_h = filename_h[:-len('.h')]
4330 if filename_h.endswith('-inl'):
4331 filename_h = filename_h[:-len('-inl')]
4332 filename_h = filename_h.replace('/public/', '/')
4333 filename_h = filename_h.replace('/internal/', '/')
4334
4335 files_belong_to_same_module = filename_cc.endswith(filename_h)
4336 common_path = ''
4337 if files_belong_to_same_module:
4338 common_path = filename_cc[:-len(filename_h)]
4339 return files_belong_to_same_module, common_path
4340
4341
4342 def UpdateIncludeState(filename, include_state, io=codecs):
4343 """Fill up the include_state with new includes found from the file.
4344
4345 Args:
4346 filename: the name of the header to read.
4347 include_state: an _IncludeState instance in which the headers are inserted.
4348 io: The io factory to use to read the file. Provided for testability.
4349
4350 Returns:
4351 True if a header was succesfully added. False otherwise.
4352 """
4353 headerfile = None
4354 try:
4355 headerfile = io.open(filename, 'r', 'utf8', 'replace')
4356 except IOError:
4357 return False
4358 linenum = 0
4359 for line in headerfile:
4360 linenum += 1
4361 clean_line = CleanseComments(line)
4362 match = _RE_PATTERN_INCLUDE.search(clean_line)
4363 if match:
4364 include = match.group(2)
4365 # The value formatting is cute, but not really used right now.
4366 # What matters here is that the key is in include_state.
4367 include_state.setdefault(include, '%s:%d' % (filename, linenum))
4368 return True
4369
4370
4371 def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
4372 io=codecs):
4373 """Reports for missing stl includes.
4374
4375 This function will output warnings to make sure you are including the headers
4376 necessary for the stl containers and functions that you use. We only give one
4377 reason to include a header. For example, if you use both equal_to<> and
4378 less<> in a .h file, only one (the latter in the file) of these will be
4379 reported as a reason to include the <functional>.
4380
4381 Args:
4382 filename: The name of the current file.
4383 clean_lines: A CleansedLines instance containing the file.
4384 include_state: An _IncludeState instance.
4385 error: The function to call with any errors found.
4386 io: The IO factory to use to read the header file. Provided for unittest
4387 injection.
4388 """
4389 required = {} # A map of header name to linenumber and the template entity.
4390 # Example of required: { '<functional>': (1219, 'less<>') }
4391
4392 for linenum in xrange(clean_lines.NumLines()):
4393 line = clean_lines.elided[linenum]
4394 if not line or line[0] == '#':
4395 continue
4396
4397 # String is special -- it is a non-templatized type in STL.
4398 matched = _RE_PATTERN_STRING.search(line)
4399 if matched:
4400 # Don't warn about strings in non-STL namespaces:
4401 # (We check only the first match per line; good enough.)
4402 prefix = line[:matched.start()]
4403 if prefix.endswith('std::') or not prefix.endswith('::'):
4404 required['<string>'] = (linenum, 'string')
4405
4406 for pattern, template, header in _re_pattern_algorithm_header:
4407 if pattern.search(line):
4408 required[header] = (linenum, template)
4409
4410 # The following function is just a speed up, no semantics are changed.
4411 if not '<' in line: # Reduces the cpu time usage by skipping lines.
4412 continue
4413
4414 for pattern, template, header in _re_pattern_templates:
4415 if pattern.search(line):
4416 required[header] = (linenum, template)
4417
4418 # The policy is that if you #include something in foo.h you don't need to
4419 # include it again in foo.cc. Here, we will look at possible includes.
4420 # Let's copy the include_state so it is only messed up within this function.
4421 include_state = include_state.copy()
4422
4423 # Did we find the header for this file (if any) and succesfully load it?
4424 header_found = False
4425
4426 # Use the absolute path so that matching works properly.
4427 abs_filename = FileInfo(filename).FullName()
4428
4429 # For Emacs's flymake.
4430 # If cpplint is invoked from Emacs's flymake, a temporary file is generated
4431 # by flymake and that file name might end with '_flymake.cc'. In that case,
4432 # restore original file name here so that the corresponding header file can be
4433 # found.
4434 # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
4435 # instead of 'foo_flymake.h'
4436 abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
4437
4438 # include_state is modified during iteration, so we iterate over a copy of
4439 # the keys.
4440 header_keys = include_state.keys()
4441 for header in header_keys:
4442 (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
4443 fullpath = common_path + header
4444 if same_module and UpdateIncludeState(fullpath, include_state, io):
4445 header_found = True
4446
4447 # If we can't find the header file for a .cc, assume it's because we don't
4448 # know where to look. In that case we'll give up as we're not sure they
4449 # didn't include it in the .h file.
4450 # TODO(unknown): Do a better job of finding .h files so we are confident that
4451 # not having the .h file means there isn't one.
4452 if filename.endswith('.cc') and not header_found:
4453 return
4454
4455 # All the lines have been processed, report the errors found.
4456 for required_header_unstripped in required:
4457 template = required[required_header_unstripped][1]
4458 if required_header_unstripped.strip('<>"') not in include_state:
4459 error(filename, required[required_header_unstripped][0],
4460 'build/include_what_you_use', 4,
4461 'Add #include ' + required_header_unstripped + ' for ' + template)
4462
4463
4464 _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')
4465
4466
4467 def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
4468 """Check that make_pair's template arguments are deduced.
4469
4470 G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are
4471 specified explicitly, and such use isn't intended in any case.
4472
4473 Args:
4474 filename: The name of the current file.
4475 clean_lines: A CleansedLines instance containing the file.
4476 linenum: The number of the line to check.
4477 error: The function to call with any errors found.
4478 """
4479 line = clean_lines.elided[linenum]
4480 match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
4481 if match:
4482 error(filename, linenum, 'build/explicit_make_pair',
4483 4, # 4 = high confidence
4484 'For C++11-compatibility, omit template arguments from make_pair'
4485 ' OR use pair directly OR if appropriate, construct a pair directly')
4486
4487
4488 def ProcessLine(filename, file_extension, clean_lines, line,
4489 include_state, function_state, nesting_state, error,
4490 extra_check_functions=[]):
4491 """Processes a single line in the file.
4492
4493 Args:
4494 filename: Filename of the file that is being processed.
4495 file_extension: The extension (dot not included) of the file.
4496 clean_lines: An array of strings, each representing a line of the file,
4497 with comments stripped.
4498 line: Number of line being processed.
4499 include_state: An _IncludeState instance in which the headers are inserted.
4500 function_state: A _FunctionState instance which counts function lines, etc.
4501 nesting_state: A _NestingState instance which maintains information about
4502 the current stack of nested blocks being parsed.
4503 error: A callable to which errors are reported, which takes 4 arguments:
4504 filename, line number, error level, and message
4505 extra_check_functions: An array of additional check functions that will be
4506 run on each source line. Each function takes 4
4507 arguments: filename, clean_lines, line, error
4508 """
4509 raw_lines = clean_lines.raw_lines
4510 ParseNolintSuppressions(filename, raw_lines[line], line, error)
4511 nesting_state.Update(filename, clean_lines, line, error)
4512 if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM:
4513 return
4514 CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
4515 CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
4516 CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
4517 CheckLanguage(filename, clean_lines, line, file_extension, include_state,
4518 nesting_state, error)
4519 CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
4520 CheckForNonStandardConstructs(filename, clean_lines, line,
4521 nesting_state, error)
4522 CheckVlogArguments(filename, clean_lines, line, error)
4523 CheckPosixThreading(filename, clean_lines, line, error)
4524 CheckInvalidIncrement(filename, clean_lines, line, error)
4525 CheckMakePairUsesDeduction(filename, clean_lines, line, error)
4526 for check_fn in extra_check_functions:
4527 check_fn(filename, clean_lines, line, error)
4528
4529 def ProcessFileData(filename, file_extension, lines, error,
4530 extra_check_functions=[]):
4531 """Performs lint checks and reports any errors to the given error function.
4532
4533 Args:
4534 filename: Filename of the file that is being processed.
4535 file_extension: The extension (dot not included) of the file.
4536 lines: An array of strings, each representing a line of the file, with the
4537 last element being empty if the file is terminated with a newline.
4538 error: A callable to which errors are reported, which takes 4 arguments:
4539 filename, line number, error level, and message
4540 extra_check_functions: An array of additional check functions that will be
4541 run on each source line. Each function takes 4
4542 arguments: filename, clean_lines, line, error
4543 """
4544 lines = (['// marker so line numbers and indices both start at 1'] + lines +
4545 ['// marker so line numbers end in a known way'])
4546
4547 include_state = _IncludeState()
4548 function_state = _FunctionState()
4549 nesting_state = _NestingState()
4550
4551 ResetNolintSuppressions()
4552
4553 CheckForCopyright(filename, lines, error)
4554
4555 if file_extension in ['h', 'hpp']:
4556 CheckForHeaderGuard(filename, lines, error)
4557
4558 RemoveMultiLineComments(filename, lines, error)
4559 clean_lines = CleansedLines(lines)
4560 for line in xrange(clean_lines.NumLines()):
4561 ProcessLine(filename, file_extension, clean_lines, line,
4562 include_state, function_state, nesting_state, error,
4563 extra_check_functions)
4564 nesting_state.CheckCompletedBlocks(filename, error)
4565
4566 CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
4567
4568 # We check here rather than inside ProcessLine so that we see raw
4569 # lines rather than "cleaned" lines.
4570 CheckForBadCharacters(filename, lines, error)
4571
4572 CheckForNewlineAtEOF(filename, lines, error)
4573
4574 def ProcessFile(filename, vlevel, extra_check_functions=[]):
4575 """Does google-lint on a single file.
4576
4577 Args:
4578 filename: The name of the file to parse.
4579
4580 vlevel: The level of errors to report. Every error of confidence
4581 >= verbose_level will be reported. 0 is a good default.
4582
4583 extra_check_functions: An array of additional check functions that will be
4584 run on each source line. Each function takes 4
4585 arguments: filename, clean_lines, line, error
4586 """
4587
4588 _SetVerboseLevel(vlevel)
4589
4590 try:
4591 # Support the UNIX convention of using "-" for stdin. Note that
4592 # we are not opening the file with universal newline support
4593 # (which codecs doesn't support anyway), so the resulting lines do
4594 # contain trailing '\r' characters if we are reading a file that
4595 # has CRLF endings.
4596 # If after the split a trailing '\r' is present, it is removed
4597 # below. If it is not expected to be present (i.e. os.linesep !=
4598 # '\r\n' as in Windows), a warning is issued below if this file
4599 # is processed.
4600
4601 if filename == '-':
4602 lines = codecs.StreamReaderWriter(sys.stdin,
4603 codecs.getreader('utf8'),
4604 codecs.getwriter('utf8'),
4605 'replace').read().split('\n')
4606 else:
4607 lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
4608
4609 carriage_return_found = False
4610 # Remove trailing '\r'.
4611 for linenum in range(len(lines)):
4612 if lines[linenum].endswith('\r'):
4613 lines[linenum] = lines[linenum].rstrip('\r')
4614 carriage_return_found = True
4615
4616 except IOError:
4617 sys.stderr.write(
4618 "Skipping input '%s': Can't open for reading\n" % filename)
4619 return
4620
4621 # Note, if no dot is found, this will give the entire filename as the ext.
4622 file_extension = filename[filename.rfind('.') + 1:]
4623
4624 # When reading from stdin, the extension is unknown, so no cpplint tests
4625 # should rely on the extension.
4626 if filename != '-' and file_extension not in _valid_extensions:
4627 sys.stderr.write('Ignoring %s; not a valid file name '
4628 '(%s)\n' % (filename, ', '.join(_valid_extensions)))
4629 else:
4630 ProcessFileData(filename, file_extension, lines, Error,
4631 extra_check_functions)
4632 if carriage_return_found and os.linesep != '\r\n':
4633 # Use 0 for linenum since outputting only one error for potentially
4634 # several lines.
4635 Error(filename, 0, 'whitespace/newline', 1,
4636 'One or more unexpected \\r (^M) found;'
4637 'better to use only a \\n')
4638
4639 sys.stderr.write('Done processing %s\n' % filename)
4640
4641
4642 def PrintUsage(message):
4643 """Prints a brief usage string and exits, optionally with an error message.
4644
4645 Args:
4646 message: The optional error message.
4647 """
4648 sys.stderr.write(_USAGE)
4649 if message:
4650 sys.exit('\nFATAL ERROR: ' + message)
4651 else:
4652 sys.exit(1)
4653
4654
4655 def PrintCategories():
4656 """Prints a list of all the error-categories used by error messages.
4657
4658 These are the categories used to filter messages via --filter.
4659 """
4660 sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
4661 sys.exit(0)
4662
4663
4664 def ParseArguments(args):
4665 """Parses the command line arguments.
4666
4667 This may set the output format and verbosity level as side-effects.
4668
4669 Args:
4670 args: The command line arguments:
4671
4672 Returns:
4673 The list of filenames to lint.
4674 """
4675 try:
4676 (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
4677 'counting=',
4678 'filter=',
4679 'root=',
4680 'linelength=',
4681 'extensions='])
4682 except getopt.GetoptError:
4683 PrintUsage('Invalid arguments.')
4684
4685 verbosity = _VerboseLevel()
4686 output_format = _OutputFormat()
4687 filters = ''
4688 counting_style = ''
4689
4690 for (opt, val) in opts:
4691 if opt == '--help':
4692 PrintUsage(None)
4693 elif opt == '--output':
4694 if val not in ('emacs', 'vs7', 'eclipse'):
4695 PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
4696 output_format = val
4697 elif opt == '--verbose':
4698 verbosity = int(val)
4699 elif opt == '--filter':
4700 filters = val
4701 if not filters:
4702 PrintCategories()
4703 elif opt == '--counting':
4704 if val not in ('total', 'toplevel', 'detailed'):
4705 PrintUsage('Valid counting options are total, toplevel, and detailed')
4706 counting_style = val
4707 elif opt == '--root':
4708 global _root
4709 _root = val
4710 elif opt == '--linelength':
4711 global _line_length
4712 try:
4713 _line_length = int(val)
4714 except ValueError:
4715 PrintUsage('Line length must be digits.')
4716 elif opt == '--extensions':
4717 global _valid_extensions
4718 try:
4719 _valid_extensions = set(val.split(','))
4720 except ValueError:
4721 PrintUsage('Extensions must be comma seperated list.')
4722
4723 if not filenames:
4724 PrintUsage('No files were specified.')
4725
4726 _SetOutputFormat(output_format)
4727 _SetVerboseLevel(verbosity)
4728 _SetFilters(filters)
4729 _SetCountingStyle(counting_style)
4730
4731 return filenames
4732
4733
4734 def main():
4735 filenames = ParseArguments(sys.argv[1:])
4736
4737 # Change stderr to write with replacement characters so we don't die
4738 # if we try to print something containing non-ASCII characters.
4739 sys.stderr = codecs.StreamReaderWriter(sys.stderr,
4740 codecs.getreader('utf8'),
4741 codecs.getwriter('utf8'),
4742 'replace')
4743
4744 _cpplint_state.ResetErrorCounts()
4745 for filename in filenames:
4746 ProcessFile(filename, _cpplint_state.verbose_level)
4747 _cpplint_state.PrintErrorCounts()
4748
4749 sys.exit(_cpplint_state.error_count > 0)
4750
4751
4752 if __name__ == '__main__':
4753 main()
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'octokit'
4
5 class ChangelogGenerator
6 attr_reader :version, :entries, :unlabeled_prs
7
8 def initialize(version)
9 unless version
10 warn 'Usage: generate_changelog.rb VERSION'
11 exit 1
12 end
13
14 @version = version
15 @entries = {
16 'feature' => { name: 'Added', entries: {} },
17 'bugfix' => { name: 'Fixed', entries: {} },
18 'backwards-incompatible' => { name: 'Changed', entries: {} }
19 }
20
21 @unlabeled_prs = []
22
23 # Setting the changelog path early lets us check that it exists
24 # before we spend time making API calls
25 changelog
26 end
27
28 def labels
29 @entries.keys
30 end
31
32 def client
33 unless @client
34 unless ENV['GITHUB_TOKEN']
35 warn 'Missing GitHub personal access token. Set $GITHUB_TOKEN with a '\
36 'personal access token to use this script.'
37 exit 1
38 end
39
40 Octokit.configure do |c|
41 c.auto_paginate = true
42 end
43
44 @client = Octokit::Client.new(access_token: ENV['GITHUB_TOKEN'])
45 end
46
47 @client
48 end
49
50 def latest
51 @latest ||= client.latest_release('puppetlabs/facter').tag_name
52 end
53
54 def commits
55 @commits ||= client.compare('puppetlabs/facter', latest, 'main').commits
56 end
57
58 def changelog
59 unless @changelog
60 @changelog = File.expand_path('CHANGELOG.md', Dir.pwd)
61
62 unless File.file?(@changelog)
63 warn "Unable to find changelog at #{@changelog}"
64 exit 1
65 end
66 end
67
68 @changelog
69 end
70
71 # Parses individual commits by scanning the commit message for valid release notes
72 # and adding them to the list of entries. Entries include extra information about
73 # the author and whether it was an internal or external contribution so we can give
74 # kudos.
75 def parse_commit(commit)
76 prs = client.commit_pulls('puppetlabs/facter', commit.sha, { accept: 'application/vnd.github.groot-preview+json' })
77
78 prs.each do |pr|
79 next if pr[:state] != 'closed' && pr[:merged_at].nil?
80
81 if pr[:labels].nil? || pr[:labels].empty?
82 unlabeled_prs << pr[:html_url] unless unlabeled_prs.include?(pr[:html_url])
83 end
84
85 pr[:labels].each do |label|
86 next unless entries.key?(label[:name])
87
88 entries[label[:name]][:entries][pr[:html_url]] = {
89 title: pr[:title],
90 number: pr[:number],
91 url: pr[:html_url],
92 author: pr[:user][:login],
93 profile: pr[:user][:html_url]
94 }
95 end
96 end
97 end
98
99 def update_changelog
100 old_lines = File.read(changelog).split("\n")
101
102 new_lines = [
103 "## [#{version}](https://github.com/puppetlabs/facter/tree/#{version}) (#{Time.now.strftime '%Y-%m-%d'})\n",
104 "[Full Changelog](https://github.com/puppetlabs/facter/compare/#{latest}...#{version})"
105 ]
106
107 entries.each_value do |type|
108 next unless type[:entries].any?
109
110 new_lines << "\n### #{type[:name]}\n"
111
112 type[:entries].each do |_, entry|
113 new_lines << "- #{entry[:title].strip} [\##{entry[:number]}](#{entry[:url]})" \
114 " ([#{entry[:author]}](#{entry[:profile]}))"
115 end
116 end
117
118 new_lines = check_unlabeled_prs(new_lines)
119
120 content = (new_lines + ["\n"] + old_lines).join("\n")
121
122 if File.write(changelog, content)
123 puts "Successfully wrote entries to #{changelog}"
124 else
125 warn "Unable to write entries to #{changelog}"
126 exit 1
127 end
128 end
129
130 def check_unlabeled_prs(content)
131 return content unless unlabeled_prs.any?
132
133 content << "\n### Unlabeled PRs:\n"
134 unlabeled_prs.each do |pr|
135 content << "- #{pr}"
136 end
137
138 content
139 end
140
141 def generate
142 puts "Loading and parsing commits for #{latest}..main"
143
144 commits.each do |commit|
145 parse_commit(commit)
146 end
147
148 if entries.each_value.all? { |type| type[:entries].empty? }
149 warn "No release notes for #{latest}..main"
150 exit 0
151 end
152
153 update_changelog
154 end
155 end
156
157 ChangelogGenerator.new(ARGV.first).generate if $PROGRAM_NAME == __FILE__
+0
-83
scripts/travis_target.sh less more
0 #!/bin/bash
1 set -ev
2
3 # Set compiler to GCC 4.8 here, as Travis overrides the global variables.
4 export CC=gcc-4.8 CXX=g++-4.8
5
6 if [ ${TRAVIS_TARGET} == CPPCHECK ]; then
7 # grab a pre-built cppcheck from s3
8 wget https://s3.amazonaws.com/kylo-pl-bucket/pcre-8.36_install.tar.bz2
9 tar xjvf pcre-8.36_install.tar.bz2 --strip 1 -C $USERDIR
10 wget https://s3.amazonaws.com/kylo-pl-bucket/cppcheck-1.69_install.tar.bz2
11 tar xjvf cppcheck-1.69_install.tar.bz2 --strip 1 -C $USERDIR
12 elif [ ${TRAVIS_TARGET} == DOXYGEN ]; then
13 # grab a pre-built doxygen 1.8.7 from s3
14 wget https://s3.amazonaws.com/kylo-pl-bucket/doxygen_install.tar.bz2
15 tar xjvf doxygen_install.tar.bz2 --strip 1 -C $USERDIR
16 elif [[ ${TRAVIS_TARGET} == DEBUG || ${TRAVIS_TARGET} == RELEASE ]]; then
17 # Prepare for generating FACTER.pot and ensure it happens every time.
18 # Ensure this happens before calling CMake.
19 wget https://s3.amazonaws.com/kylo-pl-bucket/gettext-0.19.6_install.tar.bz2
20 tar xjf gettext-0.19.6_install.tar.bz2 --strip 1 -C $USERDIR
21 rm -f locales/FACTER.pot
22
23 if [ ${TRAVIS_TARGET} == DEBUG ]; then
24 # Install coveralls.io update utility
25 pip install --user cpp-coveralls
26 fi
27 fi
28
29 # Generate build files
30 if [ ${TRAVIS_TARGET} == COMMITS ]; then
31 shopt -s nocasematch
32 git log --no-merges --pretty=%s master..$HEAD | while read line ; do
33 if [[ ! "$line" =~ ^\((maint|doc|docs|packaging|fact-[0-9]+)\)|revert ]]; then
34 echo -e \
35 "\n\n\n\tThis commit summary didn't match CONTRIBUTING.md guidelines:\n" \
36 "\n\t\t$line\n" \
37 "\tThe commit summary (i.e. the first line of the commit message) should start with one of:\n" \
38 "\t\t(FACT-<digits>) # this is most common and should be a ticket at tickets.puppetlabs.com\n" \
39 "\t\t(doc)\n" \
40 "\t\t(docs)\n" \
41 "\t\t(maint)\n" \
42 "\t\t(packaging)\n" \
43 "\n\tThis test for the commit summary is case-insensitive.\n\n\n"
44 exit 1
45 fi
46 done
47 exit 0
48 elif [ ${TRAVIS_TARGET} == DEBUG ]; then
49 cmake -DCMAKE_BUILD_TYPE=Debug -DCOVERALLS=ON -DCMAKE_PREFIX_PATH=$USERDIR .
50 else
51 cmake -DCMAKE_PREFIX_PATH=$USERDIR .
52 fi
53
54 if [ ${TRAVIS_TARGET} == CPPLINT ]; then
55 make cpplint
56 elif [ ${TRAVIS_TARGET} == DOXYGEN ]; then
57 # Build docs
58 pushd lib
59 doxygen 2>&1 | ( ! grep . )
60 ruby docs/generate.rb > /tmp/facts.md
61 popd
62 elif [ ${TRAVIS_TARGET} == CPPCHECK ]; then
63 make cppcheck
64 else
65 make -j2
66
67 # Install the bundle before testing
68 bundle install --gemfile=lib/Gemfile
69 make test ARGS=-V
70
71 # Make sure installation succeeds
72 make DESTDIR=$USERDIR install
73
74 if [ ${TRAVIS_TARGET} == DEBUG ]; then
75 # Ignore coveralls failures, keep service success uncoupled
76 coveralls --gcov gcov-4.8 --gcov-options '\-lp' -e vendor >/dev/null || true
77 else
78 # Ensure the gem is buildable
79 gem build .gemspec
80 fi
81 fi
82
0 # frozen_string_literal: true
1
2 describe Facter::Core::Aggregate do
3 subject(:aggregate_res) { Facter::Core::Aggregate.new('aggregated', fact) }
4
5 let(:fact) { double('stub_fact', name: 'stub_fact') }
6
7 it 'can be resolved' do
8 expect(aggregate_res).to be_a_kind_of LegacyFacter::Core::Resolvable
9 end
10
11 it 'can be confined and weighted' do
12 expect(aggregate_res).to be_a_kind_of LegacyFacter::Core::Suitable
13 end
14
15 it 'can be compared' do
16 aggregate1 = Facter::Core::Aggregate.new('aggregated1', fact)
17 aggregate1.options(weight: 1)
18 aggregate2 = Facter::Core::Aggregate.new('aggregated2', fact)
19 aggregate2.options(weight: 2)
20 aggregate3 = Facter::Core::Aggregate.new('aggregated3', fact)
21 aggregate3.options(weight: 3)
22
23 expect(
24 [aggregate1, aggregate2, aggregate3].sort { |a, b| b <=> a }
25 ).to eq([aggregate3, aggregate2, aggregate1])
26 end
27
28 describe 'setting options' do
29 it 'can set the timeout' do
30 aggregate_res.options(timeout: 314)
31 expect(aggregate_res.limit).to eq 314
32 end
33
34 it 'can set the weight' do
35 aggregate_res.options(weight: 27)
36 expect(aggregate_res.weight).to eq 27
37 end
38
39 it 'can set the name' do
40 aggregate_res.options(name: 'something')
41 expect(aggregate_res.name).to eq 'something'
42 end
43
44 it 'fails on unhandled options' do
45 expect do
46 aggregate_res.options(foo: 'bar')
47 end.to raise_error(ArgumentError, /Invalid aggregate options .*foo/)
48 end
49
50 it 'fact_type does not raise error' do
51 expect { aggregate_res.options(fact_type: 'custom') }.not_to raise_error
52 end
53 end
54
55 describe 'declaring chunks' do
56 it 'requires that an chunk is given a block' do
57 expect { aggregate_res.chunk(:fail) }.to raise_error(ArgumentError, /requires a block/)
58 end
59
60 it 'allows an chunk to have a list of requirements' do
61 aggregate_res.chunk(:data, require: [:other]) {}
62 expect(aggregate_res.deps[:data]).to eq [:other]
63 end
64
65 it 'converts a single chunk requirement to an array' do
66 aggregate_res.chunk(:data, require: :other) {}
67 expect(aggregate_res.deps[:data]).to eq [:other]
68 end
69
70 it 'raises an error when an unhandled option is passed' do
71 expect do
72 aggregate_res.chunk(:data, before: [:other]) {}
73 end.to raise_error(ArgumentError, /Unexpected options.*#chunk: .*before/)
74 end
75 end
76
77 describe 'handling interactions between chunks' do
78 it 'generates an error when there is a dependency cycle in chunks' do
79 aggregate_res.chunk(:first, require: [:second]) {}
80 aggregate_res.chunk(:second, require: [:first]) {}
81
82 allow(Facter).to receive(:log_exception).with(StandardError, /dependency cycles: .*[:first, :second]/)
83 expect { aggregate_res.value }.to raise_error(Facter::ResolveCustomFactError)
84 end
85
86 it 'passes all requested chunk results to the depending chunk' do
87 aggregate_res.chunk(:first) { ['foo'] }
88 aggregate_res.chunk(:second, require: [:first]) do |first|
89 [first[0] + ' bar']
90 end
91
92 output = aggregate_res.value
93 expect(output).to match_array(['foo', 'foo bar'])
94 end
95
96 it 'clones and freezes chunk results' do
97 aggregate_res.chunk(:first) { 'foo' }
98 aggregate_res.chunk(:second, require: [:first]) do |first|
99 expect(first).to be_frozen
100 end
101 end
102
103 it 'clones and freezes chunk results passed to other chunks' do
104 aggregate_res.chunk(:first) { 'foo' }
105 aggregate_res.chunk(:second, require: [:first]) do |first|
106 allow(first).to be_frozen
107 end
108
109 aggregate_res.aggregate do |chunks|
110 expect_all(chunks).to be_frozen
111 end
112 end
113 end
114
115 describe 'aggregating chunks' do
116 it 'passes all chunk results as a hash to the aggregate block' do
117 aggregate_res.chunk(:data) { 'data chunk' }
118 aggregate_res.chunk(:datum) { 'datum chunk' }
119
120 aggregate_res.aggregate do |chunks|
121 expect(chunks).to eq(data: 'data chunk', datum: 'datum chunk')
122 end
123
124 aggregate_res.value
125 end
126
127 it 'uses the result of the aggregate block as the value' do
128 aggregate_res.aggregate { 'who needs chunks anyways' }
129 expect(aggregate_res.value).to eq 'who needs chunks anyways'
130 end
131 end
132
133 describe 'evaluating' do
134 it 'evaluates the block in the context of the aggregate' do
135 expect(aggregate_res).to receive(:has_weight).with(5)
136 aggregate_res.evaluate { has_weight(5) }
137 end
138 end
139 end
0 # frozen_string_literal: true
1
2 describe LegacyFacter::Core::DirectedGraph do
3 subject(:graph) { LegacyFacter::Core::DirectedGraph.new }
4
5 describe 'detecting cycles' do
6 it 'is acyclic if the graph is empty' do
7 expect(graph).to be_acyclic
8 end
9
10 it 'is acyclic if the graph has no edges' do
11 graph[:one] = []
12 graph[:two] = []
13
14 expect(graph).to be_acyclic
15 end
16
17 it 'is acyclic if a vertex has an edge to itself' do
18 graph[:one] = [:one]
19 expect(graph).to be_acyclic
20 end
21
22 it 'is acyclic if there are no loops in the graph' do
23 graph[:one] = %i[two three]
24 graph[:two] = [:four]
25 graph[:three] = [:four]
26 graph[:four] = []
27
28 expect(graph).to be_acyclic
29 end
30
31 it 'is cyclic if there is a loop in the graph' do
32 graph[:one] = [:two]
33 graph[:two] = [:one]
34 expect(graph).not_to be_acyclic
35 end
36
37 it 'can return the cycles in the graph' do
38 graph[:one] = [:two]
39 graph[:two] = [:one]
40
41 graph[:three] = [:four]
42 graph[:four] = [:three]
43
44 first_cycle = graph.cycles.find { |cycle| cycle.include? :one }
45
46 expect(first_cycle).to match_array(%i[one two])
47 end
48 end
49
50 describe 'sorting' do
51 it 'returns the vertices in topologically sorted order' do
52 graph[:one] = %i[two three]
53 graph[:two] = [:three]
54 graph[:three] = []
55 expect(graph.tsort).to eq %i[three two one]
56 end
57
58 it 'raises an error if there is a cycle in the graph' do
59 graph[:one] = [:two]
60 graph[:two] = [:one]
61
62 expect do
63 graph.tsort
64 end.to raise_error(LegacyFacter::Core::DirectedGraph::CycleError, /found the following cycles:/)
65 end
66
67 it 'raises an error if there is an edge to a non-existent vertex' do
68 graph[:one] = %i[two three]
69 graph[:two] = [:three]
70 expect do
71 graph.tsort
72 end.to raise_error(LegacyFacter::Core::DirectedGraph::MissingVertex, /missing elements.*three/)
73 end
74 end
75 end
0 # frozen_string_literal: true
1
2 describe Facter::Core::Execution::Base do
3 subject(:executor) { Facter::Core::Execution::Posix.new }
4
5 describe '#with_env' do
6 it "executes the caller's block with the specified env vars" do
7 test_env = { 'LANG' => 'C', 'LC_ALL' => 'C', 'FOO' => 'BAR' }
8 executor.with_env test_env do
9 test_env.keys.each do |key|
10 expect(ENV[key]).to eq test_env[key]
11 end
12 end
13 end
14
15 it 'restores pre-existing environment variables' do
16 orig_env = {}
17 new_env = {}
18 # an arbitrary sentinel value to use to temporarily set the environment vars to
19 sentinel_value = 'Abracadabra'
20
21 # grab some values from the existing ENV (arbitrarily choosing 3 here)
22 ENV.keys.first(3).each do |key|
23 # save the original values so that we can test against them later
24 orig_env[key] = ENV[key]
25 # create bogus temp values for the chosen keys
26 new_env[key] = sentinel_value
27 end
28
29 # verify that, during the 'with_env', the new values are used
30 executor.with_env new_env do
31 orig_env.keys.each do |key|
32 expect(ENV[key]).to eq new_env[key]
33 end
34 end
35 end
36
37 it 'restores pre-existing environment variables to their previous values' do
38 orig_env = {}
39 new_env = {}
40 # an arbitrary sentinel value to use to temporarily set the environment vars to
41 sentinel_value = 'Abracadabra'
42
43 # grab some values from the existing ENV (arbitrarily choosing 3 here)
44 ENV.keys.first(3).each do |key|
45 # save the original values so that we can test against them later
46 orig_env[key] = ENV[key]
47 # create bogus temp values for the chosen keys
48 new_env[key] = sentinel_value
49 end
50
51 # verify that, after the 'with_env', the old values are restored
52 orig_env.keys.each do |key|
53 expect(ENV[key]).to eq orig_env[key]
54 end
55 end
56
57 it "is not affected by a 'return' statement in the yield block" do
58 @sentinel_var = :resolution_test_foo.to_s
59
60 # the intent of this test case is to test a yield block that contains a return statement. However, it's illegal
61 # to use a return statement outside of a method, so we need to create one here to give scope to the 'return'
62 def handy_method
63 ENV[@sentinel_var] = 'foo'
64 new_env = { @sentinel_var => 'bar' }
65
66 executor.with_env new_env do
67 ENV[@sentinel_var] = 'bar'
68 return
69 end
70 end
71
72 handy_method
73
74 expect(ENV[@sentinel_var]).to eq 'foo'
75 end
76 end
77
78 describe '#execute' do
79 it 'expands the command before running it' do
80 allow(File).to receive(:executable?).and_return(false)
81 allow(FileTest).to receive(:file?).and_return(false)
82 allow(File).to receive(:executable?).with('/sbin/foo').and_return(true)
83 allow(FileTest).to receive(:file?).with('/sbin/foo').and_return(true)
84 expect(Facter::Core::Execution::Popen3).to receive(:popen3e).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, '/sbin/foo')
85 .and_return('')
86 executor.execute('foo')
87 end
88
89 context 'with expand on posix' do
90 let(:test_env) { { 'LANG' => 'C', 'LC_ALL' => 'C', 'PATH' => '/sbin' } }
91
92 before do
93 test_env.each do |key, value|
94 allow(ENV).to receive(:[]).with(key).and_return(value)
95 allow(File).to receive(:executable?).with('/bin/foo').and_return(true)
96 allow(FileTest).to receive(:file?).with('/bin/foo').and_return(true)
97 end
98 end
99
100 it 'does not expant builtin command' do
101 allow(Facter::Core::Execution::Popen3).to receive(:popen3e).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, '/bin/foo')
102 .and_return('')
103 allow(Open3).to receive(:capture2).with('type /bin/foo').and_return('builtin')
104 executor.execute('/bin/foo', expand: false)
105 end
106 end
107
108 context 'with expand on windows' do
109 subject(:execution_base) { Facter::Core::Execution::Windows.new }
110
111 let(:test_env) { { 'LANG' => 'C', 'LC_ALL' => 'C', 'PATH' => '/sbin' } }
112
113 before do
114 test_env.each do |key, value|
115 allow(ENV).to receive(:[]).with(key).and_return(value)
116 end
117 end
118
119 it 'throws exception' do
120 allow(Facter::Core::Execution::Popen3).to receive(:popen3e).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, 'foo')
121 .and_return('')
122 allow(Open3).to receive(:capture2).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, 'type foo').and_return('builtin')
123 expect { execution_base.execute('foo', expand: false) }
124 .to raise_error(ArgumentError,
125 'Unsupported argument on Windows expand with value false')
126 end
127 end
128
129 context 'when there are stderr messages from file' do
130 let(:logger) { instance_spy(Facter::Log) }
131 let(:command) { '/bin/foo' }
132
133 before do
134 allow(Facter::Core::Execution::Popen3).to receive(:popen3e).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, command)
135 .and_return(['', 'some error'])
136 allow(Facter::Log).to receive(:new).with(executor).and_return(logger)
137 allow(Facter::Log).to receive(:new).with('foo').and_return(logger)
138
139 allow(File).to receive(:executable?).with(command).and_return(true)
140 allow(FileTest).to receive(:file?).with(command).and_return(true)
141 end
142
143 it 'loggs warning messages on stderr' do
144 executor.execute(command)
145 expect(logger).to have_received(:debug).with('Command /bin/foo completed with '\
146 'the following stderr message: some error')
147 end
148 end
149
150 describe 'and the command is not present' do
151 before do
152 allow(File).to receive(:executable?).and_return(false)
153 allow(FileTest).to receive(:file?).and_return(false)
154 end
155
156 it 'raises an error when the :on_fail behavior is :raise' do
157 expect { executor.execute('foo') }.to raise_error(Facter::Core::Execution::ExecutionFailure)
158 end
159
160 it 'returns the given value when :on_fail is set to a value' do
161 expect(executor.execute('foo', on_fail: nil)).to be_nil
162 end
163 end
164
165 describe 'when command execution fails' do
166 before do
167 allow(Facter::Core::Execution::Popen3).to receive(:popen3e).with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, '/bin/foo')
168 .and_raise('kaboom!')
169 allow(File).to receive(:executable?).and_return(false)
170 allow(FileTest).to receive(:file?).and_return(false)
171 allow(File).to receive(:executable?).with('/bin/foo').and_return(true)
172 allow(FileTest).to receive(:file?).with('/bin/foo').and_return(true)
173 end
174
175 it 'raises an error when the :on_fail behavior is :raise' do
176 expect { executor.execute('foo') }.to raise_error(Facter::Core::Execution::ExecutionFailure)
177 end
178
179 it 'returns the given value when :on_fail is set to a value' do
180 expect(executor.execute('foo', on_fail: nil)).to be_nil
181 end
182 end
183
184 context 'when expand command succeds' do
185 before do
186 allow(File).to receive(:executable?).and_return(false)
187 allow(FileTest).to receive(:file?).and_return(false)
188 allow(File).to receive(:executable?).with('/sbin/foo').and_return(true)
189 allow(FileTest).to receive(:file?).with('/sbin/foo').and_return(true)
190 end
191
192 it 'returns the output of the command' do
193 allow(Facter::Core::Execution::Popen3).to receive(:popen3e)
194 .with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, '/sbin/foo')
195 .and_return('hi')
196
197 expect(executor.execute('foo')).to eq 'hi'
198 end
199
200 it 'strips off trailing newlines' do
201 allow(Facter::Core::Execution::Popen3).to receive(:popen3e)
202 .with({ 'LC_ALL' => 'C', 'LANG' => 'C' }, '/sbin/foo')
203 .and_return "hi\n"
204
205 expect(executor.execute('foo')).to eq 'hi'
206 end
207 end
208 end
209 end
0 describe Facter::Core::Execution::Posix, unless: LegacyFacter::Util::Config.windows? do
1 let(:posix_executor) { Facter::Core::Execution::Posix.new }
2
3 describe '#search_paths' do
4 it 'uses the PATH environment variable plus /sbin and /usr/sbin on unix' do
5 allow(ENV).to receive(:[]).with('PATH').and_return '/bin:/usr/bin'
6 expect(posix_executor.search_paths). to eq %w[/bin /usr/bin /sbin /usr/sbin]
7 end
8 end
9
10 describe '#which' do
11 before do
12 allow(posix_executor).to receive(:search_paths).and_return ['/bin', '/sbin', '/usr/sbin']
13 end
14
15 context 'when provided with an absolute path' do
16 it 'returns the binary if executable' do
17 allow(File).to receive(:executable?).with('/opt/foo').and_return(true)
18 allow(FileTest).to receive(:file?).with('/opt/foo').and_return true
19 expect(posix_executor.which('/opt/foo')).to eq '/opt/foo'
20 end
21
22 it 'returns nil if the binary is not executable' do
23 allow(File).to receive(:executable?).with('/opt/foo').and_return(false)
24 expect(posix_executor.which('/opt/foo')).to be nil
25 end
26
27 it 'returns nil if the binary is not a file' do
28 allow(File).to receive(:executable?).with('/opt/foo').and_return(true)
29 allow(FileTest).to receive(:file?).with('/opt/foo').and_return false
30 expect(posix_executor.which('/opt/foo')).to be nil
31 end
32 end
33
34 context "when it isn't provided with an absolute path" do
35 it 'returns the absolute path if found' do
36 allow(File).to receive(:executable?).with('/bin/foo').and_return false
37 allow(FileTest).to receive(:file?).with('/sbin/foo').and_return true
38 allow(File).to receive(:executable?).with('/sbin/foo').and_return true
39 expect(posix_executor.which('foo')).to eq '/sbin/foo'
40 end
41
42 it 'returns nil if not found' do
43 allow(File).to receive(:executable?).with('/bin/foo').and_return false
44 allow(File).to receive(:executable?).with('/sbin/foo').and_return false
45 allow(File).to receive(:executable?).with('/usr/sbin/foo').and_return false
46 expect(posix_executor.which('foo')).to be nil
47 end
48 end
49 end
50
51 describe '#expand_command' do
52 it 'expands binary' do
53 allow(posix_executor).to receive(:which).with('foo').and_return '/bin/foo'
54 expect(posix_executor.expand_command('foo -a | stuff >> /dev/null')).to eq '/bin/foo -a | stuff >> /dev/null'
55 end
56
57 it 'expands double quoted binary' do
58 allow(posix_executor).to receive(:which).with('/tmp/my foo').and_return '/tmp/my foo'
59 expect(posix_executor.expand_command('"/tmp/my foo" bar')).to eq "'/tmp/my foo' bar"
60 end
61
62 it 'expands single quoted binary' do
63 allow(posix_executor).to receive(:which).with('my foo').and_return '/home/bob/my path/my foo'
64 expect(posix_executor.expand_command("'my foo' -a")).to eq "'/home/bob/my path/my foo' -a"
65 end
66
67 it 'quotes expanded binary if found in path with spaces' do
68 allow(posix_executor).to receive(:which).with('foo.sh').and_return '/home/bob/my tools/foo.sh'
69 expect(posix_executor.expand_command('foo.sh /a /b')).to eq "'/home/bob/my tools/foo.sh' /a /b"
70 end
71
72 it 'expands a multi-line command with single quotes' do
73 allow(posix_executor).to receive(:which).with('dpkg-query').and_return '/usr/bin/dpkg-query'
74 expect(posix_executor.expand_command(
75 "dpkg-query --showformat='${PACKAGE} ${VERSION}\n' --show | egrep '(^samba)"
76 )).to eq("/usr/bin/dpkg-query --showformat='${PACKAGE} ${VERSION}\n' --show | egrep '(^samba)")
77 end
78
79 it 'expands a multi-line command with double quotes' do
80 allow(posix_executor).to receive(:which).with('dpkg-query').and_return '/usr/bin/dpkg-query'
81 expect(posix_executor.expand_command(
82 "dpkg-query --showformat='${PACKAGE} ${VERSION}\n\" --show | egrep \"(^samba)"
83 )).to eq("/usr/bin/dpkg-query --showformat='${PACKAGE} ${VERSION}\n\" --show | egrep \"(^samba)")
84 end
85
86 it 'returns nil if not found' do
87 allow(posix_executor).to receive(:which).with('foo').and_return nil
88 expect(posix_executor.expand_command('foo -a | stuff >> /dev/null')).to be nil
89 end
90 end
91
92 describe '#absolute_path?' do
93 %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path|
94 it "returns true for #{path}" do
95 expect(posix_executor).to be_absolute_path(path)
96 end
97 end
98
99 %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path|
100 it "returns false for #{path}" do
101 expect(posix_executor).not_to be_absolute_path(path)
102 end
103 end
104 end
105 end
0 describe Facter::Core::Execution::Windows do
1 let(:executor) { Facter::Core::Execution::Windows.new }
2
3 before do
4 allow(LegacyFacter).to receive(:value).and_return('Windows')
5 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
6
7 stub_const('::File::PATH_SEPARATOR', ';')
8 stub_const('File::ALT_SEPARATOR', '\\')
9 end
10
11 describe '#search_paths' do
12 it 'uses the PATH environment variable to determine locations' do
13 allow(ENV).to receive(:[]).with('PATH').and_return 'C:\Windows;C:\Windows\System32'
14 expect(executor.search_paths).to eq %w[C:\Windows C:\Windows\System32]
15 end
16 end
17
18 describe '#execute' do
19 context 'when expand is false' do
20 subject(:executor) { Facter::Core::Execution::Windows.new }
21
22 it 'raises exception' do
23 expect { executor.execute('c:\foo.exe', expand: false) }
24 .to raise_error(ArgumentError,
25 'Unsupported argument on Windows expand with value false')
26 end
27 end
28 end
29
30 describe '#which' do
31 before do
32 allow(executor)
33 .to receive(:search_paths)
34 .and_return ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem']
35 allow(ENV).to receive(:[]).with('PATHEXT').and_return nil
36 end
37
38 context 'when trying to use builtin windows commands' do
39 it 'allows echo' do
40 expect(executor.which('echo')).to eq 'echo'
41 end
42
43 it 'disallows other builtin windows commands' do
44 expect(executor.which('dir')).to eq nil
45 end
46 end
47
48 context 'when it is provided with an absolute path' do
49 it 'returns the path to binary if executable' do
50 allow(File).to receive(:executable?).with('C:\Tools\foo.exe').and_return true
51
52 expect(executor.which('C:\Tools\foo.exe')).to eq 'C:\Tools\foo.exe'
53 end
54
55 it 'returns the binary if executable' do
56 allow(File).to receive(:executable?).with('\\\\remote\dir\foo.exe').and_return true
57
58 expect(executor.which('\\\\remote\dir\foo.exe')).to eq '\\\\remote\dir\foo.exe'
59 end
60
61 it 'returns nil if the binary path is not executable' do
62 allow(File).to receive(:executable?).with('C:\Tools\foo.exe').and_return false
63
64 expect(executor.which('C:\Tools\foo.exe')).to be nil
65 end
66
67 it 'returns nil if the binary is not executable' do
68 allow(File).to receive(:executable?).with('\\\\remote\dir\foo.exe').and_return false
69
70 expect(executor.which('\\\\remote\dir\foo.exe')).to be nil
71 end
72 end
73
74 context 'when it is not provided with an absolute path' do
75 it 'returns the absolute path if found' do
76 allow(File).to receive(:executable?).with('C:\Windows\system32\foo.exe').and_return false
77 allow(File).to receive(:executable?).with('C:\Windows\foo.exe').and_return true
78
79 expect(executor.which('foo.exe')).to eq 'C:\Windows\foo.exe'
80 end
81
82 it "won't check all paths if one is executable" do
83 allow(File).to receive(:executable?).with('C:\Windows\system32\foo.exe').and_return false
84 allow(File).to receive(:executable?).with('C:\Windows\foo.exe').and_return true
85
86 expect(File).not_to receive(:executable?).with('C:\Windows\System32\Wbem\foo.exe')
87 executor.which('foo.exe')
88 end
89
90 it 'returns the absolute path with file extension if found' do
91 ['.COM', '.EXE', '.BAT', '.CMD', ''].each do |ext|
92 allow(File).to receive(:executable?).with('C:\Windows\system32\foo' + ext).and_return false
93 allow(File).to receive(:executable?).with('C:\Windows\System32\Wbem\foo' + ext).and_return false
94 end
95 ['.COM', '.BAT', '.CMD', ''].each do |ext|
96 allow(File).to receive(:executable?).with('C:\Windows\foo' + ext).and_return false
97 end
98 allow(File).to receive(:executable?).with('C:\Windows\foo.EXE').and_return true
99
100 expect(executor.which('foo')).to eq 'C:\Windows\foo.EXE'
101 end
102
103 it 'returns nil if not found' do
104 allow(File).to receive(:executable?).with('C:\Windows\system32\foo.exe').and_return false
105 allow(File).to receive(:executable?).with('C:\Windows\foo.exe').and_return false
106 allow(File).to receive(:executable?).with('C:\Windows\System32\Wbem\foo.exe').and_return false
107
108 expect(executor.which('foo.exe')).to be nil
109 end
110 end
111 end
112
113 describe '#expand_command' do
114 it 'expands binary' do
115 allow(executor).to receive(:which).with('cmd').and_return 'C:\Windows\System32\cmd'
116
117 expect(executor.expand_command(
118 'cmd /c echo foo > C:\bar'
119 )).to eq 'C:\Windows\System32\cmd /c echo foo > C:\bar'
120 end
121
122 it 'returns nil if not found' do
123 allow(executor).to receive(:which).with('my foo').and_return 'C:\My Tools\my foo.exe'
124 expect(executor.expand_command('"my foo" /a /b')).to eq '"C:\My Tools\my foo.exe" /a /b'
125 end
126
127 it 'does not expand single quoted binary' do
128 allow(executor).to receive(:which).with('\'C:\My').and_return nil
129 expect(executor.expand_command('\'C:\My Tools\foo.exe\' /a /b')).to be nil
130 end
131
132 it 'quotes expanded binary if found in path with spaces' do
133 allow(executor).to receive(:which).with('foo').and_return 'C:\My Tools\foo.exe'
134 expect(executor.expand_command('foo /a /b')).to eq '"C:\My Tools\foo.exe" /a /b'
135 end
136
137 it 'expands a multi-line command with double quotes' do
138 allow(executor).to receive(:which).with('foo').and_return 'C:\My Tools\foo.exe'
139 expect(executor.expand_command(
140 'foo cmd /c\\n" show | grep "(^test)'
141 )).to eq(%q("C:\My Tools\foo.exe" cmd /c\n" show | grep "(^test)))
142 end
143
144 it 'returns nil if command not found' do
145 allow(executor).to receive(:which).with('foo').and_return nil
146 expect(executor.expand_command('foo /a | stuff >> NUL')).to be nil
147 end
148 end
149
150 describe '#absolute_path?' do
151 ['C:/foo',
152 'C:\foo',
153 '\\\\Server\Foo\Bar',
154 '\\\\?\C:\foo\bar',
155 '//Server/Foo/Bar',
156 '//?/C:/foo/bar',
157 '/\?\C:/foo\bar',
158 '\/Server\Foo/Bar',
159 'c:/foo//bar//baz'].each do |path|
160 it "returns true for #{path}" do
161 expect(executor).to be_absolute_path(path)
162 end
163 end
164
165 %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|
166 it "returns false for #{path}" do
167 expect(executor).not_to be_absolute_path(path)
168 end
169 end
170 end
171 end
0 # frozen_string_literal: true
1
2 describe Facter::Core::Execution do
3 subject(:execution) { Facter::Core::Execution }
4
5 let(:windows_impl) { instance_spy(Facter::Core::Execution::Windows) }
6 let(:impl) { Facter::Core::Execution.impl }
7 let(:logger) { impl.instance_variable_get(:@log) }
8
9 before do
10 allow(Facter::Core::Execution::Windows).to receive(:new).and_return(windows_impl)
11 end
12
13 it 'delegates #search_paths to the implementation' do
14 expect(impl).to receive(:search_paths)
15 execution.search_paths
16 end
17
18 it 'delegates #which to the implementation' do
19 expect(impl).to receive(:which).with('waffles')
20 execution.which('waffles')
21 end
22
23 it 'delegates #absolute_path? to the implementation' do
24 expect(impl).to receive(:absolute_path?).with('waffles')
25 execution.absolute_path?('waffles')
26 end
27
28 it 'delegates #absolute_path? with an optional platform to the implementation' do
29 expect(windows_impl).to receive(:absolute_path?).with('waffles')
30 execution.absolute_path?('waffles', :windows)
31 end
32
33 it 'delegates #expand_command to the implementation' do
34 expect(impl).to receive(:expand_command).with('waffles')
35 execution.expand_command('waffles')
36 end
37
38 it 'delegates #exec to #execute' do
39 expect(impl).to receive(:execute).with('waffles', on_fail: nil)
40 execution.exec('waffles')
41 end
42
43 it 'delegates #execute to the implementation' do
44 expect(impl).to receive(:execute).with('waffles', {})
45 execution.execute('waffles')
46 end
47
48 context 'when running an actual command' do
49 before do
50 allow(impl).to receive(:which).with('waffles').and_return('/under/the/honey/are/the/waffles')
51 allow(impl).to receive(:execute_command)
52 allow(logger).to receive(:warn)
53 end
54
55 context 'with default parameters' do
56 it 'executes the found command' do
57 execution.execute('waffles')
58 expect(impl).to have_received(:execute_command).with('/under/the/honey/are/the/waffles', :raise, nil, nil)
59 end
60 end
61
62 context 'with a timeout' do
63 it 'executes the found command with a timeout' do
64 execution.execute('waffles', timeout: 90)
65 expect(impl).to have_received(:execute_command).with('/under/the/honey/are/the/waffles', :raise, nil, 90)
66 end
67 end
68
69 context 'when passing deprecated arguments' do
70 %i[time_limit limit].each do |option|
71 it 'executes the found command with a timeout' do
72 execution.execute('waffles', option => 90)
73 expect(impl).to have_received(:execute_command).with('/under/the/honey/are/the/waffles', :raise, nil, 90)
74 end
75 end
76
77 it 'emits a warning' do
78 execution.execute('waffles', time_limit: 90, bad_opt: true)
79 expect(logger).to have_received(:warn)
80 .with('Unexpected key passed to Facter::Core::Execution.execute option: time_limit,bad_opt' \
81 ' - valid keys: on_fail,expand,logger,timeout')
82 end
83 end
84 end
85 end
0 # frozen_string_literal: true
1
2 describe LegacyFacter::Core::Resolvable do
3 class ResolvableClass
4 def initialize(name)
5 @name = name
6 @fact = Facter::Util::Fact.new('stub fact')
7 end
8 attr_accessor :name, :resolve_value
9 attr_reader :fact
10 include LegacyFacter::Core::Resolvable
11 end
12
13 subject(:resolvable) { ResolvableClass.new('resolvable') }
14
15 it 'has a default timeout of 0 seconds' do
16 expect(resolvable.limit).to eq 0
17 end
18
19 it 'can specify a custom timeout' do
20 resolvable.timeout = 10
21 expect(resolvable.limit).to eq 10
22 end
23
24 describe 'generating a value' do
25 it 'returns the results of #resolve_value' do
26 resolvable.resolve_value = 'stuff'
27 expect(resolvable.value).to eq 'stuff'
28 end
29
30 it 'normalizes the resolved value' do
31 allow(LegacyFacter::Util::Normalization).to receive(:normalize).and_return('stuff')
32 resolvable.resolve_value = 'stuff'
33 expect(resolvable.value).to eq('stuff')
34 end
35
36 it 'raises ResolveCustomFactError' do
37 allow(resolvable).to receive(:resolve_value).and_raise RuntimeError, 'kaboom!'
38 allow(Facter).to receive(:log_exception)
39 .with(RuntimeError, "Error while resolving custom fact fact='stub fact', " \
40 "resolution='resolvable': kaboom!")
41 expect { resolvable.value }.to raise_error(Facter::ResolveCustomFactError)
42 end
43 end
44
45 describe 'timing out' do
46 it 'uses #limit instead of #timeout to determine the timeout period' do
47 expect(resolvable).not_to receive(:timeout)
48 allow(resolvable).to receive(:limit).and_return(25)
49 allow(Timeout).to receive(:timeout).with(25)
50
51 resolvable.value
52 end
53
54 it 'returns nil if the timeout was reached' do
55 allow(Facter).to receive(:log_exception).with(Timeout::Error, /Timed out after 0\.1 seconds while resolving/)
56 allow(Timeout).to receive(:timeout).and_raise Timeout::Error
57
58 resolvable.timeout = 0.1
59
60 expect(resolvable.value).to be_nil
61 end
62 end
63
64 describe 'callbacks when flushing facts' do
65 class FlushFakeError < StandardError; end
66
67 describe '#on_flush' do
68 it 'accepts a block with on_flush' do
69 resolvable.on_flush { raise NotImplementedError }
70 end
71 end
72
73 describe '#flush' do
74 it 'calls the block passed to on_flush' do
75 resolvable.on_flush { raise FlushFakeError }
76 expect { resolvable.flush }.to raise_error FlushFakeError
77 end
78 end
79 end
80 end
0 # frozen_string_literal: true
1
2 describe LegacyFacter::Core::Suitable do
3 class SuitableClass
4 def initialize
5 @confines = []
6 end
7 attr_reader :confines
8 include LegacyFacter::Core::Suitable
9 end
10
11 subject(:suitable_obj) { SuitableClass.new }
12
13 describe 'confining on facts' do
14 it 'can add confines with a fact and a single value' do
15 suitable_obj.confine kernel: 'Linux'
16 end
17
18 it 'creates a Facter::Util::Confine object for the confine call' do
19 suitable_obj.confine kernel: 'Linux'
20 conf = suitable_obj.confines.first
21 expect(conf).to be_an_instance_of(LegacyFacter::Util::Confine).and(
22 having_attributes(fact: :kernel, values: ['Linux'])
23 )
24 end
25 end
26
27 describe 'confining on blocks' do
28 it 'can add a single fact with a block parameter' do
29 suitable_obj.confine(:one) { true }
30 end
31
32 it 'creates a Util::Confine instance for the provided fact with block parameter' do
33 block = -> { true }
34 # Facter::Util::Confine.expects(:new).with("one")
35 expect(LegacyFacter::Util::Confine).to receive(:new).with('one')
36 suitable_obj.confine('one', &block)
37 end
38
39 it 'accepts a single block parameter' do
40 suitable_obj.confine { true }
41 end
42
43 it 'creates a Util::Confine instance for the provided block parameter' do
44 block = -> { true }
45 expect(LegacyFacter::Util::Confine).to receive :new
46
47 suitable_obj.confine(&block)
48 end
49 end
50
51 describe 'determining weight' do
52 it 'is zero if no confines are set' do
53 expect(suitable_obj.weight).to eq 0
54 end
55
56 it 'defaults to the number of confines' do
57 suitable_obj.confine kernel: 'Linux'
58 expect(suitable_obj.weight).to eq 1
59 end
60
61 it 'can be explicitly set' do
62 suitable_obj.has_weight 10
63 expect(suitable_obj.weight).to eq 10
64 end
65
66 it 'prefers an explicit weight over the number of confines' do
67 suitable_obj.confine kernel: 'Linux'
68 suitable_obj.has_weight 11
69 expect(suitable_obj.weight).to eq 11
70 end
71
72 it 'returns the class instance' do
73 expect(suitable_obj.has_weight(10)).to be(suitable_obj)
74 end
75 end
76
77 describe 'determining suitability' do
78 it 'is true if all confines for the object evaluate to true' do
79 suitable_obj.confine kernel: 'Linux'
80 suitable_obj.confine operatingsystem: 'Redhat'
81
82 suitable_obj.confines.each { |confine| allow(confine).to receive(:true?).and_return(true) }
83
84 expect(suitable_obj).to be_suitable
85 end
86
87 it 'is false if any confines for the object evaluate to false' do
88 suitable_obj.confine kernel: 'Linux'
89 suitable_obj.confine operatingsystem: 'Redhat'
90 allow(suitable_obj.confines.first).to receive(:true?).and_return(false)
91
92 expect(suitable_obj).not_to be_suitable
93 end
94
95 it 'recalculates suitability on every invocation' do
96 suitable_obj.confine kernel: 'Linux'
97
98 allow(suitable_obj.confines.first).to receive(:true?).and_return(false)
99 allow(suitable_obj.confines.first).to receive(:true?).and_return(true)
100
101 expect(suitable_obj).to be_suitable
102 end
103 end
104 end
0 # frozen_string_literal: true
1
2 require 'fileutils'
3 require 'tempfile'
4 require 'pathname'
5
6 # A support module for testing files.
7 module PuppetlabsSpec
8 module Files
9 # This code exists only to support tests that run as root, pretty much.
10 # Once they have finally been eliminated this can all go... --daniel 2011-04-08
11 def self.in_tmp(path)
12 tempdir = Dir.tmpdir
13
14 Pathname.new(path).ascend do |dir|
15 return true if File.identical?(tempdir, dir)
16 end
17
18 false
19 end
20
21 def self.cleanup
22 @@global_tempfiles ||= []
23 while (path = @@global_tempfiles.pop)
24 raise "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path)
25
26 begin
27 FileUtils.rm_r path, secure: true
28 rescue Errno::ENOENT
29 puts 'failed to recursively delete files'
30 end
31 end
32 end
33
34 def make_absolute(path)
35 path = File.expand_path(path)
36 path[0] = 'c' if Puppet.features.microsoft_windows?
37 path
38 end
39
40 def tmpfilename(name)
41 # Generate a temporary file, just for the name...
42 source = Tempfile.new(name)
43 path = source.path
44 source.close!
45
46 # ...record it for cleanup,
47 @@global_tempfiles ||= []
48 @@global_tempfiles << File.expand_path(path)
49
50 # ...and bam.
51 path
52 end
53
54 def tmpdir(name)
55 path = tmpfilename(name)
56 FileUtils.mkdir_p(path)
57 path
58 end
59 end
60 end
0 # frozen_string_literal: true
1
2 # Support code for running stuff with warnings disabled.
3 module Kernel
4 def with_verbose_disabled
5 verbose = $VERBOSE
6 $VERBOSE = nil
7 result = yield
8 $VERBOSE = verbose
9 result
10 end
11 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe LegacyFacter::Util::Collection do
4 let(:external_loader) { instance_spy(LegacyFacter::Util::NothingLoader) }
5 let(:internal_loader) do
6 load = LegacyFacter::Util::Loader.new
7 allow(load).to receive(:load).and_return nil
8 allow(load).to receive(:load_all).and_return nil
9 load
10 end
11 let(:collection) { LegacyFacter::Util::Collection.new(internal_loader, external_loader) }
12 let(:logger) { instance_spy(Facter::Log) }
13
14 before do
15 Singleton.__init__(Facter::FactManager)
16 Singleton.__init__(Facter::FactLoader)
17 Singleton.__init__(Facter::ClassDiscoverer)
18 allow(Facter::Log).to receive(:new).and_return(logger)
19 end
20
21 after do
22 LegacyFacter::Util::Collection.instance_variable_set(:@log, nil)
23 end
24
25 it 'delegates its load_all method to its loader' do
26 expect(internal_loader).to receive(:load_all)
27
28 collection.load_all
29 end
30
31 describe 'when adding facts' do
32 it 'creates a new fact if no fact with the same name already exists' do
33 collection.add(:myname)
34 expect(collection.fact(:myname).name).to eq :myname
35 end
36
37 it 'accepts options' do
38 collection.add(:myname, timeout: 1) {}
39 end
40
41 it 'passes resolution specific options to the fact' do
42 fact = Facter::Util::Fact.new(:myname)
43 allow(Facter::Util::Fact).to receive(:new).with(:myname, timeout: 'myval').and_return(fact)
44
45 expect(fact).to receive(:add).with(timeout: 'myval')
46
47 collection.add(:myname, timeout: 'myval') {}
48 end
49
50 describe 'and a block is provided' do
51 it 'uses the block to add a resolution to the fact' do
52 fact = double 'fact'
53 allow(Facter::Util::Fact).to receive(:new).and_return(fact)
54 allow(fact).to receive(:add)
55
56 collection.add(:myname) {}
57
58 expect(fact).to have_received(:add)
59 end
60
61 it 'discards resolutions that throw an exception when added' do
62 allow(logger).to receive(:warn).with(/Unable to add resolve .* kaboom!/)
63
64 expect do
65 collection.add('yay') do
66 raise 'kaboom!'
67 end
68 end.not_to raise_error
69
70 collection.value('yay')
71 end
72 end
73 end
74
75 describe 'when only defining facts' do
76 it 'creates a new fact if no such fact exists' do
77 fact = Facter::Util::Fact.new(:newfact)
78 allow(Facter::Util::Fact).to receive(:new).with(:newfact, {}).and_return fact
79 expect(collection.define_fact(:newfact)).to equal fact
80 end
81
82 it 'returns an existing fact if the fact has already been defined' do
83 fact = collection.define_fact(:newfact)
84 expect(collection.define_fact(:newfact)).to equal fact
85 end
86
87 it 'passes options to newly generated facts' do
88 allow(logger).to receive(:warnonce)
89 fact = collection.define_fact(:newfact, ldapname: 'NewFact')
90 expect(fact.ldapname).to eq 'NewFact'
91 end
92
93 it 'logs an error if the fact could not be defined' do
94 expect(logger).to receive(:log_exception).with('Unable to add fact newfact: kaboom!')
95
96 collection.define_fact(:newfact) do
97 raise 'kaboom!'
98 end
99 end
100 end
101
102 describe 'when retrieving facts' do
103 before do
104 @fact = collection.add('YayNess')
105 end
106
107 it 'returns the fact instance specified by the name' do
108 expect(collection.fact('YayNess')).to equal(@fact)
109 end
110
111 it 'is case-insensitive' do
112 expect(collection.fact('yayness')).to equal(@fact)
113 end
114
115 it 'treats strings and symbols equivalently' do
116 expect(collection.fact(:yayness)).to equal(@fact)
117 end
118
119 it 'uses its loader to try to load the fact if no fact can be found' do
120 expect(collection.internal_loader).to receive(:load).with(:testing)
121 collection.fact('testing')
122 end
123
124 it 'returns nil if it cannot find or load the fact' do
125 allow(collection.internal_loader).to receive(:load).with(:testing)
126 expect(collection.fact('testing')).to be nil
127 end
128 end
129
130 describe "when returning a fact's value" do
131 before do
132 collection.add('YayNess', value: 'result', weight: 0)
133 collection.add('my_fact', value: 'my_fact_value', weight: 0)
134 collection.add('nil_core_value_custom', value: 'custom_fact_value', weight: 0)
135 end
136
137 it 'returns the result of calling :value on the fact' do
138 expect(collection.value('YayNess')).to eq 'result'
139 end
140
141 it 'is case-insensitive' do
142 expect(collection.value('yayness')).to eq 'result'
143 end
144
145 it 'treats strings and symbols equivalently' do
146 expect(collection.value(:yayness)).to eq 'result'
147 end
148
149 describe 'when the weight of the resolution is 0' do
150 before do
151 allow(Facter).to receive(:core_value).with(:yayness).and_return('core_result')
152 allow(Facter).to receive(:core_value).with(:my_fact).and_return(nil)
153 allow(Facter).to receive(:core_value).with(:non_existing_fact)
154 allow(Facter).to receive(:core_value).with(:nil_core_value_custom).and_return(nil)
155 end
156
157 context 'when there is a custom fact with the name in collection' do
158 it 'calls Facter.core_value' do
159 collection.value('yayness')
160
161 expect(Facter).to have_received(:core_value).with(:yayness)
162 end
163
164 it 'returns core facts value' do
165 expect(collection.value('yayness')).to eq('core_result')
166 end
167 end
168
169 context 'when there is no custom fact with the name in collection' do
170 it 'calls Facter.core_value' do
171 collection.value('non_existing_fact')
172
173 expect(Facter).not_to have_received(:core_value).with(:non_existing_fact)
174 end
175
176 it 'returns custom facts value' do
177 expect(collection.value('my_fact')).to eq('my_fact_value')
178 end
179 end
180
181 context 'when core fact is nil and custom fact has value' do
182 it 'returns custom fact' do
183 expect(collection.value('nil_core_value_custom')).to eq('custom_fact_value')
184 end
185 end
186 end
187
188 describe 'when the weight of the resolution is greater than 0' do
189 before do
190 collection.add('100_weight_fact', value: 'my_weight_fact_value', weight: 100)
191 collection.add('100_weight_nil_fact', value: nil, weight: 100)
192
193 allow(Facter).to receive(:core_value).with(:'100_weight_fact').and_return('core_result')
194 allow(Facter).to receive(:core_value).with(:'100_weight_nil_fact').and_return('core_100_weight_nil_fact_value')
195 allow(Facter).to receive(:core_value).with(:core_fact_only).and_return('core_fact_only_value')
196 allow(Facter).to receive(:core_value).with(:no_fact).and_return(nil)
197 end
198
199 context 'when there is a custom fact with the name in collection' do
200 it 'returns the custom fact value' do
201 expect(collection.value('100_weight_fact')).to eq('my_weight_fact_value')
202 end
203 end
204
205 context 'when the custom fact returns nil' do
206 it 'returns core fact value' do
207 expect(collection.value('100_weight_nil_fact')).to eq('core_100_weight_nil_fact_value')
208 end
209 end
210
211 context 'when no custom fact and no core fact with the name' do
212 it 'returns nil' do
213 expect(collection.value('no_fact')).to be_nil
214 end
215 end
216 end
217 end
218
219 it "returns the fact's value when the array index method is used" do
220 collection.add('myfact', value: 'foo')
221
222 expect(collection['myfact']).to eq 'foo'
223 end
224
225 it 'has a method for flushing all facts' do
226 fact = collection.add('YayNess')
227
228 expect(fact).to receive(:flush)
229
230 collection.flush
231 end
232
233 it 'has a method that returns all fact names' do
234 collection.add(:one)
235 collection.add(:two)
236
237 expect(collection.list.sort_by(&:to_s)).to eq %i[one two]
238 end
239
240 describe 'when returning a hash of values' do
241 it 'returns a hash of fact names and values with the fact names as strings' do
242 collection.add(:one, value: 'me')
243
244 expect(collection.to_hash).to eq 'one' => 'me'
245 end
246
247 it 'does not include facts that did not return a value' do
248 collection.add(:two, value: nil)
249
250 expect(collection.to_hash).not_to be_include(:two)
251 end
252 end
253
254 describe 'when iterating over facts' do
255 before do
256 collection.add(:one, value: 'ONE')
257 collection.add(:two, value: 'TWO')
258 end
259
260 it 'yields each fact name and the fact value' do
261 facts = {}
262 collection.each do |fact, value|
263 facts[fact] = value
264 end
265 expect(facts).to eq 'one' => 'ONE', 'two' => 'TWO'
266 end
267
268 it 'converts the fact name to a string' do
269 collection.each do |fact, _value|
270 expect(fact).to be_instance_of(String)
271 end
272 end
273
274 it 'onlies yield facts that have values' do
275 collection.add(:nil_fact, value: nil)
276 facts = {}
277 collection.each do |fact, value|
278 facts[fact] = value
279 end
280
281 expect(facts).not_to be_include('nil_fact')
282 end
283 end
284
285 describe 'when no facts are loaded' do
286 it 'warns when no facts were loaded' do
287 expect(logger)
288 .to receive(:warnonce)
289 .with("No facts loaded from #{internal_loader.search_path.join(File::PATH_SEPARATOR)}").once
290
291 collection.fact('one')
292 end
293 end
294
295 describe 'external facts' do
296 let(:external_loader) { SingleFactLoader.new(:test_fact, 'fact value') }
297 let(:collection) { LegacyFacter::Util::Collection.new(internal_loader, external_loader) }
298
299 it 'loads when a specific fact is requested' do
300 expect(collection.fact(:test_fact).value).to eq 'fact value'
301 end
302
303 it 'loads when facts are listed' do
304 expect(collection.list).to eq [:test_fact]
305 end
306
307 it 'loads when all facts are iterated over' do
308 facts = []
309 collection.each { |fact_name, fact_value| facts << [fact_name, fact_value] }
310
311 expect(facts).to eq [['test_fact', 'fact value']]
312 end
313
314 it 'are loaded only once' do
315 expect(external_loader).to receive(:load).with(collection)
316
317 collection.load_all
318 collection.load_all
319 end
320
321 it 'are reloaded after flushing' do
322 expect(external_loader).to receive(:load).with(collection).twice
323
324 collection.load_all
325 collection.flush
326 collection.load_all
327 end
328 end
329
330 describe '#custom_facts' do
331 it 'loads no facts' do
332 expect(collection.custom_facts).to be_empty
333 end
334
335 context 'when custom facts are valid' do
336 before do
337 collection.instance_variable_set(:@custom_facts, ['my_custom_fact'])
338 collection.instance_variable_set(:@valid_custom_facts, true)
339 end
340
341 it 'return one custom fact' do
342 expect(collection.custom_facts.size).to eq(1)
343 end
344
345 it 'returns already loaded custom facts' do
346 expect(collection.custom_facts.first).to eq('my_custom_fact')
347 end
348 end
349
350 context 'when custom fact are invalid' do
351 before do
352 collection.add('my_fact', fact_type: :custom) {}
353 end
354
355 it 'returns one fact' do
356 expect(collection.custom_facts.size).to eq(1)
357 end
358
359 it 'returns my_fact custom fact' do
360 expect(collection.custom_facts.first[0]).to eq(:my_fact)
361 end
362 end
363
364 context 'when reload custom facts' do
365 before do
366 collection.instance_variable_set(:@custom_facts, ['old_fact'])
367 collection.instance_variable_set(:@valid_custom_facts, false)
368 collection.instance_variable_set(:@loaded, false)
369 collection.add('new_fact', fact_type: :custom) {}
370 end
371
372 it 'loads all internal facts' do
373 collection.custom_facts
374
375 expect(internal_loader).to have_received(:load_all)
376 end
377
378 it 'loads one fact' do
379 expect(collection.custom_facts.size). to eq(1)
380 end
381
382 it 'loads the new fact' do
383 expect(collection.custom_facts.first[0]). to eq(:new_fact)
384 end
385 end
386
387 context "when don't reload custom facts" do
388 before do
389 collection.instance_variable_set(:@custom_facts, ['old_fact'])
390 collection.instance_variable_set(:@valid_custom_facts, false)
391 collection.instance_variable_set(:@loaded, true)
392 collection.add('new_fact', fact_type: :custom) {}
393 end
394
395 it 'loads no internal facts' do
396 collection.custom_facts
397
398 expect(internal_loader).not_to have_received(:load_all)
399 end
400
401 it 'loads one fact' do
402 expect(collection.custom_facts.size). to eq(1)
403 end
404
405 it 'loads the new fact' do
406 expect(collection.custom_facts.first[0]). to eq(:new_fact)
407 end
408 end
409 end
410
411 describe '#external_facts' do
412 before do
413 collection.add('my_external_fact', fact_type: :external) {}
414 end
415
416 context 'when external facts are loaded for the first time' do
417 it 'calls load on external_loader' do
418 collection.external_facts
419
420 expect(external_loader).to have_received(:load)
421 end
422
423 it 'return 1 fact' do
424 expect(collection.external_facts.size).to eq(1)
425 end
426
427 it 'returns external fact' do
428 expect(collection.external_facts.first[0]).to eq(:my_external_fact)
429 end
430 end
431
432 context 'when external facts were already loaded' do
433 before do
434 collection.instance_variable_set(:@external_facts, [:my_external_fact])
435 collection.instance_variable_set(:@external_facts_loaded, false)
436 end
437
438 it 'doe not call load on external_loader' do
439 collection.external_facts
440
441 expect(external_loader).not_to have_received(:load)
442 end
443
444 it 'return 1 fact' do
445 expect(collection.external_facts.size).to eq(1)
446 end
447
448 it 'returns external fact' do
449 expect(collection.external_facts.first).to eq(:my_external_fact)
450 end
451 end
452 end
453
454 class SingleFactLoader
455 def initialize(name, value)
456 @name = name
457 @value = value
458 end
459
460 def load(collection)
461 collection.add(@name, value: @value)
462 end
463 end
464 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe LegacyFacter::Util::Config do
4 include PuppetlabsSpec::Files
5
6 describe "ENV['HOME'] is unset", unless: LegacyFacter::Util::Root.root? do
7 around do |example|
8 Facter::Core::Execution.with_env('HOME' => nil) do
9 example.run
10 end
11 end
12
13 it 'does not set @external_facts_dirs' do
14 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
15 expect(LegacyFacter::Util::Config.external_facts_dirs).to be_empty
16 end
17 end
18
19 describe 'is_windows? function' do
20 it "detects windows if Ruby RbConfig::CONFIG['host_os'] returns a windows OS" do
21 host_os = %w[mswin win32 dos mingw cygwin]
22 host_os.each do |h|
23 allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return(h)
24 expect(LegacyFacter::Util::Config).to be_windows
25 end
26 end
27
28 it "does not detect windows if Ruby RbConfig::CONFIG['host_os'] returns a non-windows OS" do
29 host_os = %w[darwin linux]
30 host_os.each do |h|
31 allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return(h)
32 expect(LegacyFacter::Util::Config).not_to be_windows
33 end
34 end
35 end
36
37 describe 'is_mac? function' do
38 it "detects mac if Ruby RbConfig::CONFIG['host_os'] returns darwin" do
39 host_os = ['darwin']
40 host_os.each do |h|
41 allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return(h)
42 expect(LegacyFacter::Util::Config).to be_mac
43 end
44 end
45 end
46
47 describe 'external_facts_dirs' do
48 before do
49 allow(LegacyFacter::Util::Root).to receive(:root?).and_return(true)
50 end
51
52 it 'returns the default value for linux' do
53 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(false)
54 allow(LegacyFacter::Util::Config).to receive(:windows_data_dir).and_return(nil)
55 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
56 expect(LegacyFacter::Util::Config.external_facts_dirs)
57 .to eq [
58 '/etc/puppetlabs/facter/facts.d',
59 '/etc/facter/facts.d/',
60 '/opt/puppetlabs/facter/facts.d'
61 ]
62 end
63
64 it 'returns the default value for windows 2008' do
65 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
66 allow(LegacyFacter::Util::Config).to receive(:windows_data_dir).and_return('C:\\ProgramData')
67 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
68 expect(LegacyFacter::Util::Config.external_facts_dirs)
69 .to eq [File.join('C:\\ProgramData', 'PuppetLabs', 'facter', 'facts.d')]
70 end
71
72 it 'returns the default value for windows 2003R2' do
73 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
74 allow(LegacyFacter::Util::Config).to receive(:windows_data_dir).and_return('C:\\Documents')
75 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
76 expect(LegacyFacter::Util::Config.external_facts_dirs)
77 .to eq [File.join('C:\\Documents', 'PuppetLabs', 'facter', 'facts.d')]
78 end
79
80 it "returns the old and new (AIO) paths under user's home directory when not root" do
81 allow(LegacyFacter::Util::Root).to receive(:root?).and_return(false)
82 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
83 expect(LegacyFacter::Util::Config.external_facts_dirs)
84 .to eq [File.join(ENV['HOME'], '.facter', 'facts.d'),
85 File.join(ENV['HOME'], '.puppetlabs', 'opt', 'facter', 'facts.d')]
86 end
87
88 it 'includes additional values when user appends to the list' do
89 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
90 original_values = LegacyFacter::Util::Config.external_facts_dirs.dup
91 new_value = '/usr/share/newdir'
92 LegacyFacter::Util::Config.external_facts_dirs << new_value
93 expect(LegacyFacter::Util::Config.external_facts_dirs).to eq original_values + [new_value]
94 end
95
96 it 'onlies output new values when explicitly set' do
97 LegacyFacter::Util::Config.setup_default_ext_facts_dirs
98 new_value = ['/usr/share/newdir']
99 Facter::Options[:external_dir] = new_value
100 expect(LegacyFacter::Util::Config.external_facts_dirs).to eq new_value
101 end
102 end
103
104 describe 'override_binary_dir' do
105 it 'returns the default value for linux' do
106 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(false)
107 LegacyFacter::Util::Config.setup_default_override_binary_dir
108 expect(LegacyFacter::Util::Config.override_binary_dir).to eq '/opt/puppetlabs/puppet/bin'
109 end
110
111 it 'returns nil for windows' do
112 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
113 LegacyFacter::Util::Config.setup_default_override_binary_dir
114 expect(LegacyFacter::Util::Config.override_binary_dir).to eq nil
115 end
116
117 it 'outputs new values when explicitly set' do
118 LegacyFacter::Util::Config.setup_default_override_binary_dir
119 new_value = '/usr/share/newdir'
120 LegacyFacter::Util::Config.override_binary_dir = new_value
121 expect(LegacyFacter::Util::Config.override_binary_dir).to eq new_value
122 end
123 end
124 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe LegacyFacter::Util::Confine do
4 it 'requires a fact name' do
5 expect(LegacyFacter::Util::Confine.new('yay', true).fact).to eq 'yay'
6 end
7
8 it 'accepts a value specified individually' do
9 expect(LegacyFacter::Util::Confine.new('yay', 'test').values).to eq ['test']
10 end
11
12 it 'accepts multiple values specified at once' do
13 expect(LegacyFacter::Util::Confine.new('yay', 'test', 'other').values).to eq %w[test other]
14 end
15
16 it 'fails if no fact name is provided' do
17 expect { LegacyFacter::Util::Confine.new(nil, :test) }.to raise_error(ArgumentError)
18 end
19
20 it 'fails if no values were provided' do
21 expect { LegacyFacter::Util::Confine.new('yay') }.to raise_error(ArgumentError)
22 end
23
24 it 'has a method for testing whether it matches' do
25 expect(LegacyFacter::Util::Confine.new('yay', :test)).to respond_to(:true?)
26 end
27
28 describe 'when evaluating' do
29 def confined(fact_value, *confines)
30 allow(@fact).to receive(:value).and_return fact_value
31 LegacyFacter::Util::Confine.new('yay', *confines).true?
32 end
33
34 before do
35 @fact = double 'fact'
36 allow(Facter).to receive(:[]).and_return @fact
37 end
38
39 it 'returns false if the fact does not exist' do
40 allow(Facter).to receive(:[]).with('yay').and_return nil
41
42 expect(LegacyFacter::Util::Confine.new('yay', 'test').true?).to be false
43 end
44
45 it 'uses the returned fact to get the value' do
46 allow(Facter).to receive(:[]).with('yay').and_return @fact
47
48 expect(@fact).to receive(:value).and_return nil
49
50 LegacyFacter::Util::Confine.new('yay', 'test').true?
51 end
52
53 it 'returns false if the fact has no value' do
54 expect(confined(nil, 'test')).to be false
55 end
56
57 it "returns true if any of the provided values matches the fact's value" do
58 expect(confined('two', 'two')).to be true
59 end
60
61 it "returns true if any of the provided symbol values matches the fact's value" do
62 expect(confined(:xy, :xy)).to be true
63 end
64
65 it "returns true if any of the provided integer values matches the fact's value" do
66 expect(confined(1, 1)).to be true
67 end
68
69 it "returns true if any of the provided boolan values matches the fact's value" do
70 expect(confined(true, true)).to be true
71 end
72
73 it "returns true if any of the provided array values matches the fact's value" do
74 expect(confined([3, 4], [3, 4])).to be true
75 end
76
77 it "returns true if any of the provided symbol values matches the fact's value as a string" do
78 expect(confined(:one, 'one')).to be true
79 end
80
81 it "returns true if any of the provided string values matches case-insensitive the fact's value" do
82 expect(confined('four', 'Four')).to be true
83 end
84
85 it "returns true if any of the provided symbol values matches case-insensitive the fact's string value" do
86 expect(confined(:four, 'Four')).to be true
87 end
88
89 it "returns true if any of the provided symbol values matches the fact's string value" do
90 expect(confined('xy', :xy)).to be true
91 end
92
93 it "returns true if any of the provided regexp values matches the fact's string value" do
94 expect(confined('abc', /abc/)).to be true
95 end
96
97 it "returns true if any of the provided ranges matches the fact's value" do
98 expect(confined(6, (5..7))).to be true
99 end
100
101 it "returns false if none of the provided values matches the fact's value" do
102 expect(confined('three', 'two', 'four')).to be false
103 end
104
105 it "returns false if none of the provided integer values matches the fact's value" do
106 expect(confined(2, 1, [3, 4], (5..7))).to be false
107 end
108
109 it "returns false if none of the provided boolan values matches the fact's value" do
110 expect(confined(false, true)).to be false
111 end
112
113 it "returns false if none of the provided array values matches the fact's value" do
114 expect(confined([1, 2], [3, 4])).to be false
115 end
116
117 it "returns false if none of the provided ranges matches the fact's value" do
118 expect(confined(8, (5..7))).to be false
119 end
120
121 it 'accepts and evaluate a block argument against the fact' do
122 allow(@fact).to receive(:value).and_return 'foo'
123 confine = LegacyFacter::Util::Confine.new(:yay) { |f| f == 'foo' }
124 expect(confine.true?).to be true
125 end
126
127 it 'returns false if the block raises a StandardError when checking a fact' do
128 allow(@fact).to receive(:value).and_return 'foo'
129 confine = LegacyFacter::Util::Confine.new(:yay) { |_f| raise StandardError }
130 expect(confine.true?).to be false
131 end
132
133 it 'accepts and evaluate only a block argument with true' do
134 expect(LegacyFacter::Util::Confine.new { true }.true?).to be true
135 end
136
137 it 'accepts and evaluate only a block argument with false' do
138 expect(LegacyFacter::Util::Confine.new { false }.true?).to be false
139 end
140
141 it 'returns false if the block raises a StandardError' do
142 expect(LegacyFacter::Util::Confine.new { raise StandardError }.true?).to be false
143 end
144 end
145 end
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe LegacyFacter::Util::DirectoryLoader do
4 include PuppetlabsSpec::Files
5
6 subject(:dir_loader) { LegacyFacter::Util::DirectoryLoader.new(tmpdir('directory_loader')) }
7
8 let(:collection) { LegacyFacter::Util::Collection.new(double('internal loader'), dir_loader) }
9 let(:collection_double) { instance_spy(LegacyFacter::Util::Collection) }
10
11 it 'makes the directory available' do
12 expect(dir_loader.directories).to be_instance_of(Array)
13 end
14
15 it "does nothing bad when dir doesn't exist" do
16 fakepath = '/foobar/path'
17 my_loader = LegacyFacter::Util::DirectoryLoader.new(fakepath)
18 allow(FileTest).to receive(:exists?).with(my_loader.directories[0]).and_return(false)
19 expect { my_loader.load(collection) }.not_to raise_error
20 end
21
22 describe 'when loading facts from disk' do
23 let(:log_spy) { instance_spy(Facter::Log) }
24
25 before do
26 allow(Facter::Log).to receive(:new).and_return(log_spy)
27 end
28
29 it 'is able to load files from disk and set facts' do
30 data = { 'f1' => 'one', 'f2' => 'two' }
31 write_to_file('data.yaml', YAML.dump(data))
32
33 dir_loader.load(collection)
34
35 expect(collection.value('f1')).to eq 'one'
36 end
37
38 it 'adds fact with external type to collection' do
39 data = { 'f1' => 'one' }
40 write_to_file('data.yaml', YAML.dump(data))
41
42 dir_loader.load(collection_double)
43 file = File.join(dir_loader.directories[0], 'data.yaml')
44
45 expect(collection_double).to have_received(:add).with('f1', value: 'one', fact_type: :external, file: file)
46 end
47
48 it "ignores files that begin with '.'" do
49 not_to_be_used_collection = double('collection should not be used')
50 expect(not_to_be_used_collection).not_to receive(:add)
51
52 data = { 'f1' => 'one', 'f2' => 'two' }
53 write_to_file('.data.yaml', YAML.dump(data))
54
55 dir_loader.load(not_to_be_used_collection)
56 end
57
58 %w[bak orig].each do |ext|
59 it "ignores files with an extension of '#{ext}'" do
60 expect(log_spy).to receive(:debug).with(/#{ext}/)
61 write_to_file('data' + ".#{ext}", 'foo=bar')
62
63 dir_loader.load(collection)
64 end
65 end
66
67 it 'warns when trying to parse unknown file types' do
68 write_to_file('file.unknownfiletype', 'stuff=bar')
69 expect(log_spy).to receive(:debug).with(/file.unknownfiletype/)
70
71 dir_loader.load(collection)
72 end
73
74 it 'external facts should almost always precedence over all other facts' do
75 collection.add('f1', value: 'lower_weight_fact') do
76 has_weight(LegacyFacter::Util::DirectoryLoader::EXTERNAL_FACT_WEIGHT - 1)
77 end
78
79 data = { 'f1' => 'external_fact' }
80 write_to_file('data.yaml', YAML.dump(data))
81
82 dir_loader.load(collection)
83
84 expect(collection.value('f1')).to eq 'external_fact'
85 end
86
87 describe 'given a custom weight' do
88 subject(:dir_loader) { LegacyFacter::Util::DirectoryLoader.new(tmpdir('directory_loader'), 10) }
89
90 it 'sets that weight for loaded external facts' do
91 collection.add('f1', value: 'higher_weight_fact') { has_weight(11) }
92 data = { 'f1' => 'external_fact' }
93 write_to_file('data.yaml', YAML.dump(data))
94
95 dir_loader.load(collection)
96
97 expect(collection.value('f1')).to eq 'higher_weight_fact'
98 end
99 end
100
101 context 'when blocking external facts' do
102 before do
103 Facter::Options[:blocked_facts] = ['data.yaml']
104 end
105
106 it 'is not loading blocked file' do
107 data = { 'f1' => 'one', 'f2' => 'two' }
108 write_to_file('data.yaml', YAML.dump(data))
109
110 dir_loader.load(collection)
111
112 expect(collection_double).not_to have_received(:add)
113 end
114 end
115 end
116
117 def write_to_file(file_name, to_write)
118 file = File.join(dir_loader.directories[0], file_name)
119 File.open(file, 'w') { |f| f.print to_write }
120 end
121 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe Facter::Util::Fact do
4 subject(:fact) { Facter::Util::Fact.new('yay') }
5
6 let(:resolution) { Facter::Util::Resolution.new('yay', fact) }
7 let(:options) { { fact_type: :custom } }
8 let(:logger) { instance_spy(Facter::Log) }
9
10 before do
11 allow(Facter::Log).to receive(:new).and_return(logger)
12 end
13
14 it 'requires a name' do
15 expect { Facter::Util::Fact.new }.to raise_error(ArgumentError)
16 end
17
18 describe '#initialize' do
19 it 'persists options' do
20 fact = Facter::Util::Fact.new('yay', options)
21 options.delete(:fact_type)
22
23 expect(fact.options).to eq(fact_type: :custom)
24 end
25 end
26
27 describe '#name' do
28 it 'changing the name raises error' do
29 expect { fact.name = 'new name' }.to raise_error(NoMethodError)
30 end
31 end
32
33 describe '#add' do
34 it 'persists options' do
35 fact.add(options) {}
36 options.delete(:fact_type)
37
38 expect(fact.options).to eq(fact_type: :custom)
39 end
40 end
41
42 it 'downcases and converts the name to a symbol' do
43 expect(Facter::Util::Fact.new('YayNess').name).to eq :yayness
44 end
45
46 it 'issues a deprecation warning for use of ldapname' do
47 expect(logger).to receive(:warnonce).with('ldapname is deprecated and will be removed in a future version')
48 Facter::Util::Fact.new('YayNess', ldapname: 'fooness')
49 end
50
51 describe 'when adding resolution mechanisms using #add' do
52 it 'delegates to #define_resolution with an anonymous resolution' do
53 expect(fact).to receive(:define_resolution).with(nil, {})
54 fact.add
55 end
56 end
57
58 describe 'looking up resolutions by name' do
59 subject(:fact) { Facter::Util::Fact.new('yay') }
60
61 it 'returns nil if no such resolution exists' do
62 expect(fact.resolution('nope')).to be_nil
63 end
64
65 it 'never returns anonymous resolutions' do
66 fact.add { setcode { 'anonymous' } }
67
68 expect(fact.resolution(nil)).to be_nil
69 end
70 end
71
72 describe 'adding resolution mechanisms by name' do
73 let(:res) do
74 double 'resolution',
75 name: 'named',
76 options: nil,
77 resolution_type: :simple
78 end
79
80 it 'creates a new resolution if no such resolution exists' do
81 allow(Facter::Util::Resolution).to receive(:new).once.with('named', fact).and_return(res)
82
83 fact.define_resolution('named')
84
85 expect(fact.resolution('named')).to eq res
86 end
87
88 it 'creates a simple resolution when the type is nil' do
89 fact.define_resolution('named')
90 expect(fact.resolution('named')).to be_a_kind_of Facter::Util::Resolution
91 end
92
93 it 'creates a simple resolution when the type is :simple' do
94 fact.define_resolution('named', type: :simple)
95 expect(fact.resolution('named')).to be_a_kind_of Facter::Util::Resolution
96 end
97
98 it 'creates an aggregate resolution when the type is :aggregate' do
99 fact.define_resolution('named', type: :aggregate)
100 expect(fact.resolution('named')).to be_a_kind_of Facter::Core::Aggregate
101 end
102
103 # it "raises an error if there is an existing resolution with a different type" do
104 # pending "We need to stop rescuing all errors when instantiating resolutions"
105 # fact.define_resolution('named')
106 # expect(fact.define_resolution('named', :type => :aggregate))
107 # .to raise_error(ArgumentError, /Cannot return resolution.*already defined as simple/)
108 # end
109
110 it 'returns existing resolutions by name' do
111 allow(Facter::Util::Resolution).to receive(:new).once.with('named', fact).and_return(res)
112
113 fact.define_resolution('named')
114 fact.define_resolution('named')
115
116 expect(fact.resolution('named')).to eq res
117 end
118 end
119
120 describe 'when returning a value' do
121 it 'returns nil if there are no resolutions' do
122 expect(Facter::Util::Fact.new('yay').value).to be nil
123 end
124
125 it 'prefers the highest weight resolution' do
126 fact.add do
127 has_weight 1
128 setcode { '1' }
129 end
130
131 fact.add do
132 has_weight 2
133 setcode { '2' }
134 end
135
136 fact.add do
137 has_weight 0
138 setcode { '0' }
139 end
140
141 expect(fact.value).to eq '2'
142 end
143
144 it 'returns the first value returned by a resolution' do
145 fact.add do
146 has_weight 1
147 setcode { '1' }
148 end
149
150 fact.add do
151 has_weight 2
152 setcode { nil }
153 end
154
155 fact.add do
156 has_weight 0
157 setcode { '0' }
158 end
159
160 expect(fact.value).to eq '1'
161 end
162
163 it 'skips unsuitable resolutions' do
164 fact.add do
165 has_weight 1
166 setcode { '1' }
167 end
168
169 fact.add do
170 def suitable?
171 false
172 end
173
174 has_weight 2
175 setcode { 2 }
176 end
177
178 expect(fact.value).to eq '1'
179 end
180
181 it 'sets weight of the resolution that gave the value' do
182 fact.add do
183 has_weight 1
184 setcode { '1' }
185 end
186
187 fact.add do
188 has_weight 2
189 setcode { nil }
190 end
191
192 fact.add do
193 has_weight 0
194 setcode { '0' }
195 end
196
197 expect(fact).to be_an_instance_of(Facter::Util::Fact).and(
198 having_attributes(value: '1', used_resolution_weight: 1)
199 )
200 end
201 end
202
203 describe '#flush' do
204 subject(:fact) { Facter::Util::Fact.new(:foo) }
205
206 it 'invokes #flush on simple resolutions' do
207 simple = fact.add(type: :simple)
208 expect(simple).to receive(:flush)
209
210 fact.flush
211 end
212
213 it 'invokes #flush on aggregate resolutions' do
214 aggregate = fact.add(type: :aggregate)
215 expect(aggregate).to receive(:flush)
216
217 fact.flush
218 end
219 end
220 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe LegacyFacter::Util::Loader do
4 let(:logger) { instance_spy(Facter::Log) }
5
6 before do
7 allow(Facter::Log).to receive(:new).and_return(logger)
8 end
9
10 def loader_from(places)
11 env = places[:env] || {}
12 search_path = places[:search_path] || []
13 loader = LegacyFacter::Util::Loader.new(env)
14 allow(loader).to receive(:search_path).and_return(search_path)
15 loader
16 end
17
18 it 'has a method for loading individual facts by name' do
19 expect(LegacyFacter::Util::Loader.new).to respond_to(:load)
20 end
21
22 it 'has a method for loading all facts' do
23 expect(LegacyFacter::Util::Loader.new).to respond_to(:load_all)
24 end
25
26 it 'has a method for returning directories containing facts' do
27 expect(LegacyFacter::Util::Loader.new).to respond_to(:search_path)
28 end
29
30 describe 'when determining the search path' do
31 let(:loader) { LegacyFacter::Util::Loader.new }
32
33 it 'includes the facter subdirectory of all paths in ruby LOAD_PATH' do
34 dirs = $LOAD_PATH.collect { |d| File.expand_path('facter', d) }
35 allow(File).to receive(:directory?).and_return true
36
37 paths = loader.search_path
38
39 dirs.each do |dir|
40 expect(paths).to include(dir)
41 end
42 end
43
44 it 'excludes invalid search paths' do
45 dirs = $LOAD_PATH.collect { |d| File.expand_path('custom_facts', d) }
46 allow(File).to receive(:directory?).and_return false
47
48 paths = loader.search_path
49
50 dirs.each do |dir|
51 expect(paths).not_to include(dir)
52 end
53 end
54
55 it 'includes all search paths registered with Facter' do
56 allow(Facter::Options).to receive(:custom_dir).and_return %w[/one two/three]
57
58 allow(File).to receive(:directory?).and_return false
59 allow(File).to receive(:directory?).with('/one').and_return true
60 allow(File).to receive(:directory?).with('two/three').and_return true
61
62 paths = loader.search_path
63 expect(paths).to include('/one', 'two/three')
64 end
65
66 it 'strips paths that are valid paths but are not present' do
67 allow(Facter::Options).to receive(:custom_dir).and_return %w[/one /two]
68
69 allow(File).to receive(:directory?).and_return false
70 allow(File).to receive(:directory?).with('/one').and_return true
71 allow(File).to receive(:directory?).with('/two').and_return false
72
73 paths = loader.search_path
74 expect(paths).to match_array(['/one'])
75 end
76
77 describe 'and the FACTERLIB environment variable is set' do
78 it 'includes all paths in FACTERLIB' do
79 loader = LegacyFacter::Util::Loader.new('FACTERLIB' => "/one/path#{File::PATH_SEPARATOR}/two/path")
80
81 allow(File).to receive(:directory?).and_return false
82 allow(File).to receive(:directory?).with('/one/path').and_return true
83 allow(File).to receive(:directory?).with('/two/path').and_return true
84
85 paths = loader.search_path
86 %w[/one/path /two/path].each do |dir|
87 expect(paths).to include(dir)
88 end
89 end
90 end
91 end
92
93 describe 'when loading facts' do
94 it 'loads values from the matching environment variable if one is present' do
95 loader = loader_from(env: { 'facter_testing' => 'yayness' })
96 allow(LegacyFacter).to receive(:add)
97
98 loader.load(:testing)
99
100 expect(LegacyFacter).to have_received(:add).with('testing', { fact_type: :external, is_env: true })
101 end
102
103 it 'loads any files in the search path with names matching the fact name' do
104 loader = loader_from(search_path: %w[/one/dir /two/dir])
105
106 allow(loader).to receive(:search_path).and_return %w[/one/dir /two/dir]
107 allow(FileTest).to receive(:file?).and_return false
108 allow(FileTest).to receive(:file?).with('/one/dir/testing.rb').and_return true
109
110 expect(Kernel).to receive(:load).with('/one/dir/testing.rb')
111 loader.load(:testing)
112 end
113
114 it 'does not load any ruby files from subdirectories matching the fact name in the search path' do
115 loader = LegacyFacter::Util::Loader.new
116 allow(FileTest).to receive(:file?).and_return false
117 allow(FileTest).to receive(:file?).with('/one/dir/testing.rb').and_return true
118
119 allow(File).to receive(:directory?).with('/one/dir/testing').and_return true
120 allow(loader).to receive(:search_path).and_return %w[/one/dir]
121
122 allow(Dir).to receive(:entries).with('/one/dir/testing').and_return %w[foo.rb bar.rb]
123 %w[/one/dir/testing/foo.rb /one/dir/testing/bar.rb].each do |f|
124 allow(File).to receive(:directory?).with(f).and_return false
125 allow(Kernel).to receive(:load).with(f)
126 end
127
128 expect(Kernel).to receive(:load).with('/one/dir/testing.rb')
129 loader.load(:testing)
130 end
131
132 it "does not load files that don't end in '.rb'" do
133 loader = LegacyFacter::Util::Loader.new
134 allow(loader).to receive(:search_path).and_return %w[/one/dir]
135 allow(FileTest).to receive(:file?).and_return false
136 allow(FileTest).to receive(:file?).with('/one/dir/testing.rb').and_return false
137
138 expect(Kernel).not_to receive(:load)
139
140 loader.load(:testing)
141 end
142 end
143
144 describe 'when loading all facts' do
145 let(:loader) { LegacyFacter::Util::Loader.new }
146
147 before do
148 allow(loader).to receive(:search_path).and_return([])
149
150 allow(File).to receive(:directory?).and_return true
151 end
152
153 it 'loads all files in all search paths' do
154 loader = loader_from(search_path: %w[/one/dir /two/dir])
155
156 allow(Dir).to receive(:glob).with('/one/dir/*.rb').and_return %w[/one/dir/a.rb /one/dir/b.rb]
157 allow(Dir).to receive(:glob).with('/two/dir/*.rb').and_return %w[/two/dir/c.rb /two/dir/d.rb]
158
159 %w[/one/dir/a.rb /one/dir/b.rb /two/dir/c.rb /two/dir/d.rb].each do |f|
160 allow(FileTest).to receive(:file?).with(f).and_return true
161 expect(Kernel).to receive(:load).with(f)
162 end
163
164 loader.load_all
165 end
166
167 it 'does not try to load subdirectories of search paths' do
168 allow(loader).to receive(:search_path).and_return %w[/one/dir /two/dir]
169
170 # a.rb is a directory
171 allow(Dir).to receive(:glob).with('/one/dir/*.rb').and_return %w[/one/dir/a.rb /one/dir/b.rb]
172 allow(FileTest).to receive(:file?).with('/one/dir/a.rb').and_return false
173 allow(FileTest).to receive(:file?).with('/one/dir/b.rb').and_return true
174 allow(Kernel).to receive(:load).with('/one/dir/b.rb')
175
176 # c.rb is a directory
177 allow(Dir).to receive(:glob).with('/two/dir/*.rb').and_return %w[/two/dir/c.rb /two/dir/d.rb]
178 allow(FileTest).to receive(:file?).with('/two/dir/c.rb').and_return false
179 allow(FileTest).to receive(:file?).with('/two/dir/d.rb').and_return true
180 expect(Kernel).to receive(:load).with('/two/dir/d.rb')
181
182 loader.load_all
183 end
184
185 it 'does not raise an exception when a file is unloadable' do
186 allow(loader).to receive(:search_path).and_return %w[/one/dir]
187
188 allow(Dir).to receive(:glob).with('/one/dir/*.rb').and_return %w[/one/dir/a.rb]
189 allow(FileTest).to receive(:file?).with('/one/dir/a.rb').and_return true
190
191 allow(Facter).to receive(:log_exception).with(LoadError, 'Error loading fact /one/dir/a.rb: LoadError')
192 allow(Kernel).to receive(:load).with('/one/dir/a.rb').and_raise(LoadError)
193
194 expect { loader.load_all }.not_to raise_error
195 end
196
197 context 'when loads all facts from the environment' do
198 before do
199 Facter::Util::Resolution.with_env 'FACTER_one' => 'yayness', 'FACTER_TWO' => 'boo' do
200 loader.load_all
201 end
202 end
203
204 it 'loaded fact one' do
205 expect(LegacyFacter.value(:one)).to eq 'yayness'
206 end
207
208 it 'loaded fact two' do
209 expect(LegacyFacter.value(:two)).to eq 'boo'
210 end
211 end
212
213 it 'only load all facts once' do
214 loader = loader_from(env: {})
215 expect(loader).to receive(:load_env).once
216
217 loader.load_all
218 loader.load_all
219 end
220
221 context 'when directory path has wrong slashes' do
222 before do
223 allow(Dir).to receive(:glob).with('/one/dir/*.rb').and_return %w[/one/dir/a.rb]
224 end
225
226 dir_paths = ['//one///dir', '//one///\\dir', '/one///\/\dir', '\one///\\dir']
227
228 dir_paths.each do |dir_path|
229 it 'corrects the directory path' do
230 allow(loader).to receive(:search_path).and_return [dir_path]
231
232 loader.load_all
233
234 expect(Dir).to have_received(:glob).with('/one/dir/*.rb')
235 end
236 end
237 end
238 end
239
240 it 'loads facts on the facter search path only once' do
241 loader = loader_from(env: {})
242 loader.load_all
243
244 expect(loader).not_to receive(:kernel_load).with(/ec2/)
245 loader.load(:ec2)
246 end
247 end
0 describe LegacyFacter::Util::Normalization do
1 subject(:normalization) { LegacyFacter::Util::Normalization }
2
3 def utf16(str)
4 if String.method_defined?(:encode) && defined?(::Encoding)
5 str.encode(Encoding::UTF_16LE)
6 else
7 str
8 end
9 end
10
11 def utf8(str)
12 if String.method_defined?(:encode) && defined?(::Encoding)
13 str.encode(Encoding::UTF_8)
14 else
15 str
16 end
17 end
18
19 describe 'validating strings' do
20 describe 'and string encoding is supported', if: String.instance_methods.include?(:encoding) do
21 it 'accepts strings that are ASCII and match their encoding and converts them to UTF-8' do
22 str = 'ASCII'.encode(Encoding::ASCII)
23 normalized_str = normalization.normalize(str)
24 expect(normalized_str.encoding).to eq(Encoding::UTF_8)
25 end
26
27 it 'accepts strings that are UTF-8 and match their encoding' do
28 str = "let's make a ☃!".encode(Encoding::UTF_8)
29 expect(normalization.normalize(str)).to eq(str)
30 end
31
32 it 'converts valid non UTF-8 strings to UTF-8' do
33 str = "let's make a ☃!".encode(Encoding::UTF_16LE)
34 enc = normalization.normalize(str).encoding
35 expect(enc).to eq(Encoding::UTF_8)
36 end
37
38 it 'normalizes a frozen string returning a non-frozen string' do
39 str = 'factvalue'.encode(Encoding::UTF_16LE).freeze
40
41 normalized_str = normalization.normalize(str)
42 expect(normalized_str).not_to be_frozen
43 end
44
45 it 'rejects strings that are not UTF-8 and do not match their claimed encoding' do
46 invalid_shift_jis = "\xFF\x5C!".force_encoding(Encoding::SHIFT_JIS)
47 expect do
48 normalization.normalize(invalid_shift_jis)
49 end.to raise_error(LegacyFacter::Util::Normalization::NormalizationError,
50 /String encoding Shift_JIS is not UTF-8 and could not be converted to UTF-8/)
51 end
52
53 it "rejects strings that claim to be UTF-8 encoded but aren't" do
54 str = "\255ay!".force_encoding(Encoding::UTF_8)
55 expect do
56 normalization.normalize(str)
57 end.to raise_error(LegacyFacter::Util::Normalization::NormalizationError,
58 /String.*doesn't match the reported encoding UTF-8/)
59 end
60 end
61
62 describe 'and string encoding is not supported', unless: String.instance_methods.include?(:encoding) do
63 it 'accepts strings that are UTF-8 and match their encoding' do
64 str = "let's make a ☃!"
65 expect(normalization.normalize(str)).to eq(str)
66 end
67
68 it 'rejects strings that are not UTF-8' do
69 str = "let's make a \255\255\255!"
70 expect do
71 normalization.normalize(str)
72 end.to raise_error(LegacyFacter::Util::Normalization::NormalizationError, /String .* is not valid UTF-8/)
73 end
74 end
75 end
76
77 describe 'normalizing arrays' do
78 it 'normalizes each element in the array' do
79 arr = [utf16('first'), utf16('second'), [utf16('third'), utf16('fourth')]]
80 expected_arr = [utf8('first'), utf8('second'), [utf8('third'), utf8('fourth')]]
81
82 expect(normalization.normalize_array(arr)).to eq(expected_arr)
83 end
84 end
85
86 describe 'normalizing hashes' do
87 it 'normalizes each element in the array' do
88 hsh = { utf16('first') => utf16('second'), utf16('third') => [utf16('fourth'), utf16('fifth')] }
89 expected_hsh = { utf8('first') => utf8('second'), utf8('third') => [utf8('fourth'), utf8('fifth')] }
90
91 expect(normalization.normalize_hash(hsh)).to eq(expected_hsh)
92 end
93 end
94
95 describe '.normalize' do
96 context 'when Time object' do
97 it 'returns a string in an ISO 8601 format' do
98 value = Time.utc(2020)
99 expected = '2020-01-01T00:00:00Z'
100
101 expect(normalization.normalize(value)).to eq(expected)
102 end
103 end
104
105 context 'when Date object' do
106 it 'returns a string in an ISO 8601 format' do
107 value = Date.new(2020)
108 expected = '2020-01-01'
109
110 expect(normalization.normalize(value)).to eq(expected)
111 end
112 end
113
114 [1, 1.0, true, false, nil, :symbol].each do |val|
115 it "accepts #{val.inspect}:#{val.class}" do
116 expect(normalization.normalize(val)).to eq(val)
117 end
118 end
119
120 [Object.new, Set.new].each do |val|
121 it "rejects #{val.inspect}:#{val.class}" do
122 expect do
123 normalization.normalize(val)
124 end.to raise_error(LegacyFacter::Util::Normalization::NormalizationError, /Expected .*but was #{val.class}/)
125 end
126 end
127 end
128 end
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'tempfile'
4 require 'tmpdir'
5
6 describe LegacyFacter::Util::Parser do
7 include PuppetlabsSpec::Files
8
9 let(:data) { { 'one' => 'two', 'three' => 'four' } }
10
11 describe '#extension_matches?' do
12 it 'matches extensions when subclass uses match_extension' do
13 expect(LegacyFacter::Util::Parser.extension_matches?('myfile.foobar', 'foobar')).to be true
14 end
15
16 it 'matches extensions when subclass uses match_extension with an array' do
17 expect(LegacyFacter::Util::Parser.extension_matches?('myfile.ext3', %w[ext1 ext2 ext3])).to be true
18 end
19
20 it 'matches extension ignoring case on file' do
21 expect(LegacyFacter::Util::Parser.extension_matches?('myfile.ExT1', 'ext1')).to be true
22 end
23
24 it 'matches extension ignoring case for match_extension' do
25 expect(LegacyFacter::Util::Parser.extension_matches?('myfile.exT1', 'EXT1')).to be true
26 end
27 end
28
29 describe '#parse_executable_output' do
30 subject(:parser) { LegacyFacter::Util::Parser::Base.new('myfile.sh') }
31
32 let(:yaml_data) { "one: two\nthree: four\n" }
33 let(:keyvalue) { "one=two\nthree=four\n" }
34
35 it 'receives yaml and returns hash' do
36 expect(parser.parse_executable_output(yaml_data)).to eq data
37 end
38
39 it 'receives keyvalue and returns hash' do
40 expect(parser.parse_executable_output(keyvalue)).to eq data
41 end
42
43 it 'raises no exception on nil' do
44 expect(parser.parse_executable_output(nil)).to be_empty
45 end
46
47 it 'returns {} on invalid data' do
48 expect(parser.parse_executable_output('random')).to be_empty
49 end
50 end
51
52 shared_examples_for 'handling a not readable file' do
53 before do
54 allow(Facter::Util::FileHelper).to receive(:safe_read).with(data_file, nil).and_return(nil)
55 allow(Facter).to receive(:log_exception).at_least(:once)
56 end
57
58 it 'handles not readable file' do
59 expect { LegacyFacter::Util::Parser.parser_for(data_file).results }.not_to raise_error
60 end
61 end
62
63 describe 'yaml' do
64 let(:data_in_yaml) { YAML.dump(data) }
65 let(:data_file) { '/tmp/foo.yaml' }
66
67 it 'returns a hash of whatever is stored on disk' do
68 allow(Facter::Util::FileHelper).to receive(:safe_read).with(data_file, nil).and_return(data_in_yaml)
69
70 expect(LegacyFacter::Util::Parser.parser_for(data_file).results).to eq data
71 end
72
73 it 'handles exceptions' do
74 allow(Facter::Util::FileHelper).to receive(:safe_read)
75 .with(data_file, nil).and_return(data_in_yaml + '}')
76 allow(Facter).to receive(:log_exception).at_least(:once)
77
78 expect { LegacyFacter::Util::Parser.parser_for(data_file).results }.not_to raise_error
79 end
80
81 it_behaves_like 'handling a not readable file'
82 end
83
84 describe 'json' do
85 let(:data_in_json) { JSON.dump(data) }
86 let(:data_file) { '/tmp/foo.json' }
87
88 it 'returns a hash of whatever is stored on disk' do
89 pending('this test requires the json library') unless LegacyFacter.json?
90 allow(Facter::Util::FileHelper).to receive(:safe_read).with(data_file, nil).and_return(data_in_json)
91
92 expect(LegacyFacter::Util::Parser.parser_for(data_file).results).to eq data
93 end
94
95 it_behaves_like 'handling a not readable file'
96 end
97
98 describe 'txt' do
99 let(:data_file) { '/tmp/foo.txt' }
100
101 shared_examples_for 'txt parser' do
102 it 'returns a hash of whatever is stored on disk' do
103 allow(Facter::Util::FileHelper).to receive(:safe_read).with(data_file, nil).and_return(data_in_txt)
104
105 expect(LegacyFacter::Util::Parser.parser_for(data_file).results).to eq data
106 end
107 end
108
109 context 'when is well formed data' do
110 let(:data_in_txt) { "one=two\nthree=four\n" }
111
112 it_behaves_like 'txt parser'
113 end
114
115 context 'when there is an extra equal sign' do
116 let(:data_in_txt) { "one=two\nthree=four=five\n" }
117 let(:data) { { 'one' => 'two', 'three' => 'four=five' } }
118
119 it_behaves_like 'txt parser'
120 end
121
122 context 'when there is extra data' do
123 let(:data_in_txt) { "one=two\nfive\nthree=four\n" }
124
125 it_behaves_like 'txt parser'
126 end
127
128 it_behaves_like 'handling a not readable file'
129 end
130
131 describe 'scripts' do
132 let(:ext) { LegacyFacter::Util::Config.windows? ? '.bat' : '.sh' }
133 let(:cmd) { "/tmp/foo#{ext}" }
134 let(:data_in_txt) { "one=two\nthree=four\n" }
135 let(:yaml_data) { "one: two\nthree: four\n" }
136 let(:logger) { instance_spy(Facter::Log) }
137
138 def expects_script_to_return(path, content, result, err = nil)
139 allow(Facter::Core::Execution).to receive(:execute_command).with(path).and_return([content, err])
140 allow(File).to receive(:executable?).with(path).and_return(true)
141 allow(FileTest).to receive(:file?).with(path).and_return(true)
142
143 expect(LegacyFacter::Util::Parser.parser_for(path).results).to eq result
144 end
145
146 def expects_parser_to_return_nil_for_directory(path)
147 allow(FileTest).to receive(:file?).with(path).and_return(false)
148
149 expect(LegacyFacter::Util::Parser.parser_for(path).results).to be nil
150 end
151
152 it 'returns a hash of whatever is returned by the executable' do
153 expects_script_to_return(cmd, data_in_txt, data)
154 end
155
156 it 'does not parse a directory' do
157 expects_parser_to_return_nil_for_directory(cmd)
158 end
159
160 it 'returns structured data' do
161 expects_script_to_return(cmd, yaml_data, data)
162 end
163
164 it 'handles Symbol correctly' do
165 yaml_data = "---\n:one: :two\nthree: four\n"
166 exptected_data = { :one => :two, 'three' => 'four' }
167 expects_script_to_return(cmd, yaml_data, exptected_data)
168 end
169
170 it 'writes warning message' do
171 allow(Facter).to receive(:warn).at_least(:once)
172 allow(Facter::Log).to receive(:new).with("foo#{ext}").and_return(logger)
173
174 expects_script_to_return(cmd, yaml_data, data, 'some error')
175 expect(logger).to have_received(:warn).with("Command /tmp/foo#{ext} completed with the "\
176 'following stderr message: some error')
177 end
178
179 it 'handles Time correctly' do
180 yaml_data = "---\nfirst: 2020-07-15 05:38:12.427678398 +00:00\n"
181 allow(Facter::Core::Execution).to receive(:execute_command).with(cmd).and_return([yaml_data, nil])
182 allow(File).to receive(:executable?).with(cmd).and_return(true)
183 allow(FileTest).to receive(:file?).with(cmd).and_return(true)
184
185 expect(LegacyFacter::Util::Parser.parser_for(cmd).results['first']).to be_a(Time)
186 end
187
188 it 'returns an empty hash when the script returns nil' do
189 expects_script_to_return(cmd, nil, {})
190 end
191
192 it 'quotes scripts with spaces' do
193 path = "/h a s s p a c e s#{ext}"
194
195 expect(Facter::Core::Execution).to receive(:execute_command)
196 .with("\"#{path}\"").and_return([data_in_txt, nil])
197 expects_script_to_return(path, data_in_txt, data)
198 end
199
200 describe 'exe, bat, cmd, and com files' do
201 let(:cmds) { ['/tmp/foo.bat', '/tmp/foo.cmd', '/tmp/foo.exe', '/tmp/foo.com'] }
202
203 before do
204 cmds.each do |cmd|
205 allow(File).to receive(:executable?).with(cmd).and_return(true)
206 allow(FileTest).to receive(:file?).with(cmd).and_return(true)
207 end
208 end
209
210 it 'returns nothing parser if not on windows' do
211 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(false)
212
213 cmds.each do |cmd|
214 expect(LegacyFacter::Util::Parser.parser_for(cmd))
215 .to be_an_instance_of(LegacyFacter::Util::Parser::NothingParser)
216 end
217 end
218
219 it 'returns script parser if on windows' do
220 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
221
222 cmds.each do |cmd|
223 expect(LegacyFacter::Util::Parser.parser_for(cmd))
224 .to be_an_instance_of(LegacyFacter::Util::Parser::ScriptParser)
225 end
226 end
227 end
228
229 describe 'powershell' do
230 let(:ps1) { '/tmp/foo.ps1' }
231 let(:logger) { instance_spy(Facter::Log) }
232
233 before do
234 allow(File).to receive(:readable?).and_return(false)
235 end
236
237 def expects_to_parse_powershell(cmd, result)
238 allow(LegacyFacter::Util::Config).to receive(:windows?).and_return(true)
239 allow(FileTest).to receive(:file?).with(ps1).and_return(true)
240
241 expect(LegacyFacter::Util::Parser.parser_for(cmd).results).to eq result
242 end
243
244 it 'does not parse a directory' do
245 expects_parser_to_return_nil_for_directory(ps1)
246 end
247
248 it 'parses output from powershell' do
249 allow(Facter::Core::Execution).to receive(:execute_command).and_return([data_in_txt, nil])
250
251 expects_to_parse_powershell(ps1, data)
252 end
253
254 it 'parses yaml output from powershell' do
255 allow(Facter::Core::Execution).to receive(:execute_command).and_return([yaml_data, nil])
256
257 expects_to_parse_powershell(ps1, data)
258 end
259
260 it 'logs warning from powershell' do
261 allow(Facter::Core::Execution).to receive(:execute_command).and_return([yaml_data, 'some error'])
262 allow(Facter::Log).to receive(:new).with('foo.ps1').and_return(logger)
263
264 expects_to_parse_powershell(ps1, data)
265 expect(logger).to have_received(:warn).with('Command "powershell.exe" -NoProfile -NonInteractive -NoLogo '\
266 '-ExecutionPolicy Bypass -File "/tmp/foo.ps1" completed with the following stderr message: some error')
267 end
268
269 context 'when executing powershell' do
270 let(:sysnative_powershell) { "#{ENV['SYSTEMROOT']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe" }
271 let(:system32_powershell) { "#{ENV['SYSTEMROOT']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe" }
272
273 let(:sysnative_regexp) { /^\"#{Regexp.escape(sysnative_powershell)}\"/ }
274 let(:system32_regexp) { /^\"#{Regexp.escape(system32_powershell)}\"/ }
275 let(:powershell_regexp) { /^\"#{Regexp.escape("powershell.exe")}\"/ }
276
277 it 'prefers the sysnative alias to resolve 64-bit powershell on 32-bit ruby' do
278 allow(File).to receive(:readable?).with(sysnative_powershell).and_return(true)
279 allow(Facter::Core::Execution)
280 .to receive(:execute_command)
281 .with(sysnative_regexp)
282 .and_return([data_in_txt, nil])
283
284 expects_to_parse_powershell(ps1, data)
285 end
286
287 it "uses system32 if sysnative alias doesn't exist on 64-bit ruby" do
288 allow(File).to receive(:readable?).with(sysnative_powershell).and_return(false)
289 allow(File).to receive(:readable?).with(system32_powershell).and_return(true)
290 allow(Facter::Core::Execution).to receive(:execute_command).with(system32_regexp)
291 .and_return([data_in_txt, nil])
292
293 expects_to_parse_powershell(ps1, data)
294 end
295
296 it "uses 'powershell' as a last resort" do
297 allow(File).to receive(:readable?).with(sysnative_powershell).and_return(false)
298 allow(File).to receive(:readable?).with(system32_powershell).and_return(false)
299 allow(Facter::Core::Execution)
300 .to receive(:execute_command)
301 .with(powershell_regexp)
302 .and_return([data_in_txt, nil])
303
304 expects_to_parse_powershell(ps1, data)
305 end
306 end
307 end
308 end
309
310 describe 'nothing parser' do
311 it 'uses the nothing parser when there is no other parser' do
312 expect(LegacyFacter::Util::Parser.parser_for('this.is.not.valid').results).to be nil
313 end
314 end
315
316 describe LegacyFacter::Util::Parser::YamlParser do
317 let(:yaml_parser) { LegacyFacter::Util::Parser::YamlParser.new(nil, yaml_content) }
318
319 describe '#parse_results' do
320 context 'when yaml anchors are present' do
321 let(:yaml_content) { load_fixture('external_fact_yaml_anchor').read }
322
323 it 'parses the yaml anchors' do
324 expected_result = { 'one' => { 'test' => { 'a' => ['foo'] } }, 'two' => { 'TEST' => { 'A' => ['foo'] } } }
325
326 expect(yaml_parser.parse_results).to eq(expected_result)
327 end
328 end
329
330 context 'when yaml contains Time formatted fields' do
331 context 'when time zone is present' do
332 let(:yaml_content) { load_fixture('external_fact_yaml').read }
333
334 it 'treats it as a string' do
335 expected_result = { 'testsfact' => { 'time' => '2020-04-28 01:44:08.148119000 +01:01' } }
336
337 expect(yaml_parser.parse_results).to eq(expected_result)
338 end
339 end
340
341 context 'when time zone is missing' do
342 let(:yaml_content) { load_fixture('external_fact_yaml_no_zone').read }
343
344 it 'is interpreted as a string' do
345 expected_result = { 'testsfact' => { 'time' => '2020-04-28 01:44:08.148119000' } }
346
347 expect(yaml_parser.parse_results).to eq(expected_result)
348 end
349 end
350 end
351
352 context 'when yaml contains Date formatted fields' do
353 let(:yaml_content) { load_fixture('external_fact_yaml_date').read }
354
355 it 'loads date' do
356 expected_result = { 'testsfact' => { 'date' => Date.parse('2020-04-28') } }
357
358 expect(yaml_parser.parse_results).to eq(expected_result)
359 end
360 end
361 end
362 end
363 end
0 #! /usr/bin/env ruby
1 # frozen_string_literal: true
2
3 describe Facter::Util::Resolution do
4 subject(:resolution) { Facter::Util::Resolution.new(:foo, stub_fact) }
5
6 let(:stub_fact) { double('fact', name: :stubfact) }
7 let(:logger) { instance_spy(Facter::Log) }
8
9 before do
10 allow(Facter::Log).to receive(:new).and_return(logger)
11 end
12
13 it 'requires a name' do
14 expect { Facter::Util::Resolution.new }.to raise_error(ArgumentError)
15 end
16
17 it 'requires a fact' do
18 expect { Facter::Util::Resolution.new('yay') }.to raise_error(ArgumentError)
19 end
20
21 it 'can return its name' do
22 expect(resolution.name).to eq :foo
23 end
24
25 it 'can explicitly set a value' do
26 resolution.value = 'foo'
27 expect(resolution.value).to eq 'foo'
28 end
29
30 it 'defaults to nil for code' do
31 expect(resolution.code).to be_nil
32 end
33
34 describe 'when setting the code' do
35 it 'creates a block when given a command' do
36 resolution.setcode 'foo'
37 expect(resolution.code).to be_a_kind_of Proc
38 end
39
40 it 'stores the provided block when given a block' do
41 block = -> {}
42 resolution.setcode(&block)
43 expect(resolution.code).to equal(block)
44 end
45
46 it 'prefers a command over a block' do
47 block = -> {}
48 resolution.setcode('foo', &block)
49 expect(resolution.code).not_to eq block
50 end
51
52 it 'fails if neither a string nor block has been provided' do
53 expect { resolution.setcode }.to raise_error(ArgumentError)
54 end
55 end
56
57 describe 'when returning the value' do
58 it 'returns any value that has been provided' do
59 resolution.value = 'foo'
60 expect(resolution.value).to eq 'foo'
61 end
62
63 it 'returns a value that is equal to false' do
64 resolution.value = false
65 expect(resolution.value).to eq false
66 end
67
68 describe 'and setcode has not been called' do
69 it 'returns nil' do
70 expect(resolution.value).to be_nil
71 end
72 end
73
74 describe 'and the code is a string' do
75 it 'returns the result of executing the code' do
76 resolution.setcode '/bin/foo'
77 allow(Facter::Core::Execution).to receive(:execute).once.with('/bin/foo', anything).and_return('yup')
78
79 expect(resolution.value).to eq 'yup'
80 end
81 end
82
83 describe 'and the code is a block' do
84 it 'returns the value returned by the block' do
85 resolution.setcode { 'yayness' }
86 expect(resolution.value).to eq 'yayness'
87 end
88 end
89 end
90
91 describe 'setting options' do
92 it 'can set the value' do
93 resolution.options(value: 'something')
94 expect(resolution.value).to eq 'something'
95 end
96
97 it 'can set the timeout' do
98 resolution.options(timeout: 314)
99 expect(resolution.limit).to eq 314
100 end
101
102 it 'can set the weight' do
103 resolution.options(weight: 27)
104 expect(resolution.weight).to eq 27
105 end
106
107 it 'fact_type does not raise error' do
108 expect { resolution.options(fact_type: 'simple') }.not_to raise_error
109 end
110
111 it 'fails on unhandled options' do
112 expect do
113 resolution.options(foo: 'bar')
114 end.to raise_error(ArgumentError, /Invalid resolution options.*foo/)
115 end
116 end
117
118 describe '#has_weight' do
119 it 'returns the class instance' do
120 expect(resolution.has_weight(42)).to be(resolution)
121 end
122 end
123
124 describe 'evaluating' do
125 it 'evaluates the block in the context of the given resolution' do
126 expect(resolution).to receive(:setcode).with('code')
127
128 resolution.evaluate { setcode('code') }
129 end
130
131 it 'raises a warning if the resolution is evaluated twice' do
132 expect(logger).to receive(:warn).with(/Already evaluated foo at.*reevaluating anyways/)
133
134 resolution.evaluate {}
135 resolution.evaluate {}
136 end
137 end
138
139 describe '#<=>' do
140 let(:other_fact) { instance_spy(Facter::Util::Fact, name: :other_fact) }
141 let(:other_resolution) { Facter::Util::Resolution.new(:other_fact, other_fact) }
142
143 context 'when self has greater weight than other' do
144 before do
145 resolution.options(weight: 100)
146 other_resolution.options(weight: 99)
147 end
148
149 it 'return 1' do
150 expect(resolution <=> other_resolution).to eq(1)
151 end
152 end
153
154 context 'when self has lower weight than other' do
155 before do
156 resolution.options(weight: 99)
157 other_resolution.options(weight: 100)
158 end
159
160 it 'return -1' do
161 expect(resolution <=> other_resolution).to eq(-1)
162 end
163 end
164
165 context 'when self has equal weight to other' do
166 before do
167 resolution.options(weight: 100)
168 other_resolution.options(weight: 100)
169 end
170
171 it 'returns 0' do
172 expect(resolution <=> other_resolution).to eq(0)
173 end
174
175 context 'when self is custom and other is external' do
176 before do
177 resolution.options(fact_type: :external)
178 other_resolution.options(fact_type: :custom)
179 end
180
181 it 'returns 1' do
182 expect(resolution <=> other_resolution).to eq(1)
183 end
184 end
185
186 context 'when self is external and other is custom' do
187 before do
188 resolution.options(fact_type: :custom)
189 other_resolution.options(fact_type: :external)
190 end
191
192 it 'returns -1' do
193 expect(resolution <=> other_resolution).to eq(-1)
194 end
195 end
196 end
197 end
198 end
0 # frozen_string_literal: true
1
2 describe Facter::CacheManager do
3 subject(:cache_manager) { Facter::CacheManager.new }
4
5 let(:cache_dir) { '/etc/facter/cache' }
6 let(:searched_core_fact) do
7 instance_spy(Facter::SearchedFact, name: 'os', fact_class: instance_spy(Facts::Linux::Os::Name),
8 user_query: '', type: :core, file: nil)
9 end
10 let(:searched_custom_fact) do
11 instance_spy(Facter::SearchedFact, name: 'my_custom_fact', fact_class: nil,
12 user_query: '', type: :custom, file: nil)
13 end
14 let(:searched_external_fact) do
15 instance_spy(Facter::SearchedFact, name: 'my_external_fact', fact_class: nil,
16 user_query: '', type: :file, file: '/tmp/ext_file.txt')
17 end
18 let(:searched_facts) { [searched_core_fact, searched_custom_fact, searched_external_fact] }
19 let(:cached_core_fact) { "{\n \"os\": \"Ubuntu\",\n \"cache_format_version\": 1\n}" }
20 let(:cached_external_fact) { "{\n \"my_external_fact\": \"ext_fact\",\n\"cache_format_version\": 1\n}" }
21
22 let(:resolved_core_fact) { mock_resolved_fact('os', 'Ubuntu', '') }
23 let(:resolved_facts) { [resolved_core_fact] }
24 let(:group_name) { 'operating system' }
25 let(:cache_file_name) { File.join(cache_dir, group_name) }
26 let(:fact_groups) { instance_spy(Facter::FactGroups) }
27 let(:os_fact) { { ttls: 60, group: 'operating system' } }
28 let(:external_fact) { { ttls: 60, group: 'ext_file.txt' } }
29 let(:logger) { instance_spy(Facter::Log) }
30
31 before do
32 allow(File).to receive(:readable?).and_call_original
33 allow(File).to receive(:directory?).and_call_original
34 allow(LegacyFacter::Util::Config).to receive(:facts_cache_dir).and_return(cache_dir)
35 allow(Facter::FactGroups).to receive(:new).and_return(fact_groups)
36 allow(Facter::Options).to receive(:[]).with(:debug).and_return(false)
37 allow(Facter::Options).to receive(:[])
38 allow(Facter::Options).to receive(:[]).with(:ttls).and_return([])
39 allow(Facter::Log).to receive(:new).and_return(logger)
40 end
41
42 describe '#resolve_facts' do
43 context 'with no cache dir' do
44 before do
45 allow(File).to receive(:directory?).with(cache_dir).and_return(false)
46 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
47 allow(Facter::Options).to receive(:[]).with(:ttls).and_return(['fact'])
48 end
49
50 it 'returns searched facts' do
51 sf, _cf = cache_manager.resolve_facts(searched_facts)
52 expect(sf).to eq(searched_facts)
53 end
54
55 it 'returns no cached facts' do
56 _, cf = cache_manager.resolve_facts(searched_facts)
57 expect(cf).to be_empty
58 end
59 end
60
61 context 'with no cache false' do
62 before do
63 allow(File).to receive(:directory?).with(cache_dir).and_return(true)
64 allow(Facter::Options).to receive(:[]).with(:cache).and_return(false)
65 allow(Facter::Options).to receive(:[]).with(:ttls).and_return(['fact'])
66 end
67
68 it 'returns searched facts' do
69 sf, _cf = cache_manager.resolve_facts(searched_facts)
70 expect(sf).to eq(searched_facts)
71 end
72
73 it 'returns no cached facts' do
74 _, cf = cache_manager.resolve_facts(searched_facts)
75 expect(cf).to be_empty
76 end
77 end
78
79 context 'with cached facts' do
80 before do
81 allow(File).to receive(:directory?).with(cache_dir).and_return(true)
82 allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
83 allow(fact_groups).to receive(:get_fact_group).with('my_custom_fact').and_return(nil)
84 allow(fact_groups).to receive(:get_fact_group).with('ext_file.txt').and_return(nil)
85 allow(fact_groups).to receive(:get_group_ttls).with('ext_file.txt').and_return(nil)
86 allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(nil)
87 allow(fact_groups).to receive(:get_fact).with('my_custom_fact').and_return(nil)
88 allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
89 allow(File).to receive(:mtime).with(cache_file_name).and_return(Time.now)
90 allow(Facter::Util::FileHelper).to receive(:safe_read).with(cache_file_name).and_return(cached_core_fact)
91 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
92 end
93
94 it 'returns cached fact' do
95 allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
96 allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
97 allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(false)
98
99 _, cached_facts = cache_manager.resolve_facts(searched_facts)
100 expect(cached_facts).to be_an_instance_of(Array).and contain_exactly(
101 an_instance_of(Facter::ResolvedFact).and(having_attributes(name: 'os', value: 'Ubuntu', type: :core))
102 )
103 end
104
105 it 'returns searched fact' do
106 allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
107 allow(File).to receive(:readable?).with(cache_file_name).and_return(true)
108 allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(false)
109
110 sf, _cf = cache_manager.resolve_facts(searched_facts)
111 expect(sf).to be_an_instance_of(Array).and contain_exactly(
112 an_object_having_attributes(name: 'my_custom_fact', type: :custom),
113 an_object_having_attributes(name: 'my_external_fact', type: :file)
114 )
115 end
116
117 it 'returns cached external facts' do
118 allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
119 allow(fact_groups).to receive(:get_fact).with('my_custom_fact').and_return(nil)
120 allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(external_fact)
121 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
122 allow(Facter::Util::FileHelper).to receive(:safe_read).with(File.join(cache_dir, 'ext_file.txt'))
123 .and_return(cached_external_fact)
124 allow(File).to receive(:readable?).with(File.join(cache_dir, 'ext_file.txt')).and_return(true)
125 allow(File).to receive(:mtime).with(File.join(cache_dir, 'ext_file.txt')).and_return(Time.now)
126
127 _, cached_facts = cache_manager.resolve_facts(searched_facts)
128 expect(cached_facts).to be_an_instance_of(Array).and contain_exactly(
129 an_instance_of(Facter::ResolvedFact).and(having_attributes(name: 'my_external_fact', value: 'ext_fact',
130 type: :file))
131 )
132 end
133
134 context 'when file cannot be deleted because of access denied' do
135 let(:cache_file) { File.join(cache_dir, 'ext_file.txt') }
136
137 it 'logs warn if it cannot delete' do
138 allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(external_fact)
139 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
140 allow(Facter::Util::FileHelper).to receive(:safe_read).with(cache_file)
141 .and_return(cached_external_fact)
142 allow(JSON).to receive(:parse).with(cached_external_fact).and_raise(JSON::ParserError)
143 allow(File).to receive(:readable?).with(cache_file).and_return(true)
144 allow(File).to receive(:mtime).with(File.join(cache_dir, 'ext_file.txt')).and_return(Time.now)
145 allow(File).to receive(:delete).with(cache_file).and_raise(Errno::EACCES)
146
147 cache_manager.resolve_facts([searched_external_fact])
148 expect(logger).to have_received(:warn)
149 end
150 end
151
152 context 'when file cannot be deleted because of read-only filesystem' do
153 let(:cache_file) { File.join(cache_dir, 'ext_file.txt') }
154
155 it 'logs warn if it cannot delete' do
156 allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(external_fact)
157 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
158 allow(Facter::Util::FileHelper).to receive(:safe_read).with(cache_file)
159 .and_return(cached_external_fact)
160 allow(JSON).to receive(:parse).with(cached_external_fact).and_raise(JSON::ParserError)
161 allow(File).to receive(:readable?).with(cache_file).and_return(true)
162 allow(File).to receive(:mtime).with(File.join(cache_dir, 'ext_file.txt')).and_return(Time.now)
163 allow(File).to receive(:delete).with(cache_file).and_raise(Errno::EROFS)
164
165 cache_manager.resolve_facts([searched_external_fact])
166 expect(logger).to have_received(:warn)
167 end
168 end
169 end
170
171 context 'with timer' do
172 before do
173 allow(File).to receive(:directory?).and_return(true)
174 allow(fact_groups).to receive(:get_fact_group).and_return(group_name)
175 allow(fact_groups).to receive(:get_group_ttls).and_return(nil)
176 allow(fact_groups).to receive(:get_fact).and_return(nil)
177 allow(File).to receive(:readable?)
178 allow(File).to receive(:mtime).with(cache_file_name).and_return(Time.now)
179 allow(Facter::Util::FileHelper).to receive(:safe_read).with(cache_file_name).and_return(cached_core_fact)
180 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
181 allow(Facter::Framework::Benchmarking::Timer).to receive(:measure)
182 end
183
184 it 'returns cached external facts' do
185 allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
186 allow(fact_groups).to receive(:get_fact).with('my_custom_fact').and_return(nil)
187 allow(fact_groups).to receive(:get_fact).with('ext_file.txt').and_return(external_fact)
188 allow(Facter::Util::FileHelper).to receive(:safe_read).with(File.join(cache_dir, 'ext_file.txt'))
189 .and_return(cached_external_fact)
190 allow(File).to receive(:mtime).with(File.join(cache_dir, 'ext_file.txt')).and_return(Time.now)
191
192 cache_manager.resolve_facts(searched_facts)
193
194 expect(Facter::Framework::Benchmarking::Timer).to have_received(:measure)
195 end
196 end
197 end
198
199 describe '#cache_facts' do
200 context 'with group not cached' do
201 before do
202 allow(File).to receive(:directory?).with(cache_dir).and_return(true)
203 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
204 allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
205 allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
206 allow(File).to receive(:write).with(cache_file_name, cached_core_fact)
207 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
208 end
209
210 it 'returns without caching' do
211 cache_manager.cache_facts(resolved_facts)
212 expect(File).not_to have_received(:write).with(cache_file_name, cached_core_fact)
213 end
214 end
215
216 context 'with cache group' do
217 before do
218 allow(File).to receive(:directory?).with(cache_dir).and_return(true)
219 allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
220 allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
221 allow(fact_groups).to receive(:get_fact_group).with('my_custom_fact').and_return(nil)
222 allow(fact_groups).to receive(:get_fact_group).with('my_external_fact').and_return(nil)
223 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
224 allow(File).to receive(:write).with(cache_file_name, cached_core_fact)
225 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
226 allow(Facter::Options).to receive(:[]).with(:ttls).and_return(['fact'])
227 end
228
229 it 'caches fact' do
230 cache_manager.cache_facts(resolved_facts)
231 expect(File).to have_received(:write).with(cache_file_name, cached_core_fact)
232 end
233 end
234 end
235
236 describe '#fact_cache_enabled?' do
237 context 'with ttls' do
238 before do
239 allow(fact_groups).to receive(:get_fact).with('os').and_return(os_fact)
240 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
241 end
242
243 it 'returns true' do
244 result = cache_manager.fact_cache_enabled?('os')
245 expect(result).to be true
246 end
247 end
248
249 context 'without ttls' do
250 before do
251 allow(fact_groups).to receive(:get_fact).with('os').and_return(nil)
252 allow(fact_groups).to receive(:get_fact_group).with('os').and_return(group_name)
253 allow(Facter::Options).to receive(:[]).with(:cache).and_return(true)
254 allow(File).to receive(:delete).with(cache_file_name)
255 end
256
257 it 'returns false' do
258 allow(File).to receive(:readable?).with(cache_file_name).and_return(false)
259 result = cache_manager.fact_cache_enabled?('os')
260 expect(result).to be false
261 end
262 end
263 end
264 end
0 # frozen_string_literal: true
1
2 describe Facter::FactFilter do
3 describe '#filter_facts!' do
4 context 'when legacy facts are blocked' do
5 let(:fact_value) { 'value_1' }
6 let(:resolved_fact) { Facter::ResolvedFact.new('my_fact', fact_value, :legacy) }
7
8 before do
9 allow(Facter::Options).to receive(:[])
10 allow(Facter::Options).to receive(:[]).with(:show_legacy).and_return(false)
11 end
12
13 it 'filters blocked legacy facts' do
14 fact_filter_input = [resolved_fact]
15 Facter::FactFilter.new.filter_facts!(fact_filter_input, [])
16 expect(fact_filter_input).to eq([])
17 end
18
19 context 'when user_query is provided' do
20 it 'does not filter out the requested fact' do
21 fact_filter_input = [resolved_fact]
22 result = Facter::FactFilter.new.filter_facts!([resolved_fact], ['my_fact'])
23 expect(result).to eql(fact_filter_input)
24 end
25 end
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 describe Facter do
3 let(:fact_name) { 'os.name' }
4 let(:fact_user_query) { 'os.name' }
5 let(:fact_value) { 'ubuntu' }
6 let(:type) { :core }
7 let(:os_fact) do
8 instance_spy(Facter::ResolvedFact, name: fact_name, value: fact_value,
9 user_query: fact_user_query, type: type)
10 end
11 let(:missing_fact) do
12 instance_spy(Facter::ResolvedFact, name: 'missing_fact', value: nil,
13 user_query: 'missing_fact', type: :nil)
14 end
15 let(:empty_fact_collection) { Facter::FactCollection.new }
16 let(:logger) { instance_spy(Facter::Log) }
17 let(:fact_manager_spy) { instance_spy(Facter::FactManager) }
18 let(:fact_collection_spy) { instance_spy(Facter::FactCollection) }
19 let(:key_error) { KeyError.new('key error') }
20 let(:config_reader_double) { class_spy(Facter::ConfigReader) }
21
22 before do
23 allow(Facter::ConfigReader).to receive(:init).and_return(config_reader_double)
24 allow(config_reader_double).to receive(:cli).and_return(nil)
25 allow(config_reader_double).to receive(:global).and_return(nil)
26 allow(config_reader_double).to receive(:ttls).and_return([])
27 allow(config_reader_double).to receive(:block_list).and_return([])
28 allow(config_reader_double).to receive(:fact_groups).and_return({})
29
30 allow(Facter::Options).to receive(:[]).and_call_original
31 allow(Facter::Options).to receive(:[]).with(:blocked_facts).and_return([])
32 allow(Facter::Options).to receive(:[]).with(:block_list).and_return([])
33
34 allow(Facter::Log).to receive(:new).and_return(logger)
35 Facter.clear
36 allow(Facter::SessionCache).to receive(:invalidate_all_caches)
37 allow(Facter::FactManager).to receive(:instance).and_return(fact_manager_spy)
38 allow(Facter::FactCollection).to receive(:new).and_return(fact_collection_spy)
39 end
40
41 after do
42 Facter.instance_variable_set(:@logger, nil)
43 end
44
45 def mock_fact_manager(method, return_value)
46 allow(fact_manager_spy).to receive(method).and_return(return_value)
47 allow(fact_collection_spy)
48 .to receive(:build_fact_collection!)
49 .with(return_value)
50 .and_return(return_value.empty? ? empty_fact_collection : fact_collection_spy)
51 end
52
53 def mock_collection(method, os_name = nil, error = nil)
54 if error
55 allow(fact_collection_spy).to receive(method).with('os', 'name').and_raise(error)
56 else
57 allow(fact_collection_spy).to receive(method).with('os', 'name').and_return(os_name)
58 end
59 end
60
61 describe '#resolve' do
62 let(:cli_double) { instance_spy(Facter::Cli) }
63
64 before do
65 allow(Facter).to receive(:queried_facts)
66 end
67
68 context 'when user query and options in arguments' do
69 it 'calls queried_facts' do
70 Facter.resolve('os --show-legacy')
71
72 expect(Facter).to have_received(:queried_facts).with(['os'])
73 end
74 end
75 end
76
77 describe '#to_hash' do
78 before do
79 allow(Hash).to receive(:[]).with(fact_collection_spy).and_return({})
80 allow(Hash).to receive(:[]).with(empty_fact_collection).and_return({})
81 end
82
83 it 'returns one resolved fact' do
84 mock_fact_manager(:resolve_facts, [os_fact])
85
86 expect(Facter.to_hash).to eq({})
87 end
88
89 it 'return no resolved facts' do
90 mock_fact_manager(:resolve_facts, [])
91
92 expect(Facter.to_hash).to eq({})
93 end
94
95 context 'when custom fact with nil value' do
96 let(:type) { :custom }
97 let(:fact_value) { nil }
98 let(:fact_user_query) { '' }
99
100 it 'discards the custom fact with nil value' do
101 mock_fact_manager(:resolve_facts, [os_fact])
102
103 Facter.to_hash
104
105 expect(fact_collection_spy).to have_received(:build_fact_collection!).with([])
106 end
107 end
108 end
109
110 describe '#to_user_output' do
111 let(:json_fact_formatter) { instance_spy(Facter::JsonFactFormatter) }
112
113 before do |example|
114 resolved_fact = example.metadata[:multiple_facts] ? [os_fact, missing_fact] : [os_fact]
115 expected_json_output = if example.metadata[:multiple_facts]
116 '{"os" : {"name": "ubuntu"}, "missing_fact": null}'
117 else
118 '{"os" : {"name": "ubuntu"}}'
119 end
120
121 allow(fact_manager_spy).to receive(:resolve_facts).and_return(resolved_fact)
122 allow(json_fact_formatter).to receive(:format).with(resolved_fact).and_return(expected_json_output)
123 allow(Facter::FormatterFactory).to receive(:build).and_return(json_fact_formatter)
124 end
125
126 it 'returns one fact with value and status 0', multiple_facts: false do
127 user_query = ['os.name']
128 expected_json_output = '{"os" : {"name": "ubuntu"}}'
129
130 formatted_facts = Facter.to_user_output({}, [user_query])
131
132 expect(formatted_facts).to eq([expected_json_output, 0])
133 end
134
135 it 'returns one fact with value, one without and status 0', multiple_facts: true do
136 user_query = ['os.name', 'missing_fact']
137 expected_json_output = '{"os" : {"name": "ubuntu"}, "missing_fact": null}'
138
139 formated_facts = Facter.to_user_output({}, [user_query])
140
141 expect(formated_facts).to eq([expected_json_output, 0])
142 end
143
144 context 'when provided with --strict option' do
145 it 'returns one fact with value, one without and status 1', multiple_facts: true do
146 user_query = ['os.name', 'missing_fact']
147 expected_json_output = '{"os" : {"name": "ubuntu"}, "missing_fact": null}'
148 allow(Facter::Options).to receive(:[]).with(:strict).and_return(true)
149
150 formatted_facts = Facter.to_user_output({}, *user_query)
151
152 expect(formatted_facts).to eq([expected_json_output, 1])
153 end
154
155 it 'returns one fact and status 0', multiple_facts: false do
156 user_query = 'os.name'
157 expected_json_output = '{"os" : {"name": "ubuntu"}}'
158 allow(Facter::Options).to receive(:[]).with(anything)
159 allow(Facter::Options).to receive(:[]).with(:block_list).and_return([])
160 allow(Facter::Options).to receive(:[]).with(:strict).and_return(true)
161
162 formated_facts = Facter.to_user_output({}, user_query)
163
164 expect(formated_facts).to eq([expected_json_output, 0])
165 end
166 end
167
168 context 'when custom fact with nil value' do
169 let(:type) { :custom }
170 let(:fact_value) { nil }
171 let(:fact_user_query) { '' }
172
173 it 'discards the custom fact with nil value' do
174 user_query = ['os.name']
175
176 Facter.to_user_output({}, *user_query)
177
178 expect(json_fact_formatter).to have_received(:format).with([])
179 end
180 end
181
182 context 'when no facts are returned' do
183 let(:type) { :custom }
184 let(:fact_value) { nil }
185 let(:fact_user_query) { '' }
186 let(:expected_json_output) { {} }
187
188 before do
189 allow(fact_manager_spy).to receive(:resolve_facts).and_return([])
190 allow(json_fact_formatter).to receive(:format).with([]).and_return(expected_json_output)
191 end
192
193 it 'does not raise exceptions' do
194 expect { Facter.to_user_output({}, '') }.not_to raise_error
195 end
196 end
197 end
198
199 describe '#value' do
200 it 'downcases the user query' do
201 mock_fact_manager(:resolve_fact, [os_fact])
202 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
203
204 expect(Facter.value('OS.NAME')).to eq('Ubuntu')
205 end
206
207 it 'returns a value' do
208 mock_fact_manager(:resolve_fact, [os_fact])
209 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
210
211 expect(Facter.value('os.name')).to eq('Ubuntu')
212 end
213
214 it 'return no value' do
215 mock_fact_manager(:resolve_fact, [])
216 allow(fact_collection_spy).to receive(:value).with('os.name').and_return(nil)
217
218 expect(Facter.value('os.name')).to be nil
219 end
220
221 context 'when custom fact with nil value' do
222 let(:type) { :custom }
223 let(:fact_value) { nil }
224 let(:fact_user_query) { '' }
225
226 it 'returns the custom fact' do
227 mock_fact_manager(:resolve_fact, [os_fact])
228 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
229
230 expect(Facter.value('os.name')).to eq('Ubuntu')
231 end
232 end
233 end
234
235 describe '#fact' do
236 it 'downcases the user query' do
237 mock_fact_manager(:resolve_fact, [os_fact])
238 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
239
240 expect(Facter.fact('OS.NAME')).to be_instance_of(Facter::ResolvedFact).and have_attributes(value: 'Ubuntu')
241 end
242
243 it 'returns a fact' do
244 mock_fact_manager(:resolve_fact, [os_fact])
245 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
246
247 expect(Facter.fact('os.name')).to be_instance_of(Facter::ResolvedFact).and have_attributes(value: 'Ubuntu')
248 end
249
250 it 'can be interpolated' do
251 mock_fact_manager(:resolve_fact, [os_fact])
252 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
253
254 expect("#{Facter.fact('os.name')}-test").to eq('Ubuntu-test')
255 end
256
257 it 'returns no value' do
258 mock_fact_manager(:resolve_fact, [])
259 allow(fact_collection_spy).to receive(:value).with('os.name').and_raise(key_error)
260
261 expect(Facter.fact('os.name')).to be_nil
262 end
263
264 context 'when there is a resolved fact with type nil' do
265 before do
266 allow(fact_manager_spy).to receive(:resolve_fact).and_return([missing_fact])
267 allow(fact_collection_spy).to receive(:build_fact_collection!).with([]).and_return(empty_fact_collection)
268 allow(fact_collection_spy).to receive(:value).and_raise(KeyError)
269 end
270
271 it 'is rejected' do
272 Facter.fact('missing_fact')
273
274 expect(fact_collection_spy).to have_received(:build_fact_collection!).with([])
275 end
276
277 it 'returns nil' do
278 expect(Facter.fact('missing_fact')).to be_nil
279 end
280 end
281
282 context 'when custom fact with nill value' do
283 let(:type) { :custom }
284 let(:fact_value) { nil }
285 let(:fact_user_query) { '' }
286
287 it 'returns the custom fact' do
288 mock_fact_manager(:resolve_fact, [os_fact])
289 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
290
291 expect(Facter.fact('os.name'))
292 .to be_instance_of(Facter::ResolvedFact)
293 .and have_attributes(value: 'Ubuntu')
294 end
295 end
296 end
297
298 describe '#[]' do
299 it 'returns a fact' do
300 mock_fact_manager(:resolve_fact, [os_fact])
301 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
302
303 expect(Facter['os.name']).to be_instance_of(Facter::ResolvedFact).and(having_attributes(value: 'Ubuntu'))
304 end
305
306 it 'return no value' do
307 mock_fact_manager(:resolve_fact, [])
308 allow(fact_collection_spy).to receive(:value).with('os.name').and_raise(key_error)
309
310 expect(Facter['os.name']).to be_nil
311 end
312
313 context 'when custom fact with nill value' do
314 let(:type) { :custom }
315 let(:fact_value) { nil }
316 let(:fact_user_query) { '' }
317
318 it 'returns the custom fact' do
319 mock_fact_manager(:resolve_fact, [os_fact])
320 allow(fact_collection_spy).to receive(:value).with('os.name').and_return('Ubuntu')
321
322 expect(Facter['os.name'])
323 .to be_instance_of(Facter::ResolvedFact)
324 .and have_attributes(value: 'Ubuntu')
325 end
326 end
327 end
328
329 describe '#core_value' do
330 it 'searched in core facts and returns a value' do
331 mock_fact_manager(:resolve_core, [os_fact])
332 mock_collection(:dig, 'Ubuntu')
333
334 expect(Facter.core_value('os.name')).to eq('Ubuntu')
335 end
336
337 it 'searches os core fact and returns no value' do
338 mock_fact_manager(:resolve_core, [])
339 mock_collection(:dig, nil)
340
341 expect(Facter.core_value('os.name')).to be nil
342 end
343 end
344
345 describe 'LegacyFacter methods' do
346 before do
347 allow(LegacyFacter).to receive(:clear)
348 end
349
350 describe '#clear' do
351 it 'sends call to LegacyFacter' do
352 Facter.clear
353 expect(LegacyFacter).to have_received(:clear).once
354 end
355 end
356
357 describe '#flush' do
358 it 'sends call to LegacyFacter' do
359 allow(LegacyFacter).to receive(:flush)
360
361 Facter.flush
362
363 expect(LegacyFacter).to have_received(:flush).once
364 end
365
366 it 'invalidates core cache' do
367 allow(Facter::SessionCache).to receive(:invalidate_all_caches)
368
369 Facter.flush
370
371 expect(Facter::SessionCache).to have_received(:invalidate_all_caches)
372 end
373 end
374
375 describe '#load_external' do
376 before do
377 allow(Facter::Options).to receive(:[]=)
378 end
379
380 it 'sends call to Facter::Options' do
381 allow(Facter::Options).to receive(:[]=)
382 Facter.load_external(true)
383
384 expect(Facter::Options).to have_received(:[]=).with(:no_external_facts, false)
385 end
386
387 it 'logs a debug message' do
388 Facter.load_external(true)
389
390 expect(logger).to have_received(:debug).with('Facter.load_external(true) called. External facts will be loaded')
391 end
392 end
393
394 describe '#search' do
395 it 'sends call to Facter::Options' do
396 allow(Facter::Options).to receive(:[]=)
397 dirs = ['/dir1', '/dir2']
398 Facter.search(*dirs)
399
400 expect(Facter::Options).to have_received(:[]=).with(:custom_dir, dirs)
401 end
402 end
403
404 describe '#search_path' do
405 it 'sends call to Facter::Options' do
406 allow(Facter::Options).to receive(:custom_dir)
407 Facter.search_path
408
409 expect(Facter::Options).to have_received(:custom_dir).once
410 end
411 end
412
413 describe '#search_external' do
414 it 'sends call to Facter::Options' do
415 allow(Facter::Options).to receive(:[]=)
416 dirs = ['/dir1', '/dir2']
417 Facter.search_external(dirs)
418
419 expect(Facter::Options).to have_received(:[]=).with(:external_dir, dirs)
420 end
421 end
422
423 describe '#search_external_path' do
424 it 'sends call to Facter::Options' do
425 allow(Facter::Options).to receive(:external_dir)
426
427 Facter.search_external_path
428
429 expect(Facter::Options).to have_received(:external_dir).once
430 end
431 end
432
433 describe '#reset' do
434 it 'sends call to LegacyFacter' do
435 allow(LegacyFacter).to receive(:reset)
436
437 Facter.reset
438
439 expect(LegacyFacter).to have_received(:reset).once
440 end
441
442 it 'adds custom facts dirs' do
443 allow(Facter::Options).to receive(:[]=)
444
445 Facter.reset
446
447 expect(Facter::Options).to have_received(:[]=).with(:custom_dir, [])
448 end
449
450 it 'add external facts dirs' do
451 allow(Facter::Options).to receive(:[]=)
452
453 Facter.reset
454
455 expect(Facter::Options).to have_received(:[]=).with(:external_dir, [])
456 end
457 end
458 end
459
460 describe '#define_fact' do
461 it 'sends call to LegacyFacter' do
462 allow(LegacyFacter).to receive(:define_fact)
463
464 Facter.define_fact('fact_name') {}
465
466 expect(LegacyFacter).to have_received(:define_fact).once.with('fact_name', { fact_type: :custom })
467 end
468 end
469
470 describe '#loadfacts' do
471 it 'sends calls to LegacyFacter' do
472 allow(LegacyFacter).to receive(:loadfacts)
473
474 Facter.loadfacts
475
476 expect(LegacyFacter).to have_received(:loadfacts).once
477 end
478
479 it 'returns nil' do
480 allow(LegacyFacter).to receive(:loadfacts)
481
482 expect(Facter.loadfacts).to be_nil
483 end
484 end
485
486 describe '#trace?' do
487 it 'returns trace variable' do
488 expect(Facter).not_to be_trace
489 end
490 end
491
492 describe '#trace' do
493 after do
494 Facter.trace(false)
495 end
496
497 it 'trace variable is true' do
498 expect(Facter.trace(true)).to be_truthy
499 end
500 end
501
502 describe '#debug' do
503 let(:message) { 'test' }
504
505 before do
506 allow(Facter::Options).to receive(:[]).with(:debug).and_return(is_debug)
507 end
508
509 context 'when log level is debug' do
510 let(:is_debug) { true }
511
512 it 'logs a debug message' do
513 allow(logger).to receive(:debug).with('test')
514
515 expect(Facter.debug(message)).to be(nil)
516 end
517 end
518
519 context 'when log level is not debug' do
520 let(:is_debug) { false }
521
522 it "doesn't log anything" do
523 Facter.debug(message)
524
525 expect(logger).not_to have_received(:debug).with(message)
526 end
527 end
528 end
529
530 describe '#debugging' do
531 it 'sets log level to debug' do
532 allow(Facter::Options).to receive(:[]=)
533 Facter.debugging(true)
534
535 expect(Facter::Options).to have_received(:[]=).with(:debug, true)
536 end
537 end
538
539 describe '#debugging?' do
540 it 'returns that log_level is not debug' do
541 Facter.debugging?
542
543 expect(Facter::Options).to have_received(:[]).with(:debug)
544 end
545 end
546
547 describe '#log_exception' do
548 shared_examples 'when exception param is an exception' do
549 it 'logs exception message' do
550 exception.set_backtrace(backtrace)
551
552 Facter.log_exception(exception, message)
553
554 expect(logger).to have_received(:error).with(expected_message)
555 end
556 end
557
558 shared_examples 'when exception param is not an exception' do
559 it 'logs exception message' do
560 Facter.log_exception(exception, message)
561
562 expect(logger).to have_received(:error).with(expected_message)
563 end
564 end
565
566 context 'when trace option is false' do
567 let(:backtrace) { 'prog.rb:2:in a' }
568
569 context 'when we have an exception and a message' do
570 let(:message) { 'Some error message' }
571 let(:exception) { FlushFakeError.new }
572 let(:expected_message) { 'Some error message' }
573
574 it_behaves_like 'when exception param is an exception'
575 end
576
577 context 'when we have an exception and an empty message' do
578 let(:message) { '' }
579 let(:exception) { FlushFakeError.new }
580 let(:expected_message) { 'FlushFakeError' }
581
582 it_behaves_like 'when exception param is an exception'
583 end
584
585 context 'when we have an exception and a nil message' do
586 let(:message) { nil }
587 let(:exception) { FlushFakeError.new }
588 let(:expected_message) { 'FlushFakeError' }
589
590 it_behaves_like 'when exception param is an exception'
591 end
592
593 context 'when we have an exception and no message' do
594 let(:exception) { FlushFakeError.new }
595 let(:expected_message) { 'FlushFakeError' }
596
597 it 'logs exception message' do
598 exception.set_backtrace(backtrace)
599
600 Facter.log_exception(exception)
601
602 expect(logger).to have_received(:error).with(expected_message)
603 end
604 end
605
606 context 'when exception and message are strings' do
607 let(:message) { 'message' }
608 let(:exception) { 'exception' }
609 let(:expected_message) { 'message' }
610
611 it_behaves_like 'when exception param is not an exception'
612 end
613
614 context 'when exception and message are nil' do
615 let(:message) { nil }
616 let(:exception) { nil }
617 let(:expected_message) { '' }
618
619 it_behaves_like 'when exception param is not an exception'
620 end
621
622 context 'when exception and message are hashes' do
623 let(:message) { { 'a': 1 } }
624 let(:exception) { { 'b': 2 } }
625 let(:expected_message) { '{:a=>1}' }
626
627 it_behaves_like 'when exception param is not an exception'
628 end
629 end
630
631 context 'when trace options is true' do
632 before do
633 Facter.trace(true)
634 end
635
636 after do
637 Facter.trace(false)
638 end
639
640 let(:backtrace) { 'prog.rb:2:in a' }
641
642 context 'when we have an exception and a message' do
643 let(:message) { 'Some error message' }
644 let(:exception) { FlushFakeError.new }
645 let(:expected_message) { "Some error message\nbacktrace:\nprog.rb:2:in a" }
646
647 it_behaves_like 'when exception param is an exception'
648 end
649
650 context 'when we have an exception and an empty message' do
651 let(:message) { '' }
652 let(:exception) { FlushFakeError.new }
653 let(:expected_message) { "FlushFakeError\nbacktrace:\nprog.rb:2:in a" }
654
655 it_behaves_like 'when exception param is an exception'
656 end
657
658 context 'when we have an exception and a nil message' do
659 let(:message) { nil }
660 let(:exception) { FlushFakeError.new }
661 let(:expected_message) { "FlushFakeError\nbacktrace:\nprog.rb:2:in a" }
662
663 it_behaves_like 'when exception param is an exception'
664 end
665
666 context 'when we have an exception and no message' do
667 let(:exception) { FlushFakeError.new }
668 let(:expected_message) { "FlushFakeError\nbacktrace:\nprog.rb:2:in a" }
669
670 it 'logs exception message' do
671 exception.set_backtrace(backtrace)
672
673 Facter.log_exception(exception)
674
675 expect(logger).to have_received(:error).with(expected_message)
676 end
677 end
678
679 context 'when we have an exception with no backtrace' do
680 let(:exception) { FlushFakeError.new }
681 let(:expected_message) { 'FlushFakeError' }
682
683 it 'logs exception message' do
684 Facter.log_exception(exception)
685
686 expect(logger).to have_received(:error).with(expected_message)
687 end
688 end
689
690 context 'when exception and message are strings' do
691 let(:message) { 'message' }
692 let(:exception) { 'exception' }
693 let(:expected_message) { 'message' }
694
695 it_behaves_like 'when exception param is not an exception'
696 end
697
698 context 'when exception and message are nil' do
699 let(:message) { nil }
700 let(:exception) { nil }
701 let(:expected_message) { '' }
702
703 it_behaves_like 'when exception param is not an exception'
704 end
705
706 context 'when exception and message are hashes' do
707 let(:message) { { 'a': 1 } }
708 let(:exception) { { 'b': 2 } }
709 let(:expected_message) { '{:a=>1}' }
710
711 it_behaves_like 'when exception param is not an exception'
712 end
713 end
714 end
715
716 describe '#debugonce' do
717 context 'when debugging is active' do
718 before do
719 allow(logger).to receive(:debug)
720 allow(Facter::Log).to receive(:level=).with(:debug)
721 Facter.debugging(true)
722 end
723
724 after do
725 allow(Facter::Log).to receive(:level=).with(:warn)
726 Facter.debugging(false)
727 end
728
729 it 'calls logger with the debug message' do
730 message = 'Some error message'
731
732 Facter.debugonce(message)
733
734 expect(logger).to have_received(:debugonce).with(message)
735 end
736
737 it 'writes empty message when message is nil' do
738 Facter.debugonce(nil)
739
740 expect(logger).to have_received(:debugonce).with(nil)
741 end
742
743 it 'when message is a hash' do
744 Facter.debugonce({ warn: 'message' })
745
746 expect(logger).to have_received(:debugonce).with({ warn: 'message' })
747 end
748
749 it 'returns nil' do
750 result = Facter.debugonce({ warn: 'message' })
751
752 expect(result).to be_nil
753 end
754 end
755
756 context 'when debugging is inactive' do
757 before do
758 allow(logger).to receive(:debug)
759 end
760
761 it 'does not call the logger' do
762 Facter.debugonce('message')
763
764 expect(logger).not_to have_received(:debug)
765 end
766 end
767 end
768
769 describe '#list' do
770 before do
771 allow(Facter).to receive(:to_hash).and_return({ 'up_time' => 235, 'timezone' => 'EEST', 'virtual' => 'physical' })
772 end
773
774 it 'returns the resolved fact names' do
775 result = Facter.list
776
777 expect(result).to eq(%w[timezone up_time virtual])
778 end
779 end
780
781 describe '#warnonce' do
782 before do
783 allow(logger).to receive(:warn)
784 end
785
786 it 'calls logger with the warning message' do
787 message = 'Some error message'
788
789 Facter.warnonce(message)
790
791 expect(logger).to have_received(:warnonce).with(message)
792 end
793
794 it 'writes empty message when message is nil' do
795 Facter.warnonce(nil)
796
797 expect(logger).to have_received(:warnonce).with(nil)
798 end
799
800 it 'when message is a hash' do
801 Facter.warnonce({ warn: 'message' })
802
803 expect(logger).to have_received(:warnonce).with({ warn: 'message' })
804 end
805
806 it 'returns nil' do
807 result = Facter.warnonce({ warn: 'message' })
808
809 expect(result).to be_nil
810 end
811 end
812
813 describe '#warn' do
814 before do
815 allow(logger).to receive(:warn)
816 end
817
818 it 'calls logger' do
819 message = 'Some error message'
820
821 Facter.warn(message)
822
823 expect(logger).to have_received(:warn).with(message)
824 end
825
826 it 'when message is nil' do
827 Facter.warn(nil)
828
829 expect(logger).to have_received(:warn).with('')
830 end
831
832 it 'when message is empty string' do
833 Facter.warn('')
834 expect(logger).to have_received(:warn).with('')
835 end
836
837 it 'when message is a hash' do
838 Facter.warn({ warn: 'message' })
839
840 expect(logger).to have_received(:warn).with('{:warn=>"message"}')
841 end
842
843 it 'when message is an array' do
844 Facter.warn([1, 2, 3])
845
846 expect(logger).to have_received(:warn).with('[1, 2, 3]')
847 end
848
849 it 'returns nil' do
850 result = Facter.warn('message')
851
852 expect(result).to be_nil
853 end
854 end
855
856 describe '#each' do
857 it 'returns one resolved fact' do
858 mock_fact_manager(:resolve_facts, [os_fact])
859
860 result = {}
861 Facter.each { |name, value| result[name] = value }
862 expect(result).to eq({ fact_name => fact_value })
863 end
864 end
865 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::AioAgentVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::AioAgentVersion.new }
5
6 let(:value) { '1.2.3' }
7
8 before do
9 allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Agent' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version)
15 end
16
17 it 'returns aio_agent_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'aio_agent_version', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Disks do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Disks.new }
5
6 let(:disk) do
7 {
8 'hdisk0' => {
9 size: '20.00 GiB',
10 size_bytes: 21_474_836_480
11 }
12 }
13 end
14
15 let(:expecte_response) do
16 {
17 'hdisk0' => {
18 'size' => '20.00 GiB',
19 'size_bytes' => 21_474_836_480
20 }
21 }
22 end
23
24 before do
25 allow(Facter::Resolvers::Aix::Disks).to receive(:resolve).with(:disks).and_return(disk)
26 end
27
28 it 'calls Facter::Resolvers::Aix::Disk' do
29 fact.call_the_resolver
30 expect(Facter::Resolvers::Aix::Disks).to have_received(:resolve).with(:disks)
31 end
32
33 it 'returns resolved fact with name disk and value' do
34 expect(fact.call_the_resolver)
35 .to be_an_instance_of(Array)
36 .and contain_exactly(
37 an_object_having_attributes(name: 'disks', value: expecte_response),
38 an_object_having_attributes(name: 'blockdevices', value: 'hdisk0'),
39 an_object_having_attributes(name: 'blockdevice_hdisk0_size', value: 21_474_836_480, type: :legacy)
40 )
41 end
42
43 context 'when resolver returns empty hash' do
44 let(:disk) { {} }
45
46 it 'returns nil fact' do
47 expect(fact.call_the_resolver)
48 .to be_an_instance_of(Facter::ResolvedFact)
49 .and have_attributes(name: 'disks', value: nil)
50 end
51 end
52
53 context 'when resolver returns nil' do
54 let(:disk) { nil }
55
56 it 'returns nil fact' do
57 expect(fact.call_the_resolver)
58 .to be_an_instance_of(Facter::ResolvedFact)
59 .and have_attributes(name: 'disks', value: nil)
60 end
61 end
62 end
63 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Filesystems do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Filesystems.new }
5
6 let(:files) { 'apfs,autofs,devfs' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Filesystem).to \
10 receive(:resolve).with(:file_systems).and_return(files)
11 end
12
13 it 'calls Facter::Resolvers::Aix::Filesystem' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::Filesystem).to have_received(:resolve).with(:file_systems)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'filesystems', value: files)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Hypervisors::Lpar do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Hypervisors::Lpar.new }
5
6 let(:value) { { 'partition_number' => 13, 'partition_name' => 'aix6-7' } }
7
8 before do
9 allow(Facter::Resolvers::Lpar).to receive(:resolve).with(:lpar_partition_number)
10 .and_return(value['partition_number'])
11 allow(Facter::Resolvers::Lpar).to receive(:resolve).with(:lpar_partition_name)
12 .and_return(value['partition_name'])
13 end
14
15 it 'calls Facter::Resolvers::Lpar with lpar_partition_number' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Lpar).to have_received(:resolve).with(:lpar_partition_number)
18 end
19
20 it 'calls Facter::Resolvers::Lpar with lpar_partition_name' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Lpar).to have_received(:resolve).with(:lpar_partition_name)
23 end
24
25 it 'returns a hypervisors.lpar fact' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
27 have_attributes(name: 'hypervisors.lpar', value: value)
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Hypervisors::Wpar do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Hypervisors::Wpar.new }
5
6 let(:value) { { 'key' => 13, 'configured_id' => 14 } }
7
8 before do
9 allow(Facter::Resolvers::Wpar).to receive(:resolve).with(:wpar_key).and_return(value['key'])
10 allow(Facter::Resolvers::Wpar).to receive(:resolve).with(:wpar_configured_id).and_return(value['configured_id'])
11 end
12
13 it 'calls Facter::Resolvers::Wpar with wpar_key' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Wpar).to have_received(:resolve).with(:wpar_key)
16 end
17
18 it 'calls Facter::Resolvers::Wpar with wpar_configured_id' do
19 fact.call_the_resolver
20 expect(Facter::Resolvers::Wpar).to have_received(:resolve).with(:wpar_configured_id)
21 end
22
23 it 'returns a hypervisors.wpar fact' do
24 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
25 have_attributes(name: 'hypervisors.wpar', value: value)
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Identity::Gid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Identity::Gid.new }
5
6 let(:value) { '20' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:gid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:gid)
15 end
16
17 it 'returns identity gid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.gid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Identity::Group do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Identity::Group.new }
5
6 let(:value) { 'staff' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:group).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:group)
15 end
16
17 it 'returns identity group fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.group', value: value),
20 an_object_having_attributes(name: 'gid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Identity::Privileged do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Identity::Privileged.new }
5
6 let(:value) { 'false' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:privileged).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:privileged)
15 end
16
17 it 'returns identity privileged fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.privileged', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Identity::Uid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Identity::Uid.new }
5
6 let(:value) { '501' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:uid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:uid)
15 end
16
17 it 'returns identity uid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.uid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Identity::User.new }
5
6 let(:value) { 'root' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:user)
15 end
16
17 it 'returns id and identity user fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Interfaces do
3 subject(:fact) { Facts::Aix::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns interfaces names' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'interfaces', value: interfaces.keys.sort.join(','), type: :legacy)
20 end
21 end
22
23 describe '#call_the_resolver when resolver returns nil' do
24 let(:interfaces) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
28 have_attributes(name: 'interfaces', value: interfaces, type: :legacy)
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Aix::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::IpaddressInterfaces do
3 subject(:fact) { Facts::Aix::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Kernel.new }
5
6 let(:value) { 'AIX' }
7
8 before do
9 allow(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:kernel).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsLevel' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::OsLevel).to have_received(:resolve).with(:kernel)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Kernelmajversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Kernelmajversion.new }
5
6 let(:fact_value) { '6100' }
7 let(:resolver_value) { '6100-09-00-0000' }
8
9 before do
10 allow(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:build).and_return(resolver_value)
11 end
12
13 it 'calls Facter::Resolvers::OsLevel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::OsLevel).to have_received(:resolve).with(:build)
16 end
17
18 it 'returns kernelmajversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelmajversion', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Kernelrelease.new }
5
6 let(:value) { '6100-09-00-0000' }
7
8 before do
9 allow(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:build).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsLevel' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::OsLevel).to have_received(:resolve).with(:build)
15 end
16
17 it 'returns kernelrelease fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelrelease', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Kernelversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Kernelversion.new }
5
6 let(:resolver_value) { '6100-09-00-0000' }
7 let(:fact_value) { '6100' }
8
9 before do
10 allow(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:build).and_return(resolver_value)
11 end
12
13 it 'calls Facter::Resolvers::OsLevel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::OsLevel).to have_received(:resolve).with(:build)
16 end
17
18 it 'returns kernelversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelversion', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::LoadAverages do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::LoadAverages.new }
5
6 let(:value) { { '1m' => 0.01, '5m' => 0.02, '15m' => 0.03 } }
7
8 before do
9 allow(Facter::Resolvers::Aix::LoadAverages).to receive(:resolve).with(:load_averages).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Aix::LoadAverages' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::LoadAverages).to have_received(:resolve).with(:load_averages)
15 end
16
17 it 'returns load_averages fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'load_averages', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::MacaddressInterfaces do
3 subject(:fact) { Facts::Aix::MacaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mac: '10.16.117.100' }, 'en1' => { mac: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names macaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'macaddress_eth0',
20 value: interfaces['eth0'][:mac], type: :legacy),
21 an_object_having_attributes(name: 'macaddress_en1',
22 value: interfaces['en1'][:mac], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::AvailableBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 2_332_425 }
8 let(:value_mb) { 2.2243738174438477 }
9
10 before do
11 allow(Facter::Resolvers::Aix::Memory).to \
12 receive(:resolve).with(:swap).and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::Aix::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
18 end
19
20 it 'returns swap available bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: result),
23 an_object_having_attributes(name: 'swapfree_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28
29 it 'returns swap available memory in bytes fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
32 an_object_having_attributes(name: 'swapfree_mb', value: value, type: :legacy))
33 end
34 end
35
36 context 'when resolver returns empty hash' do
37 let(:value) { {} }
38 let(:result) { nil }
39
40 it 'returns swap available memory in bytes fact as nil' do
41 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
42 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: result),
43 an_object_having_attributes(name: 'swapfree_mb', value: result, type: :legacy))
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::Available.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.available', value: result),
22 an_object_having_attributes(name: 'swapfree', value: result, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::Capacity.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024, capacity: '7,9%' } }
7 let(:result) { '7,9%' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap memory capacity fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.capacity', value: result)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::TotalBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 2_332_999 }
8 let(:value_mb) { 2.224921226501465 }
9
10 before do
11 allow(Facter::Resolvers::Aix::Memory).to \
12 receive(:resolve).with(:swap).and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::Aix::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
18 end
19
20 it 'returns swap total memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: result),
23 an_object_having_attributes(name: 'swapsize_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28
29 it 'returns swap total memory in bytes fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
32 an_object_having_attributes(name: 'swapsize_mb', value: value, type: :legacy))
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::Total.new }
5
6 let(:value) { { available_bytes: 24, total_bytes: 1024, used_bytes: 1000 } }
7 let(:result) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.total', value: result),
22 an_object_having_attributes(name: 'swapsize', value: result, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::UsedBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 1024 }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap used memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.used_bytes', value: result)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::Swap::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::Swap::Used.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:swap).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::AvailableBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 2_332_425 }
8 let(:value_mb) { 2.2243738174438477 }
9
10 before do
11 allow(Facter::Resolvers::Aix::Memory).to \
12 receive(:resolve).with(:system).and_return(resolver_output)
13 end
14
15 it 'calls Facter::Resolvers::Aix::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
18 end
19
20 it 'returns system available memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
23 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28 let(:resolver_output) { nil }
29
30 it 'returns system available memory in bytes fact as nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
32 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
33 an_object_having_attributes(name: 'memoryfree_mb', value: value, type: :legacy))
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::Available.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
22 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::Capacity.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024, capacity: '5.3%' } }
7 let(:value) { '5.3%' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_output)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system memory capacity fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.capacity', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::TotalBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 2_332_999 }
8 let(:value_mb) { 2.224921226501465 }
9
10 before do
11 allow(Facter::Resolvers::Aix::Memory).to \
12 receive(:resolve).with(:system).and_return(resolver_output)
13 end
14
15 it 'calls Facter::Resolvers::Aix::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
18 end
19
20 it 'returns system total memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
23 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28 let(:resolver_output) { nil }
29
30 it 'returns system total memory in bytes fact as nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
32 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
33 an_object_having_attributes(name: 'memorysize_mb', value: value, type: :legacy))
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::Total.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
22 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::UsedBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 1024 }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_output)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system used memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.used_bytes', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Memory::System::Used.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Mountpoints do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Mountpoints.new }
5
6 let(:result) do
7 { '/' => { 'device' => '/dev/hd4', 'filesystem' => 'jfs2', 'options' => ['rw', 'log=/dev/hd8'],
8 'capacity' => '18.61%', 'available_bytes' => 1_747_865_600,
9 'used_bytes' => 399_618_048, 'size_bytes' => 2_147_483_648,
10 'available' => '1.63 GiB', 'used' => '381.11 MiB', 'size' => '2.00 GiB' } }
11 end
12
13 before do
14 allow(Facter::Resolvers::Aix::Mountpoints).to \
15 receive(:resolve).with(:mountpoints).and_return(result)
16 end
17
18 it 'calls Facter::Resolvers::Aix::Mountpoints' do
19 fact.call_the_resolver
20 expect(Facter::Resolvers::Aix::Mountpoints).to have_received(:resolve).with(:mountpoints)
21 end
22
23 it 'returns a resolved fact' do
24 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
25 have_attributes(name: 'mountpoints', value: result)
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::MtuInterfaces do
3 subject(:fact) { Facts::Aix::MtuInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mtu: 1500 }, 'en1' => { mtu: 1500 } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names mtu_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'mtu_eth0', value: interfaces['eth0'][:mtu], type: :legacy),
20 an_object_having_attributes(name: 'mtu_en1', value: interfaces['en1'][:mtu], type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:interfaces) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Netmask6Interfaces do
3 subject(:fact) { Facts::Aix::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::Aix::Networking' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::NetmaskInterfaces do
3 subject(:fact) { Facts::Aix::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Network6Interfaces do
3 subject(:fact) { Facts::Aix::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::NetworkInterfaces do
3 subject(:fact) { Facts::Aix::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Networking::Domain.new }
5
6 let(:value) { 'domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Networking::Fqdn.new }
5
6 let(:value) { 'host.domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:fqdn).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:fqdn)
15 end
16
17 it 'returns fqdn fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
20 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Networking::Hostname.new }
5
6 let(:value) { 'host' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Interfaces do
3 subject(:fact) { Facts::Aix::Networking::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) do
11 {
12 'ens160' => {
13 'bindings' => [
14 {
15 'address' => '10.16.116.8',
16 'netmask' => '255.255.240.0',
17 'network' => '10.16.112.0'
18 }
19 ]
20 }
21 }
22 end
23
24 it 'calls Facter::Resolvers::NetworkingLinux' do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
27 end
28
29 it 'returns networking.interfaces fact' do
30 expect(fact.call_the_resolver)
31 .to be_an_instance_of(Facter::ResolvedFact)
32 .and have_attributes(name: 'networking.interfaces', value: value)
33 end
34 end
35
36 describe '#call_the_resolver when resolver returns nil' do
37 let(:value) { {} }
38
39 it 'returns nil' do
40 expect(fact.call_the_resolver)
41 .to be_an_instance_of(Facter::ResolvedFact)
42 .and have_attributes(name: 'networking.interfaces', value: nil)
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Ip6 do
3 subject(:fact) { Facts::Aix::Networking::Ip6.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:ip6).and_return(ip6)
7 end
8
9 describe '#call_the_resolver' do
10 let(:ip6) { 'fe80::5989:97ff:75ae:dae7' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with ip6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns ipv6 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: ip6),
20 an_object_having_attributes(name: 'ipaddress6', value: ip6, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:ip6) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
29 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: nil),
30 an_object_having_attributes(name: 'ipaddress6', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Networking::Ip.new }
5
6 let(:value) { '0.16.121.255' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:ip)
15 end
16
17 it 'returns ipv4 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Mac do
3 subject(:fact) { Facts::Aix::Networking::Mac.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:mac).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { '64:5a:ed:ea:c3:25' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with mac' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'returns macaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
29 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: nil),
30 an_object_having_attributes(name: 'macaddress', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Mtu do
3 subject(:fact) { Facts::Aix::Networking::Mtu.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:mtu).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 1500 }
11
12 it 'calls Facter::Resolvers::Aix::Networking with mtu' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:mtu)
15 end
16
17 it 'returns mtu fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.mtu', value: value)
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver)
29 .to be_an_instance_of(Facter::ResolvedFact)
30 .and have_attributes(name: 'networking.mtu', value: nil)
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Netmask6 do
3 subject(:fact) { Facts::Aix::Networking::Netmask6.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:netmask6).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with netmask6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns netmask6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
29 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: nil),
30 an_object_having_attributes(name: 'netmask6', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Netmask do
3 subject(:fact) { Facts::Aix::Networking::Netmask.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:netmask).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { '10.16.122.163' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with netmask' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns netmask fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array)
29 .and contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: nil),
30 an_object_having_attributes(name: 'netmask', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Network6 do
3 subject(:fact) { Facts::Aix::Networking::Network6.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:network6).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with network6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns network6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
29 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: nil),
30 an_object_having_attributes(name: 'network6', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Network do
3 subject(:fact) { Facts::Aix::Networking::Network.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:network).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { '10.16.122.163' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with network' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns network fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array)
29 .and contain_exactly(an_object_having_attributes(name: 'networking.network', value: nil),
30 an_object_having_attributes(name: 'network', value: nil, type: :legacy))
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Networking::Primary.new }
5
6 let(:value) { 'en0' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns primary interface name' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'networking.primary', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Networking::Scope6 do
3 subject(:fact) { Facts::Aix::Networking::Scope6.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:scope6).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 'link' }
11
12 it 'calls Facter::Resolvers::Aix::Networking with scope6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:scope6)
15 end
16
17 it 'returns scope6 fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
21 an_object_having_attributes(name: 'scope6', value: value))
22 end
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns nil' do
29 expect(fact.call_the_resolver)
30 .to be_an_instance_of(Array)
31 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
32 an_object_having_attributes(name: 'scope6', value: value))
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::NimType do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::NimType.new }
5
6 let(:value) { 'standalone' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Nim).to receive(:resolve).with(:type).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Aix::Nim' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Nim).to have_received(:resolve).with(:type)
15 end
16
17 it 'returns nim_type fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'nim_type', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Os::Architecture.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Architecture).to receive(:resolve).with(:architecture).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Architecture' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Architecture).to have_received(:resolve).with(:architecture)
15 end
16
17 it 'returns os architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
20 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Os::Family.new }
5
6 let(:value) { 'Aix' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns os family fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
20 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Os::Hardware.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Hardware).to receive(:resolve).with(:hardware).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hardware' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hardware).to have_received(:resolve).with(:hardware)
15 end
16
17 it 'returns hardware model fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Os::Name.new }
5
6 let(:value) { 'AIX' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname)
14 fact.call_the_resolver
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Os::Release.new }
5
6 let(:value) { '12.0.1 ' }
7
8 before do
9 allow(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:build).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsLevel' do
13 expect(Facter::Resolvers::Aix::OsLevel).to receive(:resolve).with(:build)
14 fact.call_the_resolver
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value.strip,
20 'major' => value.split('-')[0] }),
21 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value.split('-')[0],
22 type: :legacy),
23 an_object_having_attributes(name: 'operatingsystemrelease', value: value.strip, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Partitions do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Partitions.new }
5
6 let(:value) do
7 { '/dev/hd5' => { 'filesystem' => 'boot',
8 'size_bytes' => 33_554_432,
9 'size' => '32.00 MiB',
10 'label' => 'primary_bootlv' } }
11 end
12
13 before do
14 allow(Facter::Resolvers::Aix::Partitions).to receive(:resolve).with(:partitions).and_return(value)
15 end
16
17 it 'calls Facter::Resolvers::Aix::Partitions' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::Aix::Partitions).to have_received(:resolve).with(:partitions)
20 end
21
22 it 'returns partitions fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
24 have_attributes(name: 'partitions', value: value)
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Path.new }
5
6 let(:value) { '/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java6/jre/bin:/usr/java6/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processor.new }
5
6 let(:processor) { ['Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz', 'Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz'] }
7
8 before do
9 allow(Facter::Resolvers::Aix::Processors).to receive(:resolve).with(:models).and_return(processor)
10 end
11
12 it 'calls Facter::Resolvers::Linux::Processors' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:models)
15 end
16
17 it 'returns legacy facts about each processor' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processor0', value: processor[0], type: :legacy),
20 an_object_having_attributes(name: 'processor1', value: processor[1], type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Cores do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Cores.new }
5
6 let(:cores_per_socket) { 8 }
7
8 before do
9 allow(Facter::Resolvers::Aix::Processors).to \
10 receive(:resolve).with(:cores_per_socket).and_return(cores_per_socket)
11 end
12
13 it 'calls Facter::Resolvers::Aix::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:cores_per_socket)
16 end
17
18 it 'returns processors cores fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.cores', value: cores_per_socket)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Count.new }
5
6 let(:processors_count) { '32' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Processors).to \
10 receive(:resolve).with(:logical_count).and_return(processors_count)
11 end
12
13 it 'calls Facter::Resolvers::Aix::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:logical_count)
16 end
17
18 it 'returns processors count fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors_count),
21 an_object_having_attributes(name: 'processorcount', value: processors_count, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Isa.new }
5
6 let(:processors_arch) { 'powerpc' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(processors_arch)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:processor)
16 end
17
18 it 'returns processors isa fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: processors_arch),
21 an_object_having_attributes(name: 'hardwareisa', value: processors_arch, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Models.new }
5
6 let(:models) { %w[PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8] }
7
8 before do
9 allow(Facter::Resolvers::Aix::Processors).to \
10 receive(:resolve).with(:models).and_return(models)
11 end
12
13 it 'calls Facter::Resolvers::Aix::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:models)
16 end
17
18 it 'returns processors models fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.models', value: models)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Aix::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Aix::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Processors::Threads do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Processors::Threads.new }
5
6 let(:threads_per_core) { 8 }
7
8 before do
9 allow(Facter::Resolvers::Aix::Processors).to \
10 receive(:resolve).with(:threads_per_core).and_return(threads_per_core)
11 end
12
13 it 'calls Facter::Resolvers::Aix::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Aix::Processors).to have_received(:resolve).with(:threads_per_core)
16 end
17
18 it 'returns processors threads fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.threads', value: threads_per_core)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Ruby::Platform.new }
5
6 let(:value) { 'x86_64-linux' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:platform)
15 end
16
17 it 'returns ruby platform fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
20 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Ruby::Sitedir.new }
5
6 let(:value) { '/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.5.0' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:sitedir)
15 end
16
17 it 'returns ruby site dir fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
20 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Ruby::Version.new }
5
6 let(:value) { '2.4.5' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
21 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Scope6Interfaces do
3 subject(:fact) { Facts::Aix::Scope6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Aix::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { scope6: 'link' }, 'en1' => { scope6: 'global' } } }
11
12 it 'calls Facter::Resolvers::Aix::Networking with interfaces' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with scope6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'scope6_eth0',
20 value: interfaces['eth0'][:scope6], type: :legacy),
21 an_object_having_attributes(name: 'scope6_en1',
22 value: interfaces['en1'][:scope6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28 let(:primary) { nil }
29
30 it 'returns nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Serialnumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Serialnumber.new }
5
6 let(:value) { '21684EW' }
7
8 before do
9 allow(Facter::Resolvers::Aix::Serialnumber).to receive(:resolve).with(:serialnumber).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Aix::Serialnumber' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Aix::Serialnumber).to have_received(:resolve).with(:serialnumber)
15 end
16
17 it 'returns serialnumber fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'serialnumber', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Ssh.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
9 end
10 let(:value) do
11 { 'ecdsa' => { 'fingerprints' =>
12 { 'sha1' => 'test', 'sha256' => 'test' },
13 'key' => 'test',
14 'type' => 'ecdsa' } }
15 end
16
17 before do
18 allow(Facter::Resolvers::Ssh).to \
19 receive(:resolve).with(:ssh).and_return(ssh)
20 end
21
22 it 'calls Facter::Resolvers::Ssh' do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
25 end
26
27 it 'returns a resolved fact' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
29 have_attributes(name: 'ssh', value: value)
30 end
31
32 context 'when resolver returns empty array' do
33 let(:ssh) { [] }
34
35 it 'returns nil fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'ssh', value: nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Sshalgorithmkey.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('test', 'test'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
13 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::SshfpAlgorithm.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
13 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::SystemUptime::Days.new }
5
6 let(:days) { '11' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:days).and_return(days)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:days)
16 end
17
18 it 'returns system uptime fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: days),
21 an_object_having_attributes(name: 'uptime_days', value: days, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::SystemUptime::Hours.new }
5
6 let(:hours) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:hours).and_return(hours)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:hours)
16 end
17
18 it 'returns system uptime hours fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(have_attributes(name: 'system_uptime.hours', value: hours),
21 have_attributes(name: 'uptime_hours', value: hours, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::SystemUptime::Seconds.new }
5
6 let(:seconds) { '123094' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:seconds).and_return(seconds)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:seconds)
16 end
17
18 it 'returns system uptime seconds fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(have_attributes(name: 'system_uptime.seconds', value: seconds),
21 have_attributes(name: 'uptime_seconds', value: seconds, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::SystemUptime::Uptime.new }
5
6 let(:uptime) { '10 days' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:uptime).and_return(uptime)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:uptime)
16 end
17
18 it 'returns system uptime fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(have_attributes(name: 'system_uptime.uptime', value: uptime),
21 have_attributes(name: 'uptime', value: uptime, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Aix::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Aix::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Alpine::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Alpine::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, release_file: '/etc/alpine-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '3.13.0' }
14 let(:release) { { 'full' => '3.13.0', 'major' => '3', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
19 .with(:release, release_file: '/etc/alpine-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { '3' }
35 let(:release) { { 'full' => '3', 'major' => '3' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Lsbdistcodename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Lsbdistcodename.new }
5
6 let(:value) { 'amzn' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns lsbdistcodename fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistcodename', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Lsbdistdescription do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Lsbdistdescription.new }
5
6 let(:value) { 'amzn' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:description).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:description)
15 end
16
17 it 'returns lsbdistdescription fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistdescription', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Lsbdistid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Lsbdistid.new }
5
6 let(:value) { 'amzn' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns lsbdistid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistid', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Os::Distro::Codename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Os::Distro::Codename.new }
5
6 context 'when codename is not in system-release' do
7 let(:value) { 'Amazon Linux AMI release 2017.03' }
8 let(:expected_value) { 'n/a' }
9
10 before do
11 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
12 .with(:release, release_file: '/etc/system-release').and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::SpecificReleaseFile' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
18 .with(:release, release_file: '/etc/system-release')
19 end
20
21 it "returns 'n/a' fact value" do
22 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
23 have_attributes(name: 'os.distro.codename', value: expected_value)
24 end
25 end
26
27 context 'when codename is in system-release' do
28 let(:value) { 'Amazon Linux release 2 (2017.12) LTS Release Candidate' }
29 let(:expected_value) { '2017.12' }
30
31 before do
32 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
33 .with(:release, release_file: '/etc/system-release').and_return(value)
34 end
35
36 it 'calls Facter::Resolvers::SpecificReleaseFile' do
37 fact.call_the_resolver
38 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
39 .with(:release, release_file: '/etc/system-release')
40 end
41
42 it 'returns release fact' do
43 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
44 have_attributes(name: 'os.distro.codename', value: expected_value)
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Os::Distro::Description do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Os::Distro::Description.new }
5
6 let(:value) { 'Amazon Linux AMI release 2017.03' }
7
8 before do
9 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
10 .with(:release, release_file: '/etc/system-release').and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::SpecificReleaseFile' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
16 .with(:release, release_file: '/etc/system-release')
17 end
18
19 it 'returns release fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'os.distro.description', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Os::Distro::Id do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Os::Distro::Id.new }
5
6 let(:value) { 'Amazon Linux AMI release 2017.03' }
7 let(:expected_value) { 'AmazonAMI' }
8
9 before do
10 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
11 .with(:release, release_file: '/etc/system-release').and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::SpecificReleaseFile' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
17 .with(:release, release_file: '/etc/system-release')
18 end
19
20 it 'returns release fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'os.distro.id', value: expected_value)
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Os::Distro::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/system-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/system-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
25 an_object_having_attributes(name: 'lsbdistrelease',
26 value: release['full'], type: :legacy),
27 an_object_having_attributes(name: 'lsbmajdistrelease',
28 value: release['major'], type: :legacy),
29 an_object_having_attributes(name: 'lsbminordistrelease',
30 value: release['minor'], type: :legacy))
31 end
32 end
33
34 context 'when version is retrieved from os-release file' do
35 let(:value) { nil }
36 let(:os_release) { '2' }
37 let(:release) { { 'full' => '2', 'major' => '2' } }
38
39 before do
40 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
41 end
42
43 it 'calls Facter::Resolvers::OsRelease with version' do
44 fact.call_the_resolver
45 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
46 end
47
48 it 'returns operating system name fact' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
50 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
51 an_object_having_attributes(name: 'lsbdistrelease',
52 value: release['full'], type: :legacy),
53 an_object_having_attributes(name: 'lsbmajdistrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'lsbminordistrelease',
56 value: release['minor'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.distro.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Amzn::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Amzn::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/system-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/system-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { '2' }
35 let(:release) { { 'full' => '2', 'major' => '2' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Kernelmajversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Kernelmajversion.new }
5
6 let(:fact_value) { '12' }
7 let(:resolver_value) { '12.1-RELEASE-p3' }
8
9 before do
10 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(resolver_value)
11 end
12
13 it 'calls Facter::Resolvers::OsLevel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
16 end
17
18 it 'returns kernelmajversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelmajversion', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Kernelversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Kernelversion.new }
5
6 let(:resolver_value) { '12.1-RELEASE-p3' }
7 let(:fact_value) { '12.1' }
8
9 before do
10 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(resolver_value)
11 end
12
13 it 'calls Facter::Resolvers::OsLevel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
16 end
17
18 it 'returns kernelversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelversion', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::LoadAverages do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::LoadAverages.new }
5
6 let(:value) { { '1m' => 0.01, '5m' => 0.02, '15m' => 0.03 } }
7
8 before do
9 allow(Facter::Resolvers::LoadAverages).to receive(:resolve).with(:load_averages).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Bsd::LoadAverages' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LoadAverages).to have_received(:resolve).with(:load_averages)
15 end
16
17 it 'returns load_averages fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'load_averages', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Os::Family.new }
5
6 let(:value) { 'FreeBSD' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns os family fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
20 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Processors::Count.new }
5
6 let(:processors) { '4' }
7
8 before do
9 allow(Facter::Resolvers::Bsd::Processors).to \
10 receive(:resolve).with(:logical_count).and_return(processors)
11 end
12
13 it 'calls Facter::Resolvers::Bsd::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Bsd::Processors).to have_received(:resolve).with(:logical_count)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors),
21 an_object_having_attributes(name: 'processorcount', value: processors, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Processors::Models.new }
5
6 let(:value) { 'Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz' }
7 let(:models) { [value, value] }
8
9 before do
10 allow(Facter::Resolvers::Bsd::Processors).to \
11 receive(:resolve).with(:models).and_return(models)
12 end
13
14 it 'calls Facter::Resolvers::Bsd::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Bsd::Processors).to have_received(:resolve).with(:models)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'processors.models', value: models),
22 an_object_having_attributes(name: 'processor0', value: value, type: :legacy),
23 an_object_having_attributes(name: 'processor1', value: value, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Bsd::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Bsd::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Bsd::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Macosx::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Bsd::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Lsbdistcodename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Lsbdistcodename.new }
5
6 let(:value) { 'stretch' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns lsbdistcodename fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistcodename', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Lsbdistdescription do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Lsbdistdescription.new }
5
6 let(:value) { 'stretch' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:description).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:description)
15 end
16
17 it 'returns lsbdistdescription fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistdescription', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Lsbdistid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Lsbdistid.new }
5
6 let(:value) { 'stretch' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns lsbdistid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistid', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Architecture.new }
5
6 context 'when resolver does not return x86_64' do
7 let(:value) { 'i86pc' }
8
9 before do
10 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
16 end
17
18 it 'returns architecture fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
21 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
22 end
23 end
24
25 context 'when os is 32-bit' do
26 let(:value) { 'i586' }
27 let(:fact_value) { 'i386' }
28
29 before do
30 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
31 end
32
33 it 'calls Facter::Resolvers::Uname' do
34 fact.call_the_resolver
35 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
36 end
37
38 it 'returns architecture fact' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
40 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: fact_value),
41 an_object_having_attributes(name: 'architecture', value: fact_value, type: :legacy))
42 end
43 end
44
45 context 'when resolver returns x86_64' do
46 let(:value) { 'x86_64' }
47
48 before do
49 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
50 end
51
52 it 'calls Facter::Resolvers::Uname' do
53 fact.call_the_resolver
54 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
55 end
56
57 it 'returns architecture fact' do
58 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
59 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: 'amd64'),
60 an_object_having_attributes(name: 'architecture', value: 'amd64', type: :legacy))
61 end
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Distro::Codename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Distro::Codename.new }
5
6 context 'when version codename exists in os-release' do
7 let(:value) { 'stretch' }
8
9 before do
10 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_codename).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::OsRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_codename)
16 end
17
18 it 'returns os.distro.codename fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'os.distro.codename', value: value)
21 end
22 end
23
24 context 'when version codename does not exists in os-release on Ubuntu' do
25 let(:value) { nil }
26 let(:version) { '14.04.2 LTS, Trusty Tahr' }
27 let(:result) { 'trusty' }
28
29 before do
30 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_codename).and_return(value)
31 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version).and_return(version)
32 end
33
34 it 'calls Facter::Resolvers::OsRelease with version_codename' do
35 fact.call_the_resolver
36 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_codename)
37 end
38
39 it 'calls Facter::Resolvers::OsRelease with version' do
40 fact.call_the_resolver
41 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version)
42 end
43
44 it 'returns os.distro.codename fact' do
45 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
46 have_attributes(name: 'os.distro.codename', value: result)
47 end
48 end
49
50 context 'when version codename does not exists in os-release on Debian' do
51 let(:value) { nil }
52 let(:version) { '9 (stretch)' }
53 let(:result) { 'stretch' }
54
55 before do
56 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_codename).and_return(value)
57 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version).and_return(version)
58 end
59
60 it 'calls Facter::Resolvers::OsRelease with version_codename' do
61 fact.call_the_resolver
62 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_codename)
63 end
64
65 it 'calls Facter::Resolvers::OsRelease with version' do
66 fact.call_the_resolver
67 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version)
68 end
69
70 it 'returns os.distro.codename fact' do
71 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
72 have_attributes(name: 'os.distro.codename', value: result)
73 end
74 end
75
76 context 'when version codename and version do not exist in os-release' do
77 let(:value) { nil }
78 let(:version) { nil }
79 let(:result) { nil }
80
81 before do
82 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_codename).and_return(value)
83 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version).and_return(version)
84 end
85
86 it 'calls Facter::Resolvers::OsRelease with version_codename' do
87 fact.call_the_resolver
88 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_codename)
89 end
90
91 it 'calls Facter::Resolvers::OsRelease with version' do
92 fact.call_the_resolver
93 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version)
94 end
95
96 it 'returns os.distro.codename fact' do
97 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
98 have_attributes(name: 'os.distro.codename', value: result)
99 end
100 end
101 end
102 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Distro::Description do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Distro::Description.new }
5
6 let(:value) { 'Debian GNU/Linux 9.0 (stretch)' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:pretty_name).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:pretty_name)
15 end
16
17 it 'returns os.distro.description fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'os.distro.description', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Distro::Id do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Distro::Id.new }
5
6 let(:value) { 'debian' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id)
15 end
16
17 it 'returns os.distro.id fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'os.distro.id', value: value.capitalize)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Distro::Release.new }
5
6 shared_examples 'returns distro release fact' do
7 it 'returns release fact' do
8 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
9 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: fact_value),
10 an_object_having_attributes(name: 'lsbdistrelease', value: fact_value['full'], type: :legacy),
11 an_object_having_attributes(name: 'lsbmajdistrelease', value: fact_value['major'],
12 type: :legacy),
13 an_object_having_attributes(name: 'lsbminordistrelease', value: fact_value['minor'],
14 type: :legacy))
15 end
16 end
17
18 before do
19 allow(Facter::Resolvers::DebianVersion).to receive(:resolve).with(:version).and_return(os_release_value)
20 end
21
22 context 'when version_id is retrieved successfully' do
23 let(:os_release_value) { '10.02' }
24 let(:fact_value) { { 'full' => '10.02', 'major' => '10', 'minor' => '2' } }
25
26 it 'calls Facter::Resolvers::DebianVersion' do
27 fact.call_the_resolver
28 expect(Facter::Resolvers::DebianVersion).to have_received(:resolve).with(:version)
29 end
30
31 it_behaves_like 'returns distro release fact'
32 end
33
34 context 'when version_id could not be retrieve' do
35 let(:os_release_value) { nil }
36 let(:fact_value) { nil }
37
38 it 'returns release fact' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'os.distro.release', value: fact_value)
41 end
42 end
43
44 context 'when version has no minor' do
45 let(:os_release_value) { 'bullseye/sid' }
46 let(:fact_value) { { 'full' => 'bullseye/sid', 'major' => 'bullseye/sid' } }
47
48 it_behaves_like 'returns distro release fact'
49 end
50 end
51 end
0 # frozen_string_literal: true
1
2 describe Facts::Debian::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Debian::Os::Release.new }
5
6 shared_examples 'returns os release fact' do
7 it 'returns os release fact' do
8 expect(fact.call_the_resolver).to match_array \
9 [
10 having_attributes(name: 'os.release', value: fact_value),
11 having_attributes(name: 'operatingsystemmajrelease', value: fact_value['major'],
12 type: :legacy),
13 having_attributes(name: 'operatingsystemrelease', value: fact_value['full'],
14 type: :legacy)
15 ]
16 end
17 end
18
19 shared_examples 'returns os release fact as nil' do
20 it 'returns os release fact as nil' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'os.release', value: fact_value)
23 end
24 end
25
26 before do
27 allow(Facter::Resolvers::DebianVersion).to receive(:resolve).with(:version).and_return(os_release_value)
28 end
29
30 context 'when version_id is retrieved successful' do
31 let(:os_release_value) { '10.02' }
32 let(:fact_value) { { 'full' => '10.02', 'major' => '10', 'minor' => '2' } }
33
34 it 'calls Facter::Resolvers::DebianVersion' do
35 fact.call_the_resolver
36 expect(Facter::Resolvers::DebianVersion).to have_received(:resolve).with(:version)
37 end
38
39 it_behaves_like 'returns os release fact'
40 end
41
42 context 'when version has no minor' do
43 let(:os_release_value) { 'testing/release' }
44 let(:fact_value) { { 'full' => 'testing/release', 'major' => 'testing/release' } }
45
46 it_behaves_like 'returns os release fact'
47 end
48
49 context 'when version_id could not §be retrieve' do
50 let(:os_release_value) { nil }
51 let(:fact_value) { nil }
52
53 it_behaves_like 'returns os release fact as nil'
54 end
55 end
56 end
0 # frozen_string_literal: true
1
2 describe Facts::Devuan::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Devuan::Os::Distro::Release.new }
5
6 let(:value) { '7.2.1511' }
7 let(:release) { { 'full' => '7.2.1511', 'major' => '7', 'minor' => '2' } }
8
9 before do
10 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::LsbRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:release)
16 end
17
18 it 'returns release fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
21 an_object_having_attributes(name: 'lsbdistrelease', value: value, type: :legacy),
22 an_object_having_attributes(name: 'lsbmajdistrelease',
23 value: release['major'], type: :legacy),
24 an_object_having_attributes(name: 'lsbminordistrelease',
25 value: release['minor'], type: :legacy))
26 end
27
28 context 'when lsb_release is not installed' do
29 let(:value) { nil }
30
31 before do
32 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
33 end
34
35 it 'returns release fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
37 .and have_attributes(name: 'os.distro.release', value: value)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Devuan::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Devuan::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, release_file: '/etc/devuan_version')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
19 .with(:release, release_file: '/etc/devuan_version')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { 'beowulf' }
35 let(:release) { { 'full' => 'beowulf', 'major' => 'beowulf' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Disks do
3 subject(:fact) { Facts::Freebsd::Disks.new }
4
5 let(:disk) do
6 {
7 'disks' => {
8 'sda' => {
9 'model' => 'Virtual disk',
10 'size' => '20.00 GiB',
11 'size_bytes' => 21_474_836_480,
12 'vendor' => 'VMware'
13 }
14 }
15 }
16 end
17
18 describe '#call_the_resolver' do
19 before do
20 allow(Facter::Resolvers::Freebsd::Geom).to receive(:resolve).with(:disks).and_return(disk)
21 end
22
23 it 'calls Facter::Resolvers::Freebsd::Disk' do
24 fact.call_the_resolver
25 expect(Facter::Resolvers::Freebsd::Geom).to have_received(:resolve).with(:disks)
26 end
27
28 it 'returns resolved fact with name disk and value' do
29 expect(fact.call_the_resolver)
30 .to be_an_instance_of(Facter::ResolvedFact)
31 .and have_attributes(name: 'disks', value: disk)
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Bios::ReleaseDate do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Bios::ReleaseDate.new }
5
6 let(:date) { '07/03/2018' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:bios_date).and_return(date)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:bios_date)
16 end
17
18 it 'returns bios release date fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.release_date', value: date),
21 an_object_having_attributes(name: 'bios_release_date', value: date, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Bios::Vendor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Bios::Vendor.new }
5
6 let(:vendor) { 'Phoenix Technologies LTD' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:bios_vendor).and_return(vendor)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:bios_vendor)
16 end
17
18 it 'returns bios vendor fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.vendor', value: vendor),
21 an_object_having_attributes(name: 'bios_vendor', value: vendor, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Bios::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Bios::Version.new }
5
6 let(:version) { '6.00' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:bios_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:bios_version)
16 end
17
18 it 'returns bios version fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.version', value: version),
21 an_object_having_attributes(name: 'bios_version', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Manufacturer do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Manufacturer.new }
5
6 let(:sys_vendor) { 'VMware, Inc.' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:sys_vendor).and_return(sys_vendor)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:sys_vendor)
16 end
17
18 it 'returns manufacturer fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.manufacturer', value: sys_vendor),
21 an_object_having_attributes(name: 'manufacturer', value: sys_vendor, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Product::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Product::Name.new }
5
6 let(:product_name) { 'VMware Virtual Platform' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:product_name).and_return(product_name)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:product_name)
16 end
17
18 it 'returns product name fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: product_name),
21 an_object_having_attributes(name: 'productname', value: product_name, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Product::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Product::SerialNumber.new }
5
6 let(:serial_number) { 'VMware-42 1a a9 29 31 8f fa e9-7d 69 2e 23 21 b0 0c 45' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:product_serial).and_return(serial_number)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:product_serial)
16 end
17
18 it 'returns resolved facts' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.serial_number', value: serial_number),
21 an_object_having_attributes(name: 'serialnumber', value: serial_number, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Dmi::Product::Uuid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Dmi::Product::Uuid.new }
5
6 let(:product_uuid) { '421aa929-318f-fae9-7d69-2e2321b00c45' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::DmiBios).to \
10 receive(:resolve).with(:product_uuid).and_return(product_uuid)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::DmiBios).to have_received(:resolve).with(:product_uuid)
16 end
17
18 it 'returns resolved facts' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.uuid', value: product_uuid),
21 an_object_having_attributes(name: 'uuid', value: product_uuid, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ec2Metadata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ec2Metadata.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
10 end
11
12 context 'when physical machine with no hypervisor' do
13 let(:value) { nil }
14
15 before do
16 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(nil)
17 end
18
19 it 'returns ec2 metadata fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'ec2_metadata', value: value)
22 end
23
24 it "doesn't call Ec2 resolver" do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:metadata)
27 end
28 end
29
30 shared_examples 'check ec2 resolver called with metadata' do
31 it 'calls ec2 resolver' do
32 fact.call_the_resolver
33
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:metadata)
35 end
36 end
37
38 shared_examples 'check resolved fact value' do
39 it 'returns ec2 metadata fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
41 have_attributes(name: 'ec2_metadata', value: value)
42 end
43 end
44
45 context 'when platform is kvm' do
46 let(:value) { { 'info' => 'value' } }
47
48 before do
49 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('kvm')
50 end
51
52 it_behaves_like 'check ec2 resolver called with metadata'
53 it_behaves_like 'check resolved fact value'
54 end
55
56 context 'when platform is xen' do
57 let(:value) { { 'info' => 'value' } }
58
59 before do
60 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('xen')
61 end
62
63 it_behaves_like 'check ec2 resolver called with metadata'
64 it_behaves_like 'check resolved fact value'
65 end
66
67 context 'when platform is aws' do
68 let(:value) { { 'info' => 'value' } }
69
70 before do
71 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('aws')
72 end
73
74 it_behaves_like 'check ec2 resolver called with metadata'
75 it_behaves_like 'check resolved fact value'
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ec2Userdata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ec2Userdata.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:userdata).and_return(value)
10 end
11
12 context 'when physical machine with no hypervisor' do
13 let(:value) { nil }
14
15 before do
16 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('nil')
17 end
18
19 it 'returns ec2 userdata fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'ec2_userdata', value: value)
22 end
23
24 it "doesn't call Ec2 resolver" do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:userdata)
27 end
28 end
29
30 shared_examples 'check ec2 resolver called with userdata' do
31 it 'calls ec2 resolver' do
32 fact.call_the_resolver
33
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:userdata)
35 end
36 end
37
38 shared_examples 'check resolved fact value' do
39 it 'returns ec2 userdata fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
41 have_attributes(name: 'ec2_userdata', value: value)
42 end
43 end
44
45 context 'when platform is kvm' do
46 let(:value) { { 'info' => 'value' } }
47
48 before do
49 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('kvm')
50 end
51
52 it_behaves_like 'check ec2 resolver called with userdata'
53 it_behaves_like 'check resolved fact value'
54 end
55
56 context 'when platform is xen' do
57 let(:value) { { 'info' => 'value' } }
58
59 before do
60 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('xen')
61 end
62
63 it_behaves_like 'check ec2 resolver called with userdata'
64 it_behaves_like 'check resolved fact value'
65 end
66
67 context 'when platform is aws' do
68 let(:value) { { 'info' => 'value' } }
69
70 before do
71 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('aws')
72 end
73
74 it_behaves_like 'check ec2 resolver called with userdata'
75 it_behaves_like 'check resolved fact value'
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Identity::Gid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Identity::Gid.new }
5
6 let(:value) { '20' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:gid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:gid)
15 end
16
17 it 'returns identity gid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.gid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Identity::Group do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Identity::Group.new }
5
6 let(:value) { 'staff' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:group).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:group)
15 end
16
17 it 'returns identity group fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.group', value: value),
20 an_object_having_attributes(name: 'gid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Identity::Privileged do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Identity::Privileged.new }
5
6 let(:value) { 'false' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:privileged).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:privileged)
15 end
16
17 it 'returns identity privileged fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.privileged', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Identity::Uid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Identity::Uid.new }
5
6 let(:value) { '501' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:uid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:uid)
15 end
16
17 it 'returns identity uid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.uid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Identity::User.new }
5
6 let(:value) { 'root' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:user)
15 end
16
17 it 'returns id and identity user name' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Freebsd::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::NetworkingFreeBSD' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::IpaddressInterfaces do
3 subject(:fact) { Facts::Freebsd::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingFreeBSD' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::IsVirtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::IsVirtual.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(virtual_value)
10 end
11
12 context 'when not in a virtual environment' do
13 let(:virtual_value) { 'physical' }
14
15 it 'return resolved fact with nil value' do
16 expect(fact.call_the_resolver)
17 .to be_an_instance_of(Facter::ResolvedFact)
18 .and have_attributes(name: 'is_virtual', value: false)
19 end
20 end
21
22 context 'when in a virtual environment' do
23 let(:virtual_value) { 'aws' }
24
25 it 'return resolved fact with nil value' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact)
28 .and have_attributes(name: 'is_virtual', value: true)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Kernel.new }
5
6 let(:value) { 'FreeBSD' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Kernelrelease.new }
5
6 let(:value) { '5.11' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
15 end
16
17 it 'returns kernelrelease fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelrelease', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::AvailableBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:available_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:available_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
21 an_object_having_attributes(name: 'swapfree_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::Available.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:available_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:available_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.available', value: value),
20 an_object_having_attributes(name: 'swapfree', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::Capacity.new }
5
6 let(:value) { 1024 }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:capacity).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:capacity)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'memory.swap.capacity', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::Encrypted do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::Encrypted.new }
5
6 let(:value) { true }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:encrypted).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:encrypted)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.encrypted', value: value),
20 an_object_having_attributes(name: 'swapencrypted', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::TotalBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:total_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:total_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
21 an_object_having_attributes(name: 'swapsize_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::Total.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:total_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:total_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.total', value: value),
20 an_object_having_attributes(name: 'swapsize', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::UsedBytes.new }
5
6 let(:value) { 1024 }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:used_bytes).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:used_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'memory.swap.used_bytes', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::Swap::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::Swap::Used.new }
5
6 let(:resolver_result) { 1024 }
7 let(:fact_value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SwapMemory).to receive(:resolve).with(:used_bytes).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SwapMemory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns a memory.swap.used fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.swap.used', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::AvailableBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:available_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:available_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
21 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::Available.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:available_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:available_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
20 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::Capacity.new }
5
6 let(:value) { '15.53%' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:capacity).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:capacity)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'memory.system.capacity', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::TotalBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:total_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:total_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
21 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::Total.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:total_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:total_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
20 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::UsedBytes.new }
5
6 let(:value) { 1024 }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:used_bytes).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Freebsd::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:used_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'memory.system.used_bytes', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Memory::System::Used.new }
5
6 let(:resolver_result) { 1024 }
7 let(:fact_value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::SystemMemory).to receive(:resolve).with(:used_bytes).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::SystemMemory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns a memory.system.used fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.system.used', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Mountpoints do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Mountpoints.new }
5
6 context 'when resolver returns hash' do
7 let(:resolver_output) do
8 [{ available: '63.31 GiB',
9 available_bytes: 67_979_685_888,
10 capacity: '84.64%',
11 device: '/dev/nvme0n1p2',
12 filesystem: 'ext4',
13 options: %w[rw noatime],
14 path: '/',
15 size: '434.42 GiB',
16 size_bytes: 466_449_743_872,
17 used: '348.97 GiB',
18 used_bytes: 374_704_357_376 }]
19 end
20 let(:parsed_fact) do
21 { '/' => { 'available' => '63.31 GiB',
22 'available_bytes' => 67_979_685_888,
23 'capacity' => '84.64%',
24 'device' => '/dev/nvme0n1p2',
25 'filesystem' => 'ext4',
26 'options' => %w[rw noatime],
27 'size' => '434.42 GiB',
28 'size_bytes' => 466_449_743_872,
29 'used' => '348.97 GiB',
30 'used_bytes' => 374_704_357_376 } }
31 end
32
33 before do
34 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(resolver_output)
35 end
36
37 it 'calls Facter::Resolvers::Mountpoints' do
38 fact.call_the_resolver
39 expect(Facter::Resolvers::Mountpoints).to have_received(:resolve).with(:mountpoints)
40 end
41
42 it 'returns mountpoints information' do
43 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
44 have_attributes(name: 'mountpoints', value: parsed_fact)
45 end
46 end
47
48 context 'when resolver returns nil' do
49 before do
50 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(nil)
51 end
52
53 it 'returns mountpoints information' do
54 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
55 have_attributes(name: 'mountpoints', value: nil)
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Netmask6Interfaces do
3 subject(:fact) { Facts::Freebsd::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::NetworkingLinux' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::NetmaskInterfaces do
3 subject(:fact) { Facts::Freebsd::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Network6Interfaces do
3 subject(:fact) { Facts::Freebsd::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::NetworkInterfaces do
3 subject(:fact) { Facts::Freebsd::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Dhcp do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Dhcp.new }
5
6 let(:value) { '192.168.158.6' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:dhcp).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:dhcp)
15 end
16
17 it 'returns networking.dhcp fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
20 end
21
22 context 'when dhcp can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Domain.new }
5
6 let(:value) { 'domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22
23 context 'when domain can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
29 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Fqdn.new }
5
6 let(:value) { 'host.domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:fqdn).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:fqdn)
15 end
16
17 it 'returns fqdn fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
20 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
21 end
22
23 context 'when fqdn can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
29 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Hostname.new }
5
6 let(:value) { 'host' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22
23 context 'when hostname can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
29 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Interfaces do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Interfaces.new }
5
6 let(:interfaces) do
7 {
8 'awdl0' =>
9 { mtu: 1484,
10 mac: '2e:ba:e4:83:4b:b7',
11 bindings6:
12 [{ address: 'fe80::2cba:e4ff:fe83:4bb7',
13 netmask: 'ffff:ffff:ffff:ffff::',
14 network: 'fe80::' }],
15 ip6: 'fe80::2cba:e4ff:fe83:4bb7',
16 netmask6: 'ffff:ffff:ffff:ffff::',
17 network6: 'fe80::' },
18 'bridge0' => { mtu: 1500, mac: '82:17:0e:93:9d:00' },
19 'en0' =>
20 { dhcp: '192.587.6.9',
21 mtu: 1500,
22 mac: '64:5a:ed:ea:5c:81',
23 bindings:
24 [{ address: '192.168.1.2',
25 netmask: '255.255.255.0',
26 network: '192.168.1.0' }],
27 ip: '192.168.1.2',
28 netmask: '255.255.255.0',
29 network: '192.168.1.0' },
30 'gif0' => { mtu: 1280 },
31 'lo0' =>
32 { mtu: 16_384,
33 bindings:
34 [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
35 bindings6:
36 [{ address: '::1',
37 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
38 network: '::1' },
39 { address: 'fe80::1',
40 netmask: 'ffff:ffff:ffff:ffff::',
41 network: 'fe80::' }],
42 ip: '127.0.0.1',
43 netmask: '255.0.0.0',
44 network: '127.0.0.0',
45 ip6: '::1',
46 netmask6: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
47 network6: '::1' }
48 }
49 end
50
51 before do
52 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
53 end
54
55 it 'calls Facter::Resolvers::Networking with interfaces' do
56 fact.call_the_resolver
57 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
58 end
59
60 it 'returns networking.interfaces fact' do
61 expect(fact.call_the_resolver)
62 .to be_an_instance_of(Facter::ResolvedFact)
63 .and have_attributes(name: 'networking.interfaces', value: anything)
64 end
65
66 it 'returns all interfaces' do
67 interfaces = %w[awdl0 bridge0 en0 gif0 lo0]
68
69 result = fact.call_the_resolver
70
71 expect(result.value).to include(*interfaces)
72 end
73
74 it 'returns the interface awdl0 correctly' do
75 expected = { 'mtu' => 1484,
76 'mac' => '2e:ba:e4:83:4b:b7',
77 'bindings6' =>
78 [{ 'address' => 'fe80::2cba:e4ff:fe83:4bb7',
79 'netmask' => 'ffff:ffff:ffff:ffff::',
80 'network' => 'fe80::' }],
81 'ip6' => 'fe80::2cba:e4ff:fe83:4bb7',
82 'netmask6' => 'ffff:ffff:ffff:ffff::',
83 'network6' => 'fe80::' }
84
85 result = fact.call_the_resolver
86
87 expect(result.value['awdl0']).to match(expected)
88 end
89
90 it 'returns the interface bridge0 correctly' do
91 result = fact.call_the_resolver
92
93 expect(result.value['bridge0']).to match({ 'mtu' => 1500, 'mac' => '82:17:0e:93:9d:00' })
94 end
95
96 it 'returns the interface en0 correctly' do
97 expected = { 'mtu' => 1500,
98 'mac' => '64:5a:ed:ea:5c:81',
99 'bindings' =>
100 [{ 'address' => '192.168.1.2',
101 'netmask' => '255.255.255.0',
102 'network' => '192.168.1.0' }],
103 'ip' => '192.168.1.2',
104 'netmask' => '255.255.255.0',
105 'network' => '192.168.1.0',
106 'dhcp' => '192.587.6.9' }
107
108 result = fact.call_the_resolver
109
110 expect(result.value['en0']).to match(expected)
111 end
112
113 it 'returns the interface gif0 correctly' do
114 result = fact.call_the_resolver
115
116 expect(result.value['gif0']).to match({ 'mtu' => 1280 })
117 end
118
119 context 'when interfaces can not be retrieved' do
120 let(:interfaces) { nil }
121
122 it 'returns nil' do
123 expect(fact.call_the_resolver)
124 .to be_an_instance_of(Facter::ResolvedFact)
125 .and have_attributes(name: 'networking.interfaces', value: interfaces)
126 end
127 end
128 end
129 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Ip6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Ip6.new }
5
6 let(:value) { 'fe80::2cba:e4ff:fe83:4bb7' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:ip6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :ip6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns the ip6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
20 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
21 end
22
23 context 'when ip6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
29 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Ip.new }
5
6 let(:value) { '10.0.0.1' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :ip' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:ip)
15 end
16
17 it 'returns the ip6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22
23 context 'when ip can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
29 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Mac do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Mac.new }
5
6 let(:value) { '64:5a:ed:ea:c3:25' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:mac).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :mac' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'returns macaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22
23 context 'when mac can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
29 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Mtu do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Mtu.new }
5
6 let(:value) { 1500 }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:mtu).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :mtu' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:mtu)
15 end
16
17 it 'returns mtu fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
20 end
21
22 context 'when mtu can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Netmask6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Netmask6.new }
5
6 let(:value) { 'ffff:ffff:ffff:ffff::' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:netmask6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :netmask6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns the netmask6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22
23 context 'when netmask6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
29 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Netmask do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Netmask.new }
5
6 let(:value) { '255.255.255.0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:netmask).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :netmask' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns the netmask fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22
23 context 'when netmask can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
29 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Network6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Network6.new }
5
6 let(:value) { 'ff80:3454::' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:network6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :network6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns the network6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22
23 context 'when network6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
29 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Network do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Network.new }
5
6 let(:value) { '192.168.143.0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:network).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :network' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns the network fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22
23 context 'when network can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
29 an_object_having_attributes(name: 'network', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Primary.new }
5
6 let(:value) { 'en0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :primary_interface' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns networking.primary fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
20 end
21
22 context 'when primary interface can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Networking::Scope6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Networking::Scope6.new }
5
6 let(:value) { 'link' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:scope6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with scope6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:scope6)
15 end
16
17 it 'returns scope6 fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.scope6', value: value)
21 end
22
23 context 'when scope6 can not be resolved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact)
29 .and have_attributes(name: 'networking.scope6', value: value)
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Os::Architecture.new }
5
6 let(:value) { 'amd64' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
20 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Os::Hardware.new }
5
6 let(:value) { 'amd64' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::HardwareArchitecture' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
15 end
16
17 it 'returns hardware model fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Os::Name.new }
5
6 let(:value) { 'FreeBSD' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::Freebsd::FreebsdVersion).to receive(:resolve).with(:installed_userland).and_return(value)
8 end
9
10 context 'when FreeBSD RELEASE' do
11 let(:value) { '12.1-RELEASE-p3' }
12
13 it 'calls Facter::Resolvers::Freebsd::FreebsdVersion' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::FreebsdVersion).to have_received(:resolve).with(:installed_userland)
16 end
17
18 it 'returns release fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value,
21 'major' => '12',
22 'minor' => '1',
23 'branch' => 'RELEASE-p3',
24 'patchlevel' => '3' }),
25 an_object_having_attributes(name: 'operatingsystemmajrelease', value: '12',
26 type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
28 end
29 end
30
31 context 'when FreeBSD STABLE' do
32 let(:value) { '12.1-STABLE' }
33
34 it 'calls Facter::Resolvers::Freebsd::FreebsdVersion' do
35 fact.call_the_resolver
36 expect(Facter::Resolvers::Freebsd::FreebsdVersion).to have_received(:resolve).with(:installed_userland)
37 end
38
39 it 'returns release fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
41 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value,
42 'major' => '12',
43 'minor' => '1',
44 'branch' => 'STABLE' }),
45 an_object_having_attributes(name: 'operatingsystemmajrelease', value: '12',
46 type: :legacy),
47 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
48 end
49 end
50
51 context 'when FreeBSD CURRENT' do
52 let(:value) { '13-CURRENT' }
53
54 it 'calls Facter::Resolvers::Freebsd::FreebsdVersion' do
55 fact.call_the_resolver
56 expect(Facter::Resolvers::Freebsd::FreebsdVersion).to have_received(:resolve).with(:installed_userland)
57 end
58
59 it 'returns release fact' do
60 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
61 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value,
62 'major' => '13',
63 'branch' => 'CURRENT' }),
64 an_object_having_attributes(name: 'operatingsystemmajrelease', value: '13',
65 type: :legacy),
66 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
67 end
68 end
69 end
70 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Partitions do
3 subject(:fact) { Facts::Freebsd::Partitions.new }
4
5 let(:partitions) do
6 {
7 'ada0p1' => {
8 'partlabel' => 'gptboot0',
9 'partuuid' => '503d3458-c135-11e8-bd11-7d7cd061b26f',
10 'size' => '512.00 KiB',
11 'size_bytes' => 524_288
12 }
13 }
14 end
15
16 describe '#call_the_resolver' do
17 before do
18 allow(Facter::Resolvers::Freebsd::Geom).to receive(:resolve).with(:partitions).and_return(partitions)
19 end
20
21 it 'calls Facter::Resolvers::Freebsd::Partitions' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Freebsd::Geom).to have_received(:resolve).with(:partitions)
24 end
25
26 it 'returns resolved fact with name partitions and value' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact)
29 .and have_attributes(name: 'partitions', value: partitions)
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Path.new }
5
6 let(:value) { '/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Processors::Count.new }
5
6 let(:processors) { '4' }
7
8 before do
9 allow(Facter::Resolvers::Freebsd::Processors).to \
10 receive(:resolve).with(:logical_count).and_return(processors)
11 end
12
13 it 'calls Facter::Resolvers::Freebsd::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Freebsd::Processors).to have_received(:resolve).with(:logical_count)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors),
21 an_object_having_attributes(name: 'processorcount', value: processors, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Processors::Isa.new }
5
6 let(:isa) { 'i386' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:processor)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: isa),
21 an_object_having_attributes(name: 'hardwareisa', value: isa, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Processors::Models.new }
5
6 let(:value) { 'Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz' }
7 let(:models) { [value, value] }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::Processors).to \
11 receive(:resolve).with(:models).and_return(models)
12 end
13
14 it 'calls Facter::Resolvers::Freebsd::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Freebsd::Processors).to have_received(:resolve).with(:models)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'processors.models', value: models),
22 an_object_having_attributes(name: 'processor0', value: value, type: :legacy),
23 an_object_having_attributes(name: 'processor1', value: value, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Freebsd::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Macosx::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Freebsd::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ruby::Platform.new }
5
6 let(:value) { 'amd64-freebsd12' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:platform)
15 end
16
17 it 'return ruby.platform fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
20 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ruby::Sitedir.new }
5
6 let(:value) { '/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.5.0' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:sitedir)
15 end
16
17 it 'return ruby sitedir fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
20 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ruby::Version.new }
5
6 let(:value) { '2.4.5' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
21 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Ssh.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
9 end
10 let(:value) do
11 { 'ecdsa' => { 'fingerprints' =>
12 { 'sha1' => 'test', 'sha256' => 'test' },
13 'key' => 'test',
14 'type' => 'ecdsa' } }
15 end
16
17 before do
18 allow(Facter::Resolvers::Ssh).to \
19 receive(:resolve).with(:ssh).and_return(ssh)
20 end
21
22 it 'calls Facter::Resolvers::Ssh' do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
25 end
26
27 it 'returns a resolved fact' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
29 have_attributes(name: 'ssh', value: value)
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Sshalgorithmkey.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('test', 'test'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
13 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a resolved fact' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::SshfpAlgorithm.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
13 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a resolved fact' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::SystemUptime::Days.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:days).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:days)
15 end
16
17 it 'returns days since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: value),
20 an_object_having_attributes(name: 'uptime_days', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::SystemUptime::Hours.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:hours).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:hours)
15 end
16
17 it 'returns hours since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: value),
20 an_object_having_attributes(name: 'uptime_hours', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::SystemUptime::Seconds.new }
5
6 let(:value) { 3600 }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:seconds).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:seconds)
15 end
16
17 it 'returns minutes since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: value),
20 an_object_having_attributes(name: 'uptime_seconds', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::SystemUptime::Uptime.new }
5
6 let(:value) { '6 days' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:uptime).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:uptime)
15 end
16
17 it 'returns total uptime since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: value),
20 an_object_having_attributes(name: 'uptime', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::Virtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::Virtual.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7 let(:log_spy) { instance_spy(Facter::Log) }
8
9 before do
10 allow(Facter::Log).to receive(:new).and_return(log_spy)
11 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(value)
12 end
13
14 shared_examples 'check resolved fact value' do
15 it 'return resolved fact with nil value' do
16 expect(fact.call_the_resolver)
17 .to be_an_instance_of(Facter::ResolvedFact)
18 .and have_attributes(name: 'virtual', value: value)
19 end
20 end
21
22 context 'when not in a virtual environment' do
23 let(:value) { 'physical' }
24
25 it_behaves_like 'check resolved fact value'
26 end
27
28 context 'when in a virtual environment' do
29 let(:value) { 'jail' }
30
31 it_behaves_like 'check resolved fact value'
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::ZfsFeaturenumbers do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::ZfsFeaturenumbers.new }
5
6 let(:feature_numbers) { '1,2,3,4,5' }
7
8 before do
9 allow(Facter::Resolvers::ZFS).to receive(:resolve).with(:zfs_featurenumbers).and_return(feature_numbers)
10 end
11
12 it 'calls Facter::Resolvers::ZFS' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::ZFS).to have_received(:resolve).with(:zfs_featurenumbers)
15 end
16
17 it 'returns zfs_featurenumbers fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zfs_featurenumbers', value: feature_numbers)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::ZfsVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::ZfsVersion.new }
5
6 let(:version) { '6' }
7
8 before do
9 allow(Facter::Resolvers::ZFS).to receive(:resolve).with(:zfs_version).and_return(version)
10 end
11
12 it 'calls Facter::Resolvers::ZFS' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::ZFS).to have_received(:resolve).with(:zfs_version)
15 end
16
17 it 'returns zfs_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zfs_version', value: version)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::ZpoolFeatureflags do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::ZpoolFeatureflags.new }
5
6 let(:zpool_feature_flags) { 'async_destroy,empty_bpobj,lz4_compress,multi_vdev_crash_dump,spacemap_histogram' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to \
10 receive(:resolve).with(:zpool_featureflags).and_return(zpool_feature_flags)
11 end
12
13 it 'calls Facter::Resolvers::ZPool' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_featureflags)
16 end
17
18 it 'returns the zpool_featureflags fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'zpool_featureflags', value: zpool_feature_flags)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::ZpoolFeaturenumbers do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::ZpoolFeaturenumbers.new }
5
6 let(:zpool_featurenumbers) { '1,2,3,4,5,6,7' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to \
10 receive(:resolve).with(:zpool_featurenumbers).and_return(zpool_featurenumbers)
11 end
12
13 it 'calls Facter::Resolvers::ZPool' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_featurenumbers)
16 end
17
18 it 'returns the zpool_featurenumbers fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'zpool_featurenumbers', value: zpool_featurenumbers)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Freebsd::ZpoolVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Freebsd::ZpoolVersion.new }
5
6 let(:version) { '5' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to receive(:resolve).with(:zpool_version).and_return(version)
10 end
11
12 it 'calls Facter::Resolvers::ZPool' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_version)
15 end
16
17 it 'returns the ZPool version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zpool_version', value: version)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Gentoo::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Gentoo::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/gentoo-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2007.0' }
14 let(:release) { { 'full' => '2007.0', 'major' => '2007', 'minor' => '0' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/gentoo-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { '2007.0' }
35 let(:release) { { 'full' => '2007.0', 'major' => '2007', 'minor' => '0' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::AioAgentVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::AioAgentVersion.new }
5
6 let(:value) { '1.2.3' }
7
8 before do
9 allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Agent' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version)
15 end
16
17 it 'returns aio_agent_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'aio_agent_version', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::AzMetadata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::AzMetadata.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Resolvers::Az).to receive(:resolve).with(:metadata).and_return(value)
10 end
11
12 context 'when physical machine with no hypervisor' do
13 let(:value) { nil }
14
15 before do
16 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(nil)
17 end
18
19 it 'returns az metadata fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'az_metadata', value: value)
22 end
23
24 it "doesn't call az resolver" do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Az).not_to have_received(:resolve).with(:metadata)
27 end
28 end
29
30 context 'when platform is hyperv' do
31 let(:value) { { 'info' => 'value' } }
32
33 before do
34 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('hyperv')
35 end
36
37 context 'when on Azure' do
38 it 'calls the az resolver' do
39 fact.call_the_resolver
40
41 expect(Facter::Resolvers::Az).to have_received(:resolve).with(:metadata)
42 end
43
44 it 'returns az_metadata fact' do
45 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
46 have_attributes(name: 'az_metadata', value: value)
47 end
48 end
49
50 context 'when not on Azure' do
51 let(:value) { nil }
52
53 it 'returns az_metadata fact as nil' do
54 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
55 have_attributes(name: 'az_metadata', value: value)
56 end
57 end
58 end
59 end
60 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Cloud::Provider do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Cloud::Provider.new }
5
6 context 'when on hyperv' do
7 before do
8 allow(Facter::Resolvers::Az).to receive(:resolve).with(:metadata).and_return(value)
9 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('hyperv')
10 end
11
12 context 'when az_metadata exists' do
13 let(:value) { { 'some' => 'fact' } }
14
15 it 'returns azure as cloud.provider' do
16 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
17 have_attributes(name: 'cloud.provider', value: 'azure')
18 end
19 end
20
21 context 'when az_metadata does not exist' do
22 let(:value) { {} }
23
24 it 'returns nil' do
25 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
26 have_attributes(name: 'cloud.provider', value: nil)
27 end
28 end
29 end
30
31 context 'when on a physical machine' do
32 before do
33 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(nil)
34 end
35
36 it 'returns nil' do
37 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
38 have_attributes(name: 'cloud.provider', value: nil)
39 end
40 end
41
42 describe 'when on kvm' do
43 before do
44 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
45 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('kvm')
46 end
47
48 describe 'Ec2 data exists and aws fact is set' do
49 let(:value) { { 'some' => 'fact' } }
50
51 it 'Testing things' do
52 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
53 have_attributes(name: 'cloud.provider', value: 'aws')
54 end
55 end
56
57 context 'when Ec2 data does not exist nil is returned' do
58 let(:value) { {} }
59
60 it 'returns nil' do
61 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
62 have_attributes(name: 'cloud.provider', value: nil)
63 end
64 end
65 end
66
67 describe 'when on xen' do
68 before do
69 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
70 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('xen')
71 end
72
73 describe 'Ec2 data exists and aws fact is set' do
74 let(:value) { { 'some' => 'fact' } }
75
76 it 'Testing things' do
77 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
78 have_attributes(name: 'cloud.provider', value: 'aws')
79 end
80 end
81
82 context 'when Ec2 data does not exist nil is returned' do
83 let(:value) { {} }
84
85 it 'returns nil' do
86 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
87 have_attributes(name: 'cloud.provider', value: nil)
88 end
89 end
90 end
91
92 describe 'when on gce' do
93 before do
94 allow(Facter::Resolvers::Gce).to receive(:resolve).with(:metadata).and_return(value)
95 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('gce')
96 end
97
98 describe 'and the "gce" fact has content' do
99 let(:value) { { 'some' => 'metadata' } }
100
101 it 'resolves a provider of "gce"' do
102 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
103 have_attributes(name: 'cloud.provider', value: 'gce')
104 end
105 end
106
107 context 'when the "gce" fact has no content' do
108 let(:value) { {} }
109
110 it 'resolves to nil' do
111 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
112 have_attributes(name: 'cloud.provider', value: nil)
113 end
114 end
115 end
116 end
117 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::DhcpServers do
3 subject(:fact) { Facts::Linux::DhcpServers.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:dhcp).and_return(dhcp)
8 end
9
10 describe '#call_the_resolver' do
11 let(:value) { { 'system' => '10.16.122.163', 'eth0' => '10.16.122.163', 'en1' => '10.32.10.163' } }
12 let(:interfaces) { { 'eth0' => { dhcp: '10.16.122.163' }, 'en1' => { dhcp: '10.32.10.163' } } }
13 let(:dhcp) { '10.16.122.163' }
14
15 it 'calls Facter::Resolvers::NetworkingLinux with interfaces' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'calls Facter::Resolvers::NetworkingLinux with dhcp' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:dhcp)
23 end
24
25 it 'returns dhcp_servers' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
27 have_attributes(name: 'dhcp_servers', value: value, type: :legacy)
28 end
29 end
30
31 describe '#call_the_resolver when resolver returns nil' do
32 let(:interfaces) { nil }
33 let(:dhcp) { nil }
34
35 it 'returns nil' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'dhcp_servers', value: nil, type: :legacy)
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Disks do
3 subject(:fact) { Facts::Linux::Disks.new }
4
5 let(:disk) do
6 {
7 'sda' => {
8 model: 'Virtual disk',
9 size: '20.00 GiB',
10 size_bytes: 21_474_836_480,
11 vendor: 'VMware'
12 }
13 }
14 end
15
16 let(:expecte_response) do
17 {
18 'sda' => {
19 'model' => 'Virtual disk',
20 'size' => '20.00 GiB',
21 'size_bytes' => 21_474_836_480,
22 'vendor' => 'VMware'
23 }
24 }
25 end
26
27 describe '#call_the_resolver' do
28 before do
29 allow(Facter::Resolvers::Linux::Disks).to receive(:resolve).with(:disks).and_return(disk)
30 end
31
32 it 'calls Facter::Resolvers::Linux::Disks' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Linux::Disks).to have_received(:resolve).with(:disks)
35 end
36
37 it 'returns resolved fact with name disk and value' do
38 expect(fact.call_the_resolver)
39 .to be_an_instance_of(Array)
40 .and contain_exactly(
41 an_object_having_attributes(name: 'disks', value: expecte_response),
42 an_object_having_attributes(name: 'blockdevices', value: 'sda'),
43 an_object_having_attributes(name: 'blockdevice_sda_model', value: 'Virtual disk', type: :legacy),
44 an_object_having_attributes(name: 'blockdevice_sda_size', value: 21_474_836_480, type: :legacy),
45 an_object_having_attributes(name: 'blockdevice_sda_vendor', value: 'VMware', type: :legacy)
46 )
47 end
48
49 context 'when resolver returns empty hash' do
50 let(:disk) { {} }
51
52 it 'returns nil fact' do
53 expect(fact.call_the_resolver)
54 .to be_an_instance_of(Facter::ResolvedFact)
55 .and have_attributes(name: 'disks', value: nil)
56 end
57 end
58
59 context 'when resolver returns nil' do
60 let(:disk) { nil }
61
62 it 'returns nil fact' do
63 expect(fact.call_the_resolver)
64 .to be_an_instance_of(Facter::ResolvedFact)
65 .and have_attributes(name: 'disks', value: nil)
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Bios::ReleaseDate do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Bios::ReleaseDate.new }
5
6 let(:date) { '07/03/2018' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:bios_date).and_return(date)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_date)
16 end
17
18 it 'returns bios release date fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.release_date', value: date),
21 an_object_having_attributes(name: 'bios_release_date', value: date, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Bios::Vendor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Bios::Vendor.new }
5
6 let(:vendor) { 'Phoenix Technologies LTD' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:bios_vendor).and_return(vendor)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_vendor)
16 end
17
18 it 'returns bios vendor fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.vendor', value: vendor),
21 an_object_having_attributes(name: 'bios_vendor', value: vendor, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Bios::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Bios::Version.new }
5
6 let(:version) { '6.00' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:bios_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_version)
16 end
17
18 it 'returns bios version fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.bios.version', value: version),
21 an_object_having_attributes(name: 'bios_version', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Board::AssetTag do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Board::AssetTag.new }
5
6 let(:asset_tag) { 'Not Specified' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:board_asset_tag).and_return(asset_tag)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:board_asset_tag)
16 end
17
18 it 'returns board asset_tag fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.board.asset_tag', value: asset_tag),
21 an_object_having_attributes(name: 'boardassettag', value: asset_tag, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Board::Manufacturer do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Board::Manufacturer.new }
5
6 let(:manufacturer) { 'Intel Corporation' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:board_vendor).and_return(manufacturer)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:board_vendor)
16 end
17
18 it 'returns board manufacturer fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.board.manufacturer', value: manufacturer),
21 an_object_having_attributes(name: 'boardmanufacturer', value: manufacturer, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Board::Product do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Board::Product.new }
5
6 let(:product) { '440BX Desktop Reference Platform' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:board_name).and_return(product)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:board_name)
16 end
17
18 it 'returns board product fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.board.product', value: product),
21 an_object_having_attributes(name: 'boardproductname', value: product, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Board::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Board::SerialNumber.new }
5
6 let(:serial_number) { 'None' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:board_serial).and_return(serial_number)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:board_serial)
16 end
17
18 it 'returns board serial number fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.board.serial_number', value: serial_number),
21 an_object_having_attributes(name: 'boardserialnumber', value: serial_number, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Chassis::AssetTag do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Chassis::AssetTag.new }
5
6 let(:tag) { 'No Asset Tag' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:chassis_asset_tag).and_return(tag)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:chassis_asset_tag)
16 end
17
18 it 'returns chassis asset tag fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.chassis.asset_tag', value: tag),
21 an_object_having_attributes(name: 'chassisassettag', value: tag, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Chassis::Type do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Chassis::Type.new }
5
6 let(:type) { 'Low Profile Desktop' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:chassis_type).and_return(type)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:chassis_type)
16 end
17
18 it 'returns chassis type fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.chassis.type', value: type),
21 an_object_having_attributes(name: 'chassistype', value: type, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Manufacturer do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Manufacturer.new }
5
6 let(:sys_vendor) { 'VMware, Inc.' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:sys_vendor).and_return(sys_vendor)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:sys_vendor)
16 end
17
18 it 'returns manufacturer fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.manufacturer', value: sys_vendor),
21 an_object_having_attributes(name: 'manufacturer', value: sys_vendor, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Product::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Product::Name.new }
5
6 let(:product_name) { 'VMware Virtual Platform' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:product_name).and_return(product_name)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
16 end
17
18 it 'returns product name fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: product_name),
21 an_object_having_attributes(name: 'productname', value: product_name, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Product::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Product::SerialNumber.new }
5
6 let(:serial_number) { 'VMware-42 1a a9 29 31 8f fa e9-7d 69 2e 23 21 b0 0c 45' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:product_serial).and_return(serial_number)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_serial)
16 end
17
18 it 'returns resolved facts' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.serial_number', value: serial_number),
21 an_object_having_attributes(name: 'serialnumber', value: serial_number, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Dmi::Product::Uuid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Dmi::Product::Uuid.new }
5
6 let(:product_uuid) { '421aa929-318f-fae9-7d69-2e2321b00c45' }
7
8 before do
9 allow(Facter::Resolvers::Linux::DmiBios).to \
10 receive(:resolve).with(:product_uuid).and_return(product_uuid)
11 end
12
13 it 'calls Facter::Resolvers::Linux::DmiBios' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_uuid)
16 end
17
18 it 'returns resolved facts' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'dmi.product.uuid', value: product_uuid),
21 an_object_having_attributes(name: 'uuid', value: product_uuid, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ec2Metadata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ec2Metadata.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
10 end
11
12 context 'when physical machine with no hypervisor' do
13 let(:value) { nil }
14
15 before do
16 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(nil)
17 end
18
19 it 'returns ec2 metadata fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'ec2_metadata', value: value)
22 end
23
24 it "doesn't call Ec2 resolver" do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:metadata)
27 end
28 end
29
30 shared_examples 'check ec2 resolver called with metadata' do
31 it 'calls ec2 resolver' do
32 fact.call_the_resolver
33
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:metadata)
35 end
36 end
37
38 shared_examples 'check resolved fact value' do
39 it 'returns ec2 metadata fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
41 have_attributes(name: 'ec2_metadata', value: value)
42 end
43 end
44
45 context 'when platform is kvm' do
46 let(:value) { { 'info' => 'value' } }
47
48 before do
49 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('kvm')
50 end
51
52 it_behaves_like 'check ec2 resolver called with metadata'
53 it_behaves_like 'check resolved fact value'
54 end
55
56 context 'when platform is xen' do
57 let(:value) { { 'info' => 'value' } }
58
59 before do
60 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('xen')
61 end
62
63 it_behaves_like 'check ec2 resolver called with metadata'
64 it_behaves_like 'check resolved fact value'
65 end
66
67 context 'when platform is aws' do
68 let(:value) { { 'info' => 'value' } }
69
70 before do
71 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('aws')
72 end
73
74 it_behaves_like 'check ec2 resolver called with metadata'
75 it_behaves_like 'check resolved fact value'
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ec2Userdata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ec2Userdata.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:userdata).and_return(value)
10 end
11
12 context 'when physical machine with no hypervisor' do
13 let(:value) { nil }
14
15 before do
16 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('nil')
17 end
18
19 it 'returns ec2 userdata fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'ec2_userdata', value: value)
22 end
23
24 it "doesn't call Ec2 resolver" do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:userdata)
27 end
28 end
29
30 shared_examples 'check ec2 resolver called with userdata' do
31 it 'calls ec2 resolver' do
32 fact.call_the_resolver
33
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:userdata)
35 end
36 end
37
38 shared_examples 'check resolved fact value' do
39 it 'returns ec2 userdata fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
41 have_attributes(name: 'ec2_userdata', value: value)
42 end
43 end
44
45 context 'when platform is kvm' do
46 let(:value) { { 'info' => 'value' } }
47
48 before do
49 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('kvm')
50 end
51
52 it_behaves_like 'check ec2 resolver called with userdata'
53 it_behaves_like 'check resolved fact value'
54 end
55
56 context 'when platform is xen' do
57 let(:value) { { 'info' => 'value' } }
58
59 before do
60 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('xen')
61 end
62
63 it_behaves_like 'check ec2 resolver called with userdata'
64 it_behaves_like 'check resolved fact value'
65 end
66
67 context 'when platform is aws' do
68 let(:value) { { 'info' => 'value' } }
69
70 before do
71 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return('aws')
72 end
73
74 it_behaves_like 'check ec2 resolver called with userdata'
75 it_behaves_like 'check resolved fact value'
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Filesystems do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Filesystems.new }
5
6 let(:value) { 'ext2,ext3,ext4,xfs' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Filesystems).to \
10 receive(:resolve).with(:systems).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Filesystems' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Filesystems).to have_received(:resolve).with(:systems)
16 end
17
18 it 'returns file systems fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'filesystems', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::FipsEnabled do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::FipsEnabled.new }
5
6 let(:value) { true }
7
8 before do
9 allow(Facter::Resolvers::Linux::FipsEnabled).to \
10 receive(:resolve).with(:fips_enabled).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::FipsEnabled' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::FipsEnabled).to have_received(:resolve).with(:fips_enabled)
16 end
17
18 it 'returns fips enabled fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'fips_enabled', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Gce do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Gce.new }
5
6 before do
7 allow(Facter::Resolvers::Gce).to receive(:resolve).with(:metadata).and_return(value)
8 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(vendor)
9 end
10
11 context 'when hypervisor is Gce' do
12 let(:vendor) { 'Google' }
13 let(:value) do
14 {
15 'oslogin' => {
16 'authenticate' => {
17 'sessions' => {
18 }
19 }
20 },
21 'project' => {
22 'numericProjectId' => 728_618_928_092,
23 'projectId' => 'facter-performance-history'
24 }
25 }
26 end
27
28 it 'calls Facter::Resolvers::Linux::Gce' do
29 fact.call_the_resolver
30 expect(Facter::Resolvers::Gce).to have_received(:resolve).with(:metadata)
31 end
32
33 it 'calls Facter::Resolvers::Linux::DmiBios' do
34 fact.call_the_resolver
35 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_vendor)
36 end
37
38 it 'returns gce fact' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'gce', value: value)
41 end
42 end
43
44 context 'when hypervisor is not Gce' do
45 let(:vendor) { 'unknown' }
46 let(:value) { nil }
47
48 it 'returns nil' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
50 have_attributes(name: 'gce', value: nil)
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Docker do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::Docker.new }
5
6 before do
7 allow(Facter::Resolvers::Containers).to \
8 receive(:resolve).with(:hypervisor).and_return(hv)
9 end
10
11 context 'when resolver returns docker' do
12 let(:hv) { { docker: { 'id' => 'testid' } } }
13 let(:value) { { 'id' => 'testid' } }
14
15 it 'calls Facter::Resolvers::Containers' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Containers).to have_received(:resolve).with(:hypervisor)
18 end
19
20 it 'returns virtual fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'hypervisors.docker', value: value)
23 end
24 end
25
26 context 'when resolver returns lxc' do
27 let(:hv) { { lxc: { 'name' => 'test_name' } } }
28
29 it 'returns virtual fact' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
31 have_attributes(name: 'hypervisors.docker', value: nil)
32 end
33 end
34
35 context 'when resolver returns nil' do
36 let(:hv) { nil }
37
38 it 'returns virtual fact as nil' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'hypervisors.docker', value: hv)
41 end
42 end
43
44 context 'when docker info is empty' do
45 let(:hv) { { docker: {} } }
46 let(:value) { {} }
47
48 it 'returns virtual fact as nil' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
50 have_attributes(name: 'hypervisors.docker', value: value)
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::HyperV do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::HyperV.new }
5
6 before do
7 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return(manufacturer)
8 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return(product_name)
9 end
10
11 context 'when resolver returns hyper_v' do
12 let(:manufacturer) { 'Microsoft' }
13 let(:product_name) { 'Virtual Machine' }
14 let(:value) { {} }
15
16 it 'calls Facter::Resolvers::DMIBios with :sys_vendor' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:sys_vendor)
19 end
20
21 it 'calls Facter::Resolvers::DMIBios with :product_name' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
24 end
25
26 it 'returns hyper_v fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
28 have_attributes(name: 'hypervisors.hyperv', value: value)
29 end
30 end
31
32 context 'when resolver returns nil' do
33 let(:manufacturer) { nil }
34 let(:product_name) { nil }
35 let(:value) { nil }
36
37 it 'returns virtual fact as nil' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
39 have_attributes(name: 'hypervisors.hyperv', value: value)
40 end
41 end
42
43 context 'when manufacturer is not Microsoft, but product name is Virtual Machine' do
44 let(:manufacturer) { 'unknown' }
45 let(:product_name) { 'Virtual Machine' }
46 let(:value) { {} }
47
48 it 'returns hyper-v fact' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
50 have_attributes(name: 'hypervisors.hyperv', value: value)
51 end
52 end
53
54 context 'when manufacturer is Microsoft and product name is not Virtual Machine' do
55 let(:manufacturer) { 'Microsoft' }
56 let(:product_name) { 'something_else' }
57 let(:value) { {} }
58
59 it 'returns hyper-v fact' do
60 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
61 have_attributes(name: 'hypervisors.hyperv', value: value)
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Kvm do
3 subject(:fact) { Facts::Linux::Hypervisors::Kvm.new }
4
5 describe '#call_the_resolver' do
6 context 'when hypervisor is virtualbox' do
7 before do
8 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('VirtualBox')
9 end
10
11 it 'calls Facter::Resolvers::Linux::DmiBios' do
12 fact.call_the_resolver
13
14 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
15 end
16
17 it 'has nil value' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
19 .and have_attributes(name: 'hypervisors.kvm', value: nil)
20 end
21 end
22
23 context 'when hypervisor is parallels' do
24 before do
25 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('Parallels')
26 end
27
28 it 'calls Facter::Resolvers::Linux::DmiBios' do
29 fact.call_the_resolver
30
31 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
32 end
33
34 it 'has nil value' do
35 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
36 .and have_attributes(name: 'hypervisors.kvm', value: nil)
37 end
38 end
39
40 context 'when VirtWhat retuns kvm' do
41 before do
42 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('KVM')
43 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('unknown')
44 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('unknown')
45 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('kvm')
46 end
47
48 it 'calls Facter::Resolvers::VirtWhat' do
49 fact.call_the_resolver
50
51 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
52 end
53
54 it 'returns empty hash' do
55 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
56 .and have_attributes(name: 'hypervisors.kvm', value: {})
57 end
58 end
59
60 context 'when Lspci returns kvm' do
61 before do
62 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('KVM')
63 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('unknown')
64 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('unknown')
65 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
66 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('kvm')
67 end
68
69 it 'calls Facter::Resolvers::Lspci' do
70 fact.call_the_resolver
71
72 expect(Facter::Resolvers::Lspci).to have_received(:resolve).with(:vm)
73 end
74
75 it 'returns empty hash' do
76 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
77 .and have_attributes(name: 'hypervisors.kvm', value: {})
78 end
79 end
80
81 context 'when VM is provided by AWS with KVM hypervisor' do
82 before do
83 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('KVM')
84 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('Amazon EC2')
85 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('Amazon')
86 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
87 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
88 end
89
90 it 'returns aws' do
91 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
92 .and have_attributes(name: 'hypervisors.kvm', value: { 'amazon' => true })
93 end
94 end
95
96 context 'when VM is provided by GCE with KVM hypervisor' do
97 before do
98 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('KVM')
99 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('Google')
100 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('Google')
101 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
102 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
103 end
104
105 it 'returns google cloud' do
106 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
107 .and have_attributes(name: 'hypervisors.kvm', value: { 'google' => true })
108 end
109 end
110
111 context 'when VM is provided by OpenStack with KVM hypervisor' do
112 before do
113 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('KVM')
114 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('OpenStack')
115 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('OpenStack')
116 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
117 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('kvm')
118 end
119
120 it 'returns open stack' do
121 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
122 .and have_attributes(name: 'hypervisors.kvm', value: { 'openstack' => true })
123 end
124
125 it 'returns open stack when Lspci return nil' do
126 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
127 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('OpenStack')
128 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
129 .and have_attributes(name: 'hypervisors.kvm', value: { 'openstack' => true })
130 end
131 end
132 end
133 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Lxc do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::Lxc.new }
5
6 before do
7 allow(Facter::Resolvers::Containers).to \
8 receive(:resolve).with(:hypervisor).and_return(hv)
9 end
10
11 context 'when resolver returns lxc' do
12 let(:hv) { { lxc: { 'name' => 'test_name' } } }
13 let(:value) { { 'name' => 'test_name' } }
14
15 it 'calls Facter::Resolvers::Containers' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Containers).to have_received(:resolve).with(:hypervisor)
18 end
19
20 it 'returns virtual fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'hypervisors.lxc', value: value)
23 end
24 end
25
26 context 'when resolver returns docker' do
27 let(:hv) { { docker: { 'id' => 'testid' } } }
28
29 it 'returns virtual fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
31 have_attributes(name: 'hypervisors.lxc', value: nil)
32 end
33 end
34
35 context 'when resolver returns nil' do
36 let(:hv) { nil }
37
38 it 'returns virtual fact as nil' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'hypervisors.lxc', value: hv)
41 end
42 end
43
44 context 'when lxc info is empty' do
45 let(:hv) { { lxc: {} } }
46 let(:value) { {} }
47
48 it 'returns virtual fact as empty array' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
50 have_attributes(name: 'hypervisors.lxc', value: value)
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Openvz do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::Openvz.new }
5
6 before do
7 allow(Facter::Resolvers::OpenVz).to \
8 receive(:resolve).with(:vm).and_return(ovz)
9 end
10
11 context 'when resolver returns nil' do
12 let(:ovz) { nil }
13
14 it 'calls Facter::Resolvers::OpenVz' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::OpenVz).to have_received(:resolve).with(:vm)
17 end
18
19 it 'returns virtual fact as nil' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'hypervisors.openvz', value: nil)
22 end
23 end
24
25 context 'when resolver returns openvz host' do
26 before { allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:id).and_return('0') }
27
28 let(:ovz) { 'openvzhn' }
29 let(:value) { { 'id' => 0, 'host' => true } }
30
31 it 'returns openvz info' do
32 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
33 have_attributes(name: 'hypervisors.openvz', value: value)
34 end
35 end
36
37 context 'when resolver returns openvz' do
38 before { allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:id).and_return('101') }
39
40 let(:ovz) { 'openvze' }
41 let(:value) { { 'id' => 101, 'host' => false } }
42
43 it 'returns openvz info' do
44 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
45 have_attributes(name: 'hypervisors.openvz', value: value)
46 end
47 end
48 end
49 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::SystemdNspawn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::SystemdNspawn.new }
5
6 before do
7 allow(Facter::Resolvers::Containers).to \
8 receive(:resolve).with(:hypervisor).and_return(hv)
9 end
10
11 context 'when resolver returns systemd_nspawn' do
12 let(:hv) { { systemd_nspawn: { 'id' => 'testid00' } } }
13 let(:value) { { 'id' => 'testid00' } }
14
15 it 'calls Facter::Resolvers::Containers' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Containers).to have_received(:resolve).with(:hypervisor)
18 end
19
20 it 'returns virtual fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'hypervisors.systemd_nspawn', value: value)
23 end
24 end
25
26 context 'when resolver returns docker' do
27 let(:hv) { { docker: { 'id' => 'testid' } } }
28
29 it 'returns virtual fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
31 have_attributes(name: 'hypervisors.systemd_nspawn', value: nil)
32 end
33 end
34
35 context 'when resolver returns nil' do
36 let(:hv) { nil }
37
38 it 'returns virtual fact as nil' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'hypervisors.systemd_nspawn', value: hv)
41 end
42 end
43
44 context 'when systemd_nspawn info is empty' do
45 let(:hv) { { systemd_nspawn: {} } }
46 let(:value) { {} }
47
48 it 'returns virtual fact as empty array' do
49 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
50 have_attributes(name: 'hypervisors.systemd_nspawn', value: value)
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::VirtualBox do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Hypervisors::VirtualBox.new }
5
6 let(:version) { '6.4.1' }
7 let(:revision) { '136177' }
8 let(:value) { { 'version' => version, 'revision' => revision } }
9
10 before do
11 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:virtualbox_version).and_return(version)
12 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:virtualbox_revision).and_return(revision)
13 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('VirtualBox')
14 end
15
16 it 'calls Facter::Resolvers::Linux::DmiBios' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
19 end
20
21 it 'calls Facter::Resolvers::DmiDecode with version' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::DmiDecode).to have_received(:resolve).with(:virtualbox_version)
24 end
25
26 it 'calls Facter::Resolvers::DmiDecode with revision' do
27 fact.call_the_resolver
28 expect(Facter::Resolvers::DmiDecode).to have_received(:resolve).with(:virtualbox_revision)
29 end
30
31 it 'returns virtualbox fact' do
32 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
33 have_attributes(name: 'hypervisors.virtualbox', value: value)
34 end
35
36 context 'when virtualbox is not detected' do
37 let(:value) { nil }
38
39 before do
40 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('other')
41 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('other')
42 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('other')
43 end
44
45 it 'returns nil' do
46 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
47 have_attributes(name: 'hypervisors.virtualbox', value: value)
48 end
49 end
50
51 context 'when virtualbox details are not present' do
52 let(:value) { {} }
53
54 before do
55 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:virtualbox_version).and_return(nil)
56 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:virtualbox_revision).and_return(nil)
57 end
58
59 it 'returns virtualbox fact as empty hash' do
60 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
61 have_attributes(name: 'hypervisors.virtualbox', value: value)
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Vmware do
3 subject(:fact) { Facts::Linux::Hypervisors::Vmware.new }
4
5 describe '#call_the_resolver' do
6 context 'when vmware is detected' do
7 context 'when VirtWhat resolver returns vmware' do
8 before do
9 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('vmware')
10 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return('ESXi 6.7')
11 end
12
13 it 'returns vmware' do
14 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
15 .and have_attributes(name: 'hypervisors.vmware', value: { 'version' => 'ESXi 6.7' })
16 end
17 end
18
19 context 'when vmware_version is nil' do
20 before do
21 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('vmware')
22 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return(nil)
23 end
24
25 it 'returns vmware' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
27 .and have_attributes(name: 'hypervisors.vmware', value: {})
28 end
29 end
30
31 context 'when vmware_version is empty string' do
32 before do
33 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('vmware')
34 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return('')
35 end
36
37 it 'returns vmware' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
39 .and have_attributes(name: 'hypervisors.vmware', value: {})
40 end
41 end
42
43 context 'when DmiBios resolver with product_name returns VMware' do
44 before do
45 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
46 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('VMware')
47 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return('ESXi 6.7')
48 end
49
50 it 'returns vmware' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
52 .and have_attributes(name: 'hypervisors.vmware', value: { 'version' => 'ESXi 6.7' })
53 end
54 end
55
56 context 'when Lspci resolver returns vmware' do
57 before do
58 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
59 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
60 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('vmware')
61 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return('ESXi 6.7')
62 end
63
64 it 'returns vmware' do
65 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
66 .and have_attributes(name: 'hypervisors.vmware', value: { 'version' => 'ESXi 6.7' })
67 end
68 end
69
70 context 'when DmiBios resolver with sys_vendor returns VMware, Inc.' do
71 before do
72 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
73 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
74 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
75 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('VMware, Inc.')
76 allow(Facter::Resolvers::DmiDecode).to receive(:resolve).with(:vmware_version).and_return('ESXi 6.7')
77 end
78
79 it 'returns vmware' do
80 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
81 .and have_attributes(name: 'hypervisors.vmware', value: { 'version' => 'ESXi 6.7' })
82 end
83 end
84 end
85
86 context 'when vmware is not detected' do
87 before do
88 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return('unknown')
89 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
90 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
91 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:sys_vendor).and_return('unknown')
92 end
93
94 it 'returns empty list' do
95 expect(fact.call_the_resolver).to eq([])
96 end
97 end
98 end
99 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Hypervisors::Xen do
3 subject(:fact) { Facts::Linux::Hypervisors::Xen.new }
4
5 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
6
7 describe '#call_the_resolver' do
8 before do
9 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(value)
10 end
11
12 context 'when xen hypervisor' do
13 let(:value) { 'xen' }
14
15 context 'when Xen resolver returns privileged false' do
16 before do
17 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('xenhvm')
18 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(false)
19 end
20
21 it 'returns xen' do
22 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
23 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'hvm', 'privileged' => false })
24 end
25 end
26
27 context 'when Xen resolver returns xen0' do
28 before do
29 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('xen0')
30 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('HVM domU')
31 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(true)
32 end
33
34 it 'returns xen' do
35 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
36 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'hvm', 'privileged' => true })
37 end
38 end
39
40 context 'when DmiBios resolver return HVM domU' do
41 before do
42 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('unknown')
43 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('HVM domU')
44 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(true)
45 end
46
47 it 'calls Facter::Resolvers::Linux::DmiBios' do
48 fact.call_the_resolver
49
50 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
51 end
52
53 it 'returns xen' do
54 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
55 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'hvm', 'privileged' => true })
56 end
57 end
58
59 context 'when Lspci resolver returns xenhvm' do
60 before do
61 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('unknown')
62 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
63 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(true)
64 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('xenhvm')
65 end
66
67 it 'calls Facter::Resolvers::Linux::DmiBios' do
68 fact.call_the_resolver
69
70 expect(Facter::Resolvers::Lspci).to have_received(:resolve).with(:vm)
71 end
72
73 it 'returns xen' do
74 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
75 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'hvm', 'privileged' => true })
76 end
77 end
78
79 context 'when pv context' do
80 before do
81 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
82 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return('unknown')
83 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(false)
84 end
85
86 it 'returns xen with pv context' do
87 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
88 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'pv', 'privileged' => false })
89 end
90 end
91
92 context 'when privileged' do
93 before do
94 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('xenhvm')
95 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:privileged).and_return(true)
96 end
97
98 it 'returns privileged xen' do
99 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
100 .and have_attributes(name: 'hypervisors.xen', value: { 'context' => 'hvm', 'privileged' => true })
101 end
102 end
103 end
104
105 context 'when not xen hypervisor' do
106 let(:value) { nil }
107
108 it 'returns empty array' do
109 expect(fact.call_the_resolver).to eq([])
110 end
111 end
112 end
113 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Identity::Gid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Identity::Gid.new }
5
6 let(:value) { '20' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:gid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:gid)
15 end
16
17 it 'returns identity gid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.gid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Identity::Group do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Identity::Group.new }
5
6 let(:value) { 'staff' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:group).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:group)
15 end
16
17 it 'returns identity group fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.group', value: value),
20 an_object_having_attributes(name: 'gid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Identity::Privileged do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Identity::Privileged.new }
5
6 let(:value) { 'false' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:privileged).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:privileged)
15 end
16
17 it 'returns identity privileged fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.privileged', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Identity::Uid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Identity::Uid.new }
5
6 let(:value) { '501' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:uid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:uid)
15 end
16
17 it 'returns identity uid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.uid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Identity::User.new }
5
6 let(:value) { 'testUser' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:user)
15 end
16
17 it 'returns id and identity user fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Interfaces do
3 subject(:fact) { Facts::Linux::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns interfaces names' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'interfaces', value: interfaces.keys.sort.join(','), type: :legacy)
20 end
21 end
22
23 describe '#call_the_resolver when resolver returns nil' do
24 let(:interfaces) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
28 have_attributes(name: 'interfaces', value: interfaces, type: :legacy)
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Linux::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::IpaddressInterfaces do
3 subject(:fact) { Facts::Linux::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::IsVirtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::IsVirtual.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7
8 before do
9 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(virtual_value)
10 end
11
12 context 'when not in a virtual environment' do
13 let(:virtual_value) { 'physical' }
14
15 it 'return resolved fact with nil value' do
16 expect(fact.call_the_resolver)
17 .to be_an_instance_of(Facter::ResolvedFact)
18 .and have_attributes(name: 'is_virtual', value: false)
19 end
20 end
21
22 context 'when in a virtual environment' do
23 let(:virtual_value) { 'aws' }
24
25 it 'return resolved fact with nil value' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact)
28 .and have_attributes(name: 'is_virtual', value: true)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Kernel.new }
5
6 let(:value) { 'Linux' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Kernelmajversion do
3 subject(:fact) { Facts::Linux::Kernelmajversion.new }
4
5 let(:value) { '4.15' }
6
7 before do
8 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
9 end
10
11 it 'calls Facter::Resolvers::Uname' do
12 fact.call_the_resolver
13 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
14 end
15
16 shared_examples 'kernelmajversion fact expectation' do
17 it 'returns the correct kernelmajversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelmajversion', value: value)
20 end
21 end
22
23 describe '#call_the_resolver' do
24 context 'when full version is separated by . delimeter' do
25 let(:value) { '4.15' }
26
27 include_examples 'kernelmajversion fact expectation'
28 end
29
30 context 'when full version does not have a . delimeter' do
31 let(:value) { '4test' }
32
33 include_examples 'kernelmajversion fact expectation'
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Kernelrelease.new }
5
6 let(:value) { '6100-09-00-0000' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
15 end
16
17 it 'returns kernelrelease fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelrelease', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Kernelversion do
3 subject(:fact) { Facts::Linux::Kernelversion.new }
4
5 let(:resolver_value) { '4.11' }
6
7 before do
8 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(resolver_value)
9 end
10
11 it 'calls Facter::Resolvers::Uname' do
12 fact.call_the_resolver
13 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
14 end
15
16 shared_examples 'kernelversion fact expectation' do
17 it 'returns the correct kernelversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelversion', value: fact_value)
20 end
21 end
22
23 describe '#call_the_resolver' do
24 context 'when full version includes ' do
25 let(:resolver_value) { '4.11.5-19-generic' }
26 let(:fact_value) { '4.11.5' }
27
28 include_examples 'kernelversion fact expectation'
29 end
30
31 context 'when full version does not have a . delimeter' do
32 let(:resolver_value) { '4test' }
33 let(:fact_value) { '4' }
34
35 include_examples 'kernelversion fact expectation'
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::LoadAverages do
3 subject(:fact) { Facts::Linux::LoadAverages.new }
4
5 let(:averages) do
6 {
7 '15m' => 0.0,
8 '10m' => 0.0,
9 '5m' => 0.0
10 }
11 end
12
13 describe '#call_the_resolver' do
14 before do
15 allow(Facter::Resolvers::Linux::LoadAverages).to receive(:resolve).with(:load_averages).and_return(averages)
16 end
17
18 it 'calls Facter::Resolvers::Linux::LoadAverages' do
19 fact.call_the_resolver
20 expect(Facter::Resolvers::Linux::LoadAverages).to have_received(:resolve).with(:load_averages)
21 end
22
23 it 'returns resolved fact with name disk and value' do
24 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
25 .and have_attributes(name: 'load_averages', value: averages)
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Lsbdistrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Lsbdistrelease.new }
5
6 context 'when lsb-release is installed' do
7 before do
8 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
9 end
10
11 context 'when version_id is retrieved successful' do
12 context 'when version consists of only major' do
13 let(:value) { '10' }
14 let(:value_final) { { 'full' => '10', 'major' => '10', 'minor' => nil } }
15
16 it 'calls Facter::Resolvers::LsbRelease with :name' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:release)
19 end
20
21 it 'returns release fact' do
22 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
23 contain_exactly(an_object_having_attributes(name: 'lsbdistrelease', value: value, type: :legacy),
24 an_object_having_attributes(name: 'lsbmajdistrelease',
25 value: value_final['major'], type: :legacy),
26 an_object_having_attributes(name: 'lsbminordistrelease',
27 value: value_final['minor'], type: :legacy))
28 end
29 end
30
31 context 'when versin consists of both major and minor' do
32 let(:value) { '10.08' }
33 let(:value_final) { { 'full' => '10.08', 'major' => '10', 'minor' => '08' } }
34
35 it 'calls Facter::Resolvers::LsbRelease with :name' do
36 fact.call_the_resolver
37 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:release)
38 end
39
40 it 'returns release fact' do
41 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
42 contain_exactly(an_object_having_attributes(name: 'lsbdistrelease', value: value, type: :legacy),
43 an_object_having_attributes(name: 'lsbmajdistrelease',
44 value: value_final['major'], type: :legacy),
45 an_object_having_attributes(name: 'lsbminordistrelease',
46 value: value_final['minor'], type: :legacy))
47 end
48 end
49 end
50
51 context 'when lsb-release is not installed' do
52 let(:value) { nil }
53
54 it 'returns release fact as nil' do
55 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
56 have_attributes(name: 'lsbdistrelease', value: value, type: :legacy)
57 end
58 end
59 end
60 end
61 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::MacaddressInterfaces do
3 subject(:fact) { Facts::Linux::MacaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mac: '10.16.117.100' }, 'en1' => { mac: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names macaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'macaddress_eth0',
20 value: interfaces['eth0'][:mac], type: :legacy),
21 an_object_having_attributes(name: 'macaddress_en1',
22 value: interfaces['en1'][:mac], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::AvailableBytes.new }
5
6 let(:value) { 2_332_425 }
7 let(:value_mb) { 2.2243738174438477 }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:swap_free).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_free)
17 end
18
19 it 'returns swap available bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
22 an_object_having_attributes(name: 'swapfree_mb', value: value_mb, type: :legacy))
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns swap available memory in bytes fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
31 an_object_having_attributes(name: 'swapfree_mb', value: value, type: :legacy))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::Available.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:swap_free).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_free)
17 end
18
19 it 'returns swap available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.available', value: value),
22 an_object_having_attributes(name: 'swapfree', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::Capacity.new }
5
6 let(:value) { '7.4%' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Memory).to \
10 receive(:resolve).with(:swap_capacity).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Memory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_capacity)
16 end
17
18 it 'returns swap memory capacity fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.swap.capacity', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::TotalBytes.new }
5
6 let(:value) { 2_332_425 }
7 let(:value_mb) { 2.2243738174438477 }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:swap_total).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_total)
17 end
18
19 it 'returns swap total memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
22 an_object_having_attributes(name: 'swapsize_mb', value: value_mb, type: :legacy))
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns swap total memory in bytes fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
31 an_object_having_attributes(name: 'swapsize_mb', value: value, type: :legacy))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::Total.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:swap_total).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_total)
17 end
18
19 it 'returns swap total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.total', value: value),
22 an_object_having_attributes(name: 'swapsize', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::UsedBytes.new }
5
6 let(:value) { 1024 }
7
8 before do
9 allow(Facter::Resolvers::Linux::Memory).to \
10 receive(:resolve).with(:swap_used_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Memory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_used_bytes)
16 end
17
18 it 'returns swap used memory in bytes fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.swap.used_bytes', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::Swap::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::Swap::Used.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:swap_used_bytes).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:swap_used_bytes)
17 end
18
19 it 'returns swap used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::AvailableBytes.new }
5
6 let(:value) { 2_332_425 }
7 let(:value_mb) { 2.2243738174438477 }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:memfree).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:memfree)
17 end
18
19 it 'returns system available memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
22 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns system available memory in bytes fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
31 an_object_having_attributes(name: 'memoryfree_mb', value: value, type: :legacy))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::Available.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:memfree).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:memfree)
17 end
18
19 it 'returns system available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
22 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::Capacity.new }
5
6 let(:value) { '5.3%' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Memory).to \
10 receive(:resolve).with(:capacity).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Memory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:capacity)
16 end
17
18 it 'returns system memory capacity fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.system.capacity', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::TotalBytes.new }
5
6 let(:value) { 2_332_425 }
7 let(:value_mb) { 2.2243738174438477 }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:total).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:total)
17 end
18
19 it 'returns system total memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
22 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns system total memory in bytes fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
31 an_object_having_attributes(name: 'memorysize_mb', value: value, type: :legacy))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::Total.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:total).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:total)
17 end
18
19 it 'returns system total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
22 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::UsedBytes.new }
5
6 let(:value) { 1024 }
7
8 before do
9 allow(Facter::Resolvers::Linux::Memory).to \
10 receive(:resolve).with(:used_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Memory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns system used memory in bytes fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.system.used_bytes', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Memory::System::Used.new }
5
6 let(:resolver_value) { 1024 }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Memory).to \
11 receive(:resolve).with(:used_bytes).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Memory).to have_received(:resolve).with(:used_bytes)
17 end
18
19 it 'returns system used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Mountpoints do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Mountpoints.new }
5
6 context 'when resolver returns hash' do
7 let(:resolver_output) do
8 [{ available: '63.31 GiB',
9 available_bytes: 67_979_685_888,
10 capacity: '84.64%',
11 device: '/dev/nvme0n1p2',
12 filesystem: 'ext4',
13 options: %w[rw noatime],
14 path: '/',
15 size: '434.42 GiB',
16 size_bytes: 466_449_743_872,
17 used: '348.97 GiB',
18 used_bytes: 374_704_357_376 }]
19 end
20 let(:parsed_fact) do
21 { '/' => { 'available' => '63.31 GiB',
22 'available_bytes' => 67_979_685_888,
23 'capacity' => '84.64%',
24 'device' => '/dev/nvme0n1p2',
25 'filesystem' => 'ext4',
26 'options' => %w[rw noatime],
27 'size' => '434.42 GiB',
28 'size_bytes' => 466_449_743_872,
29 'used' => '348.97 GiB',
30 'used_bytes' => 374_704_357_376 } }
31 end
32
33 before do
34 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(resolver_output)
35 end
36
37 it 'calls Facter::Resolvers::Mountpoints' do
38 fact.call_the_resolver
39 expect(Facter::Resolvers::Mountpoints).to have_received(:resolve).with(:mountpoints)
40 end
41
42 it 'returns mountpoints information' do
43 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
44 have_attributes(name: 'mountpoints', value: parsed_fact)
45 end
46 end
47
48 context 'when resolver returns nil' do
49 before do
50 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(nil)
51 end
52
53 it 'returns mountpoints information' do
54 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
55 have_attributes(name: 'mountpoints', value: nil)
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::MtuInterfaces do
3 subject(:fact) { Facts::Linux::MtuInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mtu: 1500 }, 'en1' => { mtu: 1500 } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names mtu_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'mtu_eth0', value: interfaces['eth0'][:mtu], type: :legacy),
20 an_object_having_attributes(name: 'mtu_en1', value: interfaces['en1'][:mtu], type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:interfaces) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Netmask6Interfaces do
3 subject(:fact) { Facts::Linux::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::NetworkingLinux' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::NetmaskInterfaces do
3 subject(:fact) { Facts::Linux::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Network6Interfaces do
3 subject(:fact) { Facts::Linux::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::NetworkInterfaces do
3 subject(:fact) { Facts::Linux::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Dhcp do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Dhcp.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:dhcp).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with dhcp' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:dhcp)
15 end
16
17 it 'returns dhcp fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.dhcp', value: value)
21 end
22
23 context 'when dhcp can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Domain.new }
5
6 let(:value) { 'domain' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Hostname).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Hostname).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22
23 context 'when domain can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
29 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Fqdn.new }
5
6 let(:value) { 'host.domain' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Hostname).to receive(:resolve).with(:fqdn).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Hostname).to have_received(:resolve).with(:fqdn)
15 end
16
17 it 'returns fqdn fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
20 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
21 end
22
23 context 'when fqdn can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
29 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Hostname.new }
5
6 let(:value) { 'host' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22
23 context 'when hostname can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
29 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Interfaces do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Interfaces.new }
5
6 let(:value) do
7 {
8 'ens160' => {
9 'bindings' => [
10 {
11 'address' => '10.16.116.8',
12 'netmask' => '255.255.240.0',
13 'network' => '10.16.112.0'
14 }
15 ]
16 }
17 }
18 end
19
20 before do
21 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(value)
22 end
23
24 it 'calls Facter::Resolvers::NetworkingLinux' do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
27 end
28
29 it 'returns networking.interfaces fact' do
30 expect(fact.call_the_resolver)
31 .to be_an_instance_of(Facter::ResolvedFact)
32 .and have_attributes(name: 'networking.interfaces', value: value)
33 end
34
35 context 'when interfaces can not be retrieved' do
36 let(:value) { nil }
37
38 it 'returns nil' do
39 expect(fact.call_the_resolver)
40 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.interfaces', value: value)
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Ip6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Ip6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:ip6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with ip6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns ipv6 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
20 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
21 end
22
23 context 'when ip6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
29 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Ip.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with ip' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:ip)
15 end
16
17 it 'returns ipaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22
23 context 'when ip can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
29 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Mac do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Mac.new }
5
6 let(:value) { '64:5a:ed:ea:c3:25' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:mac).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with mac' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'return macaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22
23 context 'when mac can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
29 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Mtu do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Mtu.new }
5
6 let(:value) { 1500 }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:mtu).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with mtu' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:mtu)
15 end
16
17 it 'return mtu fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.mtu', value: value)
21 end
22
23 context 'when mtu can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Netmask6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Netmask6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:netmask6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with netmask6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns netmask6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22
23 context 'when netmask6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
29 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Netmask do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Netmask.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:netmask).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with netmask' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns netmask fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22
23 context 'when netmask can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
29 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Network6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Network6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:network6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with network6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns network6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22
23 context 'when network6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
29 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Network do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Network.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:network).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with network' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns network fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22
23 context 'when network can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
29 an_object_having_attributes(name: 'network', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Primary.new }
5
6 let(:value) { 'ens160' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns networking.primary fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.primary', value: value)
21 end
22
23 context 'when primary interface can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Networking::Scope6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Networking::Scope6.new }
5
6 let(:value) { 'link' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:scope6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with scope6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:scope6)
15 end
16
17 it 'return scope6 fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
21 an_object_having_attributes(name: 'scope6', value: value))
22 end
23
24 context 'when scope6 can not be retrieved' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver)
29 .to be_an_instance_of(Array)
30 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
31 an_object_having_attributes(name: 'scope6', value: value))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Architecture.new }
5
6 before do
7 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
8 end
9
10 context 'when os is 64-bit' do
11 let(:value) { 'x86_64' }
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
16 end
17
18 it 'returns architecture fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
21 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
22 end
23 end
24
25 context 'when os is 32-bit' do
26 let(:value) { 'i686' }
27 let(:fact_value) { 'i386' }
28
29 it 'calls Facter::Resolvers::Uname' do
30 fact.call_the_resolver
31 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
32 end
33
34 it 'returns architecture fact' do
35 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
36 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: fact_value),
37 an_object_having_attributes(name: 'architecture', value: fact_value, type: :legacy))
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Distro::Codename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Distro::Codename.new }
5
6 let(:value) { 'Core' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.distro.codename', value: value),
20 an_object_having_attributes(name: 'lsbdistcodename', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Distro::Description do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Distro::Description.new }
5
6 let(:value) { 'CentOS Linux release 7.2.1511 (Core)' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:description).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:description)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.distro.description', value: value),
20 an_object_having_attributes(name: 'lsbdistdescription', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Distro::Id do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Distro::Id.new }
5
6 let(:value) { 'CentOS' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.distro.id', value: value),
20 an_object_having_attributes(name: 'lsbdistid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Distro::Release.new }
5
6 let(:value) { '7.2.1511' }
7 let(:release) { { 'full' => '7.2.1511', 'major' => '7', 'minor' => '2' } }
8
9 before do
10 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::LsbRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:release)
16 end
17
18 it 'returns release fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
21 an_object_having_attributes(name: 'lsbdistrelease', value: value, type: :legacy),
22 an_object_having_attributes(name: 'lsbmajdistrelease',
23 value: release['major'], type: :legacy),
24 an_object_having_attributes(name: 'lsbminordistrelease',
25 value: release['minor'], type: :legacy))
26 end
27
28 context 'when lsb_release is not installed' do
29 let(:value) { nil }
30
31 before do
32 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
33 end
34
35 it 'returns release fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
37 .and have_attributes(name: 'os.distro.release', value: value)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Distro::Specification do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Distro::Specification.new }
5
6 let(:value) { ':core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:lsb_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:lsb_version)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.distro.specification', value: value),
20 an_object_having_attributes(name: 'lsbrelease', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Family.new }
5
6 let(:value) { 'Debian' }
7
8 context 'when OsRelease resolver returns id_like' do
9 before do
10 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id_like).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::OsRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id_like)
16 end
17
18 it 'returns os family fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
21 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
22 end
23 end
24
25 context 'when OsRelease resolver does not return id_like and fact has to call OsRelease resolver twice' do
26 before do
27 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id_like).and_return(nil)
28 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id).and_return(value)
29 end
30
31 it 'calls Facter::Resolvers::OsRelease with id_like' do
32 fact.call_the_resolver
33 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id_like)
34 end
35
36 it 'calls Facter::Resolvers::OsRelease with id' do
37 fact.call_the_resolver
38 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id)
39 end
40
41 it 'returns os family fact' do
42 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
43 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
44 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
45 end
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Hardware.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
15 end
16
17 it 'returns hardware model fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Name.new }
5
6 let(:value) { 'Debian' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:name).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:name)
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Release.new }
5
6 let(:value) { '10.9' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value, 'major' => value }),
20 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value, type: :legacy),
21 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::ConfigMode do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::ConfigMode.new }
5
6 let(:config_mode) { 'enabled' }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:config_mode).and_return(config_mode)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:config_mode)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.config_mode', value: config_mode),
20 an_object_having_attributes(name: 'selinux_config_mode', value: config_mode, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::ConfigPolicy do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::ConfigPolicy.new }
5
6 let(:config_policy) { 'targeted' }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:config_policy).and_return(config_policy)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:config_policy)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.config_policy', value: config_policy),
20 an_object_having_attributes(name: 'selinux_config_policy', value: config_policy, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::CurrentMode do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::CurrentMode.new }
5
6 let(:current_mode) { 'permissive' }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:current_mode).and_return(current_mode)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:current_mode)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.current_mode', value: current_mode),
20 an_object_having_attributes(name: 'selinux_current_mode', value: current_mode, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::Enabled do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::Enabled.new }
5
6 let(:enabled) { true }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:enabled).and_return(enabled)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:enabled)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.enabled', value: enabled),
20 an_object_having_attributes(name: 'selinux', value: enabled, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::Enforced do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::Enforced.new }
5
6 let(:enforced) { false }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:enforced).and_return(enforced)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:enforced)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.enforced', value: enforced),
20 an_object_having_attributes(name: 'selinux_enforced', value: enforced, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Os::Selinux::PolicyVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Os::Selinux::PolicyVersion.new }
5
6 let(:policy_version) { '31' }
7
8 before do
9 allow(Facter::Resolvers::SELinux).to receive(:resolve).with(:policy_version).and_return(policy_version)
10 end
11
12 it 'calls Facter::Resolvers::SELinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::SELinux).to have_received(:resolve).with(:policy_version)
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.selinux.policy_version', value: policy_version),
20 an_object_having_attributes(name: 'selinux_policyversion',
21 value: policy_version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Partitions do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Partitions.new }
5
6 let(:mountpoints_resolver_output) do
7 [{ available: '63.31 GiB',
8 available_bytes: 67_979_685_888,
9 capacity: '84.64%',
10 device: '/dev/sda1',
11 filesystem: 'ext4',
12 options: %w[rw noatime],
13 path: '/',
14 size: '434.42 GiB',
15 size_bytes: 466_449_743_872,
16 used: '348.97 GiB',
17 used_bytes: 374_704_357_376 }]
18 end
19 let(:partitions_resolver_output) do
20 { '/dev/sda1' => { 'filesystem' => 'ext3', 'label' => '/boot', 'size' => '203.89 KiB', 'size_bytes' => 208_782,
21 'uuid' => '88077904-4fd4-476f-9af2-0f7a806ca25e' } }
22 end
23
24 let(:final_result) do
25 { '/dev/sda1' => { 'filesystem' => 'ext3', 'label' => '/boot', 'size' => '203.89 KiB', 'size_bytes' => 208_782,
26 'uuid' => '88077904-4fd4-476f-9af2-0f7a806ca25e', 'mount' => '/' } }
27 end
28
29 context 'when resolver returns hash' do
30 before do
31 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints)
32 .and_return(mountpoints_resolver_output)
33 allow(Facter::Resolvers::Partitions).to receive(:resolve).with(:partitions)
34 .and_return(partitions_resolver_output)
35 end
36
37 it 'calls Facter::Resolvers::Mountpoints' do
38 fact.call_the_resolver
39 expect(Facter::Resolvers::Mountpoints).to have_received(:resolve).with(:mountpoints)
40 end
41
42 it 'calls Facter::Resolvers::Partitions' do
43 fact.call_the_resolver
44 expect(Facter::Resolvers::Partitions).to have_received(:resolve).with(:partitions)
45 end
46
47 it 'returns partitions information' do
48 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
49 have_attributes(name: 'partitions', value: final_result)
50 end
51 end
52
53 context 'when mountpoints resolver returns nil' do
54 before do
55 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(nil)
56 allow(Facter::Resolvers::Partitions).to receive(:resolve).with(:partitions)
57 .and_return(partitions_resolver_output)
58 end
59
60 it 'returns partition information without mount info' do
61 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
62 have_attributes(name: 'partitions', value: partitions_resolver_output)
63 end
64 end
65
66 context 'when partitions resolver returns empty hash' do
67 before do
68 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints)
69 .and_return(mountpoints_resolver_output)
70 allow(Facter::Resolvers::Partitions).to receive(:resolve).with(:partitions).and_return({})
71 end
72
73 it 'returns nil' do
74 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
75 have_attributes(name: 'partitions', value: nil)
76 end
77 end
78
79 context 'when the same device is mounted in multiple places' do
80 let(:mountpoints_resolver_output) do
81 [{
82 device: '/dev/sda2',
83 filesystem: 'btrfs',
84 path: '/',
85 options: ['rw', 'relatime', 'space_cache', 'subvolid=267', 'subvol=/@/.snapshots/1/snapshot'],
86 available: '10.74 GiB',
87 available_bytes: 11_534_614_528,
88 size: '13.09 GiB',
89 size_bytes: 14_050_918_400,
90 used: '1.96 GiB',
91 used_bytes: 2_101_231_616,
92 capacity: '15.41%'
93 }, {
94 device: '/dev/sda2',
95 filesystem: 'btrfs',
96 path: '/boot/grub2/x86_64-efi',
97 options: ['rw', 'relatime', 'space_cache', 'subvolid=264', 'subvol=/@/boot/grub2/x86_64-efi'],
98 available: '10.74 GiB',
99 available_bytes: 11_534_614_528,
100 size: '13.09 GiB',
101 size_bytes: 14_050_918_400,
102 used: '1.96 GiB',
103 used_bytes: 2_101_231_616,
104 capacity: '15.41%'
105 }]
106 end
107
108 let(:partitions_resolver_output) do
109 {
110 '/dev/sda2' => {
111 size_bytes: 14_050_918_400,
112 size: '13.09 GiB',
113 filesystem: 'btrfs',
114 uuid: 'bbc18fba-8191-48c8-b8bd-30373654bb3e',
115 partuuid: 'c96cd2ea-1046-461c-b0fe-1e5aa19aba61'
116 }
117 }
118 end
119
120 let(:final_result) do
121 {
122 '/dev/sda2' => {
123 'size_bytes' => 14_050_918_400,
124 'size' => '13.09 GiB',
125 'filesystem' => 'btrfs',
126 'uuid' => 'bbc18fba-8191-48c8-b8bd-30373654bb3e',
127 'partuuid' => 'c96cd2ea-1046-461c-b0fe-1e5aa19aba61',
128 'mount' => '/'
129 }
130 }
131 end
132
133 before do
134 allow(Facter::Resolvers::Mountpoints).to receive(:resolve).with(:mountpoints)
135 .and_return(mountpoints_resolver_output)
136 allow(Facter::Resolvers::Partitions).to receive(:resolve).with(:partitions)
137 .and_return(partitions_resolver_output)
138 end
139
140 it 'calls Facter::Resolvers::Mountpoints' do
141 fact.call_the_resolver
142 expect(Facter::Resolvers::Mountpoints).to have_received(:resolve).with(:mountpoints)
143 end
144
145 it 'calls Facter::Resolvers::Partitions' do
146 fact.call_the_resolver
147 expect(Facter::Resolvers::Partitions).to have_received(:resolve).with(:partitions)
148 end
149
150 it 'returns partitions information from the first mountpoint' do
151 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
152 have_attributes(name: 'partitions', value: final_result)
153 end
154 end
155 end
156 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Path.new }
5
6 let(:value) { '/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java6/jre/bin:/usr/java6/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processor.new }
5
6 let(:processor) { ['Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz', 'Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz'] }
7
8 before do
9 allow(Facter::Resolvers::Linux::Processors).to receive(:resolve).with(:models).and_return(processor)
10 end
11
12 it 'calls Facter::Resolvers::Linux::Processors' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Processors).to have_received(:resolve).with(:models)
15 end
16
17 it 'returns legacy facts about each processor' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processor0', value: processor[0], type: :legacy),
20 an_object_having_attributes(name: 'processor1', value: processor[1], type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Cores do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Cores.new }
5
6 let(:cores_per_socket) { 4 }
7
8 before do
9 allow(Facter::Resolvers::Linux::Lscpu).to \
10 receive(:resolve).with(:cores_per_socket).and_return(cores_per_socket)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Lscpu' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Lscpu).to have_received(:resolve).with(:cores_per_socket)
16 end
17
18 it 'returns processors core fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.cores', value: cores_per_socket)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Count.new }
5
6 let(:processors_count) { '4' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Processors).to \
10 receive(:resolve).with(:processors).and_return(processors_count)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Processors).to have_received(:resolve).with(:processors)
16 end
17
18 it 'returns processors count fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors_count),
21 an_object_having_attributes(name: 'processorcount', value: processors_count, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Isa.new }
5
6 let(:processors_arch) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(processors_arch)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:processor)
16 end
17
18 it 'returns processors isa fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: processors_arch),
21 an_object_having_attributes(name: 'hardwareisa', value: processors_arch, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Models.new }
5
6 let(:models) { ['Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz', 'Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz'] }
7
8 before do
9 allow(Facter::Resolvers::Linux::Processors).to \
10 receive(:resolve).with(:models).and_return(models)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Processors).to have_received(:resolve).with(:models)
16 end
17
18 it 'returns processors models fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.models', value: models)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Physicalcount do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Physicalcount.new }
5
6 let(:physical_count) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Linux::Processors).to \
10 receive(:resolve).with(:physical_count).and_return(physical_count)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Processors).to have_received(:resolve).with(:physical_count)
16 end
17
18 it 'returns processors physical count fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.physicalcount', value: physical_count),
21 an_object_having_attributes(name: 'physicalprocessorcount',
22 value: physical_count,
23 type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Linux::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Linux::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Linux::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Processors::Threads do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Processors::Threads.new }
5
6 let(:threads_per_core) { 2 }
7
8 before do
9 allow(Facter::Resolvers::Linux::Lscpu).to \
10 receive(:resolve).with(:threads_per_core).and_return(threads_per_core)
11 end
12
13 it 'calls Facter::Resolvers::Linux::Lscpu' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Linux::Lscpu).to have_received(:resolve).with(:threads_per_core)
16 end
17
18 it 'returns processors core fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.threads', value: threads_per_core)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ruby::Platform.new }
5
6 let(:value) { 'x86_64-linux' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:platform)
15 end
16
17 it 'returns ruby platform fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
20 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ruby::Sitedir.new }
5
6 let(:value) { '/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.6.3' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
10 end
11
12 it 'call Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:sitedir)
15 end
16
17 it 'returns ruby site dir fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
20 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ruby::Version.new }
5
6 let(:value) { '2.4.5' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
21 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Scope6Interfaces do
3 subject(:fact) { Facts::Linux::Scope6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Linux::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { scope6: 'link' }, 'en1' => { scope6: 'global' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux with interfaces' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Linux::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with scope6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'scope6_eth0',
20 value: interfaces['eth0'][:scope6], type: :legacy),
21 an_object_having_attributes(name: 'scope6_en1',
22 value: interfaces['en1'][:scope6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28 let(:scope6) { nil }
29
30 it 'returns nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Ssh.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
9 end
10 let(:value) do
11 { 'ecdsa' => { 'fingerprints' =>
12 { 'sha1' => 'test', 'sha256' => 'test' },
13 'key' => 'test',
14 'type' => 'ecdsa' } }
15 end
16
17 before do
18 allow(Facter::Resolvers::Ssh).to \
19 receive(:resolve).with(:ssh).and_return(ssh)
20 end
21
22 it 'calls Facter::Resolvers::Ssh' do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
25 end
26
27 it 'returns ssh fact' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
29 have_attributes(name: 'ssh', value: value)
30 end
31
32 context 'when resolver returns empty array' do
33 let(:ssh) { [] }
34
35 it 'returns nil fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'ssh', value: nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Sshalgorithmkey.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('test', 'test'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
13 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::SshfpAlgorithm.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
13 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::SystemUptime::Days.new }
5
6 let(:value) { '2' }
7
8 context 'when on linux' do
9 before do
10 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return(nil)
11 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:days).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Uptime' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:days)
17 end
18
19 it 'returns days since last boot' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: value),
22 an_object_having_attributes(name: 'uptime_days', value: value, type: :legacy))
23 end
24 end
25
26 context 'when in docker container' do
27 before do
28 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return({ docker: '123' })
29 allow(Facter::Resolvers::Linux::DockerUptime).to receive(:resolve).with(:days).and_return(value)
30 end
31
32 it 'calls Facter::Resolvers::Uptime' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Linux::DockerUptime).to have_received(:resolve).with(:days)
35 end
36
37 it 'returns days since last boot' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
39 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: value),
40 an_object_having_attributes(name: 'uptime_days', value: value, type: :legacy))
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::SystemUptime::Hours.new }
5
6 let(:value) { '2' }
7
8 context 'when on linux' do
9 before do
10 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return(nil)
11 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:hours).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Uptime' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:hours)
17 end
18
19 it 'returns hours since last boot' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: value),
22 an_object_having_attributes(name: 'uptime_hours', value: value, type: :legacy))
23 end
24 end
25
26 context 'when in docker container' do
27 before do
28 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return({ docker: '123' })
29 allow(Facter::Resolvers::Linux::DockerUptime).to receive(:resolve).with(:hours).and_return(value)
30 end
31
32 it 'calls Facter::Resolvers::Uptime' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Linux::DockerUptime).to have_received(:resolve).with(:hours)
35 end
36
37 it 'returns hours since last boot' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
39 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: value),
40 an_object_having_attributes(name: 'uptime_hours', value: value, type: :legacy))
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::SystemUptime::Seconds.new }
5
6 let(:value) { 3600 }
7
8 context 'when on linux' do
9 before do
10 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return(nil)
11 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:seconds).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Uptime' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:seconds)
17 end
18
19 it 'returns minutes since last boot' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: value),
22 an_object_having_attributes(name: 'uptime_seconds', value: value, type: :legacy))
23 end
24 end
25
26 context 'when in docker container' do
27 before do
28 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return({ docker: '123' })
29 allow(Facter::Resolvers::Linux::DockerUptime).to receive(:resolve).with(:seconds).and_return(value)
30 end
31
32 it 'calls Facter::Resolvers::Uptime' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Linux::DockerUptime).to have_received(:resolve).with(:seconds)
35 end
36
37 it 'returns minutes since last boot' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
39 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: value),
40 an_object_having_attributes(name: 'uptime_seconds', value: value, type: :legacy))
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::SystemUptime::Uptime.new }
5
6 let(:value) { '6 days' }
7
8 context 'when on linux' do
9 before do
10 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return(nil)
11 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:uptime).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Uptime' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:uptime)
17 end
18
19 it 'returns total uptime since last boot' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: value),
22 an_object_having_attributes(name: 'uptime', value: value, type: :legacy))
23 end
24 end
25
26 context 'when in docker container' do
27 before do
28 allow(Facter::Resolvers::Containers).to receive(:resolve) .with(:hypervisor).and_return({ docker: '123' })
29 allow(Facter::Resolvers::Linux::DockerUptime).to receive(:resolve).with(:uptime).and_return(value)
30 end
31
32 it 'calls Facter::Resolvers::DockerUptime' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Linux::DockerUptime).to have_received(:resolve).with(:uptime)
35 end
36
37 it 'returns total uptime since last boot' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
39 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: value),
40 an_object_having_attributes(name: 'uptime', value: value, type: :legacy))
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns timezone fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Virtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Virtual.new }
5
6 let(:virtual_detector_double) { class_spy(Facter::Util::Facts::Posix::VirtualDetector) }
7 let(:log_spy) { instance_spy(Facter::Log) }
8
9 before do
10 allow(Facter::Log).to receive(:new).and_return(log_spy)
11 allow(Facter::Util::Facts::Posix::VirtualDetector).to receive(:platform).and_return(value)
12 end
13
14 shared_examples 'check resolved fact value' do
15 it 'return resolved fact with nil value' do
16 expect(fact.call_the_resolver)
17 .to be_an_instance_of(Facter::ResolvedFact)
18 .and have_attributes(name: 'virtual', value: value)
19 end
20 end
21
22 context 'when not in a virtual environment' do
23 let(:value) { 'physical' }
24
25 it_behaves_like 'check resolved fact value'
26 end
27
28 context 'when in a virtual environment' do
29 let(:value) { 'aws' }
30
31 it_behaves_like 'check resolved fact value'
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Linux::Xen do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linux::Xen.new }
5
6 before do
7 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(vm)
8 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:domains).and_return(domains)
9 end
10
11 context 'when is xen privileged' do
12 let(:vm) { 'xen0' }
13 let(:domains) { %w[domain1 domain2] }
14
15 it 'calls Facter::Resolvers::VirtWhat' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
18 end
19
20 it 'calls Facter::Resolvers::Xen with :domains' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
23 end
24
25 it 'returns xen fact' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => domains }),
28 an_object_having_attributes(name: 'xendomains',
29 value: domains.entries.join(','), type: :legacy))
30 end
31 end
32
33 context 'when is xen privileged but there are no domains' do
34 let(:vm) { nil }
35 let(:domains) { [] }
36
37 before do
38 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('xen0')
39 end
40
41 it 'calls Facter::Resolvers::VirtWhat' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
44 end
45
46 it 'calls Facter::Resolvers::Xen with :vm' do
47 fact.call_the_resolver
48 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:vm)
49 end
50
51 it 'calls Facter::Resolvers::Xen with :domains' do
52 fact.call_the_resolver
53 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
54 end
55
56 it 'returns xen fact' do
57 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
58 contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => domains }),
59 an_object_having_attributes(name: 'xendomains',
60 value: domains.entries.join(','), type: :legacy))
61 end
62 end
63
64 context 'when is xen privileged but domains are nil' do
65 let(:vm) { nil }
66 let(:domains) { nil }
67 let(:result) { [] }
68
69 before do
70 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('xen0')
71 end
72
73 it 'calls Facter::Resolvers::VirtWhat' do
74 fact.call_the_resolver
75 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
76 end
77
78 it 'calls Facter::Resolvers::Xen with :vm' do
79 fact.call_the_resolver
80 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:vm)
81 end
82
83 it 'calls Facter::Resolvers::Xen with :domains' do
84 fact.call_the_resolver
85 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
86 end
87
88 it 'returns xen fact' do
89 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
90 contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => result }),
91 an_object_having_attributes(name: 'xendomains',
92 value: result.entries.join(','), type: :legacy))
93 end
94 end
95
96 context 'when is xen unprivileged' do
97 let(:vm) { 'xenhvm' }
98 let(:domains) { nil }
99
100 it 'calls Facter::Resolvers::VirtWhat' do
101 fact.call_the_resolver
102 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
103 end
104
105 it 'does not call Facter::Resolvers::Xen with :domains' do
106 fact.call_the_resolver
107 expect(Facter::Resolvers::Xen).not_to have_received(:resolve).with(:domains)
108 end
109
110 it 'returns nil fact' do
111 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
112 have_attributes(name: 'xen', value: domains)
113 end
114 end
115 end
116 end
0 # frozen_string_literal: true
1
2 describe Facts::Linuxmint::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linuxmint::Os::Name.new }
5
6 let(:value) { 'linuxmint' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id)
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value.capitalize),
20 an_object_having_attributes(name: 'operatingsystem', value: value.capitalize, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Linuxmint::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Linuxmint::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/linuxmint/info',
9 regex: /^RELEASE=(\d+)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /^RELEASE=(\d+)/.match('RELEASE=19.4') }
15 let(:release) { { 'full' => '19', 'major' => '19' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/linuxmint/info',
22 regex: /^RELEASE=(\d+)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '19.4' }
39 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::AioAgentVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::AioAgentVersion.new }
5
6 let(:value) { '1.2.3' }
7
8 before do
9 allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Agent' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version)
15 end
16
17 it 'returns aio_agent_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'aio_agent_version', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::DhcpServers do
3 subject(:fact) { Facts::Macosx::DhcpServers.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:dhcp).and_return(dhcp)
8 end
9
10 describe '#call_the_resolver' do
11 let(:value) { { 'system' => '10.16.122.163', 'eth0' => '10.16.122.163', 'en1' => '10.32.10.163' } }
12 let(:interfaces) { { 'eth0' => { dhcp: '10.16.122.163' }, 'en1' => { dhcp: '10.32.10.163' } } }
13 let(:dhcp) { '10.16.122.163' }
14
15 it 'calls Facter::Resolvers::NetworkingLinux with interfaces' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'calls Facter::Resolvers::NetworkingLinux with dhcp' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:dhcp)
23 end
24
25 it 'returns dhcp_servers' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
27 have_attributes(name: 'dhcp_servers', value: value, type: :legacy)
28 end
29 end
30
31 describe '#call_the_resolver when resolver returns nil' do
32 let(:interfaces) { nil }
33 let(:dhcp) { nil }
34
35 it 'returns nil' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'dhcp_servers', value: nil, type: :legacy)
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Dmi::Product::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Dmi::Product::Name.new }
5
6 let(:value) { 'MacBookPro11,4' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::DmiBios).to receive(:resolve).with(:macosx_model).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::DmiBios' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::DmiBios).to have_received(:resolve).with(:macosx_model)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: value),
20 an_object_having_attributes(name: 'productname', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Filesystems do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Filesystems.new }
5
6 let(:value) { 'apfs,autofs,devfs' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::Filesystems).to receive(:resolve).with(:macosx_filesystems).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::Filesystems' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::Filesystems).to have_received(:resolve).with(:macosx_filesystems)
15 end
16
17 it 'returns filesystems fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'filesystems', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Identity::Gid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Identity::Gid.new }
5
6 let(:value) { '20' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:gid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:gid)
15 end
16
17 it 'returns identity gid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.gid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Identity::Group do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Identity::Group.new }
5
6 let(:value) { 'staff' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:group).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:group)
15 end
16
17 it 'returns identity group fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.group', value: value),
20 an_object_having_attributes(name: 'gid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Identity::Privileged do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Identity::Privileged.new }
5
6 let(:value) { 'false' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:privileged).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:privileged)
15 end
16
17 it 'returns identity privileged fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.privileged', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Identity::Uid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Identity::Uid.new }
5
6 let(:value) { '501' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:uid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:uid)
15 end
16
17 it 'returns identity uid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.uid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Identity::User.new }
5
6 let(:value) { 'testUser' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:user)
15 end
16
17 it 'returns id and identity user fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Interfaces do
3 subject(:fact) { Facts::Macosx::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { en1: { ip: '192.168.1.6' }, llw0: { ip: '192.168.1.3' } } }
11 let(:interfaces_names) { 'en1,llw0' }
12
13 it 'calls Facter::Resolvers::Networking' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
16 end
17
18 it 'returns interfaces names' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'interfaces', value: interfaces_names, type: :legacy)
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:interfaces) { nil }
26 let(:interfaces_names) { nil }
27
28 it 'returns nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
30 have_attributes(name: 'interfaces', value: interfaces_names, type: :legacy)
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Macosx::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::IpaddressInterfaces do
3 subject(:fact) { Facts::Macosx::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::IsVirtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::IsVirtual.new }
5
6 before do
7 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
8 .with(:model_identifier)
9 .and_return('MacBookPro11,4')
10
11 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
12 .with(:boot_rom_version)
13 .and_return('1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)')
14
15 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
16 .with(:subsystem_vendor_id)
17 .and_return('0x123')
18 end
19
20 context 'when on physical machine' do
21 it 'calls Facter::Resolvers::Macosx::SystemProfile with model_identifier' do
22 fact.call_the_resolver
23
24 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:model_identifier)
25 end
26
27 it 'calls Facter::Resolvers::Macosx::SystemProfile with boot_rom_version' do
28 fact.call_the_resolver
29
30 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:boot_rom_version)
31 end
32
33 it 'calls Facter::Resolvers::Macosx::SystemProfile with subsystem_vendor_id' do
34 fact.call_the_resolver
35
36 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:subsystem_vendor_id)
37 end
38
39 it 'returns resolved fact with false value' do
40 expect(fact.call_the_resolver)
41 .to be_an_instance_of(Facter::ResolvedFact)
42 .and have_attributes(name: 'is_virtual', value: false)
43 end
44 end
45
46 context 'when on virtual machine' do
47 context 'with hypervisor vmware' do
48 before do
49 allow(Facter::Resolvers::Macosx::SystemProfiler)
50 .to receive(:resolve)
51 .with(:model_identifier)
52 .and_return('VMware')
53 end
54
55 it 'returns resolved fact with true value' do
56 expect(fact.call_the_resolver)
57 .to be_an_instance_of(Facter::ResolvedFact)
58 .and have_attributes(name: 'is_virtual', value: true)
59 end
60 end
61
62 context 'when hypervisor VirtualBox' do
63 before do
64 allow(Facter::Resolvers::Macosx::SystemProfiler)
65 .to receive(:resolve)
66 .with(:boot_rom_version)
67 .and_return('VirtualBox')
68 end
69
70 it 'returns resolved fact with true value' do
71 expect(fact.call_the_resolver)
72 .to be_an_instance_of(Facter::ResolvedFact)
73 .and have_attributes(name: 'is_virtual', value: true)
74 end
75 end
76
77 context 'when hypervisor Parallels' do
78 before do
79 allow(Facter::Resolvers::Macosx::SystemProfiler)
80 .to receive(:resolve)
81 .with(:subsystem_vendor_id)
82 .and_return('0x1ab8')
83 end
84
85 it 'returns resolved fact with true value' do
86 expect(fact.call_the_resolver)
87 .to be_an_instance_of(Facter::ResolvedFact)
88 .and have_attributes(name: 'is_virtual', value: true)
89 end
90 end
91 end
92 end
93 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Kernel.new }
5
6 let(:value) { 'Darwin' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Kernelmajversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Kernelmajversion.new }
5
6 let(:resolver_result) { '18.7.0' }
7 let(:fact_value) { '18.7' }
8
9 before do
10 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
16 end
17
18 it 'returns a kernelmajversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelmajversion', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Kernelrelease.new }
5
6 let(:value) { '18.7.0' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
15 end
16
17 it 'returns kernelrelease fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelrelease', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Kernelversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Kernelversion.new }
5
6 let(:value) { '18.7.0' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
15 end
16
17 it 'returns kernelversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::LoadAverages do
3 subject(:fact) { Facts::Macosx::LoadAverages.new }
4
5 let(:averages) do
6 {
7 '15m' => 0.0,
8 '10m' => 0.0,
9 '5m' => 0.0
10 }
11 end
12
13 describe '#call_the_resolver' do
14 before do
15 allow(Facter::Resolvers::Macosx::LoadAverages).to receive(:resolve).with(:load_averages).and_return(averages)
16 end
17
18 it 'calls Facter::Resolvers::Macosx::LoadAverages' do
19 fact.call_the_resolver
20 expect(Facter::Resolvers::Macosx::LoadAverages).to have_received(:resolve).with(:load_averages)
21 end
22
23 it 'returns resolved fact with name disk and value' do
24 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact)
25 .and have_attributes(name: 'load_averages', value: averages)
26 end
27 end
28 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::MacaddressInterfaces do
3 subject(:fact) { Facts::Macosx::MacaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mac: '10.16.117.100' }, 'en1' => { mac: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names macaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'macaddress_eth0',
20 value: interfaces['eth0'][:mac], type: :legacy),
21 an_object_having_attributes(name: 'macaddress_en1',
22 value: interfaces['en1'][:mac], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::AvailableBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:available_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:available_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
21 an_object_having_attributes(name: 'swapfree_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::Available.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:available_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:available_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.available', value: value),
20 an_object_having_attributes(name: 'swapfree', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::Capacity do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.swap.capacity', value: 1024)
6
7 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:capacity).and_return(1024)
8 allow(Facter::ResolvedFact).to receive(:new).with('memory.swap.capacity', 1024).and_return(expected_fact)
9
10 fact = Facts::Macosx::Memory::Swap::Capacity.new
11 expect(fact.call_the_resolver).to eq(expected_fact)
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::Encrypted do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::Encrypted.new }
5
6 let(:value) { true }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:encrypted).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:encrypted)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.encrypted', value: value),
20 an_object_having_attributes(name: 'swapencrypted', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::TotalBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:total_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:total_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
21 an_object_having_attributes(name: 'swapsize_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::Total.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:total_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:total_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.swap.total', value: value),
20 an_object_having_attributes(name: 'swapsize', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::UsedBytes do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.swap.used_bytes', value: 1024)
6
7 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:used_bytes).and_return(1024)
8 allow(Facter::ResolvedFact).to receive(:new).with('memory.swap.used_bytes', 1024).and_return(expected_fact)
9
10 fact = Facts::Macosx::Memory::Swap::UsedBytes.new
11 expect(fact.call_the_resolver).to eq(expected_fact)
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::Swap::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::Swap::Used.new }
5
6 let(:resolver_result) { 1024 }
7 let(:fact_value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SwapMemory).to receive(:resolve).with(:used_bytes).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SwapMemory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns a memory.swap.used fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.swap.used', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::System::AvailableBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:available_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemMemory).to have_received(:resolve).with(:available_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
21 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::System::Available.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:available_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::SystemMemory).to have_received(:resolve).with(:available_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
20 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.system.capacity', value: '15.53%')
6
7 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:capacity).and_return('15.53%')
8 allow(Facter::ResolvedFact).to receive(:new).with('memory.system.capacity', '15.53%').and_return(expected_fact)
9
10 fact = Facts::Macosx::Memory::System::Capacity.new
11 expect(fact.call_the_resolver).to eq(expected_fact)
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::System::TotalBytes.new }
5
6 let(:value) { 1024 * 1024 }
7 let(:value_mb) { 1 }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:total_bytes).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemMemory).to have_received(:resolve).with(:total_bytes)
16 end
17
18 it 'returns a fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
21 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::System::Total.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:total_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::SystemMemory' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Macosx::SystemMemory).to have_received(:resolve).with(:total_bytes)
15 end
16
17 it 'returns a fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
20 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.system.used_bytes', value: 1024)
6
7 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:used_bytes).and_return(1024)
8 allow(Facter::ResolvedFact).to receive(:new).with('memory.system.used_bytes', 1024).and_return(expected_fact)
9
10 fact = Facts::Macosx::Memory::System::UsedBytes.new
11 expect(fact.call_the_resolver).to eq(expected_fact)
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Memory::System::Used.new }
5
6 let(:resolver_result) { 1024 }
7 let(:fact_value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Macosx::SystemMemory).to receive(:resolve).with(:used_bytes).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemMemory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns a memory.system.used fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.system.used', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Mountpoints do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Mountpoints.new }
5
6 context 'when resolver returns hash' do
7 let(:resolver_output) do
8 { '/': { available: '63.31 GiB',
9 available_bytes: 67_979_685_888,
10 capacity: '84.64%',
11 device: '/dev/nvme0n1p2',
12 filesystem: 'ext4',
13 options: %w[rw noatime],
14 size: '434.42 GiB',
15 size_bytes: 466_449_743_872,
16 used: '348.97 GiB',
17 used_bytes: 374_704_357_376 } }
18 end
19
20 let(:parsed_fact) do
21 { '/' => { 'available' => '63.31 GiB',
22 'available_bytes' => 67_979_685_888,
23 'capacity' => '84.64%',
24 'device' => '/dev/nvme0n1p2',
25 'filesystem' => 'ext4',
26 'options' => %w[rw noatime],
27 'size' => '434.42 GiB',
28 'size_bytes' => 466_449_743_872,
29 'used' => '348.97 GiB',
30 'used_bytes' => 374_704_357_376 } }
31 end
32
33 before do
34 allow(Facter::Resolvers::Macosx::Mountpoints).to \
35 receive(:resolve).with(:mountpoints).and_return(resolver_output)
36 end
37
38 it 'calls Facter::Resolvers::Macosx::Mountpoints' do
39 expect(Facter::Resolvers::Macosx::Mountpoints).to receive(:resolve).with(:mountpoints)
40 fact.call_the_resolver
41 end
42
43 it 'returns mountpoints information' do
44 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
45 have_attributes(name: 'mountpoints', value: parsed_fact)
46 end
47 end
48
49 context 'when resolver returns nil' do
50 before do
51 allow(Facter::Resolvers::Macosx::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(nil)
52 end
53
54 it 'returns mountpoints information' do
55 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
56 have_attributes(name: 'mountpoints', value: nil)
57 end
58 end
59 end
60 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::MtuInterfaces do
3 subject(:fact) { Facts::Macosx::MtuInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mtu: 1500 }, 'en1' => { mtu: 1500 } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names mtu_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'mtu_eth0', value: interfaces['eth0'][:mtu], type: :legacy),
20 an_object_having_attributes(name: 'mtu_en1', value: interfaces['en1'][:mtu], type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:interfaces) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Netmask6Interfaces do
3 subject(:fact) { Facts::Macosx::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::NetworkingLinux' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::NetmaskInterfaces do
3 subject(:fact) { Facts::Macosx::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Network6Interfaces do
3 subject(:fact) { Facts::Macosx::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::NetworkInterfaces do
3 subject(:fact) { Facts::Macosx::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Dhcp do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Dhcp.new }
5
6 let(:value) { '192.168.158.6' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:dhcp).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:dhcp)
15 end
16
17 it 'returns networking.dhcp fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
20 end
21
22 context 'when dhcp can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Domain.new }
5
6 let(:value) { 'domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22
23 context 'when domain can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
29 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Fqdn.new }
5
6 let(:value) { 'host.domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:fqdn).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:fqdn)
15 end
16
17 it 'returns fqdn fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
20 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
21 end
22
23 context 'when fqdn can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
29 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Hostname.new }
5
6 let(:value) { 'host' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22
23 context 'when hostname can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
29 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Interfaces do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Interfaces.new }
5
6 let(:interfaces) do
7 {
8 'awdl0' =>
9 { mtu: 1484,
10 mac: '2e:ba:e4:83:4b:b7',
11 bindings6:
12 [{ address: 'fe80::2cba:e4ff:fe83:4bb7',
13 netmask: 'ffff:ffff:ffff:ffff::',
14 network: 'fe80::' }],
15 ip6: 'fe80::2cba:e4ff:fe83:4bb7',
16 netmask6: 'ffff:ffff:ffff:ffff::',
17 network6: 'fe80::' },
18 'bridge0' => { mtu: 1500, mac: '82:17:0e:93:9d:00' },
19 'en0' =>
20 { dhcp: '192.587.6.9',
21 mtu: 1500,
22 mac: '64:5a:ed:ea:5c:81',
23 bindings:
24 [{ address: '192.168.1.2',
25 netmask: '255.255.255.0',
26 network: '192.168.1.0' }],
27 ip: '192.168.1.2',
28 netmask: '255.255.255.0',
29 network: '192.168.1.0' },
30 'gif0' => { mtu: 1280 },
31 'lo0' =>
32 { mtu: 16_384,
33 bindings:
34 [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
35 bindings6:
36 [{ address: '::1',
37 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
38 network: '::1' },
39 { address: 'fe80::1',
40 netmask: 'ffff:ffff:ffff:ffff::',
41 network: 'fe80::' }],
42 ip: '127.0.0.1',
43 netmask: '255.0.0.0',
44 network: '127.0.0.0',
45 ip6: '::1',
46 netmask6: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
47 network6: '::1' }
48 }
49 end
50
51 before do
52 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
53 end
54
55 it 'calls Facter::Resolvers::Networking with interfaces' do
56 fact.call_the_resolver
57 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
58 end
59
60 it 'returns networking.interfaces fact' do
61 expect(fact.call_the_resolver)
62 .to be_an_instance_of(Facter::ResolvedFact)
63 .and have_attributes(name: 'networking.interfaces', value: anything)
64 end
65
66 it 'returns all interfaces' do
67 interfaces = %w[awdl0 bridge0 en0 gif0 lo0]
68
69 result = fact.call_the_resolver
70
71 expect(result.value).to include(*interfaces)
72 end
73
74 it 'returns the interface awdl0 correctly' do
75 expected = { 'mtu' => 1484,
76 'mac' => '2e:ba:e4:83:4b:b7',
77 'bindings6' =>
78 [{ 'address' => 'fe80::2cba:e4ff:fe83:4bb7',
79 'netmask' => 'ffff:ffff:ffff:ffff::',
80 'network' => 'fe80::' }],
81 'ip6' => 'fe80::2cba:e4ff:fe83:4bb7',
82 'netmask6' => 'ffff:ffff:ffff:ffff::',
83 'network6' => 'fe80::' }
84
85 result = fact.call_the_resolver
86
87 expect(result.value['awdl0']).to match(expected)
88 end
89
90 it 'returns the interface bridge0 correctly' do
91 result = fact.call_the_resolver
92
93 expect(result.value['bridge0']).to match({ 'mtu' => 1500, 'mac' => '82:17:0e:93:9d:00' })
94 end
95
96 it 'returns the interface en0 correctly' do
97 expected = { 'mtu' => 1500,
98 'mac' => '64:5a:ed:ea:5c:81',
99 'bindings' =>
100 [{ 'address' => '192.168.1.2',
101 'netmask' => '255.255.255.0',
102 'network' => '192.168.1.0' }],
103 'ip' => '192.168.1.2',
104 'netmask' => '255.255.255.0',
105 'network' => '192.168.1.0',
106 'dhcp' => '192.587.6.9' }
107
108 result = fact.call_the_resolver
109
110 expect(result.value['en0']).to match(expected)
111 end
112
113 it 'returns the interface gif0 correctly' do
114 result = fact.call_the_resolver
115
116 expect(result.value['gif0']).to match({ 'mtu' => 1280 })
117 end
118
119 context 'when interfaces can not be retrieved' do
120 let(:interfaces) { nil }
121
122 it 'returns nil' do
123 expect(fact.call_the_resolver)
124 .to be_an_instance_of(Facter::ResolvedFact)
125 .and have_attributes(name: 'networking.interfaces', value: interfaces)
126 end
127 end
128 end
129 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Ip6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Ip6.new }
5
6 let(:value) { 'fe80::2cba:e4ff:fe83:4bb7' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:ip6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :ip6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns the ip6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
20 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
21 end
22
23 context 'when ip6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
29 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Ip.new }
5
6 let(:value) { '10.0.0.1' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :ip' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:ip)
15 end
16
17 it 'returns the ip6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22
23 context 'when ip can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
29 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Mac do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Mac.new }
5
6 let(:value) { '64:5a:ed:ea:c3:25' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:mac).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :mac' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'returns macaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22
23 context 'when mac can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
29 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Mtu do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Mtu.new }
5
6 let(:value) { 1500 }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:mtu).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :mtu' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:mtu)
15 end
16
17 it 'returns mtu fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
20 end
21
22 context 'when mtu can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Netmask6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Netmask6.new }
5
6 let(:value) { 'ffff:ffff:ffff:ffff::' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:netmask6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :netmask6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns the netmask6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22
23 context 'when netmask6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
29 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Netmask do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Netmask.new }
5
6 let(:value) { '255.255.255.0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:netmask).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :netmask' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns the netmask fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22
23 context 'when netmask can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
29 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Network6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Network6.new }
5
6 let(:value) { 'ff80:3454::' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:network6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :network6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns the network6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22
23 context 'when network6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
29 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Network do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Network.new }
5
6 let(:value) { '192.168.143.0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:network).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :network' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns the network fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22
23 context 'when network can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
29 an_object_having_attributes(name: 'network', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Primary.new }
5
6 let(:value) { 'en0' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with :primary_interface' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns networking.primary fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
20 end
21
22 context 'when primary interface can not be retrieved' do
23 let(:value) { nil }
24
25 it 'returns nil' do
26 expect(fact.call_the_resolver)
27 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
28 end
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Networking::Scope6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Networking::Scope6.new }
5
6 let(:value) { 'link' }
7
8 before do
9 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:scope6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Networking with scope6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:scope6)
15 end
16
17 it 'returns scope6 fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
21 an_object_having_attributes(name: 'scope6', value: value))
22 end
23
24 context 'when scope6 can not be resolved' do
25 let(:value) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver)
29 .to be_an_instance_of(Array)
30 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
31 an_object_having_attributes(name: 'scope6', value: value))
32 end
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Architecture.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:machine)
14 fact.call_the_resolver
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
20 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Family.new }
5
6 let(:value) { 'Darwin' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname)
14 fact.call_the_resolver
15 end
16
17 it 'returns os family fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
20 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Hardware.new }
5
6 let(:value) { 'value' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
15 end
16
17 it 'returns augeas fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Macosx::Build do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Macosx::Build.new }
5
6 let(:version) { '10.9.8' }
7
8 before do
9 allow(Facter::Resolvers::SwVers).to \
10 receive(:resolve).with(:buildversion).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::SwVers' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::SwVers).to have_received(:resolve).with(:buildversion)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.macosx.build', value: version),
21 an_object_having_attributes(name: 'macosx_buildversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Macosx::Product do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Macosx::Product.new }
5
6 let(:product) { 'Mac OS X' }
7
8 before do
9 allow(Facter::Resolvers::SwVers).to \
10 receive(:resolve).with(:productname).and_return(product)
11 end
12
13 it 'calls Facter::Resolvers::SwVers' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::SwVers).to have_received(:resolve).with(:productname)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'os.macosx.product', value: product),
21 an_object_having_attributes(name: 'macosx_productname', value: product, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Macosx::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Macosx::Version.new }
5
6 context 'when macOS version < 11' do
7 let(:resolver_output) { '10.9.8' }
8 let(:version) { { 'full' => '10.9.8', 'major' => '10.9', 'minor' => '8' } }
9
10 before do
11 allow(Facter::Resolvers::SwVers).to \
12 receive(:resolve).with(:productversion).and_return(resolver_output)
13 end
14
15 it 'calls Facter::Resolvers::SwVers' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::SwVers).to have_received(:resolve).with(:productversion)
18 end
19
20 it 'returns a resolved fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'os.macosx.version', value: version),
23 an_object_having_attributes(name: 'macosx_productversion', value: resolver_output,
24 type: :legacy),
25 an_object_having_attributes(name: 'macosx_productversion_major', value: version['major'],
26 type: :legacy),
27 an_object_having_attributes(name: 'macosx_productversion_minor', value: version['minor'],
28 type: :legacy),
29 an_object_having_attributes(name: 'macosx_productversion_patch', value: version['patch'],
30 type: :legacy))
31 end
32
33 context 'when macOS version >= 11' do
34 let(:resolver_output) { '11.2.1' }
35 let(:version) { { 'full' => '11.2.1', 'major' => '11', 'minor' => '2', 'patch' => '1' } }
36
37 before do
38 allow(Facter::Resolvers::SwVers).to \
39 receive(:resolve).with(:productversion).and_return(resolver_output)
40 end
41
42 it 'calls Facter::Resolvers::SwVers' do
43 fact.call_the_resolver
44 expect(Facter::Resolvers::SwVers).to have_received(:resolve).with(:productversion)
45 end
46
47 it 'returns a resolved fact' do
48 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
49 contain_exactly(an_object_having_attributes(name: 'os.macosx.version', value: version),
50 an_object_having_attributes(name: 'macosx_productversion', value: resolver_output,
51 type: :legacy),
52 an_object_having_attributes(name: 'macosx_productversion_major', value: version['major'],
53 type: :legacy),
54 an_object_having_attributes(name: 'macosx_productversion_minor', value: version['minor'],
55 type: :legacy),
56 an_object_having_attributes(name: 'macosx_productversion_patch', value: version['patch'],
57 type: :legacy))
58 end
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Os::Name.new }
5
6 let(:value) { 'Darwin' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname)
14 fact.call_the_resolver
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 # fozen_string_literal: true
3
4 describe Facts::Macosx::Os::Release do
5 describe '#call_the_resolver' do
6 subject(:fact) { Facts::Macosx::Os::Release.new }
7
8 let(:value) { '10.9' }
9 let(:value_final) { { 'full' => '10.9', 'major' => '10', 'minor' => '9' } }
10
11 before do
12 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return('10.9')
13 end
14
15 it 'calls Facter::Resolvers::LsbRelease' do
16 fact.call_the_resolver
17
18 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
19 end
20
21 it 'returns release fact' do
22 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
23 contain_exactly(an_object_having_attributes(name: 'os.release', value: value_final),
24 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value_final['major'],
25 type: :legacy),
26 an_object_having_attributes(name: 'operatingsystemrelease', value: value_final['full'],
27 type: :legacy))
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Path.new }
5
6 let(:value) { '/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java6/jre/bin:/usr/java6/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Cores do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Cores.new }
5
6 let(:cores_per_socket) { 4 }
7
8 before do
9 allow(Facter::Resolvers::Macosx::Processors).to \
10 receive(:resolve).with(:cores_per_socket).and_return(cores_per_socket)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:cores_per_socket)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.cores', value: cores_per_socket)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Count.new }
5
6 let(:processors) { '4' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::Processors).to \
10 receive(:resolve).with(:logicalcount).and_return(processors)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:logicalcount)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors),
21 an_object_having_attributes(name: 'processorcount', value: processors, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Isa.new }
5
6 let(:isa) { 'i386' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:processor)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: isa),
21 an_object_having_attributes(name: 'hardwareisa', value: isa, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Models.new }
5
6 let(:value) { 'Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz' }
7 let(:models) { [value, value] }
8
9 before do
10 allow(Facter::Resolvers::Macosx::Processors).to \
11 receive(:resolve).with(:models).and_return(models)
12 end
13
14 it 'calls Facter::Resolvers::Macosx::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:models)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'processors.models', value: models),
22 an_object_having_attributes(name: 'processor0', value: value, type: :legacy),
23 an_object_having_attributes(name: 'processor1', value: value, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Physicalcount do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Physicalcount.new }
5
6 let(:physicalcount) { '5' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::Processors).to \
10 receive(:resolve).with(:physicalcount).and_return(physicalcount)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:physicalcount)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.physicalcount', value: physicalcount),
21 an_object_having_attributes(name: 'physicalprocessorcount', value: physicalcount,
22 type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Macosx::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Macosx::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Processors::Threads do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Processors::Threads.new }
5
6 let(:threads_per_core) { 4 }
7
8 before do
9 allow(Facter::Resolvers::Macosx::Processors).to \
10 receive(:resolve).with(:threads_per_core).and_return(threads_per_core)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::Processors).to have_received(:resolve).with(:threads_per_core)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.threads', value: threads_per_core)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Ruby::Platform.new }
5
6 let(:value) { 'x86_64-linux' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to \
10 receive(:resolve).with(:platform).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Ruby' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:platform)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
21 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Ruby::Sitedir.new }
5
6 let(:value) { '/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.6.3' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to \
10 receive(:resolve).with(:sitedir).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Ruby' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:sitedir)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
21 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Ruby::Version.new }
5
6 let(:value) { '2.4.5' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
21 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Scope6Interfaces do
3 subject(:fact) { Facts::Macosx::Scope6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { scope6: 'link' }, 'en1' => { scope6: 'global' } } }
11
12 it 'calls Facter::Resolvers::NetworkingLinux with interfaces' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with scope6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'scope6_eth0',
20 value: interfaces['eth0'][:scope6], type: :legacy),
21 an_object_having_attributes(name: 'scope6_en1',
22 value: interfaces['en1'][:scope6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28 let(:scope6) { nil }
29
30 it 'returns nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Ssh.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
9 end
10 let(:value) do
11 { 'ecdsa' => { 'fingerprints' =>
12 { 'sha1' => 'test', 'sha256' => 'test' },
13 'key' => 'test',
14 'type' => 'ecdsa' } }
15 end
16
17 before do
18 allow(Facter::Resolvers::Ssh).to \
19 receive(:resolve).with(:ssh).and_return(ssh)
20 end
21
22 it 'calls Facter::Resolvers::Ssh' do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
25 end
26
27 it 'returns a resolved fact' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
29 have_attributes(name: 'ssh', value: value)
30 end
31
32 context 'when resolver returns empty list' do
33 let(:ssh) { [] }
34
35 it 'returns a nil fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'ssh', value: nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Sshalgorithmkey.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('test', 'test'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
13 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SshfpAlgorithm.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
13 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::BootMode do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::BootMode.new }
5
6 let(:value) { 'Normal' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:boot_mode).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:boot_mode)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.boot_mode', value: value),
21 an_object_having_attributes(name: 'sp_boot_mode', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::BootRomVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::BootRomVersion.new }
5
6 let(:value) { '194.0.0.0.0' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:boot_rom_version).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:boot_rom_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.boot_rom_version', value: value),
21 an_object_having_attributes(name: 'sp_boot_rom_version', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::BootVolume do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::BootVolume.new }
5
6 let(:value) { 'Macintosh HD' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:boot_volume).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:boot_volume)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.boot_volume', value: value),
21 an_object_having_attributes(name: 'sp_boot_volume', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::ComputerName do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::ComputerName.new }
5
6 let(:value) { 'Test1’s MacBook Pro' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:computer_name).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:computer_name)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.computer_name', value: value),
21 an_object_having_attributes(name: 'sp_local_host_name', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::Cores do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::Cores.new }
5
6 let(:value) { '' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:total_number_of_cores).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:total_number_of_cores)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.cores', value: value),
21 an_object_having_attributes(name: 'sp_number_processors', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::HardwareUuid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::HardwareUuid.new }
5
6 let(:value) { '7C3B701F-B88A-56C6-83F4-ACBD450075C4' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:hardware_uuid).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:hardware_uuid)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.hardware_uuid', value: value),
21 an_object_having_attributes(name: 'sp_platform_uuid', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::KernelVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::KernelVersion.new }
5
6 let(:value) { 'Darwin 18.7.0' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:kernel_version).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:kernel_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.kernel_version', value: value),
21 an_object_having_attributes(name: 'sp_kernel_version', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::L2CachePerCore do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::L2CachePerCore.new }
5
6 let(:value) { '256 KB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:l2_cache_per_core).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:l2_cache_per_core)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.l2_cache_per_core', value: value),
21 an_object_having_attributes(name: 'sp_l2_cache_core', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::L3Cache do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::L3Cache.new }
5
6 let(:value) { '6 MB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:l3_cache).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:l3_cache)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.l3_cache', value: value),
21 an_object_having_attributes(name: 'sp_l3_cache', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::Memory do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::Memory.new }
5
6 let(:value) { '16 GB' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:memory).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:memory)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.memory', value: value),
21 an_object_having_attributes(name: 'sp_physical_memory', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::ModelIdentifier do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::ModelIdentifier.new }
5
6 let(:value) { 'MacBookPro11,4' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:model_identifier).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:model_identifier)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.model_identifier', value: value),
21 an_object_having_attributes(name: 'sp_machine_model', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::ModelName do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::ModelName.new }
5
6 let(:value) { 'MacBook Pro' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:model_name).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:model_name)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.model_name', value: value),
21 an_object_having_attributes(name: 'sp_machine_name', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::ProcessorName do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::ProcessorName.new }
5
6 let(:value) { 'Intel Core i7' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:processor_name).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:processor_name)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.processor_name', value: value),
21 an_object_having_attributes(name: 'sp_cpu_type', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::ProcessorSpeed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::ProcessorSpeed.new }
5
6 let(:value) { '2.8 GHz' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:processor_speed).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:processor_speed)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.processor_speed', value: value),
21 an_object_having_attributes(name: 'sp_current_processor_speed', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::Processors do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::Processors.new }
5
6 let(:value) { '1' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:number_of_processors).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:number_of_processors)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.processors', value: value),
21 an_object_having_attributes(name: 'sp_packages', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::SecureVirtualMemory do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::SecureVirtualMemory.new }
5
6 let(:value) { 'Enabled' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:secure_virtual_memory).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:secure_virtual_memory)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.secure_virtual_memory', value: value),
21 an_object_having_attributes(name: 'sp_secure_vm', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::SerialNumber.new }
5
6 let(:value) { 'C02WW1LAG8WL' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:serial_number_system).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:serial_number_system)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.serial_number', value: value),
21 an_object_having_attributes(name: 'sp_serial_number', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::SmcVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::SmcVersion.new }
5
6 let(:value) { '2.29f24' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:smc_version_system).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:smc_version_system)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.smc_version', value: value),
21 an_object_having_attributes(name: 'sp_smc_version_system', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::SystemVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::SystemVersion.new }
5
6 let(:value) { 'macOS 10.14.6 (18G95)' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:system_version).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:system_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.system_version', value: value),
21 an_object_having_attributes(name: 'sp_os_version', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::Uptime.new }
5
6 let(:value) { '26 days 22:12' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:time_since_boot).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:time_since_boot)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.uptime', value: value),
21 an_object_having_attributes(name: 'sp_uptime', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemProfiler::Username do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemProfiler::Username.new }
5
6 let(:value) { 'Test1 Test2 (test1.test2)' }
7
8 before do
9 allow(Facter::Resolvers::Macosx::SystemProfiler).to \
10 receive(:resolve).with(:user_name).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SystemProfiler' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:user_name)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_profiler.username', value: value),
21 an_object_having_attributes(name: 'sp_user_name', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemUptime::Days.new }
5
6 let(:days) { '11' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:days).and_return(days)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:days)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: days),
21 an_object_having_attributes(name: 'uptime_days', value: days, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemUptime::Hours.new }
5
6 let(:hours) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:hours).and_return(hours)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:hours)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: hours),
21 an_object_having_attributes(name: 'uptime_hours', value: hours, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemUptime::Seconds.new }
5
6 let(:seconds) { '123094' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:seconds).and_return(seconds)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:seconds)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: seconds),
21 an_object_having_attributes(name: 'uptime_seconds', value: seconds, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::SystemUptime::Uptime.new }
5
6 let(:uptime) { '10 days' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to \
10 receive(:resolve).with(:uptime).and_return(uptime)
11 end
12
13 it 'calls Facter::Resolvers::Uptime' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:uptime)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: uptime),
21 an_object_having_attributes(name: 'uptime', value: uptime, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Macosx::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Macosx::Virtual do
3 subject(:fact) { Facts::Macosx::Virtual.new }
4
5 describe '#call_the_resolver' do
6 before do
7 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
8 .with(:model_identifier)
9 .and_return('MacBookPro11,4')
10
11 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
12 .with(:boot_rom_version)
13 .and_return('1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)')
14
15 allow(Facter::Resolvers::Macosx::SystemProfiler).to receive(:resolve)
16 .with(:subsystem_vendor_id)
17 .and_return('0x123')
18 end
19
20 context 'when on physical machine' do
21 it 'calls Facter::Resolvers::Macosx::SystemProfile with model_identifier' do
22 fact.call_the_resolver
23
24 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:model_identifier)
25 end
26
27 it 'calls Facter::Resolvers::Macosx::SystemProfile with boot_rom_version' do
28 fact.call_the_resolver
29
30 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:boot_rom_version)
31 end
32
33 it 'calls Facter::Resolvers::Macosx::SystemProfile with subsystem_vendor_id' do
34 fact.call_the_resolver
35
36 expect(Facter::Resolvers::Macosx::SystemProfiler).to have_received(:resolve).with(:subsystem_vendor_id)
37 end
38
39 it 'returns resolved fact with true value' do
40 expect(fact.call_the_resolver)
41 .to be_an_instance_of(Facter::ResolvedFact)
42 .and have_attributes(name: 'virtual', value: 'physical')
43
44 fact.call_the_resolver
45 end
46 end
47
48 context 'when on virtual machine' do
49 context 'with hypervisor vmware' do
50 before do
51 allow(Facter::Resolvers::Macosx::SystemProfiler)
52 .to receive(:resolve)
53 .with(:model_identifier)
54 .and_return('VMware')
55 end
56
57 it 'returns resolved fact with true value' do
58 expect(fact.call_the_resolver)
59 .to be_an_instance_of(Facter::ResolvedFact)
60 .and have_attributes(name: 'virtual', value: 'vmware')
61 end
62 end
63
64 context 'when hypervisor VirtualBox' do
65 before do
66 allow(Facter::Resolvers::Macosx::SystemProfiler)
67 .to receive(:resolve)
68 .with(:boot_rom_version)
69 .and_return('VirtualBox')
70 end
71
72 it 'returns resolved fact with true value' do
73 expect(fact.call_the_resolver)
74 .to be_an_instance_of(Facter::ResolvedFact)
75 .and have_attributes(name: 'virtual', value: 'virtualbox')
76 end
77 end
78
79 context 'when hypervisor Parallels' do
80 before do
81 allow(Facter::Resolvers::Macosx::SystemProfiler)
82 .to receive(:resolve)
83 .with(:subsystem_vendor_id)
84 .and_return('0x1ab8')
85 end
86
87 it 'returns resolved fact with true value' do
88 expect(fact.call_the_resolver)
89 .to be_an_instance_of(Facter::ResolvedFact)
90 .and have_attributes(name: 'virtual', value: 'parallels')
91 end
92 end
93 end
94 end
95 end
0 # frozen_string_literal: true
1
2 describe Facts::Mageia::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Mageia::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/mageia-release',
9 regex: /Mageia release ([0-9.]+)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /Mageia release ([0-9.]+)/.match('Mageia release 19.4') }
15 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/mageia-release',
22 regex: /Mageia release ([0-9.]+)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '19.4' }
39 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Mariner::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Mariner::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/mariner-release',
9 regex: /CBL\-Mariner ([0-9.]+)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /CBL\-Mariner ([0-9.]+)/.match('CBL-Mariner 2.0.20220824') }
15 let(:release) { { 'full' => '2.0.20220824', 'major' => '2', 'minor' => '0' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/mariner-release',
22 regex: /CBL\-Mariner ([0-9.]+)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '2.0.20220824' }
39 let(:release) { { 'full' => '2.0.20220824', 'major' => '2', 'minor' => '0' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Meego::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Meego::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/meego-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/meego-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { 'beowulf' }
35 let(:release) { { 'full' => 'beowulf', 'major' => 'beowulf' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Oel::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Oel::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/enterprise-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/enterprise-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { 'beowulf' }
35 let(:release) { { 'full' => 'beowulf', 'major' => 'beowulf' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Ol::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Ol::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/oracle-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/oracle-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { 'beowulf' }
35 let(:release) { { 'full' => 'beowulf', 'major' => 'beowulf' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Openwrt::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Openwrt::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/openwrt_version',
9 regex: /^(\d+.\d+.*)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /^(\d+.\d+.*)/.match('19.4') }
15 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/openwrt_version',
22 regex: /^(\d+.\d+.*)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '19.4' }
39 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Ovs::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Ovs::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::ReleaseFromFirstLine).to receive(:resolve)
8 .with(:release, release_file: '/etc/ovs-release')
9 .and_return(value)
10 end
11
12 context 'when version is retrieved from specific file' do
13 let(:value) { '2.13.0' }
14 let(:release) { { 'full' => '2.13.0', 'major' => '2', 'minor' => '13' } }
15
16 it 'calls Facter::Resolvers::ReleaseFromFirstLine with version' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::ReleaseFromFirstLine).to have_received(:resolve)
19 .with(:release, release_file: '/etc/ovs-release')
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
25 an_object_having_attributes(name: 'operatingsystemmajrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'operatingsystemrelease',
28 value: release['full'], type: :legacy))
29 end
30 end
31
32 context 'when version is retrieved from os-release file' do
33 let(:value) { nil }
34 let(:os_release) { 'beowulf' }
35 let(:release) { { 'full' => 'beowulf', 'major' => 'beowulf' } }
36
37 before do
38 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
39 end
40
41 it 'calls Facter::Resolvers::OsRelease with version' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
44 end
45
46 it 'returns operating system name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
49 an_object_having_attributes(name: 'operatingsystemmajrelease',
50 value: release['major'], type: :legacy),
51 an_object_having_attributes(name: 'operatingsystemrelease',
52 value: release['full'], type: :legacy))
53 end
54
55 context 'when release can\'t be received' do
56 let(:os_release) { nil }
57
58 it 'returns operating system name fact' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
60 have_attributes(name: 'os.release', value: nil)
61 end
62 end
63 end
64 end
65 end
0 # frozen_string_literal: true
1
2 describe Facts::Photon::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Photon::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/lsb-release',
9 regex: /DISTRIB_RELEASE="(\d+)\.(\d+)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /DISTRIB_RELEASE="(\d+)\.(\d+)/.match('DISTRIB_RELEASE="19.4') }
15 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/lsb-release',
22 regex: /DISTRIB_RELEASE="(\d+)\.(\d+)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '19.4' }
39 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Lsbdistcodename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Lsbdistcodename.new }
5
6 let(:value) { 'rhel' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns lsbdistcodename fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistcodename', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Lsbdistdescription do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Lsbdistdescription.new }
5
6 let(:value) { 'rhel' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:description).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:description)
15 end
16
17 it 'returns lsbdistdescription fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistdescription', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Lsbdistid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Lsbdistid.new }
5
6 let(:value) { 'rhel' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns lsbdistid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistid', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Distro::Codename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Distro::Codename.new }
5
6 let(:value) { 'Fedora' }
7
8 before do
9 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::RedHatRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'os.distro.codename', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Distro::Description do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Distro::Description.new }
5
6 let(:value) { 'CentOS Linux release 7.2.1511 (Core)' }
7
8 before do
9 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve)
10 .with(:description).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::RedHatRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve)
16 .with(:description)
17 end
18
19 it 'returns release fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'os.distro.description', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Distro::Id do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Distro::Id.new }
5
6 let(:value) { 'RedHatEnterprise' }
7
8 before do
9 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::RedHatRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'os.distro.id', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Distro::Release.new }
5
6 before do
7 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:version).and_return(value)
8 end
9
10 context 'when os is RedHat' do
11 context 'when version has only major and minor' do
12 let(:value) { '6.2' }
13 let(:release) { { 'full' => '6.2', 'major' => '6', 'minor' => '2' } }
14
15 it 'calls Facter::Resolvers::RedHatRelease with version' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
18 end
19
20 it 'returns operating system name fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
23 an_object_having_attributes(name: 'lsbdistrelease',
24 value: release['full'], type: :legacy),
25 an_object_having_attributes(name: 'lsbmajdistrelease',
26 value: release['major'], type: :legacy),
27 an_object_having_attributes(name: 'lsbminordistrelease',
28 value: release['minor'], type: :legacy))
29 end
30 end
31
32 context 'when version also contains build number' do
33 let(:value) { '7.4.1708' }
34 let(:release) { { 'full' => '7.4.1708', 'major' => '7', 'minor' => '4' } }
35
36 it 'calls Facter::Resolvers::RedHatRelease with version' do
37 fact.call_the_resolver
38 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
39 end
40
41 it 'returns operating system name fact' do
42 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
43 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
44 an_object_having_attributes(name: 'lsbdistrelease',
45 value: release['full'], type: :legacy),
46 an_object_having_attributes(name: 'lsbmajdistrelease',
47 value: release['major'], type: :legacy),
48 an_object_having_attributes(name: 'lsbminordistrelease',
49 value: release['minor'], type: :legacy))
50 end
51 end
52 end
53
54 context 'when os is Centos' do
55 let(:value) { nil }
56 let(:red_release) { '6.2' }
57 let(:release) { { 'full' => '6.2', 'major' => '6', 'minor' => '2' } }
58
59 before do
60 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(red_release)
61 end
62
63 it 'calls Facter::Resolvers::OsRelease with version_id' do
64 fact.call_the_resolver
65 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
66 end
67
68 it 'calls Facter::Resolvers::RedHatRelease with version' do
69 fact.call_the_resolver
70 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
71 end
72
73 it 'returns operating system name fact' do
74 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
75 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
76 an_object_having_attributes(name: 'lsbdistrelease',
77 value: release['full'], type: :legacy),
78 an_object_having_attributes(name: 'lsbmajdistrelease',
79 value: release['major'], type: :legacy),
80 an_object_having_attributes(name: 'lsbminordistrelease',
81 value: release['minor'], type: :legacy))
82 end
83 end
84 end
85 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Family.new }
5
6 let(:value) { 'RedHat' }
7
8 it 'returns os family fact' do
9 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
10 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
11 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Name.new }
5
6 let(:value) { 'RedHat' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:name).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:name)
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Rhel::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Rhel::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:version).and_return(value)
8 end
9
10 context 'when os is RedHat' do
11 context 'when version has only major and minor' do
12 let(:value) { '6.2' }
13 let(:release) { { 'full' => '6.2', 'major' => '6', 'minor' => '2' } }
14
15 it 'calls Facter::Resolvers::RedHatRelease with version' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
18 end
19
20 it 'returns operating system name fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
23 an_object_having_attributes(name: 'operatingsystemmajrelease',
24 value: release['major'], type: :legacy),
25 an_object_having_attributes(name: 'operatingsystemrelease',
26 value: release['full'], type: :legacy))
27 end
28 end
29
30 context 'when version also contains build number' do
31 let(:value) { '7.4.1708' }
32 let(:release) { { 'full' => '7.4.1708', 'major' => '7', 'minor' => '4' } }
33
34 it 'calls Facter::Resolvers::RedHatRelease with version' do
35 fact.call_the_resolver
36 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
37 end
38
39 it 'returns operating system name fact' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
41 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
42 an_object_having_attributes(name: 'operatingsystemmajrelease',
43 value: release['major'], type: :legacy),
44 an_object_having_attributes(name: 'operatingsystemrelease',
45 value: release['full'], type: :legacy))
46 end
47 end
48 end
49
50 context 'when os is Centos' do
51 let(:value) { nil }
52 let(:red_release) { '6.2' }
53 let(:release) { { 'full' => '6.2', 'major' => '6', 'minor' => '2' } }
54
55 before do
56 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(red_release)
57 end
58
59 it 'calls Facter::Resolvers::OsRelease with version_id' do
60 fact.call_the_resolver
61 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
62 end
63
64 it 'calls Facter::Resolvers::RedHatRelease with version' do
65 fact.call_the_resolver
66 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:version)
67 end
68
69 it 'returns operating system name fact' do
70 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
71 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
72 an_object_having_attributes(name: 'operatingsystemmajrelease',
73 value: release['major'], type: :legacy),
74 an_object_having_attributes(name: 'operatingsystemrelease',
75 value: release['full'], type: :legacy))
76 end
77 end
78 end
79 end
0 # frozen_string_literal: true
1
2 describe Facts::Slackware::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Slackware::Os::Release.new }
5
6 before do
7 allow(Facter::Resolvers::SpecificReleaseFile).to receive(:resolve)
8 .with(:release, { release_file: '/etc/slackware-version',
9 regex: /Slackware ([0-9.]+)/ })
10 .and_return(value)
11 end
12
13 context 'when version is retrieved from specific file' do
14 let(:value) { /Slackware ([0-9.]+)/.match('Slackware 19.4') }
15 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
16
17 it 'calls Facter::Resolvers::SpecificReleaseFile with version' do
18 fact.call_the_resolver
19 expect(Facter::Resolvers::SpecificReleaseFile).to have_received(:resolve)
20 receive(:resolve)
21 .with(:release, { release_file: '/etc/slackware-version',
22 regex: /Slackware ([0-9.]+)/ })
23 .and_return(value)
24 end
25
26 it 'returns operating system name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
29 an_object_having_attributes(name: 'operatingsystemmajrelease',
30 value: release['major'], type: :legacy),
31 an_object_having_attributes(name: 'operatingsystemrelease',
32 value: release['full'], type: :legacy))
33 end
34 end
35
36 context 'when version is retrieved from os-release file' do
37 let(:value) { nil }
38 let(:os_release) { '19.4' }
39 let(:release) { { 'full' => '19.4', 'major' => '19', 'minor' => '4' } }
40
41 before do
42 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release)
43 end
44
45 it 'calls Facter::Resolvers::OsRelease with version' do
46 fact.call_the_resolver
47 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
48 end
49
50 it 'returns operating system name fact' do
51 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
52 contain_exactly(an_object_having_attributes(name: 'os.release', value: release),
53 an_object_having_attributes(name: 'operatingsystemmajrelease',
54 value: release['major'], type: :legacy),
55 an_object_having_attributes(name: 'operatingsystemrelease',
56 value: release['full'], type: :legacy))
57 end
58
59 context 'when release can\'t be received' do
60 let(:os_release) { nil }
61
62 it 'returns operating system name fact' do
63 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
64 have_attributes(name: 'os.release', value: nil)
65 end
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Lsbdistcodename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Lsbdistcodename.new }
5
6 let(:value) { 'sles' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:codename).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:codename)
15 end
16
17 it 'returns lsbdistcodename fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistcodename', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Lsbdistdescription do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Lsbdistdescription.new }
5
6 let(:value) { 'sles' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:description).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:description)
15 end
16
17 it 'returns lsbdistdescription fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistdescription', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Lsbdistid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Lsbdistid.new }
5
6 let(:value) { 'sles' }
7
8 before do
9 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:distributor_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::LsbRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:distributor_id)
15 end
16
17 it 'returns lsbdistid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'lsbdistid', value: value, type: :legacy)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Distro::Codename do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Distro::Codename.new }
5
6 context 'when codename is not in os-release' do
7 let(:value) { nil }
8 let(:expected_value) { 'n/a' }
9
10 before do
11 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
12 .with(:version_codename).and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::OsRelease' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
18 .with(:version_codename)
19 end
20
21 it "returns 'n/a' fact value" do
22 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
23 have_attributes(name: 'os.distro.codename', value: expected_value)
24 end
25 end
26
27 context 'when codename is empty' do
28 let(:value) { '' }
29 let(:expected_value) { 'n/a' }
30
31 before do
32 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
33 .with(:version_codename).and_return(value)
34 end
35
36 it 'calls Facter::Resolvers::OsRelease' do
37 fact.call_the_resolver
38 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
39 .with(:version_codename)
40 end
41
42 it "returns 'n/a' fact value" do
43 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
44 have_attributes(name: 'os.distro.codename', value: expected_value)
45 end
46 end
47
48 context 'when codename is in os-release' do
49 let(:value) { 'SP1' }
50 let(:expected_value) { 'SP1' }
51
52 before do
53 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
54 .with(:version_codename).and_return(value)
55 end
56
57 it 'calls Facter::Resolvers::OsRelease' do
58 fact.call_the_resolver
59 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
60 .with(:version_codename)
61 end
62
63 it 'returns release fact' do
64 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
65 have_attributes(name: 'os.distro.codename', value: expected_value)
66 end
67 end
68 end
69 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Distro::Description do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Distro::Description.new }
5
6 let(:value) { 'SUSE Linux Enterprise Server 15' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
10 .with(:pretty_name).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::OsRelease' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
16 .with(:pretty_name)
17 end
18
19 it 'returns release fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'os.distro.description', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Distro::Id do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Distro::Id.new }
5
6 context 'when sles 12' do
7 let(:expected_value) { 'SUSE LINUX' }
8
9 before do
10 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
11 .with(:version_id).and_return('12.1')
12 end
13
14 it 'calls Facter::Resolvers::OsRelease' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
17 .with(:version_id)
18 end
19
20 it 'returns release fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
22 have_attributes(name: 'os.distro.id', value: expected_value)
23 end
24 end
25
26 context 'when sles 15' do
27 let(:expected_value) { 'SUSE' }
28
29 before do
30 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
31 .with(:version_id).and_return('15')
32 end
33
34 it 'calls Facter::Resolvers::OsRelease' do
35 fact.call_the_resolver
36 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
37 .with(:version_id)
38 end
39
40 it 'returns release fact' do
41 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
42 have_attributes(name: 'os.distro.id', value: expected_value)
43 end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Distro::Release.new }
5
6 before do
7 allow(Facter::Resolvers::OsRelease).to receive(:resolve)
8 .with(:version_id)
9 .and_return(value)
10 end
11
12 context 'when version has .' do
13 let(:value) { '12.1' }
14 let(:release) { { 'full' => '12.1', 'major' => '12', 'minor' => '1' } }
15
16 it 'calls Facter::Resolvers::OsRelease with version_id' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::OsRelease).to have_received(:resolve)
19 .with(:version_id)
20 end
21
22 it 'returns operating system name fact' do
23 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
24 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
25 an_object_having_attributes(name: 'lsbdistrelease',
26 value: release['full'], type: :legacy),
27 an_object_having_attributes(name: 'lsbmajdistrelease',
28 value: release['major'], type: :legacy),
29 an_object_having_attributes(name: 'lsbminordistrelease',
30 value: release['minor'], type: :legacy))
31 end
32 end
33
34 context 'when version is simple' do
35 let(:value) { '15' }
36 let(:release) { { 'full' => '15', 'major' => '15', 'minor' => nil } }
37
38 before do
39 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(value)
40 end
41
42 it 'calls Facter::Resolvers::OsRelease with version' do
43 fact.call_the_resolver
44 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
45 end
46
47 it 'returns operating system name fact' do
48 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
49 contain_exactly(an_object_having_attributes(name: 'os.distro.release', value: release),
50 an_object_having_attributes(name: 'lsbdistrelease',
51 value: release['full'], type: :legacy),
52 an_object_having_attributes(name: 'lsbmajdistrelease',
53 value: release['major'], type: :legacy),
54 an_object_having_attributes(name: 'lsbminordistrelease',
55 value: release['minor'], type: :legacy))
56 end
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Family.new }
5
6 let(:value) { 'Suse' }
7
8 it 'returns os family fact' do
9 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
10 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
11 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Sles::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Sles::Os::Release.new }
5
6 let(:value) { '12.1' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 expect(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id)
14 fact.call_the_resolver
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value, 'major' => '12',
20 'minor' => '1' }),
21 an_object_having_attributes(name: 'operatingsystemmajrelease', value: '12', type: :legacy),
22 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::AioAgentVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::AioAgentVersion.new }
5
6 let(:value) { '1.2.3' }
7
8 before do
9 allow(Facter::Resolvers::AioAgentVersion).to receive(:resolve).with(:aio_agent_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Agent' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::AioAgentVersion).to have_received(:resolve).with(:aio_agent_version)
15 end
16
17 it 'returns aio_agent_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'aio_agent_version', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::CurrentZone do
3 subject(:fact) { Facts::Solaris::CurrentZone.new }
4
5 let(:value) { 'global' }
6
7 before do
8 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve).with(:current_zone_name).and_return('global')
9 end
10
11 describe '#call_the_resolver' do
12 it 'returns a fact' do
13 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
14 contain_exactly(
15 an_object_having_attributes(name: 'solaris_zones.current', value: value, type: :core),
16 an_object_having_attributes(name: 'zonename', value: value, type: :legacy)
17 )
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::DhcpServers do
3 subject(:fact) { Facts::Solaris::DhcpServers.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:dhcp).and_return(dhcp)
8 end
9
10 describe '#call_the_resolver' do
11 let(:value) { { 'system' => '10.16.122.163', 'eth0' => '10.16.122.163', 'en1' => '10.32.10.163' } }
12 let(:interfaces) { { 'eth0' => { dhcp: '10.16.122.163' }, 'en1' => { dhcp: '10.32.10.163' } } }
13 let(:dhcp) { '10.16.122.163' }
14
15 it 'calls Facter::Resolvers::Solaris::Networking with interfaces' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'calls Facter::Resolvers::Solaris::Networking with dhcp' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:dhcp)
23 end
24
25 it 'returns dhcp_servers' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
27 have_attributes(name: 'dhcp_servers', value: value, type: :legacy)
28 end
29 end
30
31 describe '#call_the_resolver when resolver returns nil' do
32 let(:interfaces) { nil }
33 let(:dhcp) { nil }
34
35 it 'returns nil' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'dhcp_servers', value: nil, type: :legacy)
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Disks do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Disks.new }
5
6 let(:disks) do
7 {
8 'sd0' => {
9 product: 'VMware IDE CDR00Revision',
10 size: '0 bytes',
11 size_bytes: 0,
12 vendor: 'NECVMWar'
13 },
14 'sd1' => {
15 product: 'Virtual disk Revision',
16 size: '20.00 GiB',
17 size_bytes: 21_474_836_480,
18 vendor: 'VMware'
19 }
20 }
21 end
22
23 let(:expected_response) do
24 {
25 'sd0' => {
26 'product' => 'VMware IDE CDR00Revision',
27 'size' => '0 bytes',
28 'size_bytes' => 0,
29 'vendor' => 'NECVMWar'
30 },
31 'sd1' => {
32 'product' => 'Virtual disk Revision',
33 'size' => '20.00 GiB',
34 'size_bytes' => 21_474_836_480,
35 'vendor' => 'VMware'
36 }
37 }
38 end
39
40 before do
41 allow(Facter::Resolvers::Solaris::Disks).to receive(:resolve).with(:disks).and_return(disks)
42 end
43
44 it 'calls Facter::Resolvers::Solaris::Disks' do
45 fact.call_the_resolver
46 expect(Facter::Resolvers::Solaris::Disks).to have_received(:resolve).with(:disks)
47 end
48
49 it 'returns disks fact' do
50 expect(fact.call_the_resolver)
51 .to be_an_instance_of(Array)
52 .and contain_exactly(
53 an_object_having_attributes(name: 'disks', value: expected_response),
54 an_object_having_attributes(name: 'blockdevices', value: 'sd0,sd1'),
55 an_object_having_attributes(name: 'blockdevice_sd0_size', value: 0, type: :legacy),
56 an_object_having_attributes(name: 'blockdevice_sd0_vendor', value: 'NECVMWar', type: :legacy),
57 an_object_having_attributes(name: 'blockdevice_sd1_size', value: 21_474_836_480, type: :legacy),
58 an_object_having_attributes(name: 'blockdevice_sd1_vendor', value: 'VMware', type: :legacy)
59 )
60 end
61
62 context 'when resolver returns empty hash' do
63 let(:disks) { {} }
64
65 it 'returns nil fact' do
66 expect(fact.call_the_resolver)
67 .to be_an_instance_of(Facter::ResolvedFact)
68 .and have_attributes(name: 'disks', value: nil)
69 end
70 end
71
72 context 'when resolver returns nil' do
73 let(:disks) { nil }
74
75 it 'returns nil fact' do
76 expect(fact.call_the_resolver)
77 .to be_an_instance_of(Facter::ResolvedFact)
78 .and have_attributes(name: 'disks', value: nil)
79 end
80 end
81 end
82 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Bios::ReleaseDate do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Bios::ReleaseDate.new }
5
6 let(:date) { '07/03/2018' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:bios_release_date).and_return(date)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:bios_release_date)
18 end
19
20 it 'returns bios release date fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.bios.release_date', value: date),
23 an_object_having_attributes(name: 'bios_release_date', value: date, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Bios::Vendor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Bios::Vendor.new }
5
6 let(:vendor) { 'Phoenix Technologies LTD' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:bios_vendor).and_return(vendor)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:bios_vendor)
18 end
19
20 it 'returns bios vendor fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.bios.vendor', value: vendor),
23 an_object_having_attributes(name: 'bios_vendor', value: vendor, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Bios::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Bios::Version.new }
5
6 let(:version) { '6.00' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:bios_version).and_return(version)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:bios_version)
18 end
19
20 it 'returns bios version fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.bios.version', value: version),
23 an_object_having_attributes(name: 'bios_version', value: version, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Chassis::AssetTag do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Chassis::AssetTag.new }
5
6 let(:tag) { 'No Asset Tag' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:chassis_asset_tag).and_return(tag)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:chassis_asset_tag)
18 end
19
20 it 'returns chassis asset tag fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.chassis.asset_tag', value: tag),
23 an_object_having_attributes(name: 'chassisassettag', value: tag, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Chassis::Type do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Chassis::Type.new }
5
6 let(:type) { 'Low Profile Desktop' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:chassis_type).and_return(type)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:chassis_type)
18 end
19
20 it 'returns chassis type fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.chassis.type', value: type),
23 an_object_having_attributes(name: 'chassistype', value: type, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Manufacturer do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Manufacturer.new }
5
6 let(:manufacturer) { 'VMware, Inc.' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 context 'when i386' do
14 let(:isa) { 'i386' }
15
16 before do
17 allow(Facter::Resolvers::Solaris::Dmi).to \
18 receive(:resolve).with(:manufacturer).and_return(manufacturer)
19 end
20
21 it 'calls Facter::Resolvers::Solaris::Dmi' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:manufacturer)
24 end
25
26 it 'returns manufacturer fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'dmi.manufacturer', value: manufacturer),
29 an_object_having_attributes(name: 'manufacturer', value: manufacturer, type: :legacy))
30 end
31 end
32
33 context 'when sparc' do
34 let(:isa) { 'sparc' }
35
36 before do
37 allow(Facter::Resolvers::Solaris::DmiSparc).to \
38 receive(:resolve).with(:manufacturer).and_return(manufacturer)
39 end
40
41 it 'calls Facter::Resolvers::Solaris::DmiSparc' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::Solaris::DmiSparc).to have_received(:resolve).with(:manufacturer)
44 end
45
46 it 'returns manufacturer fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'dmi.manufacturer', value: manufacturer),
49 an_object_having_attributes(name: 'manufacturer', value: manufacturer, type: :legacy))
50 end
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Product::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Product::Name.new }
5
6 let(:product_name) { 'VMware Virtual Platform' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 context 'when i386' do
14 let(:isa) { 'i386' }
15
16 before do
17 allow(Facter::Resolvers::Solaris::Dmi).to \
18 receive(:resolve).with(:product_name).and_return(product_name)
19 end
20
21 it 'calls Facter::Resolvers::Solaris::Dmi' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:product_name)
24 end
25
26 it 'returns product name fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: product_name),
29 an_object_having_attributes(name: 'productname', value: product_name, type: :legacy))
30 end
31 end
32
33 context 'when sparc' do
34 let(:isa) { 'sparc' }
35
36 before do
37 allow(Facter::Resolvers::Solaris::DmiSparc).to \
38 receive(:resolve).with(:product_name).and_return(product_name)
39 end
40
41 it 'calls Facter::Resolvers::Solaris::DmiSparc' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::Solaris::DmiSparc).to have_received(:resolve).with(:product_name)
44 end
45
46 it 'returns product name fact' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: product_name),
49 an_object_having_attributes(name: 'productname', value: product_name, type: :legacy))
50 end
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Product::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Product::SerialNumber.new }
5
6 let(:serial_number) { 'VMware-42 1a a9 29 31 8f fa e9-7d 69 2e 23 21 b0 0c 45' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 context 'when i386' do
14 let(:isa) { 'i386' }
15
16 before do
17 allow(Facter::Resolvers::Solaris::Dmi).to \
18 receive(:resolve).with(:serial_number).and_return(serial_number)
19 end
20
21 it 'calls Facter::Resolvers::Solaris::Dmi' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:serial_number)
24 end
25
26 it 'returns resolved facts' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'dmi.product.serial_number', value: serial_number),
29 an_object_having_attributes(name: 'serialnumber', value: serial_number, type: :legacy))
30 end
31 end
32
33 context 'when sparc' do
34 let(:isa) { 'sparc' }
35
36 before do
37 allow(Facter::Resolvers::Solaris::DmiSparc).to \
38 receive(:resolve).with(:serial_number).and_return(serial_number)
39 end
40
41 it 'calls Facter::Resolvers::Solaris::DmiSparc' do
42 fact.call_the_resolver
43 expect(Facter::Resolvers::Solaris::DmiSparc).to have_received(:resolve).with(:serial_number)
44 end
45
46 it 'returns resolved facts' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'dmi.product.serial_number', value: serial_number),
49 an_object_having_attributes(name: 'serialnumber', value: serial_number, type: :legacy))
50 end
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Dmi::Product::Uuid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Dmi::Product::Uuid.new }
5
6 let(:product_uuid) { '421aa929-318f-fae9-7d69-2e2321b00c45' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Dmi).to \
10 receive(:resolve).with(:product_uuid).and_return(product_uuid)
11 allow(Facter::Resolvers::Uname).to \
12 receive(:resolve).with(:processor).and_return('i386')
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Dmi' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Dmi).to have_received(:resolve).with(:product_uuid)
18 end
19
20 it 'returns resolved facts' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'dmi.product.uuid', value: product_uuid),
23 an_object_having_attributes(name: 'uuid', value: product_uuid, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Filesystems do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Filesystems.new }
5
6 let(:files) { 'apfs,autofs,devfs' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Filesystem).to \
10 receive(:resolve).with(:file_systems).and_return(files)
11 end
12
13 it 'calls Facter::Resolvers::Solaris::Filesystem' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Solaris::Filesystem).to have_received(:resolve).with(:file_systems)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'filesystems', value: files)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Hypervisors::Ldom do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Hypervisors::Ldom.new }
5
6 before do
7 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve)
8 end
9
10 context 'when Ldom resolver returns values' do
11 let(:value) do
12 {
13 'chassis_serial' => 'AK00358110',
14 'control_domain' => 'opdx-a0-sun2',
15 'domain_name' => 'sol11-9',
16 'domain_uuid' => 'd7a3a4df-ce8c-47a9-b396-cb5a5f30c0b2',
17 'role_control' => false,
18 'role_io' => false,
19 'role_root' => false,
20 'role_service' => false
21 }
22 end
23
24 before do
25 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return('AK00358110')
26 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return('opdx-a0-sun2')
27 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return('sol11-9')
28 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return('false')
29 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return('false')
30 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return('false')
31 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return('false')
32 allow(Facter::Resolvers::Solaris::Ldom)
33 .to receive(:resolve)
34 .with(:domain_uuid)
35 .and_return('d7a3a4df-ce8c-47a9-b396-cb5a5f30c0b2')
36 end
37
38 it 'returns virtual fact as physical' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'hypervisors.ldom', value: value)
41 end
42 end
43
44 context 'when ldom resolver returns nil' do
45 before do
46 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return(nil)
47 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return(nil)
48 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return(nil)
49 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
50 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
51 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return(nil)
52 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return(nil)
53 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return(nil)
54 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_uuid).and_return(nil)
55 end
56
57 context 'when role_control is false' do
58 let(:value) { nil }
59
60 it 'returns virtual fact as physical' do
61 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
62 have_attributes(name: 'hypervisors.ldom', value: value)
63 end
64 end
65 end
66
67 context 'when ldom resolver returns empty string' do
68 before do
69 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return('')
70 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return('')
71 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return('')
72 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return('')
73 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return('')
74 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return('')
75 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return('')
76 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return('')
77 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_uuid).and_return('')
78 end
79
80 context 'when role_control is false' do
81 let(:value) { nil }
82
83 it 'returns virtual fact as physical' do
84 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
85 have_attributes(name: 'hypervisors.ldom', value: value)
86 end
87 end
88 end
89 end
90 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Hypervisors::Zone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Hypervisors::Zone.new }
5
6 before do
7 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve)
8 allow(Facter::Resolvers::Solaris::Zone).to receive(:resolve)
9 end
10
11 context 'when current zone name is nil' do
12 let(:current_zone_name) { nil }
13
14 before do
15 allow(Facter::Resolvers::Solaris::ZoneName)
16 .to receive(:resolve)
17 .with(:current_zone_name)
18 .and_return(current_zone_name)
19 end
20
21 it 'returns virtual fact as physical' do
22 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
23 have_attributes(name: 'hypervisors.zone', value: nil)
24 end
25 end
26
27 context 'when zone resolver call is nil' do
28 let(:current_zone_name) { 'global' }
29 let(:zones) { nil }
30
31 before do
32 allow(Facter::Resolvers::Solaris::ZoneName)
33 .to receive(:resolve)
34 .with(:current_zone_name)
35 .and_return(current_zone_name)
36 allow(Facter::Resolvers::Solaris::Zone).to receive(:resolve).with(:zone).and_return(zones)
37 end
38
39 it 'returns current zone details' do
40 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
41 have_attributes(name: 'hypervisors.zone', value: nil)
42 end
43 end
44
45 context 'when current zone name is valid' do
46 let(:current_zone_name) { 'global' }
47 let(:zones) do
48 [
49 {
50 brand: 'solaris',
51 id: 0,
52 iptype: 'shared',
53 name: 'global',
54 uuid: '1234',
55 status: 'running',
56 path: 'my/path'
57 },
58 {
59 brand: 'solaris',
60 id: 1,
61 iptype: 'not_shared',
62 name: 'global2',
63 uuid: '4321',
64 status: 'running',
65 path: 'my/path'
66 }
67 ]
68 end
69
70 before do
71 allow(Facter::Resolvers::Solaris::ZoneName)
72 .to receive(:resolve)
73 .with(:current_zone_name)
74 .and_return(current_zone_name)
75 allow(Facter::Resolvers::Solaris::Zone).to receive(:resolve).with(:zone).and_return(zones)
76 end
77
78 it 'returns current zone details' do
79 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
80 have_attributes(name: 'hypervisors.zone', value: {
81 'brand' => 'solaris',
82 'id' => 0,
83 'ip_type' => 'shared',
84 'name' => 'global',
85 'uuid' => '1234'
86 })
87 end
88 end
89 end
90 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Identity::Gid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Identity::Gid.new }
5
6 let(:value) { '20' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:gid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:gid)
15 end
16
17 it 'returns identity gid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.gid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Identity::Group do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Identity::Group.new }
5
6 let(:value) { 'staff' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:group).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:group)
15 end
16
17 it 'returns identity group fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.group', value: value),
20 an_object_having_attributes(name: 'gid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Identity::Privileged do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Identity::Privileged.new }
5
6 let(:value) { 'false' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:privileged).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:privileged)
15 end
16
17 it 'returns identity privileged fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.privileged', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Identity::Uid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Identity::Uid.new }
5
6 let(:value) { '501' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:uid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:uid)
15 end
16
17 it 'returns identity uid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'identity.uid', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Identity::User.new }
5
6 let(:value) { 'root' }
7
8 before do
9 allow(Facter::Resolvers::PosxIdentity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::PosxIdentity' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::PosxIdentity).to have_received(:resolve).with(:user)
15 end
16
17 it 'returns id and identity user name' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Interfaces do
3 subject(:fact) { Facts::Solaris::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns interfaces names' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'interfaces', value: interfaces.keys.sort.join(','), type: :legacy)
20 end
21 end
22
23 describe '#call_the_resolver when resolver returns nil' do
24 let(:interfaces) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
28 have_attributes(name: 'interfaces', value: interfaces, type: :legacy)
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Solaris::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::IpaddressInterfaces do
3 subject(:fact) { Facts::Solaris::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::IsVirtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::IsVirtual.new }
5
6 let(:processor) { 'i386' }
7 let(:logger_double) { instance_spy(Facter::Log) }
8
9 before do
10 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:processor).and_return(processor)
11 end
12
13 context 'when no hypervisor is found' do
14 let(:vm) { false }
15 let(:current_zone_name) { 'global' }
16 let(:role_control) { 'false' }
17
18 before do
19 allow(Facter::Resolvers::Solaris::ZoneName)
20 .to receive(:resolve)
21 .with(:current_zone_name)
22 .and_return(current_zone_name)
23 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
24 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
25 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return('unkown')
26 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return('unkown')
27 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
28 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
29 end
30
31 it 'returns is_virtual fact as false' do
32 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
33 have_attributes(name: 'is_virtual', value: vm)
34 end
35 end
36
37 context 'when Ldom role_control is false, ldom hypervisor is found' do
38 let(:vm) { true }
39 let(:role_control) { 'false' }
40
41 before do
42 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(vm)
43 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(role_control)
44 end
45
46 it 'returns is_virtual fact as true' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
48 have_attributes(name: 'is_virtual', value: vm)
49 end
50 end
51
52 context 'when Ldom role_control is true' do
53 let(:role_control) { 'true' }
54 let(:vm) { false }
55 let(:current_zone_name) { 'global' }
56
57 before do
58 allow(Facter::Resolvers::Solaris::ZoneName)
59 .to receive(:resolve)
60 .with(:current_zone_name)
61 .and_return(current_zone_name)
62 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(vm)
63 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(role_control)
64 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return(nil)
65 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return(nil)
66 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
67 end
68
69 it 'returns is_virtual fact as false' do
70 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
71 have_attributes(name: 'is_virtual', value: vm)
72 end
73 end
74
75 context 'when zone hypervisor is found' do
76 let(:vm) { true }
77
78 before do
79 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve).with(:current_zone_name).and_return(vm)
80 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
81 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
82 end
83
84 it 'returns is_virtual fact as physical' do
85 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
86 have_attributes(name: 'is_virtual', value: vm)
87 end
88 end
89
90 context 'when xen hypervisor is found' do
91 let(:current_zone_name) { 'global' }
92 let(:role_control) { 'false' }
93 let(:xen_vm) { true }
94
95 before do
96 allow(Facter::Resolvers::Solaris::ZoneName)
97 .to receive(:resolve)
98 .with(:current_zone_name)
99 .and_return(current_zone_name)
100 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
101 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
102 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return('unkown')
103 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return('unkown')
104 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(xen_vm)
105 end
106
107 it 'returns is_virtual fact' do
108 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
109 have_attributes(name: 'is_virtual', value: xen_vm)
110 end
111 end
112
113 context 'when other hypervisors' do
114 let(:vm) { 'global' }
115
116 before do
117 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve).with(:current_zone_name).and_return(vm)
118 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
119 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
120 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
121 end
122
123 context 'when processor is i386' do
124 let(:processor) { 'i386' }
125 let(:dmi) { class_double(Facter::Resolvers::Solaris::Dmi).as_stubbed_const }
126
127 before do
128 allow(dmi).to receive(:resolve)
129 end
130
131 it 'calls Dmi resolver for product_name' do
132 fact.call_the_resolver
133 expect(dmi).to have_received(:resolve).with(:product_name)
134 end
135
136 it 'calls Dmi resolver for bios_vendor' do
137 fact.call_the_resolver
138 expect(dmi).to have_received(:resolve).with(:bios_vendor)
139 end
140 end
141
142 context 'when processor is sparc' do
143 let(:processor) { 'sparc' }
144 let(:dmi) { class_spy(Facter::Resolvers::Solaris::DmiSparc).as_stubbed_const }
145
146 before do
147 allow(dmi).to receive(:resolve)
148 end
149
150 it 'calls DmiSparc resolver for product_name' do
151 fact.call_the_resolver
152 expect(dmi).to have_received(:resolve).with(:product_name)
153 end
154
155 it 'calls DmiSparc resolver for bios_vendor' do
156 fact.call_the_resolver
157 expect(dmi).to have_received(:resolve).with(:bios_vendor)
158 end
159 end
160 end
161 end
162 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Kernel.new }
5
6 let(:value) { 'SunOs' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelname)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Kernelmajversion do
3 subject(:fact) { Facts::Solaris::Kernelmajversion.new }
4
5 let(:resolver_value) { '4.15' }
6
7 before do
8 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelversion).and_return(resolver_value)
9 end
10
11 it 'calls Facter::Resolvers::Uname' do
12 fact.call_the_resolver
13 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelversion)
14 end
15
16 shared_examples 'kernelmajversion fact expectation' do
17 it 'returns the correct kernelmajversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelmajversion', value: fact_value)
20 end
21 end
22
23 describe '#call_the_resolver' do
24 context 'when on Solaris 11.4' do
25 let(:resolver_value) { '11.4.0.15.0' }
26 let(:fact_value) { '11.4' }
27
28 include_examples 'kernelmajversion fact expectation'
29 end
30
31 context 'when on Solaris 11.3' do
32 let(:resolver_value) { '11.3' }
33 let(:fact_value) { '11' }
34
35 include_examples 'kernelmajversion fact expectation'
36 end
37
38 context 'when full version does not have a . delimeter' do
39 let(:resolver_value) { '4test' }
40 let(:fact_value) { '4test' }
41
42 include_examples 'kernelmajversion fact expectation'
43 end
44 end
45 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Kernelrelease.new }
5
6 let(:value) { '5.11' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelrelease).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelrelease)
15 end
16
17 it 'returns kernelrelease fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelrelease', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Kernelversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Kernelversion.new }
5
6 let(:value) { '11.4.0.15.0' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:kernelversion)
15 end
16
17 it 'returns kernelversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernelversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ldom do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Ldom.new }
5
6 before do
7 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve)
8 end
9
10 context 'when Ldom resolver returns values' do
11 let(:value) do
12 {
13 'domainchassis' => 'AK00358110',
14 'domaincontrol' => 'opdx-a0-sun2',
15 'domainname' => 'sol11-9',
16 'domainrole' =>
17 {
18 'control' => 'false',
19 'impl' => 'LDoms',
20 'io' => 'false',
21 'root' => 'false',
22 'service' => 'false'
23 },
24 'domainuuid' => 'd7a3a4df-ce8c-47a9-b396-cb5a5f30c0b2'
25 }
26 end
27
28 before do
29 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return('AK00358110')
30 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return('opdx-a0-sun2')
31 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return('sol11-9')
32 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return('false')
33 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return('LDoms')
34 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return('false')
35 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return('false')
36 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return('false')
37 allow(Facter::Resolvers::Solaris::Ldom)
38 .to receive(:resolve)
39 .with(:domain_uuid)
40 .and_return('d7a3a4df-ce8c-47a9-b396-cb5a5f30c0b2')
41 end
42
43 it 'returns virtual fact as ldom' do
44 expect(fact.call_the_resolver).to match_array(
45 [
46 an_object_having_attributes(name: 'ldom', value: value, type: :core),
47 an_object_having_attributes(name: 'ldom_domainchassis', value: 'AK00358110', type: :legacy),
48 an_object_having_attributes(name: 'ldom_domaincontrol', value: 'opdx-a0-sun2', type: :legacy),
49 an_object_having_attributes(name: 'ldom_domainname', value: 'sol11-9', type: :legacy),
50 an_object_having_attributes(name: 'ldom_domainrole_control', value: 'false', type: :legacy),
51 an_object_having_attributes(name: 'ldom_domainrole_impl', value: 'LDoms', type: :legacy),
52 an_object_having_attributes(name: 'ldom_domainrole_io', value: 'false', type: :legacy),
53 an_object_having_attributes(name: 'ldom_domainrole_root', value: 'false', type: :legacy),
54 an_object_having_attributes(name: 'ldom_domainrole_service', value: 'false', type: :legacy),
55 an_object_having_attributes(name: 'ldom_domainuuid', value: 'd7a3a4df-ce8c-47a9-b396-cb5a5f30c0b2',
56 type: :legacy)
57 ]
58 )
59 end
60 end
61
62 context 'when ldom resolver returns nil' do
63 before do
64 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return(nil)
65 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return(nil)
66 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return(nil)
67 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
68 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
69 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return(nil)
70 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return(nil)
71 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return(nil)
72 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_uuid).and_return(nil)
73 end
74
75 context 'when role_control is false' do
76 let(:value) { nil }
77
78 it 'returns virtual fact as nil' do
79 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
80 have_attributes(name: 'ldom', value: value)
81 end
82 end
83 end
84
85 context 'when ldom resolver returns empty string' do
86 before do
87 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:chassis_serial).and_return('')
88 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:control_domain).and_return('')
89 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_name).and_return('')
90 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return('')
91 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return('')
92 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_io).and_return('')
93 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_root).and_return('')
94 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_service).and_return('')
95 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:domain_uuid).and_return('')
96 end
97
98 context 'when role_control is false' do
99 let(:value) { nil }
100
101 it 'returns virtual fact as nil' do
102 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
103 have_attributes(name: 'ldom', value: value)
104 end
105 end
106 end
107 end
108 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::LoadAverages do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::LoadAverages.new }
5
6 let(:value) { { '1m' => 0.01, '5m' => 0.02, '15m' => 0.03 } }
7
8 before do
9 allow(Facter::Resolvers::LoadAverages).to receive(:resolve).with(:load_averages).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Bsd::LoadAverages' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::LoadAverages).to have_received(:resolve).with(:load_averages)
15 end
16
17 it 'returns load_averages fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'load_averages', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::MacaddressInterfaces do
3 subject(:fact) { Facts::Solaris::MacaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mac: '10.16.117.100' }, 'en1' => { mac: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names macaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'macaddress_eth0',
20 value: interfaces['eth0'][:mac], type: :legacy),
21 an_object_having_attributes(name: 'macaddress_en1',
22 value: interfaces['en1'][:mac], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::AvailableBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 2_332_425 }
8 let(:value_mb) { 2.2243738174438477 }
9
10 before do
11 allow(Facter::Resolvers::Solaris::Memory).to \
12 receive(:resolve).with(:swap).and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
18 end
19
20 it 'returns swap available bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: result),
23 an_object_having_attributes(name: 'swapfree_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28
29 it 'returns swap available memory in bytes fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: value),
32 an_object_having_attributes(name: 'swapfree_mb', value: value, type: :legacy))
33 end
34 end
35
36 context 'when resolver returns empty hash' do
37 let(:value) { {} }
38 let(:result) { nil }
39
40 it 'returns swap available memory in bytes fact as nil' do
41 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
42 contain_exactly(an_object_having_attributes(name: 'memory.swap.available_bytes', value: result),
43 an_object_having_attributes(name: 'swapfree_mb', value: result, type: :legacy))
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::Available.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.available', value: result),
22 an_object_having_attributes(name: 'swapfree', value: result, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::Capacity.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024, capacity: '7,9%' } }
7 let(:result) { '7,9%' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap memory capacity fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.capacity', value: result)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::TotalBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 2_332_999 }
8 let(:value_mb) { 2.224921226501465 }
9
10 before do
11 allow(Facter::Resolvers::Solaris::Memory).to \
12 receive(:resolve).with(:swap).and_return(value)
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
18 end
19
20 it 'returns swap total memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: result),
23 an_object_having_attributes(name: 'swapsize_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28
29 it 'returns swap total memory in bytes fact as nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(an_object_having_attributes(name: 'memory.swap.total_bytes', value: value),
32 an_object_having_attributes(name: 'swapsize_mb', value: value, type: :legacy))
33 end
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::Total.new }
5
6 let(:value) { { available_bytes: 24, total_bytes: 1024, used_bytes: 1000 } }
7 let(:result) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.swap.total', value: result),
22 an_object_having_attributes(name: 'swapsize', value: result, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::UsedBytes.new }
5
6 let(:value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:result) { 1024 }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:swap).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap used memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.used_bytes', value: result)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::Swap::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::Swap::Used.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:swap).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:swap)
17 end
18
19 it 'returns swap used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.swap.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::AvailableBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::AvailableBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 2_332_425 }
8 let(:value_mb) { 2.2243738174438477 }
9
10 before do
11 allow(Facter::Resolvers::Solaris::Memory).to \
12 receive(:resolve).with(:system).and_return(resolver_output)
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
18 end
19
20 it 'returns system available memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
23 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28 let(:resolver_output) { nil }
29
30 it 'returns system available memory in bytes fact as nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
32 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
33 an_object_having_attributes(name: 'memoryfree_mb', value: value, type: :legacy))
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::Available.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system available memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
22 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::Capacity.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024, capacity: '5.3%' } }
7 let(:value) { '5.3%' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_output)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system memory capacity fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.capacity', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::TotalBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::TotalBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 2_332_999 }
8 let(:value_mb) { 2.224921226501465 }
9
10 before do
11 allow(Facter::Resolvers::Solaris::Memory).to \
12 receive(:resolve).with(:system).and_return(resolver_output)
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Memory' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
18 end
19
20 it 'returns system total memory in bytes fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
23 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
24 end
25
26 context 'when resolver returns nil' do
27 let(:value) { nil }
28 let(:resolver_output) { nil }
29
30 it 'returns system total memory in bytes fact as nil' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
32 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
33 an_object_having_attributes(name: 'memorysize_mb', value: value, type: :legacy))
34 end
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::Total.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '2.22 MiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system total memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
22 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::UsedBytes.new }
5
6 let(:resolver_output) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { 1024 }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_output)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system used memory in bytes fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.used_bytes', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Memory::System::Used.new }
5
6 let(:resolver_value) { { available_bytes: 2_332_425, total_bytes: 2_332_999, used_bytes: 1024 } }
7 let(:value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Memory).to \
11 receive(:resolve).with(:system).and_return(resolver_value)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Memory' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Memory).to have_received(:resolve).with(:system)
17 end
18
19 it 'returns system used memory fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'memory.system.used', value: value)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Mountpoints do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Mountpoints.new }
5
6 context 'when resolver returns hash' do
7 let(:resolver_output) do
8 [{ available: '5.59 GiB',
9 available_bytes: 6_006_294_016,
10 capacity: '41.76%',
11 device: 'rpool/ROOT/solaris',
12 filesystem: 'zfs',
13 options: ['dev=4490002'],
14 path: '/',
15 size: '9.61 GiB',
16 size_bytes: 10_313_577_472,
17 used: '4.01 GiB',
18 used_bytes: 4_307_283_456 }]
19 end
20
21 let(:parsed_fact) do
22 { '/' => { 'available' => '5.59 GiB',
23 'available_bytes' => 6_006_294_016,
24 'capacity' => '41.76%',
25 'device' => 'rpool/ROOT/solaris',
26 'filesystem' => 'zfs',
27 'options' => ['dev=4490002'],
28 'size' => '9.61 GiB',
29 'size_bytes' => 10_313_577_472,
30 'used' => '4.01 GiB',
31 'used_bytes' => 4_307_283_456 } }
32 end
33
34 before do
35 allow(Facter::Resolvers::Solaris::Mountpoints)
36 .to receive(:resolve).with(:mountpoints).and_return(resolver_output)
37 end
38
39 it 'calls Facter::Resolvers::Mountpoints' do
40 fact.call_the_resolver
41 expect(Facter::Resolvers::Solaris::Mountpoints).to have_received(:resolve).with(:mountpoints)
42 end
43
44 it 'returns mountpoints information' do
45 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
46 have_attributes(name: 'mountpoints', value: parsed_fact)
47 end
48 end
49
50 context 'when resolver returns nil' do
51 before do
52 allow(Facter::Resolvers::Solaris::Mountpoints).to receive(:resolve).with(:mountpoints).and_return(nil)
53 end
54
55 it 'returns mountpoints information' do
56 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
57 have_attributes(name: 'mountpoints', value: nil)
58 end
59 end
60 end
61 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::MtuInterfaces do
3 subject(:fact) { Facts::Solaris::MtuInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mtu: 1500 }, 'en1' => { mtu: 1500 } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names mtu_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'mtu_eth0', value: interfaces['eth0'][:mtu], type: :legacy),
20 an_object_having_attributes(name: 'mtu_en1', value: interfaces['en1'][:mtu], type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver returns nil' do
25 let(:interfaces) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Netmask6Interfaces do
3 subject(:fact) { Facts::Solaris::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::Solaris::Networking' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::NetmaskInterfaces do
3 subject(:fact) { Facts::Solaris::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Network6Interfaces do
3 subject(:fact) { Facts::Solaris::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::NetworkInterfaces do
3 subject(:fact) { Facts::Solaris::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Dhcp do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Dhcp.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:dhcp).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::NetworkingLinux with dhcp' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:dhcp)
15 end
16
17 it 'returns dhcp fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.dhcp', value: value)
21 end
22
23 context 'when dhcp can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.dhcp', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Domain.new }
5
6 let(:value) { 'domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Fqdn.new }
5
6 let(:value) { 'host.domain' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:fqdn).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:fqdn)
15 end
16
17 it 'returns fqdn fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
20 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Hostname.new }
5
6 let(:value) { 'host' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Interfaces do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Interfaces.new }
5
6 let(:value) do
7 {
8 'ens160' => {
9 'bindings' => [
10 {
11 'address' => '10.16.116.8',
12 'netmask' => '255.255.240.0',
13 'network' => '10.16.112.0'
14 }
15 ]
16 }
17 }
18 end
19
20 before do
21 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:interfaces).and_return(value)
22 end
23
24 it 'calls Facter::Resolvers::Solaris::Networking' do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:interfaces)
27 end
28
29 it 'returns networking.interfaces fact' do
30 expect(fact.call_the_resolver)
31 .to be_an_instance_of(Facter::ResolvedFact)
32 .and have_attributes(name: 'networking.interfaces', value: value)
33 end
34
35 context 'when interfaces can not be retrieved' do
36 let(:value) { nil }
37
38 it 'returns nil' do
39 expect(fact.call_the_resolver)
40 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.interfaces', value: value)
41 end
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Ip6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Ip6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:ip6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with ip6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns ipv6 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
20 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
21 end
22
23 context 'when ip6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
29 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Ip.new }
5
6 let(:value) { '10.0.0.1' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Ipaddress).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Macosx::Ipaddress' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Ipaddress).to have_received(:resolve).with(:ip)
15 end
16
17 it 'return ip fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Mac do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Mac.new }
5
6 let(:value) { '64:5a:ed:ea:c3:25' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:mac).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with mac' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'return macaddress fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22
23 context 'when mac can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
29 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Mtu do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Mtu.new }
5
6 let(:value) { 1500 }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:mtu).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with mtu' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:mtu)
15 end
16
17 it 'return mtu fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.mtu', value: value)
21 end
22
23 context 'when mtu can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.mtu', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Netmask6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Netmask6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:netmask6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with netmask6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns netmask6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22
23 context 'when netmask6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
29 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Netmask do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Netmask.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:netmask).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with netmask' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns netmask fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22
23 context 'when netmask can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
29 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Network6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Network6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:network6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with network6' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns network6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22
23 context 'when network6 can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
29 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Network do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Network.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:network).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking with network' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns network fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array)
19 .and contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22
23 context 'when network can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
29 an_object_having_attributes(name: 'network', value: value, type: :legacy))
30 end
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Networking::Primary.new }
5
6 let(:value) { 'ens160' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Solaris::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Solaris::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns networking.primary fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.primary', value: value)
21 end
22
23 context 'when primary interface can not be retrieved' do
24 let(:value) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver)
28 .to be_an_instance_of(Facter::ResolvedFact).and have_attributes(name: 'networking.primary', value: value)
29 end
30 end
31 end
32 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Os::Architecture.new }
5
6 let(:value) { 'i86pc' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:machine)
14 fact.call_the_resolver
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
20 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Os::Family.new }
5
6 let(:value) { 'Solaris' }
7
8 it 'returns os family fact' do
9 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
10 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
11 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
12 end
13 end
14 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Os::Hardware.new }
5
6 let(:value) { 'i86pc' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:machine).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::HardwareArchitecture' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:machine)
15 end
16
17 it 'returns hardware model fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Os::Name.new }
5
6 let(:value) { 'Solaris' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uname' do
13 expect(Facter::Resolvers::Uname).to receive(:resolve).with(:kernelname)
14 fact.call_the_resolver
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Os::Release.new }
5
6 let(:value) { { 'full' => '10_u11', 'minor' => '11', 'major' => '10' } }
7
8 before do
9 allow(Facter::Resolvers::Solaris::OsRelease).to receive(:resolve).with(:full).and_return('10_u11')
10 allow(Facter::Resolvers::Solaris::OsRelease).to receive(:resolve).with(:major).and_return('10')
11 allow(Facter::Resolvers::Solaris::OsRelease).to receive(:resolve).with(:minor).and_return('11')
12 end
13
14 it 'calls Facter::Resolvers::SolarisRelease with full' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::OsRelease).to have_received(:resolve).with(:full)
17 end
18
19 it 'calls Facter::Resolvers::SolarisRelease with major' do
20 fact.call_the_resolver
21 expect(Facter::Resolvers::Solaris::OsRelease).to have_received(:resolve).with(:major)
22 end
23
24 it 'calls Facter::Resolvers::SolarisRelease with minor' do
25 fact.call_the_resolver
26 expect(Facter::Resolvers::Solaris::OsRelease).to have_received(:resolve).with(:minor)
27 end
28
29 it 'returns os.release, operatingsystemmajrelease and operatingsystemrelease fact' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(an_object_having_attributes(name: 'os.release', value: value),
32 an_object_having_attributes(name: 'operatingsystemmajrelease',
33 value: value['major'], type: :legacy),
34 an_object_having_attributes(name: 'operatingsystemrelease', value: value['full'],
35 type: :legacy))
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Path.new }
5
6 let(:value) { '/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java6/jre/bin:/usr/java6/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Cores do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Cores.new }
5
6 let(:cores_per_socket) { 4 }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Processors).to \
10 receive(:resolve).with(:cores_per_socket).and_return(cores_per_socket)
11 end
12
13 it 'calls Facter::Resolvers::Solaris::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:cores_per_socket)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.cores', value: cores_per_socket)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Count.new }
5
6 let(:processors) { '4' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Processors).to \
10 receive(:resolve).with(:logical_count).and_return(processors)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:logical_count)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.count', value: processors),
21 an_object_having_attributes(name: 'processorcount', value: processors, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Isa.new }
5
6 let(:isa) { 'i386' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to \
10 receive(:resolve).with(:processor).and_return(isa)
11 end
12
13 it 'calls Facter::Resolvers::Uname' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Uname).to have_received(:resolve).with(:processor)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: isa),
21 an_object_having_attributes(name: 'hardwareisa', value: isa, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Models do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Models.new }
5
6 let(:value) { 'Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz' }
7 let(:models) { [value, value] }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Processors).to \
11 receive(:resolve).with(:models).and_return(models)
12 end
13
14 it 'calls Facter::Resolvers::Solaris::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:models)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'processors.models', value: models),
22 an_object_having_attributes(name: 'processor0', value: value, type: :legacy),
23 an_object_having_attributes(name: 'processor1', value: value, type: :legacy))
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Physicalcount do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Physicalcount.new }
5
6 let(:physicalcount) { '5' }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Processors).to \
10 receive(:resolve).with(:physical_count).and_return(physicalcount)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:physical_count)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'processors.physicalcount', value: physicalcount),
21 an_object_having_attributes(name: 'physicalprocessorcount', value: physicalcount,
22 type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Speed do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Speed.new }
5
6 let(:speed) { 1_800_000_000 }
7 let(:converted_speed) { '1.80 GHz' }
8
9 before do
10 allow(Facter::Resolvers::Solaris::Processors).to \
11 receive(:resolve).with(:speed).and_return(speed)
12 end
13
14 it 'calls Facter::Resolvers::Macosx::Processors' do
15 fact.call_the_resolver
16 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:speed)
17 end
18
19 it 'returns a resolved fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
21 have_attributes(name: 'processors.speed', value: converted_speed)
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Processors::Threads do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Processors::Threads.new }
5
6 let(:threads_per_core) { 4 }
7
8 before do
9 allow(Facter::Resolvers::Solaris::Processors).to \
10 receive(:resolve).with(:threads_per_core).and_return(threads_per_core)
11 end
12
13 it 'calls Facter::Resolvers::Solaris::Processors' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Solaris::Processors).to have_received(:resolve).with(:threads_per_core)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'processors.threads', value: threads_per_core)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Ruby::Platform.new }
5
6 let(:value) { 'x86_64-linux' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 expect(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
14 fact.call_the_resolver
15 end
16
17 it 'return ruby.platform fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
20 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Ruby::Sitedir.new }
5
6 let(:value) { '/opt/puppetlabs/puppet/lib/ruby/site_ruby/2.5.0' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 expect(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
14 fact.call_the_resolver
15 end
16
17 it 'return ruby sitedir fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
20 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Ruby::Version.new }
5
6 let(:value) { '2.4.5' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
21 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Ssh.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
9 end
10 let(:value) do
11 { 'ecdsa' => { 'fingerprints' =>
12 { 'sha1' => 'test', 'sha256' => 'test' },
13 'key' => 'test',
14 'type' => 'ecdsa' } }
15 end
16
17 before do
18 allow(Facter::Resolvers::Ssh).to \
19 receive(:resolve).with(:ssh).and_return(ssh)
20 end
21
22 it 'calls Facter::Resolvers::Ssh' do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
25 end
26
27 it 'returns a resolved fact' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
29 have_attributes(name: 'ssh', value: value)
30 end
31
32 context 'when resolver returns empty array' do
33 let(:ssh) { [] }
34
35 it 'returns nil fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'ssh', value: nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Sshalgorithmkey.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('test', 'test'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
13 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::SshfpAlgorithm.new }
5
6 let(:ssh) do
7 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
8 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
9 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
10 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
11 end
12 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
13 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
14
15 before do
16 allow(Facter::Resolvers::Ssh).to \
17 receive(:resolve).with(:ssh).and_return(ssh)
18 end
19
20 it 'calls Facter::Resolvers::Ssh' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ssh).to have_received(:resolve).with(:ssh)
23 end
24
25 it 'returns a list of resolved facts' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
27 contain_exactly(an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
28 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value]))
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::SystemUptime::Days.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:days).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:days)
15 end
16
17 it 'returns days since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: value),
20 an_object_having_attributes(name: 'uptime_days', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::SystemUptime::Hours.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:hours).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:hours)
15 end
16
17 it 'returns hours since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: value),
20 an_object_having_attributes(name: 'uptime_hours', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::SystemUptime::Seconds.new }
5
6 let(:value) { 3600 }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:seconds).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:seconds)
15 end
16
17 it 'returns minutes since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: value),
20 an_object_having_attributes(name: 'uptime_seconds', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::SystemUptime::Uptime.new }
5
6 let(:value) { '6 days' }
7
8 before do
9 allow(Facter::Resolvers::Uptime).to receive(:resolve).with(:uptime).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Uptime).to have_received(:resolve).with(:uptime)
15 end
16
17 it 'returns total uptime since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: value),
20 an_object_having_attributes(name: 'uptime', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Virtual do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::Virtual.new }
5
6 let(:processor) { 'i386' }
7
8 before do
9 allow(Facter::Resolvers::Uname).to receive(:resolve).with(:processor).and_return(processor)
10 end
11
12 context 'when no hypervisor is found' do
13 let(:vm) { 'physical' }
14 let(:current_zone_name) { 'global' }
15 let(:role_control) { 'false' }
16
17 before do
18 allow(Facter::Resolvers::Solaris::ZoneName)
19 .to receive(:resolve)
20 .with(:current_zone_name)
21 .and_return(current_zone_name)
22 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
23 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
24 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return('unkown')
25 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return('unkown')
26 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
27 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
28 end
29
30 it 'returns virtual fact as physical' do
31 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
32 have_attributes(name: 'virtual', value: vm)
33 end
34 end
35
36 context 'when Ldom role_control is false, ldom hypervisor is found' do
37 let(:vm) { 'LDoms' }
38 let(:role_control) { 'false' }
39
40 before do
41 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(vm)
42 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(role_control)
43 end
44
45 it 'returns virtual fact as LDoms' do
46 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
47 have_attributes(name: 'virtual', value: vm)
48 end
49 end
50
51 context 'when Ldom role_control is true' do
52 let(:role_control) { 'true' }
53 let(:vm) { 'physical' }
54 let(:current_zone_name) { 'global' }
55
56 before do
57 allow(Facter::Resolvers::Solaris::ZoneName)
58 .to receive(:resolve)
59 .with(:current_zone_name)
60 .and_return(current_zone_name)
61 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(vm)
62 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(role_control)
63 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return(nil)
64 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return(nil)
65 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
66 end
67
68 it 'returns virtual fact as physical' do
69 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
70 have_attributes(name: 'virtual', value: vm)
71 end
72 end
73
74 context 'when zone hypervisor is found' do
75 let(:vm) { 'zone' }
76
77 before do
78 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve).with(:current_zone_name).and_return(vm)
79 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
80 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
81 end
82
83 it 'returns virtual fact as physical' do
84 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
85 have_attributes(name: 'virtual', value: vm)
86 end
87 end
88
89 context 'when xen hypervisor is found' do
90 let(:current_zone_name) { 'global' }
91 let(:role_control) { 'false' }
92 let(:xen_vm) { 'xenhvm' }
93
94 before do
95 allow(Facter::Resolvers::Solaris::ZoneName)
96 .to receive(:resolve)
97 .with(:current_zone_name)
98 .and_return(current_zone_name)
99 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
100 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
101 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:product_name).and_return('unkown')
102 allow(Facter::Resolvers::Solaris::Dmi).to receive(:resolve).with(:bios_vendor).and_return('unkown')
103 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(xen_vm)
104 end
105
106 it 'returns virtual fact' do
107 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
108 have_attributes(name: 'virtual', value: xen_vm)
109 end
110 end
111
112 context 'when other hypervisors' do
113 let(:vm) { 'global' }
114
115 before do
116 allow(Facter::Resolvers::Solaris::ZoneName).to receive(:resolve).with(:current_zone_name).and_return(vm)
117 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_impl).and_return(nil)
118 allow(Facter::Resolvers::Solaris::Ldom).to receive(:resolve).with(:role_control).and_return(nil)
119 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
120 end
121
122 context 'when processor is i386' do
123 let(:processor) { 'i386' }
124 let(:dmi) { class_double(Facter::Resolvers::Solaris::Dmi).as_stubbed_const }
125
126 before do
127 allow(dmi).to receive(:resolve)
128 end
129
130 it 'calls Dmi resolver for product_name' do
131 fact.call_the_resolver
132 expect(dmi).to have_received(:resolve).with(:product_name)
133 end
134
135 it 'calls Dmi resolver for bios_vendor' do
136 fact.call_the_resolver
137 expect(dmi).to have_received(:resolve).with(:bios_vendor)
138 end
139 end
140
141 context 'when processor is sparc' do
142 let(:processor) { 'sparc' }
143 let(:dmi) { class_double(Facter::Resolvers::Solaris::DmiSparc).as_stubbed_const }
144
145 before do
146 allow(dmi).to receive(:resolve)
147 end
148
149 it 'calls DmiSparc resolver for product_name' do
150 fact.call_the_resolver
151 expect(dmi).to have_received(:resolve).with(:product_name)
152 end
153
154 it 'calls DmiSparc resolver for bios_vendor' do
155 fact.call_the_resolver
156 expect(dmi).to have_received(:resolve).with(:bios_vendor)
157 end
158 end
159 end
160 end
161 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::ZfsFeaturenumbers do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::ZfsFeaturenumbers.new }
5
6 let(:feature_numbers) { '1,2,3,4,5' }
7
8 before do
9 allow(Facter::Resolvers::ZFS).to receive(:resolve).with(:zfs_featurenumbers).and_return(feature_numbers)
10 end
11
12 it 'calls Facter::Resolvers::ZFS' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::ZFS).to have_received(:resolve).with(:zfs_featurenumbers)
15 end
16
17 it 'returns zfs_featurenumbers fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zfs_featurenumbers', value: feature_numbers)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::ZfsVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::ZfsVersion.new }
5
6 let(:version) { '6' }
7
8 before do
9 allow(Facter::Resolvers::ZFS).to receive(:resolve).with(:zfs_version).and_return(version)
10 end
11
12 it 'calls Facter::Resolvers::ZFS' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::ZFS).to have_received(:resolve).with(:zfs_version)
15 end
16
17 it 'returns zfs_version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zfs_version', value: version)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::Zones do
3 subject(:fact) { Facts::Solaris::Zones.new }
4
5 let(:zone_name) { 'global' }
6 let(:result) do
7 { brand: 'solaris',
8 id: '0',
9 iptype: 'shared',
10 name: 'global',
11 uuid: nil,
12 status: nil,
13 path: nil }
14 end
15
16 let(:result_fact) do
17 { zone_name => { 'brand' => 'solaris',
18 'id' => '0',
19 'ip_type' => 'shared',
20 'status' => nil,
21 'path' => nil } }
22 end
23
24 before do
25 allow(Facter::Resolvers::Solaris::Zone).to receive(:resolve).with(:zone).and_return([result])
26 end
27
28 describe '#call_the_resolver' do
29 it 'returns a fact' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
31 contain_exactly(
32 an_object_having_attributes(name: 'solaris_zones.zones', value: result_fact, type: :core),
33 an_object_having_attributes(name: 'zone_global_id', value: result[:id], type: :legacy),
34 an_object_having_attributes(name: 'zone_global_uuid', value: result[:uuid], type: :legacy),
35 an_object_having_attributes(name: 'zone_global_name', value: result[:name], type: :legacy),
36 an_object_having_attributes(name: 'zone_global_path', value: result[:path], type: :legacy),
37 an_object_having_attributes(name: 'zone_global_status', value: result[:status], type: :legacy),
38 an_object_having_attributes(name: 'zone_global_brand', value: result[:brand], type: :legacy),
39 an_object_having_attributes(name: 'zone_global_iptype', value: result[:iptype], type: :legacy),
40 an_object_having_attributes(name: 'zones', value: 1, type: :legacy)
41 )
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::ZpoolFeatureflags do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::ZpoolFeatureflags.new }
5
6 let(:zpool_feature_flags) { 'async_destroy,empty_bpobj,lz4_compress,multi_vdev_crash_dump,spacemap_histogram' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to \
10 receive(:resolve).with(:zpool_featureflags).and_return(zpool_feature_flags)
11 end
12
13 it 'calls Facter::Resolvers::ZPool' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_featureflags)
16 end
17
18 it 'returns the zpool_featureflags fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'zpool_featureflags', value: zpool_feature_flags)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::ZpoolFeaturenumbers do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::ZpoolFeaturenumbers.new }
5
6 let(:zpool_featurenumbers) { '1,2,3,4,5,6,7' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to \
10 receive(:resolve).with(:zpool_featurenumbers).and_return(zpool_featurenumbers)
11 end
12
13 it 'calls Facter::Resolvers::ZPool' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_featurenumbers)
16 end
17
18 it 'returns the zpool_featurenumbers fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'zpool_featurenumbers', value: zpool_featurenumbers)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Solaris::ZpoolVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Solaris::ZpoolVersion.new }
5
6 let(:version) { '5' }
7
8 before do
9 allow(Facter::Resolvers::Zpool).to receive(:resolve).with(:zpool_version).and_return(version)
10 end
11
12 it 'calls Facter::Resolvers::ZPool' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Zpool).to have_received(:resolve).with(:zpool_version)
15 end
16
17 it 'returns the ZPool version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'zpool_version', value: version)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Ubuntu::Lsbdistrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Ubuntu::Lsbdistrelease.new }
5
6 context 'when lsb-release is installed' do
7 before do
8 allow(Facter::Resolvers::LsbRelease).to receive(:resolve).with(:release).and_return(value)
9 end
10
11 context 'when version_id is retrieved successful' do
12 let(:value) { '18.04' }
13 let(:value_final) { { 'full' => '18.04', 'major' => '18.04', 'minor' => nil } }
14
15 it 'calls Facter::Resolvers::LsbRelease with :name' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::LsbRelease).to have_received(:resolve).with(:release)
18 end
19
20 it 'returns release fact' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'lsbdistrelease', value: value, type: :legacy),
23 an_object_having_attributes(name: 'lsbmajdistrelease',
24 value: value_final['major'], type: :legacy),
25 an_object_having_attributes(name: 'lsbminordistrelease',
26 value: value_final['minor'], type: :legacy))
27 end
28 end
29
30 context 'when lsb-release is not installed' do
31 let(:value) { nil }
32
33 it 'returns release fact as nil' do
34 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
35 have_attributes(name: 'lsbdistrelease', value: value, type: :legacy)
36 end
37 end
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 describe Facts::Ubuntu::Os::Distro::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Ubuntu::Os::Distro::Release.new }
5
6 shared_examples 'returns distro release fact' do
7 it 'returns release fact' do
8 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
9 have_attributes(name: 'os.distro.release', value: fact_value)
10 end
11 end
12
13 before do
14 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(os_release_value)
15 end
16
17 context 'when version_id is retrieved successful' do
18 let(:os_release_value) { '18.04' }
19 let(:fact_value) { { 'full' => '18.04', 'major' => '18.04' } }
20
21 it 'calls Facter::Resolvers::OsRelease with :version_id' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
24 end
25
26 it_behaves_like 'returns distro release fact'
27 end
28
29 context 'when version_id could not be retrieved' do
30 let(:os_release_value) { nil }
31 let(:fact_value) { nil }
32
33 it_behaves_like 'returns distro release fact'
34 end
35
36 context 'when version has no minor' do
37 let(:os_release_value) { 'experimental_release' }
38 let(:fact_value) { { 'full' => 'experimental_release', 'major' => 'experimental_release' } }
39
40 it_behaves_like 'returns distro release fact'
41 end
42 end
43 end
0 # frozen_string_literal: true
1
2 describe Facts::Ubuntu::Os::Release do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Ubuntu::Os::Release.new }
5
6 let(:value) { '10.9' }
7
8 before do
9 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:version_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::OsRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:version_id)
15 end
16
17 it 'returns release fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value, 'major' => value }),
20 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value, type: :legacy),
21 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Augeas::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Augeas::Version.new }
5
6 let(:version) { '1.12.0' }
7
8 before do
9 allow(Facter::Resolvers::Augeas).to \
10 receive(:resolve).with(:augeas_version).and_return(version)
11 end
12
13 it 'calls Facter::Resolvers::Augeas' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Augeas).to have_received(:resolve).with(:augeas_version)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'augeas.version', value: version),
21 an_object_having_attributes(name: 'augeasversion', value: version, type: :legacy))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::AzMetadata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::AzMetadata.new }
5
6 before do
7 allow(Facter::Resolvers::Az).to receive(:resolve).with(:metadata).and_return(value)
8 end
9
10 context 'when physical machine with no hypervisor' do
11 let(:value) { nil }
12
13 before do
14 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return(nil)
15 end
16
17 it 'returns az metadata fact as nil' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'az_metadata', value: value)
20 end
21
22 it "doesn't call az resolver" do
23 fact.call_the_resolver
24 expect(Facter::Resolvers::Az).not_to have_received(:resolve).with(:metadata)
25 end
26 end
27
28 context 'when platform is hyperv' do
29 let(:value) { { 'info' => 'value' } }
30
31 before do
32 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('hyperv')
33 end
34
35 context 'when on Azure' do
36 it 'calls the az resolver' do
37 fact.call_the_resolver
38
39 expect(Facter::Resolvers::Az).to have_received(:resolve).with(:metadata)
40 end
41
42 it 'returns az_metadata fact' do
43 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
44 have_attributes(name: 'az_metadata', value: value)
45 end
46 end
47
48 context 'when not on Azure' do
49 let(:value) { nil }
50
51 it 'returns az_metadata fact as nil' do
52 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
53 have_attributes(name: 'az_metadata', value: value)
54 end
55 end
56 end
57 end
58 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Cloud::Provider do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Cloud::Provider.new }
5
6 describe 'when on xen' do
7 before do
8 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
9 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('xen')
10 end
11
12 describe 'Ec2 data exists and aws fact is set' do
13 let(:value) { { 'some' => 'fact' } }
14
15 it 'Testing things' do
16 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
17 have_attributes(name: 'cloud.provider', value: 'aws')
18 end
19 end
20
21 context 'when Ec2 data does not exist nil is returned' do
22 let(:value) { {} }
23
24 it 'returns nil' do
25 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
26 have_attributes(name: 'cloud.provider', value: nil)
27 end
28 end
29 end
30
31 describe 'when on kvm' do
32 before do
33 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
34 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('kvm')
35 end
36
37 describe 'Ec2 data exists and aws fact is set' do
38 let(:value) { { 'some' => 'fact' } }
39
40 it 'Testing things' do
41 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
42 have_attributes(name: 'cloud.provider', value: 'aws')
43 end
44 end
45
46 context 'when Ec2 data does not exist nil is returned' do
47 let(:value) { {} }
48
49 it 'returns nil' do
50 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
51 have_attributes(name: 'cloud.provider', value: nil)
52 end
53 end
54 end
55
56 context 'when on hyperv' do
57 before do
58 allow(Facter::Resolvers::Az).to receive(:resolve).with(:metadata).and_return(value)
59 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('hyperv')
60 end
61
62 context 'when az_metadata exists' do
63 let(:value) { { 'some' => 'fact' } }
64
65 it 'returns azure as cloud.provider' do
66 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
67 have_attributes(name: 'cloud.provider', value: 'azure')
68 end
69 end
70
71 context 'when az_metadata does not exist' do
72 let(:value) { {} }
73
74 it 'returns nil' do
75 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
76 have_attributes(name: 'cloud.provider', value: nil)
77 end
78 end
79 end
80
81 context 'when on gce' do
82 before do
83 allow(Facter::Resolvers::Gce).to receive(:resolve).with(:metadata).and_return(value)
84 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('gce')
85 end
86
87 describe 'with the "gce" fact having data' do
88 let(:value) { { 'some' => 'metadata' } }
89
90 it 'resolves a provider of "gce"' do
91 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
92 have_attributes(name: 'cloud.provider', value: 'gce')
93 end
94 end
95
96 context 'with the "gce" fact being empty' do
97 let(:value) { {} }
98
99 it 'resolves to nil' do
100 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
101 have_attributes(name: 'cloud.provider', value: nil)
102 end
103 end
104 end
105
106 context 'when on a physical machine' do
107 before do
108 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return(nil)
109 end
110
111 it 'returns nil' do
112 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
113 have_attributes(name: 'cloud.provider', value: nil)
114 end
115 end
116 end
117 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::DhcpServers do
3 subject(:fact) { Facts::Windows::DhcpServers.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:dhcp).and_return(dhcp)
8 end
9
10 describe '#call_the_resolver' do
11 let(:value) { { 'system' => '10.16.122.163', 'eth0' => '10.16.122.163', 'en1' => '10.32.10.163' } }
12 let(:interfaces) { { 'eth0' => { dhcp: '10.16.122.163' }, 'en1' => { dhcp: '10.32.10.163' } } }
13 let(:dhcp) { '10.16.122.163' }
14
15 it 'calls Facter::Resolvers::Windows::Networking with interfaces' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'calls Facter::Resolvers::NetworkingLinux with dhcp' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:dhcp)
23 end
24
25 it 'returns dhcp_servers' do
26 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
27 have_attributes(name: 'dhcp_servers', value: value, type: :legacy)
28 end
29 end
30
31 describe '#call_the_resolver when resolver returns nil' do
32 let(:interfaces) { nil }
33 let(:dhcp) { nil }
34
35 it 'returns nil' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
37 have_attributes(name: 'dhcp_servers', value: nil, type: :legacy)
38 end
39 end
40 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Dmi::Manufacturer do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Dmi::Manufacturer.new }
5
6 let(:value) { 'VMware, Inc.' }
7
8 before do
9 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::DMIBios' do
13 expect(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer)
14 fact.call_the_resolver
15 end
16
17 it 'returns manufacturer fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'dmi.manufacturer', value: value),
20 an_object_having_attributes(name: 'manufacturer', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Dmi::Product::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Dmi::Product::Name.new }
5
6 let(:value) { 'VMware7,1' }
7
8 before do
9 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::DMIComputerSystem' do
13 expect(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name)
14 fact.call_the_resolver
15 end
16
17 it 'returns product name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'dmi.product.name', value: value),
20 an_object_having_attributes(name: 'productname', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Dmi::Product::SerialNumber do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Dmi::Product::SerialNumber.new }
5
6 let(:value) { 'VMware-42 1a 0d 03 0a b7 98 28-78 98 5e 85 a0 ad 18 47' }
7 let(:expected_resolved_fact) { double(Facter::ResolvedFact, name: 'dmi.product.serial_number', value: value) }
8 let(:resolved_legacy_fact) { double(Facter::ResolvedFact, name: 'serialnumber', value: value, type: :legacy) }
9
10 before do
11 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:serial_number).and_return(value)
12 end
13
14 it 'calls Facter::Resolvers::DMIBios' do
15 expect(Facter::Resolvers::DMIBios).to receive(:resolve).with(:serial_number)
16 fact.call_the_resolver
17 end
18
19 it 'returns serial_number fact' do
20 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
21 contain_exactly(an_object_having_attributes(name: 'dmi.product.serial_number', value: value),
22 an_object_having_attributes(name: 'serialnumber', value: value, type: :legacy))
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Dmi::Product::Uuid do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Dmi::Product::Uuid.new }
5
6 let(:value) { '030D1A42-B70A-2898-7898-5E85A0AD1847' }
7
8 before do
9 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:uuid).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::DMIComputerSystem' do
13 expect(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:uuid)
14 fact.call_the_resolver
15 end
16
17 it 'returns product uuid fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'dmi.product.uuid', value: value),
20 an_object_having_attributes(name: 'uuid', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ec2Metadata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ec2Metadata.new }
5
6 before do
7 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:metadata).and_return(value)
8 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return(hypervisor)
9 end
10
11 context 'when hypervisor is not kvm or xen' do
12 let(:hypervisor) { nil }
13 let(:value) { nil }
14
15 it 'returns ec2 metadata fact as nil' do
16 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
17 have_attributes(name: 'ec2_metadata', value: nil)
18 end
19
20 it "doesn't call Ec2 resolver" do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:metadata)
23 end
24 end
25
26 context 'when hypervisor is xen' do
27 let(:hypervisor) { 'xen' }
28
29 context 'when resolver returns a value' do
30 let(:value) { 'some custom value' }
31
32 it 'calls Facter::Resolvers::Ec2' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:metadata)
35 end
36
37 it 'returns ec2 metadata fact' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
39 have_attributes(name: 'ec2_metadata', value: value)
40 end
41 end
42
43 context 'when resolver returns empty string' do
44 let(:value) { '' }
45
46 it 'returns ec2 userdata fact as nil' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
48 have_attributes(name: 'ec2_metadata', value: nil)
49 end
50 end
51
52 context 'when resolver returns nil' do
53 let(:value) { nil }
54
55 it 'returns ec2 metadata fact as nil' do
56 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
57 have_attributes(name: 'ec2_metadata', value: nil)
58 end
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ec2Userdata do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ec2Userdata.new }
5
6 before do
7 allow(Facter::Resolvers::Ec2).to receive(:resolve).with(:userdata).and_return(value)
8 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return(hypervisor)
9 end
10
11 context 'when hypervisor is not kvm or xen' do
12 let(:hypervisor) { nil }
13 let(:value) { nil }
14
15 it 'returns ec2 userdata fact as nil' do
16 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
17 have_attributes(name: 'ec2_userdata', value: nil)
18 end
19
20 it "doesn't call Ec2 resolver" do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::Ec2).not_to have_received(:resolve).with(:userdata)
23 end
24 end
25
26 context 'when hypervisor is xen' do
27 let(:hypervisor) { 'xen' }
28
29 context 'when resolver returns a value' do
30 let(:value) { 'some custom value' }
31
32 it 'calls Facter::Resolvers::Ec2' do
33 fact.call_the_resolver
34 expect(Facter::Resolvers::Ec2).to have_received(:resolve).with(:userdata)
35 end
36
37 it 'returns ec2 userdata fact' do
38 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
39 have_attributes(name: 'ec2_userdata', value: value)
40 end
41 end
42
43 context 'when resolver returns empty string' do
44 let(:value) { '' }
45
46 it 'returns ec2 userdata fact as nil' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
48 have_attributes(name: 'ec2_userdata', value: nil)
49 end
50 end
51
52 context 'when resolver returns nil' do
53 let(:value) { nil }
54
55 it 'returns ec2 userdata fact as nil' do
56 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
57 have_attributes(name: 'ec2_userdata', value: nil)
58 end
59 end
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Facterversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Facterversion.new }
5
6 let(:value) { '4.0.3' }
7
8 before do
9 allow(Facter::Resolvers::Facterversion).to receive(:resolve).with(:facterversion).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Facterversion' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Facterversion).to have_received(:resolve).with(:facterversion)
15 end
16
17 it 'returns facterversion fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'facterversion', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::FipsEnabled do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::FipsEnabled.new }
5
6 let(:value) { true }
7
8 before do
9 allow(Facter::Resolvers::Windows::Fips).to receive(:resolve).with(:fips_enabled).and_return(value)
10 end
11
12 it 'calls Facter::Windows::Resolvers::Fips' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Fips).to have_received(:resolve).with(:fips_enabled)
15 end
16
17 it 'returns true if fips enabled' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'fips_enabled', value: true)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Hypervisors::Hyperv do
3 describe '#call_the_resolver' do
4 context 'when is not HyperV hypervisor' do
5 it 'returns nil' do
6 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.hyperv', value: nil)
7 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
8 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('value')
9 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.hyperv', nil).and_return(expected_fact)
10
11 fact = Facts::Windows::Hypervisors::Hyperv.new
12 expect(fact.call_the_resolver).to eq(expected_fact)
13 end
14 end
15
16 context 'when is HyperV hypervisor and CpuidSource resolver returns the required output' do
17 it 'returns a fact' do
18 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.hyperv', value: {})
19 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('hyperv')
20 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.hyperv', {}).and_return(expected_fact)
21
22 fact = Facts::Windows::Hypervisors::Hyperv.new
23 expect(fact.call_the_resolver).to eq(expected_fact)
24 end
25 end
26
27 context 'when is HyperV hypervisor and DmiBios resolver returns the required output' do
28 it 'returns a fact' do
29 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.hyperv', value: {})
30 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
31 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('Microsoft Enterprise')
32 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.hyperv', {}).and_return(expected_fact)
33
34 fact = Facts::Windows::Hypervisors::Hyperv.new
35 expect(fact.call_the_resolver).to eq(expected_fact)
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Hypervisors::Kvm do
3 describe '#call_the_resolver' do
4 context 'when is not kvm hypervisor' do
5 it 'returns nil' do
6 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.kvm', value: nil)
7 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
8 allow(Facter::Resolvers::NetKVM).to receive(:resolve).with(:kvm).and_return(false)
9 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('value')
10 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.kvm', nil).and_return(expected_fact)
11
12 fact = Facts::Windows::Hypervisors::Kvm.new
13 expect(fact.call_the_resolver).to eq(expected_fact)
14 end
15 end
16
17 context 'when is kvm hypervisor but product name is parallels' do
18 it 'returns a fact' do
19 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.kvm', value: nil)
20 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('kvm')
21 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('Parallels')
22 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.kvm', nil).and_return(expected_fact)
23
24 fact = Facts::Windows::Hypervisors::Kvm.new
25 expect(fact.call_the_resolver).to eq(expected_fact)
26 end
27 end
28
29 context 'when is kvm hypervisor and openstack' do
30 it 'returns a fact' do
31 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.kvm', value: { openstack: true })
32 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('kvm')
33 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('OpenStack')
34 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('value')
35 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.kvm', openstack: true).and_return(expected_fact)
36
37 fact = Facts::Windows::Hypervisors::Kvm.new
38 expect(fact.call_the_resolver).to eq(expected_fact)
39 end
40 end
41
42 context 'when is kvm hypervisor and gce' do
43 it 'returns a fact' do
44 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.kvm', value: { google: true })
45 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('gce')
46 allow(Facter::Resolvers::NetKVM).to receive(:resolve).with(:kvm).and_return(true)
47 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('value')
48 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('Google')
49 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.kvm', google: true).and_return(expected_fact)
50
51 fact = Facts::Windows::Hypervisors::Kvm.new
52 expect(fact.call_the_resolver).to eq(expected_fact)
53 end
54 end
55 end
56 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Hypervisors::Virtualbox do
3 describe '#call_the_resolver' do
4 context 'when is not Virtualbox hypervisor' do
5 it 'returns nil' do
6 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.virtualbox', value: nil)
7 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
8 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('value')
9 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.virtualbox', nil).and_return(expected_fact)
10
11 fact = Facts::Windows::Hypervisors::Virtualbox.new
12 expect(fact.call_the_resolver).to eq(expected_fact)
13 end
14 end
15
16 context 'when is VirtualBox hypervisor and CpuidSource resolver returns the required output' do
17 it 'returns a fact' do
18 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.virtualbox', value:
19 { revision: ' 13.4', version: ' 13.4' })
20 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('virtualbox')
21 allow(Facter::Resolvers::Windows::Virtualization)
22 .to receive(:resolve).with(:oem_strings)
23 .and_return(['vboxVer_ 13.4', 'vboxRev_ 13.4'])
24 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.virtualbox', revision: ' 13.4', version: ' 13.4')
25 .and_return(expected_fact)
26
27 fact = Facts::Windows::Hypervisors::Virtualbox.new
28 expect(fact.call_the_resolver).to eq(expected_fact)
29 end
30 end
31
32 context 'when is VirtualBox hypervisor and DMIComputerSystem resolver returns the required output' do
33 it 'returns a fact' do
34 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.virtualbox', value:
35 { revision: '', version: '' })
36 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
37 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('VirtualBox')
38 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:oem_strings).and_return(['', ''])
39 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.virtualbox', revision: '', version: '')
40 .and_return(expected_fact)
41
42 fact = Facts::Windows::Hypervisors::Virtualbox.new
43 expect(fact.call_the_resolver).to eq(expected_fact)
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Hypervisors::Vmware do
3 describe '#call_the_resolver' do
4 context 'when is not VMware hypervisor' do
5 it 'returns nil' do
6 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.vmware', value: nil)
7 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
8 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('value')
9 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.vmware', nil).and_return(expected_fact)
10
11 fact = Facts::Windows::Hypervisors::Vmware.new
12 expect(fact.call_the_resolver).to eq(expected_fact)
13 end
14 end
15
16 context 'when is VMware hypervisor and virtualization resolver returns the required output' do
17 it 'returns a fact' do
18 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.vmware', value: {})
19 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('vmware')
20 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.vmware', {}).and_return(expected_fact)
21
22 fact = Facts::Windows::Hypervisors::Vmware.new
23 expect(fact.call_the_resolver).to eq(expected_fact)
24 end
25 end
26
27 context 'when is VMware hypervisor and DmiBios resolver returns the required output' do
28 it 'returns a fact' do
29 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.vmware', value: {})
30 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
31 allow(Facter::Resolvers::DMIBios).to receive(:resolve).with(:manufacturer).and_return('VMware, Inc.')
32 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.vmware', {}).and_return(expected_fact)
33
34 fact = Facts::Windows::Hypervisors::Vmware.new
35 expect(fact.call_the_resolver).to eq(expected_fact)
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Hypervisors::Xen do
3 describe '#call_the_resolver' do
4 context 'when is not xen hypervisor' do
5 it 'returns nil' do
6 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.xen', value: nil)
7 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
8 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.xen', nil).and_return(expected_fact)
9
10 fact = Facts::Windows::Hypervisors::Xen.new
11 expect(fact.call_the_resolver).to eq(expected_fact)
12 end
13 end
14
15 context 'when is xen hypervisor and context not hvm' do
16 it 'returns a fact' do
17 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.xen', value: { context: 'pv' })
18 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('xen')
19 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('PV domU')
20 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.xen', context: 'pv').and_return(expected_fact)
21
22 fact = Facts::Windows::Hypervisors::Xen.new
23 expect(fact.call_the_resolver).to eq(expected_fact)
24 end
25 end
26
27 context 'when is xen hypervisor and context hvm' do
28 it 'returns a fact' do
29 expected_fact = double(Facter::ResolvedFact, name: 'hypervisors.xen', value: { context: 'hvm' })
30 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('xen')
31 allow(Facter::Resolvers::DMIComputerSystem).to receive(:resolve).with(:name).and_return('HVM domU')
32 allow(Facter::ResolvedFact).to receive(:new).with('hypervisors.xen', context: 'hvm').and_return(expected_fact)
33
34 fact = Facts::Windows::Hypervisors::Xen.new
35 expect(fact.call_the_resolver).to eq(expected_fact)
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Identity::Privileged do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'identity.privileged', value: 'value')
6 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('identity.privileged', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Identity::Privileged.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Identity::User do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Identity::User.new }
5
6 let(:value) { 'User\Administrator' }
7
8 before do
9 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:user).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Identity' do
13 expect(Facter::Resolvers::Identity).to receive(:resolve).with(:user)
14 fact.call_the_resolver
15 end
16
17 it 'returns user name' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'identity.user', value: value),
20 an_object_having_attributes(name: 'id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Interfaces do
3 subject(:fact) { Facts::Windows::Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns interfaces names' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'interfaces', value: interfaces.keys.join(','), type: :legacy)
20 end
21 end
22
23 describe '#call_the_resolver when resolver returns nil' do
24 let(:interfaces) { nil }
25
26 it 'returns nil' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
28 have_attributes(name: 'interfaces', value: interfaces, type: :legacy)
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ipaddress6Interfaces do
3 subject(:fact) { Facts::Windows::Ipaddress6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip6: 'fe80::99bf:da20:ad3:9bfe' }, 'en1' => { ip6: 'fe80::99bf:da20:ad3:9bfe' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress6_eth0',
20 value: interfaces['eth0'][:ip6], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress6_en1',
22 value: interfaces['en1'][:ip6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::IpaddressInterfaces do
3 subject(:fact) { Facts::Windows::IpaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { ip: '10.16.117.100' }, 'en1' => { ip: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names ipaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ipaddress_eth0',
20 value: interfaces['eth0'][:ip], type: :legacy),
21 an_object_having_attributes(name: 'ipaddress_en1',
22 value: interfaces['en1'][:ip], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::IsVirtual do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = instance_double(Facter::ResolvedFact, name: 'is_virtual', value: 'value')
6 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:is_virtual).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('is_virtual', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::IsVirtual.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Kernel do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Kernel.new }
5
6 let(:kernel_version) { '2016' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernel).and_return(kernel_version)
10 end
11
12 it 'calls Facter::Resolvers::Kernel' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernel)
15 end
16
17 it 'returns kernel fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'kernel', value: kernel_version)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Kernelmajversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Kernelmajversion.new }
5
6 let(:kernel_maj_version) { '2016' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to \
10 receive(:resolve).with(:kernelmajorversion).and_return(kernel_maj_version)
11 end
12
13 it 'calls Facter::Resolvers::Kernel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernelmajorversion)
16 end
17
18 it 'returns kernelmajversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelmajversion', value: kernel_maj_version)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Kernelrelease do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Kernelrelease.new }
5
6 let(:kernel_release) { '2016' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to \
10 receive(:resolve).with(:kernelversion).and_return(kernel_release)
11 end
12
13 it 'calls Facter::Resolvers::Kernel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernelversion)
16 end
17
18 it 'returns kernelrelease fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelrelease', value: kernel_release)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Kernelversion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Kernelversion.new }
5
6 let(:kernel_version) { '2016' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to \
10 receive(:resolve).with(:kernelversion).and_return(kernel_version)
11 end
12
13 it 'calls Facter::Resolvers::Kernel' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernelversion)
16 end
17
18 it 'returns kernelversion fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'kernelversion', value: kernel_version)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::MacaddressInterfaces do
3 subject(:fact) { Facts::Windows::MacaddressInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mac: '10.16.117.100' }, 'en1' => { mac: '10.16.117.255' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names macaddress_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'macaddress_eth0',
20 value: interfaces['eth0'][:mac], type: :legacy),
21 an_object_having_attributes(name: 'macaddress_en1',
22 value: interfaces['en1'][:mac], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::AvailableBytes do
3 subject(:fact) { Facts::Windows::Memory::System::AvailableBytes.new }
4
5 before do
6 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:available_bytes).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 3_331_551_232 }
11 let(:value_mb) { 3177.21484375 }
12
13 it 'calls Facter::Resolvers::Memory' do
14 expect(Facter::Resolvers::Memory).to receive(:resolve).with(:available_bytes)
15 fact.call_the_resolver
16 end
17
18 it 'returns available memory fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
21 an_object_having_attributes(name: 'memoryfree_mb', value: value_mb, type: :legacy))
22 end
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns available memory fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.system.available_bytes', value: value),
31 an_object_having_attributes(name: 'memoryfree_mb', value: value, type: :legacy))
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::Available do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Memory::System::Available.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:available_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Memory' do
13 expect(Facter::Resolvers::Memory).to receive(:resolve).with(:available_bytes)
14 fact.call_the_resolver
15 end
16
17 it 'returns free memory fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.available', value: value),
20 an_object_having_attributes(name: 'memoryfree', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::Capacity do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.system.capacity', value: 'value')
6 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:capacity).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('memory.system.capacity', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Memory::System::Capacity.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::TotalBytes do
3 subject(:fact) { Facts::Windows::Memory::System::TotalBytes.new }
4
5 before do
6 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:total_bytes).and_return(value)
7 end
8
9 describe '#call_the_resolver' do
10 let(:value) { 3_331_551_232 }
11 let(:value_mb) { 3177.21484375 }
12
13 it 'calls Facter::Resolvers::Memory' do
14 expect(Facter::Resolvers::Memory).to receive(:resolve).with(:total_bytes)
15 fact.call_the_resolver
16 end
17
18 it 'returns total memory im bytes fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
20 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
21 an_object_having_attributes(name: 'memorysize_mb', value: value_mb, type: :legacy))
22 end
23 end
24
25 describe '#call_the_resolver when resolver returns nil' do
26 let(:value) { nil }
27
28 it 'returns total memory in bytes fact as nil' do
29 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
30 contain_exactly(an_object_having_attributes(name: 'memory.system.total_bytes', value: value),
31 an_object_having_attributes(name: 'memorysize_mb', value: value, type: :legacy))
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::Total do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Memory::System::Total.new }
5
6 let(:value) { '1.00 KiB' }
7
8 before do
9 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:total_bytes).and_return(1024)
10 end
11
12 it 'calls Facter::Resolvers::Memory' do
13 expect(Facter::Resolvers::Memory).to receive(:resolve).with(:total_bytes)
14 fact.call_the_resolver
15 end
16
17 it 'returns memory size fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'memory.system.total', value: value),
20 an_object_having_attributes(name: 'memorysize', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::UsedBytes do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'memory.system.used_bytes', value: 1024)
6 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:used_bytes).and_return(1024)
7 allow(Facter::ResolvedFact).to receive(:new).with('memory.system.used_bytes', 1024).and_return(expected_fact)
8
9 fact = Facts::Windows::Memory::System::UsedBytes.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Memory::System::Used do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Memory::System::Used.new }
5
6 let(:resolver_result) { 1024 }
7 let(:fact_value) { '1.00 KiB' }
8
9 before do
10 allow(Facter::Resolvers::Memory).to receive(:resolve).with(:used_bytes).and_return(resolver_result)
11 end
12
13 it 'calls Facter::Resolvers::Macosx::SwapMemory' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Memory).to have_received(:resolve).with(:used_bytes)
16 end
17
18 it 'returns a memory.system.used fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'memory.system.used', value: fact_value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::MtuInterfaces do
3 subject(:fact) { Facts::Windows::MtuInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { mtu: 1500 }, 'en1' => { mtu: 1500 } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names mtu_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'mtu_eth0', value: interfaces['eth0'][:mtu], type: :legacy),
20 an_object_having_attributes(name: 'mtu_en1', value: interfaces['en1'][:mtu], type: :legacy))
21 end
22 end
23
24 describe '#call_the_resolver when resolver reeturns nil' do
25 let(:interfaces) { nil }
26
27 it 'returns nil' do
28 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Netmask6Interfaces do
3 subject(:fact) { Facts::Windows::Netmask6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) do
11 { 'eth0' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' },
12 'en1' => { netmask6: 'fe80::99bf:da20:ad3:9bfe' } }
13 end
14
15 it 'calls Facter::Resolvers::Windows::Networking' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
18 end
19
20 it 'returns legacy facts with names netmask6_<interface_name>' do
21 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
22 contain_exactly(an_object_having_attributes(name: 'netmask6_eth0',
23 value: interfaces['eth0'][:netmask6], type: :legacy),
24 an_object_having_attributes(name: 'netmask6_en1',
25 value: interfaces['en1'][:netmask6], type: :legacy))
26 end
27 end
28
29 describe '#call_the_resolver when resolver return nil' do
30 let(:interfaces) { nil }
31
32 it 'returns nil' do
33 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::NetmaskInterfaces do
3 subject(:fact) { Facts::Windows::NetmaskInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { netmask: '10.255.255.255' }, 'en1' => { netmask: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names netmask_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'netmask_eth0',
20 value: interfaces['eth0'][:netmask], type: :legacy),
21 an_object_having_attributes(name: 'netmask_en1',
22 value: interfaces['en1'][:netmask], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Network6Interfaces do
3 subject(:fact) { Facts::Windows::Network6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network6: '::1' }, 'en1' => { network6: 'fe80::' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network6_eth0',
20 value: interfaces['eth0'][:network6], type: :legacy),
21 an_object_having_attributes(name: 'network6_en1',
22 value: interfaces['en1'][:network6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::NetworkInterfaces do
3 subject(:fact) { Facts::Windows::NetworkInterfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { network: '10.255.255.255' }, 'en1' => { network: '10.17.255.255' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with names network_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'network_eth0',
20 value: interfaces['eth0'][:network], type: :legacy),
21 an_object_having_attributes(name: 'network_en1',
22 value: interfaces['en1'][:network], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Dhcp do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Dhcp.new }
5
6 let(:value) { '10.16.122.163' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:dhcp).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:dhcp)
15 end
16
17 it 'returns ipaddress fact' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Facter::ResolvedFact)
20 .and have_attributes(name: 'networking.dhcp', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Domain do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Domain.new }
5
6 let(:value) { 'domain.net' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:domain).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:domain)
15 end
16
17 it 'returns domain fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.domain', value: value),
20 an_object_having_attributes(name: 'domain', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Fqdn do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Fqdn.new }
5
6 before do
7 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:domain).and_return(domain_name)
8 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(hostname)
9 end
10
11 context 'when domain and hostname could be resolved' do
12 let(:domain_name) { 'domain' }
13 let(:hostname) { 'hostname' }
14 let(:value) { "#{hostname}.#{domain_name}" }
15
16 it 'calls Facter::Resolvers::Windows::Networking' do
17 fact.call_the_resolver
18 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:domain)
19 end
20
21 it 'calls Facter::Resolvers::Hostname' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
24 end
25
26 it 'returns fqdn fact' do
27 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
28 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
29 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
30 end
31 end
32
33 context 'when it fails to retrieve hostname' do
34 let(:domain_name) { 'domain' }
35 let(:hostname) { nil }
36 let(:value) { nil }
37
38 it 'returns nil' do
39 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
40 have_attributes(name: 'networking.fqdn', value: nil)
41 end
42 end
43
44 context 'when it fails to retrieve domain' do
45 let(:domain_name) { nil }
46 let(:hostname) { 'hostname' }
47 let(:value) { hostname }
48
49 it 'returns hostname as fqdn' do
50 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
51 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
52 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
53 end
54 end
55
56 context 'when domain is empty string' do
57 let(:domain_name) { '' }
58 let(:hostname) { 'hostname' }
59 let(:value) { hostname }
60
61 it 'returns hostname as fqdn without a trailing dot' do
62 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
63 contain_exactly(an_object_having_attributes(name: 'networking.fqdn', value: value),
64 an_object_having_attributes(name: 'fqdn', value: value, type: :legacy))
65 end
66 end
67
68 context 'when hostname is empty' do
69 let(:domain_name) { 'domain' }
70 let(:hostname) { '' }
71 let(:value) { nil }
72
73 it 'returns nil' do
74 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
75 have_attributes(name: 'networking.fqdn', value: nil)
76 end
77 end
78 end
79 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Hostname do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Hostname.new }
5
6 let(:value) { 'hostname' }
7
8 before do
9 allow(Facter::Resolvers::Hostname).to receive(:resolve).with(:hostname).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Hostname' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Hostname).to have_received(:resolve).with(:hostname)
15 end
16
17 it 'returns hostname fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.hostname', value: value),
20 an_object_having_attributes(name: 'hostname', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Interfaces do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'networking.interfaces', value: 'value')
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('networking.interfaces', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Networking::Interfaces.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Ip6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Ip6.new }
5
6 let(:value) { 'fe80::5989:97ff:75ae:dae7' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:ip6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:ip6)
15 end
16
17 it 'returns ipv6 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip6', value: value),
20 an_object_having_attributes(name: 'ipaddress6', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Ip do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Ip.new }
5
6 let(:value) { '0.16.121.255' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:ip).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:ip)
15 end
16
17 it 'returns ipv4 address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.ip', value: value),
20 an_object_having_attributes(name: 'ipaddress', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Mac do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Mac.new }
5
6 let(:value) { '00:50:56:9A:7E:98' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:mac).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:mac)
15 end
16
17 it 'returns mac address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.mac', value: value),
20 an_object_having_attributes(name: 'macaddress', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Mtu do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'networking.mtu', value: 'value')
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:mtu).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('networking.mtu', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Networking::Mtu.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Netmask6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Netmask6.new }
5
6 let(:value) { 'ffff:ffff:ffff:ffff::' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:netmask6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:netmask6)
15 end
16
17 it 'returns netmask for ipv6 ip address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask6', value: value),
20 an_object_having_attributes(name: 'netmask6', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Netmask do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Netmask.new }
5
6 let(:value) { '255.255.240.0' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:netmask).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:netmask)
15 end
16
17 it 'returns netmask for ipv4 ip address fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.netmask', value: value),
20 an_object_having_attributes(name: 'netmask', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Network6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Network6.new }
5
6 let(:value) { 'fe80::' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:network6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:network6)
15 end
16
17 it 'returns network ipv6 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network6', value: value),
20 an_object_having_attributes(name: 'network6', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Network do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Network.new }
5
6 let(:value) { '10.16.112.0' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:network).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:network)
15 end
16
17 it 'returns network ipv4 fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'networking.network', value: value),
20 an_object_having_attributes(name: 'network', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Primary do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Primary.new }
5
6 let(:value) { 'Ethernet0' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:primary_interface).and_return(value)
10 end
11
12 it 'calls Facter::Windows::Resolvers::Fips' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:primary_interface)
15 end
16
17 it 'returns true if fips enabled' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
19 have_attributes(name: 'networking.primary', value: value)
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Networking::Scope6 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Networking::Scope6.new }
5
6 let(:value) { 'link' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:scope6).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:scope6)
15 end
16
17 it 'returns scope for ipv6 address' do
18 expect(fact.call_the_resolver)
19 .to be_an_instance_of(Array)
20 .and contain_exactly(an_object_having_attributes(name: 'networking.scope6', value: value),
21 an_object_having_attributes(name: 'scope6', value: value))
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Architecture do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Architecture.new }
5
6 let(:value) { 'x64' }
7
8 before do
9 allow(Facter::Resolvers::HardwareArchitecture).to receive(:resolve).with(:architecture).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::HardwareArchitecture' do
13 expect(Facter::Resolvers::HardwareArchitecture).to receive(:resolve).with(:architecture)
14 fact.call_the_resolver
15 end
16
17 it 'returns architecture fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.architecture', value: value),
20 an_object_having_attributes(name: 'architecture', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Family do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Family.new }
5
6 let(:value) { 'windows' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernel).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Kernel' do
13 expect(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernel)
14 fact.call_the_resolver
15 end
16
17 it 'returns os family fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.family', value: value),
20 an_object_having_attributes(name: 'osfamily', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Hardware do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Hardware.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::HardwareArchitecture).to receive(:resolve).with(:hardware).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::HardwareArchitecture' do
13 expect(Facter::Resolvers::HardwareArchitecture).to receive(:resolve).with(:hardware)
14 fact.call_the_resolver
15 end
16
17 it 'returns hardware model fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.hardware', value: value),
20 an_object_having_attributes(name: 'hardwaremodel', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Name do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Name.new }
5
6 let(:value) { 'windows' }
7
8 before do
9 allow(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernel).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Kernel' do
13 expect(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernel)
14 fact.call_the_resolver
15 end
16
17 it 'returns operating system name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.name', value: value),
20 an_object_having_attributes(name: 'operatingsystem', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Release do
3 subject(:fact) { Facts::Windows::Os::Release.new }
4
5 before do
6 allow(Facter::Resolvers::WinOsDescription).to receive(:resolve).with(:consumerrel).and_return(value)
7 allow(Facter::Resolvers::WinOsDescription).to receive(:resolve).with(:description).and_return(value)
8 allow(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernelmajorversion).and_return(value)
9 allow(Facter::Resolvers::Kernel).to receive(:resolve).with(:kernelversion).and_return(value)
10 end
11
12 describe '#call_the_resolver' do
13 let(:value) { '2019' }
14
15 it 'calls Facter::Resolvers::WinOsDescription with consumerrel' do
16 fact.call_the_resolver
17 expect(Facter::Resolvers::WinOsDescription).to have_received(:resolve).with(:consumerrel)
18 end
19
20 it 'calls Facter::Resolvers::WinOsDescription with description' do
21 fact.call_the_resolver
22 expect(Facter::Resolvers::WinOsDescription).to have_received(:resolve).with(:description)
23 end
24
25 it 'calls Facter::Resolvers::Kernel with kernelmajorversion' do
26 fact.call_the_resolver
27 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernelmajorversion)
28 end
29
30 it 'calls Facter::Resolvers::Kernel with kernelversion' do
31 fact.call_the_resolver
32 expect(Facter::Resolvers::Kernel).to have_received(:resolve).with(:kernelversion)
33 end
34
35 it 'returns release fact' do
36 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
37 contain_exactly(an_object_having_attributes(name: 'os.release', value: { 'full' => value, 'major' => value }),
38 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value, type: :legacy),
39 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
40 end
41 end
42
43 describe '#call_the_resolver when resolvers return nil' do
44 let(:value) { nil }
45
46 it 'returns release fact as nil' do
47 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
48 contain_exactly(an_object_having_attributes(name: 'os.release', value: value),
49 an_object_having_attributes(name: 'operatingsystemmajrelease', value: value, type: :legacy),
50 an_object_having_attributes(name: 'operatingsystemrelease', value: value, type: :legacy))
51 end
52 end
53 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::DisplayVersion do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::DisplayVersion.new }
5
6 let(:value) { '1607' }
7
8 before do
9 allow(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:display_version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::ProductRelease' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::ProductRelease).to have_received(:resolve).with(:display_version)
15 end
16
17 it 'returns os release id fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.display_version', value: value),
20 an_object_having_attributes(name: 'windows_display_version', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::EditionId do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::EditionId.new }
5
6 let(:value) { 'ServerStandard' }
7
8 before do
9 allow(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:edition_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::ProductRelease' do
13 expect(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:edition_id)
14 fact.call_the_resolver
15 end
16
17 it 'returns os edition id fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.edition_id', value: value),
20 an_object_having_attributes(name: 'windows_edition_id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::InstallationType do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::InstallationType.new }
5
6 let(:value) { 'Server' }
7
8 before do
9 allow(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:installation_type).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::ProductRelease' do
13 expect(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:installation_type)
14 fact.call_the_resolver
15 end
16
17 it 'returns os installation type fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.installation_type', value: value),
20 an_object_having_attributes(name: 'windows_installation_type', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::ProductName do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::ProductName.new }
5
6 let(:value) { 'Windows Server 2016 Standard' }
7
8 before do
9 allow(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:product_name).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::ProductRelease' do
13 expect(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:product_name)
14 fact.call_the_resolver
15 end
16
17 it 'returns os product name fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.product_name', value: value),
20 an_object_having_attributes(name: 'windows_product_name', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::ReleaseId do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::ReleaseId.new }
5
6 let(:value) { '1607' }
7
8 before do
9 allow(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:release_id).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::ProductRelease' do
13 expect(Facter::Resolvers::ProductRelease).to receive(:resolve).with(:release_id)
14 fact.call_the_resolver
15 end
16
17 it 'returns os release id fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.release_id', value: value),
20 an_object_having_attributes(name: 'windows_release_id', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Os::Windows::System32 do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Os::Windows::System32.new }
5
6 let(:value) { 'C:\Windows\system32' }
7
8 before do
9 allow(Facter::Resolvers::System32).to receive(:resolve).with(:system32).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::System32' do
13 expect(Facter::Resolvers::System32).to receive(:resolve).with(:system32)
14 fact.call_the_resolver
15 end
16
17 it 'returns system32 path' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'os.windows.system32', value: value),
20 an_object_having_attributes(name: 'system32', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Path do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Path.new }
5
6 let(:value) { '/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java6/jre/bin:/usr/java6/bin' }
7
8 before do
9 allow(Facter::Resolvers::Path).to \
10 receive(:resolve).with(:path).and_return(value)
11 end
12
13 it 'calls Facter::Resolvers::Path' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Path).to have_received(:resolve).with(:path)
16 end
17
18 it 'returns path fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'path', value: value)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processor do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Processor.new }
5
6 let(:processor) { ['Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz', 'Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz'] }
7
8 before do
9 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:models).and_return(processor)
10 end
11
12 it 'calls Facter::Resolvers::Processors' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Processors).to have_received(:resolve).with(:models)
15 end
16
17 it 'returns legacy facts about each processor' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processor0', value: processor[0], type: :legacy),
20 an_object_having_attributes(name: 'processor1', value: processor[1], type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Cores do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = instance_double(Facter::ResolvedFact, name: 'processors.cores', value: 2)
6 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:cores_per_socket).and_return(2)
7 allow(Facter::ResolvedFact).to receive(:new).with('processors.cores', 2).and_return(expected_fact)
8
9 fact = Facts::Windows::Processors::Cores.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Count do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Processors::Count.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:count).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Processors' do
13 expect(Facter::Resolvers::Processors).to receive(:resolve).with(:count)
14 fact.call_the_resolver
15 end
16
17 it 'returns number of processors' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processors.count', value: value),
20 an_object_having_attributes(name: 'processorcount', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Isa do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Processors::Isa.new }
5
6 let(:value) { 'x86_64' }
7
8 before do
9 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:isa).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Processors' do
13 expect(Facter::Resolvers::Processors).to receive(:resolve).with(:isa)
14 fact.call_the_resolver
15 end
16
17 it 'returns isa fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processors.isa', value: value),
20 an_object_having_attributes(name: 'hardwareisa', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Models do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = double(Facter::ResolvedFact, name: 'processors.models', value: 'value')
6 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:models).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('processors.models', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Processors::Models.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Physicalcount do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Processors::Physicalcount.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:physicalcount).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Processors' do
13 expect(Facter::Resolvers::Processors).to receive(:resolve).with(:physicalcount)
14 fact.call_the_resolver
15 end
16
17 it 'returns number of physical processors' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'processors.physicalcount', value: value),
20 an_object_having_attributes(name: 'physicalprocessorcount', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Processors::Threads do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = instance_double(Facter::ResolvedFact, name: 'processors.threads', value: 2)
6 allow(Facter::Resolvers::Processors).to receive(:resolve).with(:threads_per_core).and_return(2)
7 allow(Facter::ResolvedFact).to receive(:new).with('processors.threads', 2).and_return(expected_fact)
8
9 fact = Facts::Windows::Processors::Threads.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ruby::Platform do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ruby::Platform.new }
5
6 let(:value) { 'x64-mingw32' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 expect(Facter::Resolvers::Ruby).to receive(:resolve).with(:platform)
14 fact.call_the_resolver
15 end
16
17 it 'returns ruby platform fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.platform', value: value),
20 an_object_having_attributes(name: 'rubyplatform', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ruby::Sitedir do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ruby::Sitedir.new }
5
6 let(:value) { 'C:/Program Files/Puppet Labs/Puppet/puppet/lib/ruby/site_ruby/2.5.0' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 expect(Facter::Resolvers::Ruby).to receive(:resolve).with(:sitedir)
14 fact.call_the_resolver
15 end
16
17 it 'returns ruby sitedir fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.sitedir', value: value),
20 an_object_having_attributes(name: 'rubysitedir', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ruby::Version do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ruby::Version.new }
5
6 let(:value) { '2.5.7' }
7
8 before do
9 allow(Facter::Resolvers::Ruby).to receive(:resolve).with(:version).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Ruby' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Ruby).to have_received(:resolve).with(:version)
15 end
16
17 it 'returns ruby version fact' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'ruby.version', value: value),
20 an_object_having_attributes(name: 'rubyversion', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Scope6Interfaces do
3 subject(:fact) { Facts::Windows::Scope6Interfaces.new }
4
5 before do
6 allow(Facter::Resolvers::Windows::Networking).to receive(:resolve).with(:interfaces).and_return(interfaces)
7 end
8
9 describe '#call_the_resolver' do
10 let(:interfaces) { { 'eth0' => { scope6: 'link' }, 'en1' => { scope6: 'global' } } }
11
12 it 'calls Facter::Resolvers::Windows::Networking' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Networking).to have_received(:resolve).with(:interfaces)
15 end
16
17 it 'returns legacy facts with scope6_<interface_name>' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'scope6_eth0',
20 value: interfaces['eth0'][:scope6], type: :legacy),
21 an_object_having_attributes(name: 'scope6_en1',
22 value: interfaces['en1'][:scope6], type: :legacy))
23 end
24 end
25
26 describe '#call_the_resolver when resolver returns nil' do
27 let(:interfaces) { nil }
28
29 it 'returns nil' do
30 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Ssh do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Ssh.new }
5
6 context 'when user is privileged' do
7 let(:ssh) do
8 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
9 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
10 end
11 let(:value) do
12 { 'ecdsa' => { 'fingerprints' =>
13 { 'sha1' => 'test', 'sha256' => 'test' },
14 'key' => 'test',
15 'type' => 'ecdsa' } }
16 end
17
18 before do
19 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
20 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(ssh)
21 end
22
23 it 'calls Facter::Resolvers::Windows::Ssh' do
24 fact.call_the_resolver
25 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
26 end
27
28 it 'calls Facter::Resolvers::Windows::Identity' do
29 fact.call_the_resolver
30 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
31 end
32
33 it 'returns ssh fact' do
34 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
35 have_attributes(name: 'ssh', value: value)
36 end
37 end
38
39 context 'when user is privileged but no ssh key found' do
40 let(:value) { nil }
41
42 before do
43 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
44 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return({})
45 end
46
47 it 'calls Facter::Resolvers::Windows::Ssh' do
48 fact.call_the_resolver
49 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
50 end
51
52 it 'calls Facter::Resolvers::Windows::Identity' do
53 fact.call_the_resolver
54 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
55 end
56
57 it 'returns ssh fact' do
58 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
59 have_attributes(name: 'ssh', value: value)
60 end
61 end
62
63 context 'when user is not privileged' do
64 let(:value) { nil }
65
66 before do
67 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(false)
68 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(value)
69 end
70
71 it "doesn't call Facter::Resolvers::Windows::Ssh" do
72 fact.call_the_resolver
73 expect(Facter::Resolvers::Windows::Ssh).not_to have_received(:resolve).with(:ssh)
74 end
75
76 it 'calls Facter::Resolvers::Windows::Identity' do
77 fact.call_the_resolver
78 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
79 end
80
81 it 'returns ssh fact' do
82 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
83 have_attributes(name: 'ssh', value: value)
84 end
85 end
86 end
87 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Sshalgorithmkey do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Sshalgorithmkey.new }
5
6 context 'when user is privileged' do
7 let(:ssh) do
8 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
9 .new('test', 'test'), 'ecdsa', 'test', 'ecdsa'),
10 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
11 .new('test', 'test'), 'rsa', 'test', 'rsa')]
12 end
13 let(:legacy_fact1) { { name: 'ecdsa', value: 'test' } }
14 let(:legacy_fact2) { { name: 'rsa', value: 'test' } }
15
16 before do
17 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
18 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(ssh)
19 end
20
21 it 'calls Facter::Resolvers::Windows::Identity' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
24 end
25
26 it 'calls Facter::Resolvers::Windows::Ssh' do
27 fact.call_the_resolver
28 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
29 end
30
31 it 'returns a list of resolved facts' do
32 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
33 contain_exactly(
34 an_object_having_attributes(name: "ssh#{legacy_fact1[:name]}key", value: legacy_fact1[:value]),
35 an_object_having_attributes(name: "ssh#{legacy_fact2[:name]}key", value: legacy_fact2[:value])
36 )
37 end
38 end
39
40 context 'when user is privileged but no ssh key found' do
41 let(:value) { nil }
42
43 before do
44 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
45 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(nil)
46 end
47
48 it 'calls Facter::Resolvers::Windows::Ssh' do
49 fact.call_the_resolver
50 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
51 end
52
53 it 'calls Facter::Resolvers::Windows::Identity' do
54 fact.call_the_resolver
55 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
56 end
57
58 it 'returns no facts' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
60 end
61 end
62
63 context 'when user is not privileged' do
64 let(:value) { nil }
65
66 before do
67 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(false)
68 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(value)
69 end
70
71 it 'calls Facter::Resolvers::Windows::Identity' do
72 fact.call_the_resolver
73 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
74 end
75
76 it "doesn't call Facter::Resolvers::Windows::Ssh" do
77 fact.call_the_resolver
78 expect(Facter::Resolvers::Windows::Ssh).not_to have_received(:resolve).with(:ssh)
79 end
80
81 it 'returns no facts' do
82 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
83 end
84 end
85 end
86 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::SshfpAlgorithm do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::SshfpAlgorithm.new }
5
6 context 'when user is privileged' do
7 let(:ssh) do
8 [Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
9 .new('sha11', 'sha2561'), 'ecdsa', 'test', 'ecdsa'),
10 Facter::Util::Resolvers::Ssh.new(Facter::Util::Resolvers::FingerPrint
11 .new('sha12', 'sha2562'), 'rsa', 'test', 'rsa')]
12 end
13 let(:legacy_fact1) { { name: 'ecdsa', value: "sha11\nsha2561" } }
14 let(:legacy_fact2) { { name: 'rsa', value: "sha12\nsha2562" } }
15
16 before do
17 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
18 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(ssh)
19 end
20
21 it 'calls Facter::Resolvers::Windows::Identity' do
22 fact.call_the_resolver
23 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
24 end
25
26 it 'calls Facter::Resolvers::Windows::Ssh' do
27 fact.call_the_resolver
28 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
29 end
30
31 it 'returns a list of resolved facts' do
32 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
33 contain_exactly(
34 an_object_having_attributes(name: "sshfp_#{legacy_fact1[:name]}", value: legacy_fact1[:value]),
35 an_object_having_attributes(name: "sshfp_#{legacy_fact2[:name]}", value: legacy_fact2[:value])
36 )
37 end
38 end
39
40 context 'when user is privileged but no ssh key found' do
41 let(:value) { nil }
42
43 before do
44 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
45 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(nil)
46 end
47
48 it 'calls Facter::Resolvers::Windows::Ssh' do
49 fact.call_the_resolver
50 expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
51 end
52
53 it 'calls Facter::Resolvers::Windows::Identity' do
54 fact.call_the_resolver
55 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
56 end
57
58 it 'returns no facts' do
59 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
60 end
61 end
62
63 context 'when user is not privileged' do
64 let(:value) { nil }
65
66 before do
67 allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(false)
68 allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(value)
69 end
70
71 it 'calls Facter::Resolvers::Windows::Identity' do
72 fact.call_the_resolver
73 expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
74 end
75
76 it "doesn't call Facter::Resolvers::Windows::Ssh" do
77 fact.call_the_resolver
78 expect(Facter::Resolvers::Windows::Ssh).not_to have_received(:resolve).with(:ssh)
79 end
80
81 it 'returns no facts' do
82 expect(fact.call_the_resolver).to be_an_instance_of(Array).and contain_exactly
83 end
84 end
85 end
86 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::SystemUptime::Days do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::SystemUptime::Days.new }
5
6 let(:value) { '2' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Uptime).to receive(:resolve).with(:days).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Uptime).to have_received(:resolve).with(:days)
15 end
16
17 it 'returns days since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.days', value: value),
20 an_object_having_attributes(name: 'uptime_days', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::SystemUptime::Hours do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::SystemUptime::Hours.new }
5
6 let(:value) { '9' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Uptime).to receive(:resolve).with(:hours).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Uptime).to have_received(:resolve).with(:hours)
15 end
16
17 it 'returns hours since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.hours', value: value),
20 an_object_having_attributes(name: 'uptime_hours', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::SystemUptime::Seconds do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::SystemUptime::Seconds.new }
5
6 let(:value) { '34974' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Uptime).to receive(:resolve).with(:seconds).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Uptime).to have_received(:resolve).with(:seconds)
15 end
16
17 it 'returns seconds since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.seconds', value: value),
20 an_object_having_attributes(name: 'uptime_seconds', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::SystemUptime::Uptime do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::SystemUptime::Uptime.new }
5
6 let(:value) { '9:42 hours' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Uptime).to receive(:resolve).with(:uptime).and_return(value)
10 end
11
12 it 'calls Facter::Resolvers::Windows::Uptime' do
13 fact.call_the_resolver
14 expect(Facter::Resolvers::Windows::Uptime).to have_received(:resolve).with(:uptime)
15 end
16
17 it 'returns time since last boot' do
18 expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
19 contain_exactly(an_object_having_attributes(name: 'system_uptime.uptime', value: value),
20 an_object_having_attributes(name: 'uptime', value: value, type: :legacy))
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Timezone do
3 describe '#call_the_resolver' do
4 subject(:fact) { Facts::Windows::Timezone.new }
5
6 let(:timezone) { 'UTC' }
7
8 before do
9 allow(Facter::Resolvers::Windows::Timezone).to \
10 receive(:resolve).with(:timezone).and_return(timezone)
11 end
12
13 it 'calls Facter::Resolvers::Timezone' do
14 fact.call_the_resolver
15 expect(Facter::Resolvers::Windows::Timezone).to have_received(:resolve).with(:timezone)
16 end
17
18 it 'returns a resolved fact' do
19 expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
20 have_attributes(name: 'timezone', value: timezone)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facts::Windows::Virtual do
3 describe '#call_the_resolver' do
4 it 'returns a fact' do
5 expected_fact = instance_double(Facter::ResolvedFact, name: 'virtual', value: 'value')
6 allow(Facter::Resolvers::Windows::Virtualization).to receive(:resolve).with(:virtual).and_return('value')
7 allow(Facter::ResolvedFact).to receive(:new).with('virtual', 'value').and_return(expected_fact)
8
9 fact = Facts::Windows::Virtual.new
10 expect(fact.call_the_resolver).to eq(expected_fact)
11 end
12 end
13 end
0 # frozen_string_literal: true
1
2 describe Facter::Framework::Benchmarking::Timer do
3 let(:tms_mock) { instance_spy(Benchmark::Tms) }
4
5 describe '#measure' do
6 context 'when timing option is true' do
7 before do
8 allow(Facter::Options).to receive(:[]).with(:timing).and_return(true)
9 allow(tms_mock).to receive(:format).with('%r').and_return('(0.123)')
10 allow(Benchmark).to receive(:measure).and_return(tms_mock)
11 end
12
13 it 'prints fact name and time it took to resolve it' do
14 expect do
15 Facter::Framework::Benchmarking::Timer.measure('my_fact') {}
16 end.to output("fact 'my_fact', took: (0.123) seconds\n").to_stdout
17 end
18
19 it 'adds prefix to printed message' do
20 expect do
21 Facter::Framework::Benchmarking::Timer.measure('my_fact', 'my_custom_prefix') {}
22 end.to output("my_custom_prefix fact 'my_fact', took: (0.123) seconds\n").to_stdout
23 end
24 end
25
26 context 'when timing option is false' do
27 before do
28 allow(Facter::Options).to receive(:[]).with(:timing).and_return(false)
29 end
30
31 it 'does not print any message' do
32 expect do
33 Facter::Framework::Benchmarking::Timer.measure('my_fact') {}
34 end.not_to output.to_stdout
35 end
36 end
37 end
38 end
0 # frozen_string_literal: true
1
2 describe Facter::FactCollection do
3 subject(:fact_collection) { Facter::FactCollection.new }
4
5 describe '#build_fact_collection!' do
6 let(:user_query) { 'os' }
7 let(:resolved_fact) { Facter::ResolvedFact.new(fact_name, fact_value, type, user_query) }
8 let(:logger) { instance_spy(Facter::Log) }
9
10 before do
11 allow(Facter::Log).to receive(:new).and_return(logger)
12 end
13
14 context 'when fact has some value' do
15 let(:fact_name) { 'os.version' }
16 let(:fact_value) { '1.2.3' }
17 let(:type) { :core }
18
19 it 'adds fact to collection' do
20 fact_collection.build_fact_collection!([resolved_fact])
21 expected_hash = { 'os' => { 'version' => fact_value } }
22
23 expect(fact_collection).to eq(expected_hash)
24 end
25 end
26
27 context 'when fact value is nil' do
28 context 'when fact type is legacy' do
29 let(:fact_name) { 'os.version' }
30 let(:fact_value) { nil }
31 let(:type) { :legacy }
32
33 it 'does not add fact to collection' do
34 fact_collection.build_fact_collection!([resolved_fact])
35 expected_hash = {}
36
37 expect(fact_collection).to eq(expected_hash)
38 end
39 end
40
41 context 'when fact type is core' do
42 let(:fact_name) { 'os.version' }
43 let(:fact_value) { nil }
44 let(:type) { :core }
45
46 it 'does not add fact to collection' do
47 fact_collection.build_fact_collection!([resolved_fact])
48 expected_hash = {}
49
50 expect(fact_collection).to eq(expected_hash)
51 end
52 end
53
54 context 'when fact type is :legacy' do
55 context 'when fact name contains dots' do
56 let(:fact_name) { 'my.dotted.external.fact.name' }
57 let(:fact_value) { 'legacy_fact_value' }
58 let(:type) { :legacy }
59
60 it 'returns a fact with dot in it`s name' do
61 fact_collection.build_fact_collection!([resolved_fact])
62
63 expected_hash = { 'my.dotted.external.fact.name' => 'legacy_fact_value' }
64
65 expect(fact_collection).to eq(expected_hash)
66 end
67 end
68 end
69 end
70
71 context 'when fact cannot be added to collection' do
72 let(:fact_name) { 'mygroup.fact1' }
73 let(:fact_value) { 'g1_f1_value' }
74 let(:type) { :custom }
75
76 let(:resolved_fact2) { Facter::ResolvedFact.new('mygroup.fact1.subfact1', 'g1_sg1_f1_value', type) }
77
78 it 'logs error' do
79 allow(Facter::Options).to receive(:[]).with(:force_dot_resolution).and_return(true)
80 fact_collection.build_fact_collection!([resolved_fact, resolved_fact2])
81
82 expect(logger).to have_received(:error)
83 end
84 end
85 end
86
87 describe '#value' do
88 it 'raises KeyError for a non-existent key' do
89 expect { fact_collection.value('non-existent') }.to raise_error(KeyError)
90 end
91
92 context 'when string value' do
93 before do
94 fact_collection['fact_name'] = 'value'
95 end
96
97 it 'returns the value by key' do
98 expect(fact_collection.value('fact_name')).to eql('value')
99 end
100 end
101
102 context 'when array value' do
103 before do
104 fact_collection['fact_name'] = [1, 2, 3]
105 end
106
107 it 'returns the value by key' do
108 expect(fact_collection.value('fact_name')).to eql([1, 2, 3])
109 end
110
111 it 'returns the value by index' do
112 expect(fact_collection.value('fact_name.1')).to be(2)
113 end
114
115 it 'raises KeyError when nested key is not found in array' do
116 expect { fact_collection.value('fact_name.1.2') }.to raise_error(KeyError)
117 end
118
119 it 'raises TypeError for a non-existent index' do
120 expect { fact_collection.value('fact_name.4') }.to raise_error(TypeError)
121 end
122 end
123
124 context 'when hash value' do
125 before do
126 fact_collection['fact_name'] = { 'key1' => 'value' }
127 end
128
129 it 'returns the value by key' do
130 expect(fact_collection.value('fact_name')).to eql({ 'key1' => 'value' })
131 end
132
133 it 'returns the value by nested key' do
134 expect(fact_collection.value('fact_name.key1')).to eql('value')
135 end
136
137 it 'raises KeyError when nested key is not found in array' do
138 expect { fact_collection.value('fact_name.key1.2') }.to raise_error(KeyError)
139 end
140 end
141
142 context 'when deeply nested hash and array value' do
143 before do
144 fact_collection['fact_name'] = { 'key1' => [0, { 'key2' => 'value2' }, 2] }
145 end
146
147 it 'returns the value by key' do
148 expect(fact_collection.value('fact_name')).to eql({ 'key1' => [0, { 'key2' => 'value2' }, 2] })
149 end
150
151 it 'returns the value by nested key and index' do
152 expect(fact_collection.value('fact_name.key1.1.key2')).to eql('value2')
153 end
154
155 it 'raises KeyError when nested key is not found' do
156 expect { fact_collection.value('fact_name.key1.2.not') }.to raise_error(KeyError)
157 end
158
159 it 'raises TypeError when nested index is not found' do
160 expect { fact_collection.value('fact_name.key1.5') }.to raise_error(TypeError)
161 end
162 end
163 end
164 end
0 # frozen_string_literal: true
1
2 describe Facter::ResolvedFact do
3 context 'when is a legacy fact' do
4 subject(:resolved_fact) { Facter::ResolvedFact.new('fact_name', 'fact_value', :legacy) }
5
6 it 'responds to legacy? method with true' do
7 expect(resolved_fact.legacy?).to be(true)
8 end
9
10 it 'responds to core? method with false' do
11 expect(resolved_fact.core?).to be(false)
12 end
13 end
14
15 context 'when is a core fact' do
16 subject(:resolved_fact) { Facter::ResolvedFact.new('fact_name', 'fact_value') }
17
18 it 'responds to legacy? method with true' do
19 expect(resolved_fact.legacy?).to be(false)
20 end
21
22 it 'responds to core? method with false' do
23 expect(resolved_fact.core?).to be(true)
24 end
25
26 it 'can be interpolated' do
27 expect("#{resolved_fact}-test").to eq('fact_value-test')
28 end
29
30 it 'interpolation of nil value will be empty string' do
31 resolved = Facter::ResolvedFact.new('fact_name', nil)
32 expect("#{resolved}-test").to eq('-test')
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facter::QueryParser do
3 describe '#parse' do
4 it 'creates one core searched fact' do
5 query_list = ['os.name']
6
7 os_name_class = 'Facter::Ubuntu::OsName'
8 os_family_class = 'Facter::Ubuntu::OsFamily'
9
10 loaded_fact_os_name = double(Facter::LoadedFact, name: 'os.name', klass: os_name_class, type: :core, file: nil)
11 loaded_fact_os_family = double(Facter::LoadedFact, name: 'os.family', klass: os_family_class, type: :core,
12 file: nil)
13 loaded_facts = [loaded_fact_os_name, loaded_fact_os_family]
14
15 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
16
17 expect(matched_facts).to be_an_instance_of(Array).and \
18 contain_exactly(an_instance_of(Facter::SearchedFact).and(having_attributes(fact_class: os_name_class)))
19 end
20
21 it 'creates one legacy fact' do
22 query_list = ['ipaddress_ens160']
23
24 networking_class = 'Facter::Ubuntu::NetworkInterface'
25 os_family_class = 'Facter::Ubuntu::OsFamily'
26
27 loaded_fact_networking = double(Facter::LoadedFact, name: 'ipaddress_.*', klass: networking_class, type: :legacy,
28 file: nil)
29 loaded_fact_os_family = double(Facter::LoadedFact, name: 'os.family', klass: os_family_class, type: :core,
30 file: nil)
31 loaded_facts = [loaded_fact_networking, loaded_fact_os_family]
32
33 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
34
35 expect(matched_facts).to be_an_instance_of(Array).and \
36 contain_exactly(an_instance_of(Facter::SearchedFact).and(having_attributes(fact_class: networking_class)))
37 end
38
39 it 'creates a searched fact correctly without name collision' do
40 query_list = ['ssh.rsa.key']
41
42 ssh_class = 'Facter::El::Ssh'
43 ssh_key_class = 'Facter::El::Sshalgorithmkey'
44
45 loaded_fact_ssh_key = instance_spy(Facter::LoadedFact, name: 'ssh.*key', klass: ssh_key_class, type: :legacy)
46 loaded_fact_ssh = instance_spy(Facter::LoadedFact, name: 'ssh', klass: ssh_class, type: :core)
47 loaded_facts = [loaded_fact_ssh_key, loaded_fact_ssh]
48
49 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
50
51 expect(matched_facts).to be_an_instance_of(Array).and \
52 contain_exactly(an_instance_of(Facter::SearchedFact).and(having_attributes(fact_class: ssh_class)))
53 end
54
55 it 'creates one custom searched fact' do
56 query_list = ['custom_fact']
57 os_name_class = 'Facter::Ubuntu::OsName'
58
59 loaded_fact_os_name = double(Facter::LoadedFact, name: 'os.name', klass: os_name_class, type: :core, file: nil)
60 loaded_fact_custom_fact = double(Facter::LoadedFact, name: 'custom_fact', klass: nil, type: :custom, file: nil)
61 loaded_facts = [loaded_fact_os_name, loaded_fact_custom_fact]
62
63 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
64
65 expect(matched_facts).to be_an_instance_of(Array).and \
66 contain_exactly(an_instance_of(Facter::SearchedFact).and(having_attributes(fact_class: nil, type: :custom)))
67 end
68
69 it 'queries if param is symbol' do
70 query_list = [:path]
71 path_class = 'Facter::Ubuntu::Path'
72 loaded_fact_path = double(Facter::LoadedFact, name: 'path', klass: path_class, type: :core, file: nil)
73 loaded_facts = [loaded_fact_path]
74
75 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
76
77 expect(matched_facts).to be_an_instance_of(Array).and \
78 contain_exactly(an_instance_of(Facter::SearchedFact).and(having_attributes(fact_class: path_class)))
79 end
80
81 context 'when fact does not exist' do
82 let(:query_list) { ['non_existing_fact'] }
83 let(:loaded_facts) { [] }
84
85 it 'creates a nil fact' do
86 matched_facts = Facter::QueryParser.parse(query_list, loaded_facts)
87 expect(matched_facts).to be_an_instance_of(Array).and contain_exactly(
88 an_object_having_attributes(name: 'non_existing_fact', user_query: 'non_existing_fact', type: :nil)
89 )
90 end
91 end
92 end
93 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::AioAgentVersion do
3 describe '#resolve' do
4 before do
5 allow(Facter::Util::FileHelper)
6 .to receive(:safe_read)
7 .with('/opt/puppetlabs/puppet/VERSION', nil)
8 .and_return('7.0.1')
9 end
10
11 after do
12 Facter::Resolvers::AioAgentVersion.invalidate_cache
13 end
14
15 it 'calls FileHelper.safe_read' do
16 Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)
17
18 expect(Facter::Util::FileHelper).to have_received(:safe_read).with('/opt/puppetlabs/puppet/VERSION', nil)
19 end
20
21 it 'detects puppet version' do
22 expect(Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)).to eql('7.0.1')
23 end
24
25 context 'when AIO puppet agent is a dev build' do
26 before do
27 allow(Facter::Util::FileHelper)
28 .to receive(:safe_read)
29 .with('/opt/puppetlabs/puppet/VERSION', nil)
30 .and_return('7.0.1.8.g12345678')
31 end
32
33 it 'only shows the first 4 groups of digits' do
34 expect(Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)).to eql('7.0.1.8')
35 end
36 end
37
38 context 'when there is no AIO puppet agent' do
39 before do
40 allow(Facter::Util::FileHelper)
41 .to receive(:safe_read)
42 .with('/opt/puppetlabs/puppet/VERSION', nil)
43 .and_return(nil)
44 end
45
46 it 'resolves to nil' do
47 expect(Facter::Resolvers::AioAgentVersion.resolve(:aio_agent_version)).to be(nil)
48 end
49 end
50 end
51 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Architecture do
3 describe '#resolve' do
4 before do
5 odm1 = instance_double(Facter::Util::Aix::ODMQuery)
6 odm2 = instance_double(Facter::Util::Aix::ODMQuery)
7
8 allow(Facter::Util::Aix::ODMQuery).to receive(:new).and_return(odm1, odm2)
9 allow(odm1).to receive(:equals).with('PdDvLn', 'processor/sys/proc_rspc').and_return(odm1)
10 allow(odm1).to receive(:equals).with('status', '1').and_return(odm1)
11 allow(odm1).to receive(:execute).and_return('proc8')
12
13 allow(odm2).to receive(:equals).with('name', 'proc8').and_return(odm2)
14 allow(odm2).to receive(:equals).with('attribute', 'type').and_return(odm2)
15 allow(odm2).to receive(:execute).and_return(result)
16 end
17
18 after do
19 Facter::Resolvers::Architecture.invalidate_cache
20 end
21
22 context 'when line contains value' do
23 let(:result) { 'value = x86' }
24
25 it 'detects architecture' do
26 expect(Facter::Resolvers::Architecture.resolve(:architecture)).to eql('x86')
27 end
28 end
29
30 context 'when line does not value' do
31 let(:result) { 'test = x86' }
32
33 it 'detects architecture as nil' do
34 expect(Facter::Resolvers::Architecture.resolve(:architecture)).to be(nil)
35 end
36 end
37
38 context 'when fails to retrieve fact' do
39 let(:result) { nil }
40
41 it 'detects architecture as nil' do
42 expect(Facter::Resolvers::Architecture.resolve(:architecture)).to be(nil)
43 end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Disks do
3 subject(:resolver) { Facter::Resolvers::Aix::Disks }
4
5 let(:logger_spy) { instance_spy(Facter::Log) }
6
7 before do
8 resolver.instance_variable_set(:@log, logger_spy)
9 allow(Facter::Core::Execution).to receive(:execute).with('lspv', logger: logger_spy)
10 .and_return(result)
11 end
12
13 after do
14 resolver.invalidate_cache
15 end
16
17 context 'when retrieving disks name fails' do
18 let(:result) { '' }
19
20 it 'returns nil' do
21 expect(resolver.resolve(:disks)).to be_nil
22 end
23 end
24
25 context 'when lspv is successful' do
26 let(:result) { load_fixture('lspv_output').read }
27
28 let(:disks) do
29 { 'hdisk0' => { size: '30.00 GiB', size_bytes: 32_212_254_720 } }
30 end
31
32 before do
33 allow(Facter::Core::Execution).to receive(:execute).with('lspv hdisk0', logger: logger_spy)
34 .and_return(load_fixture('lspv_disk_output').read)
35 end
36
37 it 'returns disks informations' do
38 expect(resolver.resolve(:disks)).to eql(disks)
39 end
40
41 context 'when second lspv call fails' do
42 before do
43 allow(Facter::Core::Execution).to receive(:execute).with('lspv hdisk0', logger: logger_spy)
44 .and_return('')
45 end
46
47 it 'returns disks informations' do
48 expect(resolver.resolve(:disks)).to eq({})
49 end
50 end
51 end
52 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::FfiHelper do
3 let(:averages) { double('FFI::MemoryPointer', size: 24) }
4 let(:averages_size) { double('FFI::MemoryPointer', write_int: 24) }
5
6 before do
7 allow(::FFI::MemoryPointer).to receive(:new).with(:long_long, 3).and_return(averages)
8 allow(::FFI::MemoryPointer).to receive(:new).with(:int, 1).and_return(averages_size)
9 end
10
11 after do
12 Facter::Resolvers::Aix::LoadAverages.invalidate_cache
13 end
14
15 it 'returns load average' do
16 allow(Facter::Resolvers::Aix::FfiHelper::Libc).to receive(:getkerninfo).and_return(0)
17 allow(averages).to receive(:read_array_of_long_long).with(3).and_return([655.36, 1310.72, 1966.08])
18
19 expect(Facter::Resolvers::Aix::FfiHelper.read_load_averages).to eq([0.01, 0.02, 0.03])
20 end
21
22 it 'does not return load average' do
23 allow(Facter::Resolvers::Aix::FfiHelper::Libc).to receive(:getkerninfo).and_return(-1)
24 expect(Facter::Resolvers::Aix::FfiHelper.read_load_averages).to be_nil
25 end
26 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Filesystem do
3 let(:filesystems) { 'ahafs,cdrfs,namefs,procfs,sfs' }
4
5 after do
6 Facter::Resolvers::Aix::Filesystem.invalidate_cache
7 end
8
9 context 'when vfs file is readable' do
10 before do
11 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
12 .with('/etc/vfs')
13 .and_return(load_fixture('aix_filesystems').readlines)
14 end
15
16 it 'returns filesystems' do
17 result = Facter::Resolvers::Aix::Filesystem.resolve(:file_systems)
18
19 expect(result).to eq(filesystems)
20 end
21 end
22
23 context 'when vfs file is not readable' do
24 before do
25 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
26 .with('/etc/vfs')
27 .and_return([])
28 end
29
30 it 'returns nil' do
31 result = Facter::Resolvers::Aix::Filesystem.resolve(:file_systems)
32
33 expect(result).to be(nil)
34 end
35 end
36 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Hardware do
3 describe '#resolve' do
4 before do
5 odm = double('ODMQuery')
6
7 allow(Facter::Util::Aix::ODMQuery).to receive(:new).and_return(odm)
8 allow(odm).to receive(:equals).with('name', 'sys0').and_return(odm)
9 allow(odm).to receive(:equals).with('attribute', 'modelname')
10 allow(odm).to receive(:execute).and_return(result)
11 end
12
13 after do
14 Facter::Resolvers::Hardware.invalidate_cache
15 end
16
17 context 'when line contains value' do
18 let(:result) { 'value = hardware' }
19
20 it 'detects hardware' do
21 expect(Facter::Resolvers::Hardware.resolve(:hardware)).to eql('hardware')
22 end
23 end
24
25 context 'when line does not contain value' do
26 let(:result) { 'test = hardware' }
27
28 it 'detects hardware as nil' do
29 expect(Facter::Resolvers::Hardware.resolve(:hardware)).to be(nil)
30 end
31 end
32
33 context 'when fails to retrieve fact' do
34 let(:result) { nil }
35
36 it 'detects hardware as nil' do
37 expect(Facter::Resolvers::Hardware.resolve(:hardware)).to be(nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::LoadAverages do
3 let(:load_averages) { [0.01, 0.02, 0.03] }
4
5 before do
6 allow(Facter::Resolvers::Aix::FfiHelper).to receive(:read_load_averages)
7 .and_return(load_averages)
8 end
9
10 it 'returns load average' do
11 result = Facter::Resolvers::Aix::LoadAverages.resolve(:load_averages)
12
13 expect(result).to eq('15m' => 0.03, '1m' => 0.01, '5m' => 0.02)
14 end
15 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Memory do
3 subject(:resolver) { Facter::Resolvers::Aix::Memory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute).with('svmon', logger: log_spy)
10 .and_return(svmon_content)
11 allow(Facter::Core::Execution).to receive(:execute).with('pagesize', logger: log_spy)
12 .and_return(pagesize_content)
13 end
14
15 after do
16 resolver.invalidate_cache
17 end
18
19 context 'when svmon call fails' do
20 let(:svmon_content) { '' }
21 let(:pagesize_content) { '' }
22
23 it 'returns nil for system' do
24 expect(resolver.resolve(:system)).to be_nil
25 end
26
27 it 'returns nil for swap' do
28 expect(resolver.resolve(:swap)).to be_nil
29 end
30 end
31
32 context 'when pagesize call fails' do
33 let(:svmon_content) { load_fixture('svmon_content').read }
34 let(:pagesize_content) { '' }
35
36 it 'returns nil for system' do
37 expect(resolver.resolve(:system)).to be_nil
38 end
39
40 it 'returns nil for swap' do
41 expect(resolver.resolve(:swap)).to be_nil
42 end
43 end
44
45 context 'when svmon returns invalid content' do
46 let(:svmon_content) { 'some_errors_on_stdout' }
47 let(:pagesize_content) { '4096' }
48
49 it 'returns empty hash for system' do
50 expect(resolver.resolve(:system)).to be_a(Hash).and contain_exactly
51 end
52
53 it 'returns empty hash for swap' do
54 expect(resolver.resolve(:swap)).to be_a(Hash).and contain_exactly
55 end
56 end
57
58 context 'when all calls return valid content' do
59 let(:svmon_content) { load_fixture('svmon_content').read }
60 let(:pagesize_content) { '4096' }
61
62 let(:system_memory) do
63 { available_bytes: 4_966_027_264, capacity: '42.19%', total_bytes: 8_589_934_592, used_bytes: 3_623_907_328 }
64 end
65
66 let(:swap_memory) do
67 { available_bytes: 525_660_160, capacity: '2.09%', total_bytes: 536_870_912, used_bytes: 11_210_752 }
68 end
69
70 it 'returns facts for system memory' do
71 expect(resolver.resolve(:system)).to eq(system_memory)
72 end
73
74 it 'returns facts for swap memory' do
75 expect(resolver.resolve(:swap)).to eq(swap_memory)
76 end
77 end
78 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Mountpoints do
3 let(:mountpoints) do
4 { '/' => { available: '1.63 GiB', available_bytes: 1_747_865_600, capacity: '18.61%', device: '/dev/hd4',
5 filesystem: 'jfs2', options: ['rw', 'log=/dev/hd8'], size: '2.00 GiB', size_bytes: 2_147_483_648,
6 used: '381.11 MiB', used_bytes: 399_618_048 },
7 '/opt' => { device: '/dev/hd10opt', filesystem: 'jfs2', options: ['rw', 'log=/dev/hd8'] },
8 '/usr' => { available: '2.84 GiB', available_bytes: 3_049_021_440, capacity: '43.21%', device: '/dev/hd2',
9 filesystem: 'jfs2', options: ['rw', 'log=/dev/hd8'], size: '5.00 GiB', size_bytes: 5_368_709_120,
10 used: '2.16 GiB', used_bytes: 2_319_687_680 },
11 '/var' => { available: '205.06 MiB', available_bytes: 215_023_616, capacity: '0.76%', device: '/dev/hd3',
12 filesystem: 'x', options: ['rw', 'nodev', 'log=/dev/hd3'], size: '206.64 MiB',
13 size_bytes: 216_678_912, used: '1.58 MiB', used_bytes: 1_655_296 },
14 '/tmp/salam' => { available: '63.57 GiB', available_bytes: 68_253_413_376, capacity: '7.20%',
15 device: '/var/share', filesystem: 'nfs3', options: [], size: '68.50 GiB',
16 size_bytes: 73_549_217_792, used: '4.93 GiB', used_bytes: 5_295_804_416 } }
17 end
18 let(:log_spy) { instance_spy(Facter::Log) }
19
20 before do
21 Facter::Resolvers::Aix::Mountpoints.instance_variable_set(:@log, log_spy)
22 allow(Facter::Core::Execution).to receive(:execute).with('mount', logger: log_spy)
23 .and_return(load_fixture('mount').read)
24 allow(Facter::Core::Execution).to receive(:execute).with('df -P', logger: log_spy)
25 .and_return(load_fixture('df').read)
26 end
27
28 it "only skips lines containing the string 'node'" do
29 result = Facter::Resolvers::Aix::Mountpoints.resolve(:mountpoints)
30
31 expect(result).to include('/var')
32 end
33
34 it 'returns mountpoints' do
35 result = Facter::Resolvers::Aix::Mountpoints.resolve(:mountpoints)
36
37 expect(result).to eq(mountpoints)
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Networking do
3 subject(:networking_resolver) { Facter::Resolvers::Aix::Networking }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:ffi_interfaces) do
7 {
8 'en0' => { bindings: [{ address: '10.32.77.1', netmask: '255.255.255.0', network: '10.32.77.0' }] },
9 'lo0' => { bindings: [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
10 bindings6: [{ address: '::1', netmask: '::', network: '::', scope6: 'host' }] }
11 }
12 end
13
14 before do
15 networking_resolver.instance_variable_set(:@log, log_spy)
16 allow(Facter::Core::Execution).to receive(:execute)
17 .with('netstat -rn', logger: log_spy)
18 .and_return(netstat_rn)
19
20 allow(Facter::Core::Execution).to receive(:execute)
21 .with('netstat -in', logger: log_spy)
22 .and_return(netstat_in)
23
24 allow(Facter::Resolvers::Aix::FfiHelper).to receive(:read_interfaces).and_return(ffi_interfaces)
25 end
26
27 after do
28 networking_resolver.invalidate_cache
29 end
30
31 context 'when netstat command exists' do
32 let(:netstat_in) { load_fixture('netstat_in').read }
33 let(:netstat_rn) { load_fixture('netstat_rn').read }
34 let(:interfaces) do
35 {
36 'en0' => { bindings: [{ address: '10.32.77.1', netmask: '255.255.255.0', network: '10.32.77.0' }],
37 ip: '10.32.77.1', mac: '0a:c6:24:39:41:03', mtu: 1500, netmask: '255.255.255.0',
38 network: '10.32.77.0' },
39 'lo0' => { bindings: [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
40 bindings6: [{ address: '::1', netmask: '::', network: '::', scope6: 'host' }], ip: '127.0.0.1',
41 ip6: '::1', mtu: 16_896, netmask: '255.0.0.0', netmask6: '::', network: '127.0.0.0',
42 network6: '::', scope6: 'host' }
43 }
44 end
45
46 let(:primary) { 'en0' }
47
48 it 'returns primary interface' do
49 expect(networking_resolver.resolve(:primary_interface)).to eq(primary)
50 end
51
52 it 'returns ipv4 for primary interface' do
53 expect(networking_resolver.resolve(:ip)).to eq(interfaces[primary][:ip])
54 end
55
56 it 'returns interfaces fact' do
57 expect(networking_resolver.resolve(:interfaces)).to eq(interfaces)
58 end
59
60 it 'returns mtu fact' do
61 expect(networking_resolver.resolve(:mtu)).to eq(interfaces[primary][:mtu])
62 end
63 end
64
65 context 'when netstat command does not exist' do
66 let(:netstat_in) { '' }
67 let(:netstat_rn) { '' }
68
69 it 'returns primary interface' do
70 expect(networking_resolver.resolve(:primary_interface)).to be_nil
71 end
72
73 it 'returns interfaces fact' do
74 expect(networking_resolver.resolve(:interfaces)).to eq(ffi_interfaces)
75 end
76
77 it 'returns mtu fact' do
78 expect(networking_resolver.resolve(:mtu)).to be_nil
79 end
80 end
81 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Nim do
3 subject(:nim_resolver) { Facter::Resolvers::Aix::Nim }
4
5 after do
6 nim_resolver.invalidate_cache
7 end
8
9 before do
10 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
11 .with('/etc/niminfo')
12 .and_return(niminfo_content)
13 end
14
15 context 'when niminfo file is not readable' do
16 let(:type) { nil }
17 let(:niminfo_content) { nil }
18
19 it 'returns nil' do
20 expect(nim_resolver.resolve(:type)).to eq(type)
21 end
22 end
23
24 context 'when niminfo file is readable' do
25 context 'when NIM_CONFIGURATION field is missing from file' do
26 let(:type) { nil }
27 let(:niminfo_content) { load_fixture('niminfo_wo_nim_configuration').read }
28
29 it 'returns nil' do
30 expect(nim_resolver.resolve(:type)).to eq(type)
31 end
32 end
33
34 context 'when NIM_CONFIGURATION field is not master nor standalone' do
35 let(:type) { nil }
36 let(:niminfo_content) { load_fixture('niminfo_w_wrong_nim_configuration').read }
37
38 it 'returns nil' do
39 expect(nim_resolver.resolve(:type)).to eq(type)
40 end
41 end
42
43 context 'when NIM_CONFIGURATION field is correct' do
44 let(:type) { nil }
45 let(:niminfo_content) { load_fixture('niminfo_nim_configuration').read }
46
47 it 'returns master' do
48 expect(nim_resolver.resolve(:type)).to eq(type)
49 end
50 end
51 end
52 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::OsLevel do
3 subject(:os_level) { Facter::Resolvers::Aix::OsLevel }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 os_level.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('/usr/bin/oslevel -s', { logger: log_spy })
11 .and_return(output)
12 end
13
14 after do
15 os_level.invalidate_cache
16 end
17
18 describe 'when command returns an output' do
19 let(:output) { '6100-09-00-0000' }
20
21 it 'returns build' do
22 expect(os_level.resolve(:build)).to eq(output)
23 end
24 end
25
26 describe 'when command returns empty string' do
27 let(:output) { '' }
28
29 it 'returns build as nil' do
30 expect(os_level.resolve(:build)).to be_nil
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Partitions do
3 subject(:resolver) { Facter::Resolvers::Aix::Partitions }
4
5 let(:odm_query_spy) { instance_spy(Facter::Util::Aix::ODMQuery) }
6 let(:logger_spy) { instance_spy(Facter::Log) }
7
8 before do
9 resolver.instance_variable_set(:@log, logger_spy)
10 allow(Facter::Util::Aix::ODMQuery).to receive(:new).and_return(odm_query_spy)
11 allow(odm_query_spy).to receive(:equals).with('PdDvLn', 'logical_volume/lvsubclass/lvtype')
12 allow(odm_query_spy).to receive(:execute).and_return(result)
13 end
14
15 after do
16 resolver.invalidate_cache
17 end
18
19 context 'when retrieving partitions name fails' do
20 let(:result) { nil }
21
22 before do
23 allow(odm_query_spy).to receive(:execute).and_return(result)
24 end
25
26 it 'returns nil' do
27 expect(resolver.resolve(:partitions)).to be_nil
28 end
29 end
30
31 context 'when CuDv query succesful' do
32 let(:result) { load_fixture('partitions_cudv_query').read }
33
34 let(:partitions) do
35 { '/dev/hd5' => { filesystem: 'boot', label: 'primary_bootlv', size: '32.00 MiB', size_bytes: 33_554_432 } }
36 end
37
38 before do
39 allow(Facter::Core::Execution).to receive(:execute).with('lslv -L hd5', logger: logger_spy)
40 .and_return(load_fixture('lslv_output').read)
41 allow(Facter::Core::Execution).to receive(:execute).with('lslv -L hd6', logger: logger_spy)
42 .and_return('')
43 end
44
45 it 'returns partitions informations' do
46 expect(resolver.resolve(:partitions)).to eql(partitions)
47 end
48 end
49 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Processors do
3 subject(:resolver) { Facter::Resolvers::Aix::Processors }
4
5 let(:odm_query_spy) { instance_spy(Facter::Util::Aix::ODMQuery) }
6 let(:odm_query_spy2) { instance_spy(Facter::Util::Aix::ODMQuery) }
7 let(:odm_query_spy3) { instance_spy(Facter::Util::Aix::ODMQuery) }
8 let(:odm_query_spy4) { instance_spy(Facter::Util::Aix::ODMQuery) }
9 let(:logger_spy) { instance_spy(Facter::Log) }
10
11 before do
12 resolver.instance_variable_set(:@log, logger_spy)
13 allow(Facter::Util::Aix::ODMQuery).to receive(:new).and_return(odm_query_spy,
14 odm_query_spy2,
15 odm_query_spy3,
16 odm_query_spy4)
17 allow(odm_query_spy).to receive(:equals).with('class', 'processor')
18 allow(odm_query_spy).to receive(:execute).and_return(result)
19 end
20
21 after do
22 resolver.invalidate_cache
23 end
24
25 context 'when PdDv query fails' do
26 let(:result) { nil }
27
28 it 'returns nil' do
29 expect(resolver.resolve(:speed)).to be_nil
30 end
31 end
32
33 context 'when PdDv query succesful but CuDv fails' do
34 let(:result) { load_fixture('processors_pddv').read }
35
36 before do
37 allow(odm_query_spy2).to receive(:equals).with('PdDvLn', 'processor/sys/proc_rspc')
38 allow(odm_query_spy2).to receive(:execute).and_return(nil)
39 end
40
41 it 'returns nil' do
42 expect(resolver.resolve(:speed)).to be_nil
43 end
44 end
45
46 context 'when CuAt query fails' do
47 let(:result) { load_fixture('processors_pddv').read }
48
49 before do
50 allow(odm_query_spy2).to receive(:equals).with('PdDvLn', 'processor/sys/proc_rspc')
51 allow(odm_query_spy2).to receive(:execute).and_return(load_fixture('processors_cudv').read)
52
53 allow(odm_query_spy3).to receive(:equals).with('name', 'proc0')
54 allow(odm_query_spy3).to receive(:execute).and_return(nil)
55 end
56
57 it 'returns nil' do
58 expect(resolver.resolve(:speed)).to be_nil
59 end
60 end
61
62 context 'when all queries returns an output' do
63 let(:result) { load_fixture('processors_pddv').read }
64 let(:models) do
65 %w[PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8
66 PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8 PowerPC_POWER8]
67 end
68
69 before do
70 allow(odm_query_spy2).to receive(:equals).with('PdDvLn', 'processor/sys/proc_rspc')
71 allow(odm_query_spy2).to receive(:execute).and_return(load_fixture('processors_cudv').read)
72
73 allow(odm_query_spy3).to receive(:equals).with('name', 'proc0')
74 allow(odm_query_spy3).to receive(:execute).and_return(load_fixture('processors_cuat').read)
75
76 allow(odm_query_spy4).to receive(:equals).with('name', 'proc8')
77 allow(odm_query_spy4).to receive(:execute).and_return(load_fixture('processors_cuat').read)
78 end
79
80 it 'returns speed fact' do
81 expect(resolver.resolve(:speed)).to eq(3_425_000_000)
82 end
83
84 it 'returns models fact' do
85 expect(resolver.resolve(:models)).to eq(models)
86 end
87
88 it 'returns logical_count fact' do
89 expect(resolver.resolve(:logical_count)).to eq(8)
90 end
91
92 it 'returns cores fact' do
93 expect(resolver.resolve(:cores_per_socket)).to eq(1)
94 end
95
96 it 'returns threads fact' do
97 expect(resolver.resolve(:threads_per_core)).to eq(8)
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Aix::Serialnumber do
3 describe '#resolve' do
4 before do
5 odm = instance_spy('Facter::Util::Aix::ODMQuery')
6
7 allow(Facter::Util::Aix::ODMQuery).to receive(:new).and_return(odm)
8 allow(odm).to receive(:equals).with('name', 'sys0').and_return(odm)
9 allow(odm).to receive(:equals).with('attribute', 'systemid')
10 allow(odm).to receive(:execute).and_return(result)
11 end
12
13 after do
14 Facter::Resolvers::Aix::Serialnumber.invalidate_cache
15 end
16
17 context 'when line contains value' do
18 let(:result) { 'value = "IBM,0221684EW"' }
19
20 it 'detects serialnumber' do
21 expect(Facter::Resolvers::Aix::Serialnumber.resolve(:serialnumber)).to eql('21684EW')
22 end
23 end
24
25 context 'when line does not include value key' do
26 let(:result) { 'test = IBM,0221684EW' }
27
28 it 'detects serialnumber as nil' do
29 expect(Facter::Resolvers::Aix::Serialnumber.resolve(:serialnumber)).to be(nil)
30 end
31 end
32
33 context 'when fails to retrieve fact' do
34 let(:result) { '' }
35
36 it 'detects serialnumber as nil' do
37 expect(Facter::Resolvers::Aix::Serialnumber.resolve(:serialnumber)).to be(nil)
38 end
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Augeas do
3 subject(:augeas) { Facter::Resolvers::Augeas }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:readable) { false }
7
8 before do
9 augeas.instance_variable_set(:@log, log_spy)
10 allow(File).to receive(:readable?).with('/opt/puppetlabs/puppet/bin/augparse').and_return(readable)
11 end
12
13 after do
14 augeas.invalidate_cache
15 end
16
17 context 'when augparse is installed' do
18 before do
19 allow(Facter::Core::Execution).to receive(:execute)
20 .with('augparse --version 2>&1', logger: log_spy)
21 .and_return('augparse 1.12.0 <http://augeas.net/>')
22 end
23
24 it 'returns build' do
25 expect(augeas.resolve(:augeas_version)).to eq('1.12.0')
26 end
27 end
28
29 context 'when augparse is installed with puppet-agent package on unix based systems' do
30 let(:readable) { true }
31
32 before do
33 allow(Facter::Core::Execution).to receive(:execute)
34 .with('/opt/puppetlabs/puppet/bin/augparse --version 2>&1', logger: log_spy)
35 .and_return('augparse 1.12.0 <http://augeas.net/>')
36 end
37
38 it 'returns build' do
39 expect(augeas.resolve(:augeas_version)).to eq('1.12.0')
40 end
41 end
42
43 context 'when augparse is not installed' do
44 before do
45 allow(Facter::Core::Execution).to receive(:execute)
46 .with('augparse --version 2>&1', logger: log_spy)
47 .and_return('sh: augparse: command not found')
48 end
49
50 context 'when augeas gem > 0.5.0 is installed' do
51 before do
52 allow(Gem).to receive(:loaded_specs).and_return({ 'augeas' => true })
53 allow(Augeas).to receive(:create).and_return('1.12.0')
54 end
55
56 it 'returns build' do
57 expect(augeas.resolve(:augeas_version)).to eq('1.12.0')
58 end
59 end
60
61 context 'when augeas gem <= 0.5.0 is installed' do
62 before do
63 allow(Gem).to receive(:loaded_specs).and_return({})
64 allow(Augeas).to receive(:open).and_return('1.12.0')
65 end
66
67 it 'returns build' do
68 expect(augeas.resolve(:augeas_version)).to eq('1.12.0')
69 end
70 end
71
72 context 'when augeas gem is not installed' do
73 let(:exception) { LoadError.new('load_error_message') }
74
75 before do
76 allow(Facter::Resolvers::Augeas).to receive(:require).with('augeas').and_raise(exception)
77 end
78
79 it 'raises a LoadError error' do
80 augeas.resolve(:augeas_version)
81
82 expect(log_spy).to have_received(:debug).with(/Resolving fact augeas_version, but got load_error_message/)
83 end
84 end
85 end
86 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Az do
3 subject(:az) { Facter::Resolvers::Az }
4
5 let(:uri) { 'http://169.254.169.254/metadata/instance?api-version=2020-09-01' }
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 allow(Facter::Util::Resolvers::Http).to receive(:get_request)
10 .with(uri, { Metadata: 'true' }, { session: 5 }).and_return(output)
11 az.instance_variable_set(:@log, log_spy)
12 end
13
14 after do
15 az.invalidate_cache
16 end
17
18 context 'when no exception is thrown' do
19 let(:output) { '{"azEnvironment":"AzurePublicCloud"}' }
20
21 it 'returns az metadata' do
22 expect(az.resolve(:metadata)).to eq({ 'azEnvironment' => 'AzurePublicCloud' })
23 end
24 end
25
26 context 'when an exception is thrown' do
27 let(:output) { '' }
28
29 it 'returns empty az metadata' do
30 expect(az.resolve(:metadata)).to eq({})
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::BaseResolver do
3 let(:fact) { 'fact' }
4 let(:resolver) do
5 Class.new(Facter::Resolvers::BaseResolver) do
6 init_resolver
7
8 def self.post_resolve(fact_name, _options)
9 @fact_list[fact_name] = 'value'
10 @fact_list
11 end
12 end
13 end
14
15 describe '#log' do
16 before do
17 allow(Facter::Log).to receive(:new).with(resolver).and_return('logger')
18 end
19
20 it 'initializes the log' do
21 resolver.log
22
23 expect(Facter::Log).to have_received(:new).with(resolver)
24 end
25
26 it 'initializes the log only once' do
27 resolver.log
28 resolver.log
29
30 expect(Facter::Log).to have_received(:new).with(resolver).once
31 end
32 end
33
34 describe '#invalidate_cache' do
35 it 'clears the facts_list' do
36 resolver.resolve(fact)
37
38 resolver.invalidate_cache
39
40 expect(resolver.resolve('fact2')).to eq('value')
41 end
42 end
43
44 describe '#subscribe_to_manager' do
45 before do
46 allow(Facter::SessionCache).to receive(:subscribe).with(resolver)
47 end
48
49 it 'calls the CacheManager subscribe method' do
50 resolver.subscribe_to_manager
51
52 expect(Facter::SessionCache).to have_received(:subscribe).with(resolver)
53 end
54 end
55
56 describe '#resolve' do
57 context 'when fact is resolved successfully' do
58 before do
59 allow(resolver).to receive(:post_resolve)
60 allow(Facter::SessionCache).to receive(:subscribe).with(resolver)
61 end
62
63 it 'calls the CacheManager subscribe method' do
64 resolver.resolve(fact)
65
66 expect(Facter::SessionCache).to have_received(:subscribe).with(resolver)
67 end
68
69 it 'calls the post_resolve method' do
70 resolver.resolve(fact)
71
72 expect(resolver).to have_received(:post_resolve).with(fact, {})
73 end
74 end
75
76 context 'when Load Error is raised' do
77 before do
78 allow(resolver).to receive(:post_resolve).and_raise(LoadError)
79 allow(Facter::Log).to receive(:new).with(resolver).and_return(instance_double(Facter::Log, debug: nil))
80 end
81
82 it 'logs the Load Error exception' do
83 resolver.resolve(fact)
84
85 expect(resolver.log).to have_received(:debug).with(/Resolving fact #{fact}, but got LoadError/)
86 end
87
88 it 'sets the fact to nil' do
89 expect(resolver.resolve(fact)).to eq(nil)
90 end
91 end
92 end
93
94 describe '#validate_resolution' do
95 before do
96 allow(resolver).to receive(:cache_nil_for_unresolved_facts)
97 end
98
99 it 'sets the fact to nil if undefined' do
100 resolver.cache_nil_for_unresolved_facts('unresolved_fact')
101 expect(resolver.resolve('unresolved_fact')).to be_nil
102 end
103
104 it 'does not overwrite values' do
105 resolver.resolve('my_fact')
106 resolver.cache_nil_for_unresolved_facts('my_fact')
107
108 expect(resolver.post_resolve('my_fact', {})).to eq({ 'my_fact' => 'value' })
109 end
110 end
111
112 describe '#post_resolve' do
113 let(:resolver) { Class.new(Facter::Resolvers::BaseResolver) }
114
115 it 'raises NotImplementedError error' do
116 expect { resolver.post_resolve(fact, {}) }.to \
117 raise_error(NotImplementedError,
118 'You must implement post_resolve(fact_name, options) method in ')
119 end
120 end
121 end
0 # frozen_string_literal: true
1
2 describe Facter::Bsd::FfiHelper do
3 describe '#sysctl' do
4 let(:oids) { [1, 2, 3] }
5 let(:name) { double('FFI::MemoryPointer') }
6 let(:oldlenp) { double('FFI::MemoryPointer') }
7 let(:oldp) { double('FFI::MemoryPointer') }
8
9 before do
10 allow(FFI::MemoryPointer).to receive(:new).with(:uint, oids.size).and_return(name)
11 allow(name).to receive(:write_array_of_uint).with(oids)
12 end
13
14 context 'when expecting a string' do
15 let(:result) { 'Hello World !' }
16
17 before do
18 allow(FFI::MemoryPointer).to receive(:new)
19 .with(:size_t)
20 .and_return(oldlenp)
21 allow(Facter::Bsd::FfiHelper::Libc).to receive(:sysctl)
22 .with(name, oids.size, FFI::Pointer::NULL, oldlenp, FFI::Pointer::NULL, 0)
23 .and_return(0)
24 allow(oldlenp).to receive(:read)
25 .with(:size_t)
26 .and_return(result.length)
27 allow(FFI::MemoryPointer).to receive(:new)
28 .with(:uint8_t, result.length)
29 .and_return(oldp)
30 allow(Facter::Bsd::FfiHelper::Libc).to receive(:sysctl)
31 .with(name, oids.size, oldp, oldlenp, FFI::Pointer::NULL, 0)
32 .and_return(0)
33 allow(oldp).to receive(:read_string)
34 .and_return(result)
35 end
36
37 it 'does what is expected' do
38 expect(Facter::Bsd::FfiHelper.sysctl(:string, oids)).to eq(result)
39 end
40 end
41
42 context 'when expecting an uint32_t' do
43 let(:result) { 42 }
44 let(:oldlen) { instance_double('Integer') }
45
46 before do
47 allow(FFI::MemoryPointer).to receive(:new)
48 .with(:size_t)
49 .and_return(oldlenp)
50 allow(FFI).to receive(:type_size)
51 .with(:uint32_t)
52 .and_return(4)
53 allow(oldlenp).to receive(:write)
54 .with(:size_t, 4)
55 allow(oldlenp).to receive(:read)
56 .and_return(oldlen)
57 allow(FFI::MemoryPointer).to receive(:new)
58 .with(:uint8_t, oldlen)
59 .and_return(oldp)
60 allow(Facter::Bsd::FfiHelper::Libc).to receive(:sysctl)
61 .with(name, oids.size, oldp, oldlenp, FFI::Pointer::NULL, 0)
62 .and_return(0)
63 allow(oldp).to receive(:read)
64 .with(:uint32_t)
65 .and_return(result)
66 end
67
68 it 'does what is expected' do
69 expect(Facter::Bsd::FfiHelper.sysctl(:uint32_t, oids)).to eq(result)
70 end
71 end
72 end
73 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Bsd::Processors do
3 subject(:resolver) { Facter::Resolvers::Bsd::Processors }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:logicalcount) { 2 }
7 let(:models) do
8 ['Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz', 'Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz']
9 end
10 let(:speed_expected) { 2_592_000_000 }
11
12 before do
13 allow(Facter::Bsd::FfiHelper)
14 .to receive(:sysctl)
15 .with(:uint32_t, [6, 3])
16 .and_return(logicalcount)
17 allow(Facter::Bsd::FfiHelper)
18 .to receive(:sysctl)
19 .with(:string, [6, 2])
20 .and_return(models[0])
21 allow(Facter::Bsd::FfiHelper)
22 .to receive(:sysctl)
23 .with(:uint32_t, [6, 12])
24 .and_return(2592)
25
26 resolver.instance_variable_set(:@log, log_spy)
27 end
28
29 after do
30 resolver.invalidate_cache
31 end
32
33 it 'returns number of processors' do
34 expect(resolver.resolve(:logical_count)).to eq(logicalcount)
35 end
36
37 it 'returns list of models' do
38 expect(resolver.resolve(:models)).to eq(models)
39 end
40
41 it 'returns speed of processors' do
42 expect(resolver.resolve(:speed)).to eq(speed_expected)
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Containers do
3 subject(:containers_resolver) { Facter::Resolvers::Containers }
4
5 before do
6 allow(Facter::Util::FileHelper).to receive(:safe_read)
7 .with('/proc/1/cgroup', nil)
8 .and_return(cgroup_output)
9 allow(Facter::Util::FileHelper).to receive(:safe_read)
10 .with('/proc/1/environ', nil)
11 .and_return(environ_output)
12 end
13
14 after do
15 containers_resolver.invalidate_cache
16 end
17
18 context 'when hypervisor is docker' do
19 let(:cgroup_output) { load_fixture('docker_cgroup').read }
20 let(:environ_output) { 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
21 let(:result) { { docker: { 'id' => 'ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90' } } }
22
23 it 'return docker for vm' do
24 expect(containers_resolver.resolve(:vm)).to eq('docker')
25 end
26
27 it 'return docker info for hypervisor' do
28 expect(containers_resolver.resolve(:hypervisor)).to eq(result)
29 end
30 end
31
32 context 'when hypervisor is nspawn' do
33 let(:cgroup_output) { load_fixture('cgroup_file').read }
34 let(:environ_output) { 'PATH=/usr/local/sbin:/bincontainer=systemd-nspawnTERM=xterm-256color' }
35 let(:result) { { systemd_nspawn: { 'id' => 'ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90' } } }
36
37 before do
38 allow(Facter::Util::FileHelper).to receive(:safe_read)
39 .with('/etc/machine-id', nil)
40 .and_return("ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90\n")
41 end
42
43 it 'return nspawn for vm' do
44 expect(containers_resolver.resolve(:vm)).to eq('systemd_nspawn')
45 end
46
47 it 'return nspawn info for hypervisor' do
48 expect(containers_resolver.resolve(:hypervisor)).to eq(result)
49 end
50 end
51
52 context 'when hypervisor is lxc and it is discovered by cgroup' do
53 let(:cgroup_output) { load_fixture('lxc_cgroup').read }
54 let(:environ_output) { 'PATH=/usr/local/sbin:/sbin:/bin' }
55 let(:result) { { lxc: { 'name' => 'lxc_container' } } }
56
57 it 'return lxc for vm' do
58 expect(containers_resolver.resolve(:vm)).to eq('lxc')
59 end
60
61 it 'return lxc info for hypervisor' do
62 expect(containers_resolver.resolve(:hypervisor)).to eq(result)
63 end
64 end
65
66 context 'when hypervisor is lxc and it is discovered by environ' do
67 let(:cgroup_output) { load_fixture('cgroup_file').read }
68 let(:environ_output) { 'container=lxcroot' }
69 let(:result) { { lxc: {} } }
70
71 it 'return lxc for vm' do
72 expect(containers_resolver.resolve(:vm)).to eq('lxc')
73 end
74
75 it 'return lxc info for hypervisor' do
76 expect(containers_resolver.resolve(:hypervisor)).to eq(result)
77 end
78 end
79
80 context 'when hypervisor is neighter lxc nor docker' do
81 let(:cgroup_output) { load_fixture('cgroup_file').read }
82 let(:environ_output) { 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin' }
83 let(:result) { nil }
84
85 it 'return lxc for vm' do
86 expect(containers_resolver.resolve(:vm)).to eq(nil)
87 end
88
89 it 'return lxc info for hypervisor' do
90 expect(containers_resolver.resolve(:hypervisor)).to eq(result)
91 end
92 end
93 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::DebianVersion do
3 after do
4 Facter::Resolvers::DebianVersion.invalidate_cache
5 end
6
7 context 'when file debian_version is readable' do
8 before do
9 allow(Facter::Util::FileHelper).to receive(:safe_read)
10 .with('/etc/debian_version').and_return("10.01\n")
11 end
12
13 it 'returns version' do
14 result = Facter::Resolvers::DebianVersion.resolve(:version)
15
16 expect(result).to eq('10.01')
17 end
18 end
19
20 context 'when file debian_version is not readable' do
21 before do
22 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/etc/debian_version').and_return('')
23 end
24
25 it 'returns nil' do
26 result = Facter::Resolvers::DebianVersion.resolve(:version)
27
28 expect(result).to be(nil)
29 end
30 end
31 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Disks do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Linux::Disks }
5
6 let(:disk_files) { %w[sr0/device sr0/size sda/device sda/size] }
7
8 let(:paths) do
9 {
10 model: '/device/model',
11 size: '/size',
12 vendor: '/device/vendor',
13 type: '/queue/rotational',
14 serial: 'false',
15 wwn: 'false'
16 }
17 end
18
19 let(:disks) do
20 {
21 sr0: {
22 size: '12',
23 rotational: '0',
24 model: 'model1',
25 vendor: 'vendor1',
26 serial: 'AJDI2491AK',
27 wwn: '239090190.0'
28 },
29 sda: {
30 size: '231',
31 rotational: '1',
32 model: 'model2',
33 vendor: 'vendor2',
34 serial: 'B2EI34F1AL',
35 wwn: '29429191.0'
36 }
37 }
38 end
39
40 before do
41 allow(Facter::Util::FileHelper).to receive(:dir_children).with('/sys/block').and_return(%w[sda sr0])
42 end
43
44 after do
45 Facter::Resolvers::Linux::Disks.invalidate_cache
46 end
47
48 context 'when device dir for blocks exists' do
49 let(:expected_output) do
50 { 'sr0' => { model: 'model1', serial: 'AJDI2491AK', wwn: '239090190.0',
51 size: '6.00 KiB', size_bytes: 6144, vendor: 'vendor1', type: 'ssd' },
52 'sda' => { model: 'model2', serial: 'B2EI34F1AL', wwn: '29429191.0',
53 size: '115.50 KiB', size_bytes: 118_272, vendor: 'vendor2', type: 'hdd' } }
54 end
55
56 before do
57 disk_files.each do |disk_file|
58 allow(File).to receive(:readable?).with("/sys/block/#{disk_file}").and_return(true)
59 end
60
61 paths.each do |fact, value|
62 disks.each do |disk, values|
63 case value
64 when '/size'
65 allow(Facter::Util::FileHelper).to receive(:safe_read)
66 .with("/sys/block/#{disk}#{value}", nil).and_return(values[:size])
67 when '/queue/rotational'
68 allow(Facter::Util::FileHelper).to receive(:safe_read)
69 .with("/sys/block/#{disk}#{value}", nil).and_return(values[:rotational])
70 when '/device/model'
71 allow(Facter::Util::FileHelper).to receive(:safe_read)
72 .with("/sys/block/#{disk}#{value}", nil).and_return(values[:model])
73 when '/device/vendor'
74 allow(Facter::Util::FileHelper).to receive(:safe_read)
75 .with("/sys/block/#{disk}#{value}", nil).and_return(values[:vendor])
76 when 'false'
77 allow(Facter::Core::Execution).to receive(:execute)
78 .with("lsblk -dn -o #{fact} /dev/#{disk}", on_fail: '', timeout: 1)
79 .and_return(values[fact])
80 end
81 end
82 end
83 end
84
85 context 'when all files are readable' do
86 it 'returns disks fact' do
87 expect(resolver.resolve(:disks)).to eql(expected_output)
88 end
89 end
90
91 context 'when size files are not readable' do
92 let(:expected_output) do
93 { 'sr0' => { model: 'model1', vendor: 'vendor1', type: 'ssd', serial: 'AJDI2491AK', wwn: '239090190.0' },
94 'sda' => { model: 'model2', vendor: 'vendor2', type: 'hdd', serial: 'B2EI34F1AL', wwn: '29429191.0' } }
95 end
96
97 before do
98 paths.each do |_key, value|
99 disks.each do |disk, _values|
100 if value == '/size'
101 allow(Facter::Util::FileHelper).to receive(:safe_read)
102 .with("/sys/block/#{disk}#{value}", nil).and_return(nil)
103 end
104 end
105 end
106 end
107
108 it 'returns disks fact' do
109 expect(resolver.resolve(:disks)).to eql(expected_output)
110 end
111 end
112
113 context 'when device vendor and model files are not readable' do
114 let(:expected_output) do
115 { 'sr0' => { size: '6.00 KiB', size_bytes: 6144, serial: 'AJDI2491AK', wwn: '239090190.0' },
116 'sda' => { size: '115.50 KiB', size_bytes: 118_272, serial: 'B2EI34F1AL', wwn: '29429191.0' } }
117 end
118
119 before do
120 paths.each do |_key, value|
121 disks.each do |disk, _values|
122 if value != '/size'
123 allow(Facter::Util::FileHelper).to receive(:safe_read)
124 .with("/sys/block/#{disk}#{value}", nil).and_return(nil)
125 end
126 end
127 end
128 end
129
130 it 'returns disks fact' do
131 expect(resolver.resolve(:disks)).to eql(expected_output)
132 end
133 end
134
135 context 'when serial and wwn are not returned by lsblk' do
136 let(:expected_output) do
137 { 'sr0' => { size: '6.00 KiB', size_bytes: 6144, model: 'model1', vendor: 'vendor1', type: 'ssd' },
138 'sda' => { size: '115.50 KiB', size_bytes: 118_272, model: 'model2', vendor: 'vendor2', type: 'hdd' } }
139 end
140
141 before do
142 paths.each do |fact, value|
143 disks.each do |disk, _values|
144 next unless value == 'false'
145
146 allow(Facter::Core::Execution).to receive(:execute)
147 .with("lsblk -dn -o #{fact} /dev/#{disk}", on_fail: '', timeout: 1)
148 .and_return('')
149 end
150 end
151 end
152
153 it 'returns disks fact' do
154 expect(resolver.resolve(:disks)).to eql(expected_output)
155 end
156 end
157 end
158
159 context 'when all files inside device dir for blocks are missing' do
160 before do
161 disk_files.each do |disk_file|
162 allow(File).to receive(:readable?).with("/sys/block/#{disk_file}").and_return(false)
163 end
164 end
165
166 it 'returns disks fact as nil' do
167 expect(resolver.resolve(:disks)).to be(nil)
168 end
169 end
170 end
171 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::DmiDecode do
3 describe '#resolve' do
4 subject(:dmidecode) { Facter::Resolvers::DmiDecode }
5
6 before do
7 allow(Facter::Core::Execution).to receive(:execute)
8 .with('dmidecode', logger: instance_of(Facter::Log)).and_return(command_output)
9 end
10
11 after { dmidecode.invalidate_cache }
12
13 context 'when virtualbox hypervisor' do
14 let(:command_output) { load_fixture('dmi_decode_virtualbox').read }
15
16 it 'detects virtualbox version' do
17 expect(dmidecode.resolve(:virtualbox_version)).to eql('6.1.4')
18 end
19
20 it 'detects virtualbox revision' do
21 expect(dmidecode.resolve(:virtualbox_revision)).to eql('136177')
22 end
23
24 it 'does not detect vmware_version' do
25 expect(dmidecode.resolve(:vmware_version)).to be_nil
26 end
27 end
28
29 context 'when vmware hypervisor' do
30 let(:command_output) { load_fixture('dmi_decode_vmware').read }
31
32 it 'does not detects virtualbox version' do
33 expect(dmidecode.resolve(:virtualbox_version)).to be_nil
34 end
35
36 it 'does not detect detects virtualbox revision' do
37 expect(dmidecode.resolve(:virtualbox_revision)).to be_nil
38 end
39
40 it 'detect vmware_version' do
41 expect(dmidecode.resolve(:vmware_version)).to eq('ESXi 6.7')
42 end
43 end
44
45 context 'when dmidecode command failed' do
46 let(:command_output) { 'command not found: dmidecode' }
47
48 it 'detects virtualbox version as nil' do
49 expect(dmidecode.resolve(:virtualbox_version)).to be(nil)
50 end
51
52 it 'detects virtualbox revision as nil' do
53 expect(dmidecode.resolve(:virtualbox_revision)).to be(nil)
54 end
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::DmiBios do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Linux::DmiBios }
5
6 let(:test_dir) { '/sys/class/dmi' }
7
8 before do
9 allow(File).to receive(:directory?).with(test_dir).and_return(true)
10 allow(Facter::Util::FileHelper).to receive(:safe_read)
11 .with("/sys/class/dmi/id/#{file}", nil).and_return(file_content)
12 end
13
14 after do
15 Facter::Resolvers::Linux::DmiBios.invalidate_cache
16 end
17
18 context 'when bios_date file exists' do
19 let(:file_content) { '12/12/2018' }
20 let(:file) { 'bios_date' }
21
22 it 'returns bios_release_date' do
23 expect(resolver.resolve(:bios_date)).to eq(file_content)
24 end
25 end
26
27 context 'when bios_vendor file exists' do
28 let(:file_content) { 'Phoenix Technologies LTD' }
29 let(:file) { 'bios_vendor' }
30
31 it 'returns bios_release_date' do
32 expect(resolver.resolve(:bios_vendor)).to eq(file_content)
33 end
34 end
35
36 context 'when bios_version file exists' do
37 let(:file_content) { '6.00' }
38 let(:file) { 'bios_version' }
39
40 it 'returns bios_version' do
41 expect(resolver.resolve(:bios_version)).to eq(file_content)
42 end
43 end
44
45 context 'when board_vendor file exists' do
46 let(:file_content) { 'Intel Corporation' }
47 let(:file) { 'board_vendor' }
48
49 it 'returns board_manufacturer' do
50 expect(resolver.resolve(:board_vendor)).to eq(file_content)
51 end
52 end
53
54 context 'when board_asset_tag file exists' do
55 let(:file_content) { 'Not Specified' }
56 let(:file) { 'board_asset_tag' }
57
58 it 'returns board_manufacturer' do
59 expect(resolver.resolve(:board_asset_tag)).to eq(file_content)
60 end
61 end
62
63 context 'when board_asset_tag file exists but is empty' do
64 let(:file_content) { "\n" }
65 let(:file) { 'board_asset_tag' }
66
67 it 'returns board_manufacturer' do
68 expect(resolver.resolve(:board_asset_tag)).to be(nil)
69 end
70 end
71
72 context 'when board_name file exists' do
73 let(:file_content) { '440BX Desktop Reference Platform' }
74 let(:file) { 'board_name' }
75
76 it 'returns board_product' do
77 expect(resolver.resolve(:board_name)).to eq(file_content)
78 end
79 end
80
81 context 'when board_serial file exists' do
82 let(:file_content) { 'None' }
83 let(:file) { 'board_serial' }
84
85 it 'returns board_serial_number' do
86 expect(resolver.resolve(:board_serial)).to eq(file_content)
87 end
88 end
89
90 context 'when chassis_asset_tag file exists' do
91 let(:file_content) { 'No Asset Tag' }
92 let(:file) { 'chassis_asset_tag' }
93
94 it 'returns chassis_asset_tag' do
95 expect(resolver.resolve(:chassis_asset_tag)).to eq(file_content)
96 end
97 end
98
99 context 'when chassis_type file exists' do
100 let(:file_content) { '4' }
101 let(:file) { 'chassis_type' }
102
103 it 'returns chassis_type' do
104 expect(resolver.resolve(:chassis_type)).to eq('Low Profile Desktop')
105 end
106 end
107
108 context 'when sys_vendor file exists' do
109 let(:file_content) { 'VMware, Inc.' }
110 let(:file) { 'sys_vendor' }
111
112 it ' returns sys_vendor' do
113 expect(resolver.resolve(:sys_vendor)).to eq(file_content)
114 end
115 end
116
117 context 'when product_name file exists' do
118 let(:file_content) { 'VMware Virtual Platform' }
119 let(:file) { 'product_name' }
120
121 it 'returns product_name' do
122 expect(resolver.resolve(:product_name)).to eq(file_content)
123 end
124 end
125
126 context 'when product_serial file exists' do
127 let(:file_content) { 'VMware-42 1a 02 ea e6 27 76 b8-a1 23 a7 8a d3 12 ee cf' }
128 let(:file) { 'product_serial' }
129
130 it 'returns product_serial_number' do
131 expect(resolver.resolve(:product_serial)).to eq(file_content)
132 end
133 end
134
135 context 'when product_uuid file exists' do
136 let(:file_content) { 'ea021a42-27e6-b876-a123-a78ad312eecf' }
137 let(:file) { 'product_uuid' }
138
139 it 'returns product_uuid' do
140 expect(resolver.resolve(:product_uuid)).to eq(file_content)
141 end
142 end
143
144 context 'when product_serial file does not exists' do
145 let(:file_content) { nil }
146 let(:file) { 'product_serial' }
147
148 it 'returns nil' do
149 expect(resolver.resolve(:product_serial)).to be(nil)
150 end
151 end
152
153 context 'when product_uuid file does not exist' do
154 let(:file_content) { nil }
155 let(:file) { 'product_uuid' }
156
157 it 'returns nil' do
158 expect(resolver.resolve(:product_uuid)).to be(nil)
159 end
160 end
161
162 context 'when product_name contains garbled information' do
163 let(:file_content) { "Supermicro^L\x8DD$Pptal0\n" }
164 let(:file) { 'sys_vendor' }
165
166 it 'returns product_name with the invalid characters replaced' do
167 expect(resolver.resolve(:sys_vendor)).to eq('Supermicro^L�D$Pptal0')
168 end
169 end
170 end
171 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Ec2 do
3 subject(:ec2) { Facter::Resolvers::Ec2 }
4
5 let(:base_uri) { 'http://169.254.169.254/latest' }
6 let(:userdata_uri) { "#{base_uri}/user-data/" }
7 let(:metadata_uri) { "#{base_uri}/meta-data/" }
8 let(:token_uri) { "#{base_uri}/api/token" }
9 let(:log_spy) { instance_spy(Facter::Log) }
10
11 before do
12 Facter::Util::Resolvers::Http.instance_variable_set(:@log, log_spy)
13 end
14
15 after do
16 ec2.invalidate_cache
17 Facter::Util::Resolvers::AwsToken.reset
18 Facter::Util::Resolvers::Http.instance_variable_set(:@log, nil)
19 end
20
21 shared_examples_for 'ec2' do
22 let(:paths) do
23 {
24 'meta-data/' => "instance_type\nami_id\nsecurity-groups",
25 'meta-data/instance_type' => 'c1.medium',
26 'meta-data/ami_id' => 'ami-5d2dc934',
27 'meta-data/security-groups' => "group1\ngroup2"
28 }
29 end
30
31 before do
32 stub_request(:get, userdata_uri).with(headers: headers).to_return(status: 200, body: 'userdata')
33 paths.each_pair do |path, body|
34 stub_request(:get, "#{base_uri}/#{path}").with(headers: headers).to_return(status: 200, body: body)
35 end
36 end
37
38 context 'with common metadata paths' do
39 it 'recursively fetches all the ec2 metadata' do
40 expect(ec2.resolve(:metadata)).to match(
41 {
42 'instance_type' => 'c1.medium',
43 'ami_id' => 'ami-5d2dc934',
44 'security-groups' => "group1\ngroup2"
45 }
46 )
47 end
48
49 it 'returns userdata' do
50 expect(ec2.resolve(:userdata)).to eql('userdata')
51 end
52
53 it 'parses ec2 network/ directory as a multi-level hash' do
54 network_hash = {
55 'network' => {
56 'interfaces' => {
57 'macs' => {
58 '12:34:56:78:9a:bc' => {
59 'accountId' => '41234'
60 }
61 }
62 }
63 }
64 }
65 stub_request(:get, metadata_uri).with(headers: headers).to_return(status: 200, body: 'network/')
66 stub_request(:get, "#{metadata_uri}network/")
67 .with(headers: headers)
68 .to_return(status: 200, body: 'interfaces/')
69 stub_request(:get, "#{metadata_uri}network/interfaces/")
70 .with(headers: headers)
71 .to_return(status: 200, body: 'macs/')
72 stub_request(:get, "#{metadata_uri}network/interfaces/macs/")
73 .with(headers: headers)
74 .to_return(status: 200, body: '12:34:56:78:9a:bc/')
75 stub_request(:get, "#{metadata_uri}network/interfaces/macs/12:34:56:78:9a:bc/")
76 .with(headers: headers)
77 .to_return(status: 200, body: 'accountId')
78 stub_request(:get, "#{metadata_uri}network/interfaces/macs/12:34:56:78:9a:bc/accountId")
79 .with(headers: headers)
80 .to_return(status: 200, body: '41234')
81
82 expect(ec2.resolve(:metadata)).to match(hash_including(network_hash))
83 end
84
85 it 'fetches the available data' do
86 stub_request(:get, "#{metadata_uri}instance_type").with(headers: headers).to_return(status: 404)
87
88 expect(ec2.resolve(:metadata)).to match(
89 {
90 'instance_type' => '',
91 'ami_id' => 'ami-5d2dc934',
92 'security-groups' => "group1\ngroup2"
93 }
94 )
95 end
96 end
97
98 context 'when an exception is thrown' do
99 before do
100 stub_request(:get, userdata_uri).to_raise(StandardError)
101 stub_request(:get, metadata_uri).to_raise(StandardError)
102 end
103
104 it 'returns empty ec2 metadata' do
105 expect(ec2.resolve(:metadata)).to eql({})
106 end
107
108 it 'returns empty ec2 userdata' do
109 expect(ec2.resolve(:userdata)).to eql('')
110 end
111 end
112 end
113
114 context 'when IMDSv2' do
115 before do
116 stub_request(:put, token_uri).to_return(status: 200, body: token)
117 end
118
119 let(:token) { 'v2_token' }
120 let(:headers) { { 'X-aws-ec2-metadata-token' => token } }
121
122 it_behaves_like 'ec2'
123 end
124
125 context 'when IMDSv1' do
126 before do
127 stub_request(:put, token_uri).to_return(status: 404, body: 'Not Found')
128 end
129
130 let(:token) { nil }
131 let(:headers) { { 'Accept' => '*/*' } }
132
133 it_behaves_like 'ec2'
134 end
135
136 it 'does not add headers if token is nil' do
137 allow(Facter::Resolvers::Ec2).to receive(:v2_token).and_return(nil)
138
139 stub_request(:get, metadata_uri).with { |request| !request.headers.key?('X-aws-ec2-metadata-token') }
140 stub_request(:get, userdata_uri).with { |request| !request.headers.key?('X-aws-ec2-metadata-token') }
141
142 ec2.resolve(:userdata)
143 end
144
145 context 'when data received are ASCII-8BIT encoded' do
146 let(:headers) { { 'Accept' => '*/*' } }
147
148 it 'converts data received to UTF-8 encoding' do
149 allow(Facter::Resolvers::Ec2).to receive(:v2_token).and_return(nil)
150
151 expected_str = "nofail\xCC\xA6\"".strip
152 bin_str = "nofail\xCC\xA6\"".strip.force_encoding(Encoding::ASCII_8BIT)
153 stub_request(:get, userdata_uri).with(headers: headers).to_return(status: 200, body: bin_str, headers:
154 { 'Content-Type' => 'application/octet-stream' })
155 stub_request(:get, metadata_uri).with { |request| !request.headers.key?('X-aws-ec2-metadata-token') }
156
157 expect(ec2.resolve(:userdata)).to eql(expected_str)
158 end
159 end
160 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::EosRelease do
3 subject(:eos_release) { Facter::Resolvers::EosRelease }
4
5 before do
6 allow(Facter::Util::FileHelper).to receive(:safe_read)
7 .with('/etc/Eos-release', nil)
8 .and_return('name version')
9 end
10
11 it 'returns name' do
12 expect(eos_release.resolve(:name)).to eq('name')
13 end
14
15 it 'returns version' do
16 expect(eos_release.resolve(:version)).to eq('version')
17 end
18 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Facterversion do
3 describe '#resolve' do
4 let(:version) { '4.0.1' }
5
6 after { Facter::Resolvers::Facterversion.invalidate_cache }
7
8 it 'detects facter version' do
9 stub_const('Facter::VERSION', version)
10 expect(Facter::Resolvers::Facterversion.resolve(:facterversion)).to eql('4.0.1')
11 end
12
13 context 'when there are new lines in the version file' do
14 let(:version) { '4.0.2' }
15
16 it 'removes them' do
17 stub_const('Facter::VERSION', version)
18 expect(Facter::Resolvers::Facterversion.resolve(:facterversion)).to eq('4.0.2')
19 end
20 end
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Filesystems do
3 let(:systems) { 'ext2,ext3,ext4,xfs' }
4
5 after do
6 Facter::Resolvers::Linux::Filesystems.invalidate_cache
7 end
8
9 context 'when filesystems is readable' do
10 before do
11 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
12 .with('/proc/filesystems', nil)
13 .and_return(load_fixture('filesystems').readlines)
14 end
15
16 it 'returns systems' do
17 result = Facter::Resolvers::Linux::Filesystems.resolve(:systems)
18
19 expect(result).to eq(systems)
20 end
21 end
22
23 context 'when filesystems is not readable' do
24 before do
25 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
26 .with('/proc/filesystems', nil).and_return(nil)
27 end
28
29 it 'returns nil' do
30 result = Facter::Resolvers::Linux::Filesystems.resolve(:systems)
31
32 expect(result).to be(nil)
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::FipsEnabled do
3 describe '#resolve' do
4 before do
5 allow(Facter::Util::FileHelper).to receive(:safe_read)\
6 .with('/proc/sys/crypto/fips_enabled').and_return(file_content)
7 end
8
9 after do
10 Facter::Resolvers::Linux::FipsEnabled.invalidate_cache
11 end
12
13 context 'when fips is not enabled' do
14 let(:file_content) { '0' }
15
16 it 'returns fips is not enabled' do
17 result = Facter::Resolvers::Linux::FipsEnabled.resolve(:fips_enabled)
18
19 expect(result).to eq(false)
20 end
21 end
22
23 context 'when fips_enabled file is missing' do
24 let(:file_content) { '' }
25
26 it 'returns fips is not enabled' do
27 result = Facter::Resolvers::Linux::FipsEnabled.resolve(:fips_enabled)
28
29 expect(result).to eq(false)
30 end
31 end
32
33 context 'when fips is enabled' do
34 let(:file_content) { '1' }
35
36 it 'returns fips is enabled' do
37 result = Facter::Resolvers::Linux::FipsEnabled.resolve(:fips_enabled)
38
39 expect(result).to eq(true)
40 end
41 end
42 end
43 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::DmiBios do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Freebsd::DmiBios }
5
6 let(:bios_date) { '12/12/2018' }
7 let(:bios_vendor) { 'Phoenix Technologies LTD' }
8 let(:bios_version) { '6.00' }
9 let(:product_name) { 'VMware Virtual Platform' }
10 let(:product_serial) { 'VMware-42 1a 02 ea e6 27 76 b8-a1 23 a7 8a d3 12 ee cf' }
11 let(:product_uuid) { 'ea021a42-27e6-b876-a123-a78ad312eecf' }
12 let(:sys_vendor) { 'VMware, Inc.' }
13
14 before do
15 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
16 .with(:get, 'smbios.bios.reldate')
17 .and_return(bios_date)
18 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
19 .with(:get, 'smbios.bios.vendor')
20 .and_return(bios_vendor)
21 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
22 .with(:get, 'smbios.bios.version')
23 .and_return(bios_version)
24 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
25 .with(:get, 'smbios.system.product')
26 .and_return(product_name)
27 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
28 .with(:get, 'smbios.system.serial')
29 .and_return(product_serial)
30 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
31 .with(:get, 'smbios.system.uuid')
32 .and_return(product_uuid)
33 allow(Facter::Freebsd::FfiHelper).to receive(:kenv)
34 .with(:get, 'smbios.system.maker')
35 .and_return(sys_vendor)
36 end
37
38 after do
39 Facter::Resolvers::Freebsd::DmiBios.invalidate_cache
40 end
41
42 context 'when bios_date is available' do
43 it 'returns bios_release_date' do
44 expect(resolver.resolve(:bios_date)).to eq(bios_date)
45 end
46 end
47
48 context 'when bios_vendor is available' do
49 it 'returns bios_release_date' do
50 expect(resolver.resolve(:bios_vendor)).to eq(bios_vendor)
51 end
52 end
53
54 context 'when bios_version is available' do
55 it 'returns bios_version' do
56 expect(resolver.resolve(:bios_version)).to eq(bios_version)
57 end
58 end
59
60 context 'when sys_vendor is available' do
61 it ' returns sys_vendor' do
62 expect(resolver.resolve(:sys_vendor)).to eq(sys_vendor)
63 end
64 end
65
66 context 'when product_name is available' do
67 it 'returns product_name' do
68 expect(resolver.resolve(:product_name)).to eq(product_name)
69 end
70 end
71
72 context 'when product_serial is available' do
73 it 'returns product_serial_number' do
74 expect(resolver.resolve(:product_serial)).to eq(product_serial)
75 end
76 end
77
78 context 'when product_uuid is available' do
79 it 'returns product_uuid' do
80 expect(resolver.resolve(:product_uuid)).to eq(product_uuid)
81 end
82 end
83 end
84 end
0 # frozen_string_literal: true
1
2 describe Facter::Freebsd::FfiHelper do
3 describe '#sysctl_by_name' do
4 let(:name) { 'foo.bar.baz' }
5 let(:oldlenp) { double('FFI::MemoryPointer') }
6 let(:oldp) { double('FFI::MemoryPointer') }
7
8 context 'when expecting a string' do
9 let(:result) { 'Hello World !' }
10
11 before do
12 allow(FFI::MemoryPointer).to receive(:new)
13 .with(:size_t)
14 .and_return(oldlenp)
15 allow(Facter::Freebsd::FfiHelper::Libc).to receive(:sysctlbyname)
16 .with(name, FFI::Pointer::NULL, oldlenp, FFI::Pointer::NULL, 0)
17 .and_return(0)
18 allow(oldlenp).to receive(:read)
19 .with(:size_t)
20 .and_return(result.length)
21 allow(FFI::MemoryPointer).to receive(:new)
22 .with(:uint8_t, result.length)
23 .and_return(oldp)
24 allow(Facter::Freebsd::FfiHelper::Libc).to receive(:sysctlbyname)
25 .with(name, oldp, oldlenp, FFI::Pointer::NULL, 0)
26 .and_return(0)
27 allow(oldp).to receive(:read_string)
28 .and_return(result)
29 end
30
31 it 'does what is expected' do
32 expect(Facter::Freebsd::FfiHelper.sysctl_by_name(:string, name)).to eq(result)
33 end
34 end
35
36 context 'when expecting an uint32_t' do
37 let(:result) { 42 }
38 let(:oldlen) { double('Integer') }
39
40 before do
41 allow(FFI::MemoryPointer).to receive(:new)
42 .with(:size_t)
43 .and_return(oldlenp)
44 allow(FFI).to receive(:type_size)
45 .with(:uint32_t)
46 .and_return(4)
47 allow(oldlenp).to receive(:write)
48 .with(:size_t, 4)
49 allow(oldlenp).to receive(:read)
50 .and_return(oldlen)
51 allow(FFI::MemoryPointer).to receive(:new)
52 .with(:uint8_t, oldlen)
53 .and_return(oldp)
54 allow(Facter::Freebsd::FfiHelper::Libc).to receive(:sysctlbyname)
55 .with(name, oldp, oldlenp, FFI::Pointer::NULL, 0)
56 .and_return(0)
57 allow(oldp).to receive(:read)
58 .with(:uint32_t)
59 .and_return(result)
60 end
61
62 it 'does what is expected' do
63 expect(Facter::Freebsd::FfiHelper.sysctl_by_name(:uint32_t, name)).to eq(result)
64 end
65 end
66 end
67 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::FreebsdVersion do
3 subject(:freebsd_version) { Facter::Resolvers::Freebsd::FreebsdVersion }
4
5 before do
6 allow(Facter::Core::Execution).to receive(:execute)
7 .with('/bin/freebsd-version -k', logger: freebsd_version.log)
8 .and_return("13.0-CURRENT\n")
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('/bin/freebsd-version -ru', logger: freebsd_version.log)
11 .and_return("12.1-RELEASE-p3\n12.0-STABLE\n")
12 end
13
14 it 'returns installed kernel' do
15 expect(freebsd_version.resolve(:installed_kernel)).to eq('13.0-CURRENT')
16 end
17
18 it 'returns running kernel' do
19 expect(freebsd_version.resolve(:running_kernel)).to eq('12.1-RELEASE-p3')
20 end
21
22 it 'returns installed userland' do
23 expect(freebsd_version.resolve(:installed_userland)).to eq('12.0-STABLE')
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::Geom do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Freebsd::Geom }
5
6 before do
7 allow(Facter::Freebsd::FfiHelper).to receive(:sysctl_by_name)
8 .with(:string, 'kern.geom.confxml')
9 .and_return(load_fixture('kern.geom.confxml').read)
10 end
11
12 describe 'disks resolution' do
13 let(:expected_output) do
14 {
15 'ada0' => {
16 model: 'Samsung SSD 850 PRO 512GB',
17 serial_number: 'S250NXAG959927J',
18 size: '476.94 GiB',
19 size_bytes: 512_110_190_592
20 }
21 }
22 end
23
24 it 'returns disks fact' do
25 expect(resolver.resolve(:disks)).to eql(expected_output)
26 end
27 end
28
29 describe 'partitions resolution' do
30 let(:expected_output) do
31 {
32 'ada0p1' => {
33 partlabel: 'gptboot0',
34 partuuid: '503d3458-c135-11e8-bd11-7d7cd061b26f',
35 size: '512.00 KiB',
36 size_bytes: 524_288
37 },
38 'ada0p2' => {
39 partlabel: 'swap0',
40 partuuid: '5048d40d-c135-11e8-bd11-7d7cd061b26f',
41 size: '2.00 GiB',
42 size_bytes: 2_147_483_648
43 },
44 'ada0p3' => {
45 partlabel: 'zfs0',
46 partuuid: '504f1547-c135-11e8-bd11-7d7cd061b26f',
47 size: '474.94 GiB',
48 size_bytes: 509_961_306_112
49 }
50 }
51 end
52
53 it 'returns partitions fact' do
54 expect(resolver.resolve(:partitions)).to eql(expected_output)
55 end
56 end
57 end
58 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::Processors do
3 subject(:resolver) { Facter::Resolvers::Freebsd::Processors }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:logicalcount) { 2 }
7 let(:models) do
8 ['Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz', 'Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz']
9 end
10 let(:speed_expected) { 2_592_000_000 }
11
12 before do
13 allow(Facter::Freebsd::FfiHelper)
14 .to receive(:sysctl_by_name)
15 .with(:uint32_t, 'hw.ncpu')
16 .and_return(logicalcount)
17 allow(Facter::Freebsd::FfiHelper)
18 .to receive(:sysctl_by_name)
19 .with(:string, 'hw.model')
20 .and_return(models[0])
21 allow(Facter::Freebsd::FfiHelper)
22 .to receive(:sysctl_by_name)
23 .with(:uint32_t, 'hw.clockrate')
24 .and_return(2592)
25
26 resolver.instance_variable_set(:@log, log_spy)
27 end
28
29 after do
30 resolver.invalidate_cache
31 end
32
33 it 'returns number of processors' do
34 expect(resolver.resolve(:logical_count)).to eq(logicalcount)
35 end
36
37 it 'returns list of models' do
38 expect(resolver.resolve(:models)).to eq(models)
39 end
40
41 it 'returns speed of processors' do
42 expect(resolver.resolve(:speed)).to eq(speed_expected)
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::SwapMemory do
3 subject(:swap_memory) { Facter::Resolvers::Freebsd::SwapMemory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:available_bytes) { 4_294_967_296 }
7 let(:total_bytes) { 4_294_967_296 }
8 let(:used_bytes) { 0 }
9 let(:capacity) { '0%' }
10 let(:encrypted) { true }
11
12 before do
13 swap_memory.instance_variable_set(:@log, log_spy)
14 allow(Facter::Core::Execution).to receive(:execute)
15 .with('swapinfo -k', logger: log_spy)
16 .and_return(load_fixture('freebsd_swapinfo').read)
17 end
18
19 it 'returns available swap memory in bytes' do
20 expect(swap_memory.resolve(:available_bytes)).to eq(available_bytes)
21 end
22
23 it 'returns total swap memory in bytes' do
24 expect(swap_memory.resolve(:total_bytes)).to eq(total_bytes)
25 end
26
27 it 'returns used swap memory in bytes' do
28 expect(swap_memory.resolve(:used_bytes)).to eq(used_bytes)
29 end
30
31 it 'returns capacity of swap memory' do
32 expect(swap_memory.resolve(:capacity)).to eq(capacity)
33 end
34
35 it 'returns true because swap memory is encrypted' do
36 expect(swap_memory.resolve(:encrypted)).to eq(encrypted)
37 end
38 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::SystemMemory do
3 subject(:system_memory) { Facter::Resolvers::Freebsd::SystemMemory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:available_bytes) { 2_696_462_336 }
7 let(:total_bytes) { 17_043_554_304 }
8 let(:used_bytes) { 14_347_091_968 }
9 let(:capacity) { '84.18%' }
10
11 before do
12 system_memory.instance_variable_set(:@log, log_spy)
13 allow(Facter::Freebsd::FfiHelper).to receive(:sysctl_by_name)
14 .with(:long, 'hw.physmem')
15 .and_return(17_043_554_304)
16
17 allow(Facter::Core::Execution).to receive(:execute)
18 .with('vmstat -H --libxo json', logger: log_spy)
19 .and_return(load_fixture('freebsd_vmstat').read)
20 end
21
22 it 'returns available system memory in bytes' do
23 expect(system_memory.resolve(:available_bytes)).to eq(available_bytes)
24 end
25
26 it 'returns total system memory in bytes' do
27 expect(system_memory.resolve(:total_bytes)).to eq(total_bytes)
28 end
29
30 it 'returns used system memory in bytes' do
31 expect(system_memory.resolve(:used_bytes)).to eq(used_bytes)
32 end
33
34 it 'returns memory capacity' do
35 expect(system_memory.resolve(:capacity)).to eq(capacity)
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Freebsd::Virtual do
3 subject(:jail_resolver) { Facter::Resolvers::Freebsd::Virtual }
4
5 after do
6 jail_resolver.invalidate_cache
7 end
8
9 before do
10 allow(Facter::Freebsd::FfiHelper).to receive(:sysctl_by_name).with(:long, 'security.jail.jailed').and_return(jailed)
11 allow(Facter::Freebsd::FfiHelper).to receive(:sysctl_by_name).with(:string, 'kern.vm_guest').and_return(vm)
12 end
13
14 let(:jailed) { 0 }
15 let(:vm) { nil }
16
17 context 'when not jailed' do
18 context 'when running on bare metal' do
19 it 'returns nil' do
20 expect(jail_resolver.resolve(:vm)).to be_nil
21 end
22 end
23
24 context 'when running in a vm' do
25 let(:vm) { 'xen' }
26
27 it 'returns xen' do
28 expect(jail_resolver.resolve(:vm)).to eq('xenu')
29 end
30 end
31 end
32
33 context 'when jailed' do
34 let(:jailed) { 1 }
35
36 context 'when running on bare metal' do
37 it 'returns jail' do
38 expect(jail_resolver.resolve(:vm)).to eq('jail')
39 end
40 end
41
42 context 'when running in a vm' do
43 let(:vm) { 'xen' }
44
45 it 'returns jail' do
46 expect(jail_resolver.resolve(:vm)).to eq('jail')
47 end
48 end
49 end
50 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Gce do
3 let(:gce_metadata_url) { 'http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=json' }
4 let(:gce_url_headers) { { "Metadata-Flavor": 'Google', "Accept": 'application/json' } }
5
6 before do
7 allow(Facter::Util::Resolvers::Http).to receive(:get_request)
8 .with(gce_metadata_url, gce_url_headers)
9 .and_return(http_response_body)
10 end
11
12 after do
13 Facter::Resolvers::Gce.invalidate_cache
14 end
15
16 context 'when http request is successful' do
17 let(:http_response_body) { load_fixture('gce').read }
18 let(:value) do
19 {
20 'instance' => {
21 'attributes' => {
22 },
23 'cpuPlatform' => 'Intel Broadwell',
24 'description' => '',
25 'disks' => [
26 {
27 'deviceName' => 'instance-3',
28 'index' => 0,
29 'interface' => 'SCSI',
30 'mode' => 'READ_WRITE',
31 'type' => 'PERSISTENT'
32 }
33 ],
34 'guestAttributes' => {
35 },
36 'hostname' => 'instance-3.c.facter-performance-history.internal',
37 'id' => 2_206_944_706_428_651_580,
38 'image' => 'ubuntu-2004-focal-v20200810',
39 'legacyEndpointAccess' => {
40 '0.1' => 0,
41 'v1beta1' => 0
42 },
43 'licenses' => [
44 {
45 'id' => '2211838267635035815'
46 }
47 ],
48 'machineType' => 'n1-standard-2',
49 'maintenanceEvent' => 'NONE',
50 'name' => 'instance-3',
51 'networkInterfaces' => [
52 {
53 'accessConfigs' => [
54 {
55 'externalIp' => '34.89.230.102',
56 'type' => 'ONE_TO_ONE_NAT'
57 }
58 ],
59 'dnsServers' => [
60 '169.254.169.254'
61 ],
62 'forwardedIps' => [],
63 'gateway' => '10.156.0.1',
64 'ip' => '10.156.0.4',
65 'ipAliases' => [],
66 'mac' => '42:01:0a:9c:00:04',
67 'mtu' => 1460,
68 'network' => 'default',
69 'subnetmask' => '255.255.240.0',
70 'targetInstanceIps' => []
71 }
72 ],
73 'preempted' => 'FALSE',
74 'remainingCpuTime' => -1,
75 'scheduling' => {
76 'automaticRestart' => 'TRUE',
77 'onHostMaintenance' => 'MIGRATE',
78 'preemptible' => 'FALSE'
79 },
80 'serviceAccounts' => {
81 '728618928092-compute@developer.gserviceaccount.com' => {
82 'aliases' => [
83 'default'
84 ],
85 'email' => '728618928092-compute@developer.gserviceaccount.com',
86 'scopes' => [
87 'https://www.googleapis.com/auth/devstorage.read_only',
88 'https://www.googleapis.com/auth/logging.write',
89 'https://www.googleapis.com/auth/monitoring.write',
90 'https://www.googleapis.com/auth/servicecontrol',
91 'https://www.googleapis.com/auth/service.management.readonly',
92 'https://www.googleapis.com/auth/trace.append'
93 ]
94 },
95 'default' => {
96 'aliases' => [
97 'default'
98 ],
99 'email' => '728618928092-compute@developer.gserviceaccount.com',
100 'scopes' => [
101 'https://www.googleapis.com/auth/devstorage.read_only',
102 'https://www.googleapis.com/auth/logging.write',
103 'https://www.googleapis.com/auth/monitoring.write',
104 'https://www.googleapis.com/auth/servicecontrol',
105 'https://www.googleapis.com/auth/service.management.readonly',
106 'https://www.googleapis.com/auth/trace.append'
107 ]
108 }
109 },
110 'tags' => [],
111 'virtualClock' => {
112 'driftToken' => '0'
113 },
114 'zone' => 'europe-west3-c'
115 },
116 'oslogin' => {
117 'authenticate' => {
118 'sessions' => {
119 }
120 }
121 },
122 'project' => {
123 'attributes' => {
124 'ssh-keys' => 'john_doe:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDA9D8Op48TtEiDmb+Gtna3Bs9B' \
125 " google-ssh {\"userName\":\"john.doe@puppet.com\",\"expireOn\":\"2020-08-13T12:17:19+0000\"}\n"
126 },
127 'numericProjectId' => 728_618_928_092,
128 'projectId' => 'facter-performance-history'
129 }
130 }
131 end
132
133 it 'returns gce data' do
134 result = Facter::Resolvers::Gce.resolve(:metadata)
135
136 expect(result).to eq(value)
137 end
138 end
139
140 context 'when http request fails' do
141 let(:http_response_body) { 'Request failed with error code: 404' }
142
143 it 'returns nil' do
144 result = Facter::Resolvers::Gce.resolve(:metadata)
145
146 expect(result).to be(nil)
147 end
148 end
149 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Hostname do
3 subject(:hostname_resolver) { Facter::Resolvers::Hostname }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 describe '#resolve' do
8 before do
9 hostname_resolver.instance_variable_set(:@log, log_spy)
10 allow(Socket).to receive(:gethostname).and_return(host)
11 allow(Facter::Util::FileHelper).to receive(:safe_read)
12 .with('/etc/resolv.conf')
13 .and_return("nameserver 10.10.0.10\nnameserver 10.10.1.10\nsearch baz\ndomain baz\n")
14 end
15
16 after do
17 hostname_resolver.invalidate_cache
18 end
19
20 context 'when hostname returns fqdn' do
21 let(:hostname) { 'foo' }
22 let(:domain) { 'bar' }
23 let(:host) { "#{hostname}.#{domain}" }
24 let(:fqdn) { "#{hostname}.#{domain}" }
25
26 it 'detects hostname' do
27 expect(hostname_resolver.resolve(:hostname)).to eql(hostname)
28 end
29
30 it 'returns networking Domain' do
31 expect(hostname_resolver.resolve(:domain)).to eq(domain)
32 end
33
34 it 'returns fqdn' do
35 expect(hostname_resolver.resolve(:fqdn)).to eq(fqdn)
36 end
37 end
38
39 context 'when hostname returns host' do
40 let(:hostname) { 'foo' }
41 let(:domain) { 'baz' }
42 let(:host) { hostname }
43 let(:fqdn) { "#{hostname}.#{domain}" }
44
45 before do
46 allow(Socket).to receive(:getaddrinfo).and_return(domain)
47 end
48
49 it 'detects hostname' do
50 expect(hostname_resolver.resolve(:hostname)).to eql(hostname)
51 end
52
53 it 'returns networking Domain' do
54 expect(hostname_resolver.resolve(:domain)).to eq(domain)
55 end
56
57 it 'returns fqdn' do
58 expect(hostname_resolver.resolve(:fqdn)).to eq(fqdn)
59 end
60 end
61
62 context 'when hostname could not be retrieved' do
63 let(:host) { nil }
64
65 it 'detects that hostname is nil' do
66 expect(hostname_resolver.resolve(:hostname)).to be(nil)
67 end
68 end
69
70 context 'when /etc/resolve.conf is inaccessible' do
71 let(:host) { 'foo' }
72 let(:domain) { '' }
73
74 before do
75 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/etc/resolv.conf').and_return('')
76 allow(Socket).to receive(:getaddrinfo).and_return(domain)
77 end
78
79 it 'detects that domain is nil' do
80 expect(hostname_resolver.resolve(:domain)).to be(nil)
81 end
82 end
83
84 context 'when getaddrinfo throws exception' do
85 let(:host) { 'foo' }
86
87 before do
88 allow(Socket).to receive(:getaddrinfo).and_raise('socket exception')
89 end
90
91 it 'detects domain from resolv.conf' do
92 expect(hostname_resolver.resolve(:domain)).to eq('baz')
93 end
94 end
95 end
96 end
0 # frozen_string_literal: true
1
2 require 'ostruct'
3
4 describe Facter::Resolvers::PosxIdentity do
5 before do
6 allow(Etc).to receive(:getpwuid)
7 .and_return(OpenStruct.new(name: 'test1.test2',
8 passwd: '********',
9 uid: 501,
10 gid: 20,
11 gecos: 'Test1 Test2',
12 dir: '/Users/test1.test2',
13 shell: '/bin/zsh',
14 change: 0,
15 uclass: '',
16 expire: 0))
17
18 allow(Etc).to receive(:getgrgid)
19 .with(20)
20 .and_return(OpenStruct.new(name: 'staff',
21 passwd: '*',
22 gid: 20,
23 mem: ['root', 'test1.test2', '_serialnumberd', 'jenkins']))
24 end
25
26 shared_examples_for 'a resolved fact' do |fact_name, value|
27 subject { Facter::Resolvers::PosxIdentity.resolve(fact_name) }
28
29 it { is_expected.to eql(value) }
30 end
31
32 describe 'GID' do
33 it_behaves_like 'a resolved fact', :gid, 20
34 end
35
36 describe 'GROUP' do
37 it_behaves_like 'a resolved fact', :group, 'staff'
38 end
39
40 describe 'PRIVILEGED' do
41 it_behaves_like 'a resolved fact', :privileged, false
42 end
43
44 describe 'UID' do
45 it_behaves_like 'a resolved fact', :uid, 501
46 end
47
48 describe 'USER' do
49 it_behaves_like 'a resolved fact', :user, 'test1.test2'
50 end
51 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::DockerUptime do
3 subject(:resolver) { Facter::Resolvers::Linux::DockerUptime }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 after { Facter::Resolvers::Linux::DockerUptime.invalidate_cache }
8
9 before do
10 resolver.instance_variable_set(:@log, log_spy)
11 end
12
13 context 'when the uptime is less than 1 minutes' do
14 before do
15 allow(Facter::Core::Execution)
16 .to receive(:execute)
17 .with('ps -o etime= -p "1"', logger: log_spy)
18 .and_return('20')
19
20 allow(Facter::Util::Resolvers::UptimeHelper)
21 .to receive(:create_uptime_hash)
22 .with(20)
23 .and_return({ days: 0, hours: 0, seconds: 20, uptime: '0:00 hours' })
24 end
25
26 it 'returns 0 days' do
27 expect(resolver.resolve(:days)).to eq(0)
28 end
29
30 it 'returns 0 hours' do
31 expect(resolver.resolve(:hours)).to eq(0)
32 end
33
34 it 'returns 20 seconds' do
35 expect(resolver.resolve(:seconds)).to eq(20)
36 end
37
38 it 'returns 0:00 hours for uptime' do
39 expect(resolver.resolve(:uptime)).to eq('0:00 hours')
40 end
41 end
42
43 context 'when the uptime is more than 1 minute and less than 1 hour' do
44 before do
45 allow(Facter::Core::Execution)
46 .to receive(:execute)
47 .with('ps -o etime= -p "1"', logger: log_spy)
48 .and_return('10:20')
49
50 allow(Facter::Util::Resolvers::UptimeHelper)
51 .to receive(:create_uptime_hash)
52 .with(620)
53 .and_return({ days: 0, hours: 0, seconds: 620, uptime: '0:10 hours' })
54 end
55
56 it 'returns 0 days' do
57 expect(resolver.resolve(:days)).to eq(0)
58 end
59
60 it 'returns 0 hours' do
61 expect(resolver.resolve(:hours)).to eq(0)
62 end
63
64 it 'returns 620 seconds' do
65 expect(resolver.resolve(:seconds)).to eq(620)
66 end
67
68 it 'returns 0:10 hours for uptime' do
69 expect(resolver.resolve(:uptime)).to eq('0:10 hours')
70 end
71 end
72
73 context 'when the uptime is more than 1 hour but less than 1 day' do
74 before do
75 allow(Facter::Core::Execution)
76 .to receive(:execute)
77 .with('ps -o etime= -p "1"', logger: log_spy)
78 .and_return('3:10:20')
79
80 allow(Facter::Util::Resolvers::UptimeHelper)
81 .to receive(:create_uptime_hash)
82 .with(11_420)
83 .and_return({ days: 0, hours: 3, seconds: 11_420, uptime: '3:10 hours' })
84 end
85
86 it 'returns 0 days' do
87 expect(resolver.resolve(:days)).to eq(0)
88 end
89
90 it 'returns 3 hours' do
91 expect(resolver.resolve(:hours)).to eq(3)
92 end
93
94 it 'returns 11420 seconds' do
95 expect(resolver.resolve(:seconds)).to eq(11_420)
96 end
97
98 it 'returns 3:10 hours for uptime' do
99 expect(resolver.resolve(:uptime)).to eq('3:10 hours')
100 end
101 end
102
103 context 'when the uptime is 1 day' do
104 before do
105 allow(Facter::Core::Execution)
106 .to receive(:execute)
107 .with('ps -o etime= -p "1"', logger: log_spy)
108 .and_return('1-3:10:20')
109
110 allow(Facter::Util::Resolvers::UptimeHelper)
111 .to receive(:create_uptime_hash)
112 .with(97_820)
113 .and_return({ days: 1, hours: 27, seconds: 97_820, uptime: '1 day' })
114 end
115
116 it 'returns 1 day' do
117 expect(resolver.resolve(:days)).to eq(1)
118 end
119
120 it 'returns 27 hours' do
121 expect(resolver.resolve(:hours)).to eq(27)
122 end
123
124 it 'returns 97820 seconds' do
125 expect(resolver.resolve(:seconds)).to eq(97_820)
126 end
127
128 it 'returns 1 day for uptime' do
129 expect(resolver.resolve(:uptime)).to eq('1 day')
130 end
131 end
132
133 context 'when the uptime is more than 2 day' do
134 before do
135 allow(Facter::Core::Execution)
136 .to receive(:execute)
137 .with('ps -o etime= -p "1"', logger: log_spy)
138 .and_return('2-3:10:20')
139
140 allow(Facter::Util::Resolvers::UptimeHelper)
141 .to receive(:create_uptime_hash)
142 .with(184_220)
143 .and_return({ days: 2, hours: 51, seconds: 184_220, uptime: '2 days' })
144 end
145
146 it 'returns 2 days' do
147 expect(resolver.resolve(:days)).to eq(2)
148 end
149
150 it 'returns 51 hours' do
151 expect(resolver.resolve(:hours)).to eq(51)
152 end
153
154 it 'returns 184220 seconds' do
155 expect(resolver.resolve(:seconds)).to eq(184_220)
156 end
157
158 it 'returns 2 days for uptime' do
159 expect(resolver.resolve(:uptime)).to eq('2 days')
160 end
161 end
162 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Hostname do
3 subject(:hostname_resolver) { Facter::Resolvers::Linux::Hostname }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 shared_examples 'detects values' do
8 it 'detects hostname' do
9 expect(hostname_resolver.resolve(:hostname)).to eq(hostname)
10 end
11
12 it 'returns networking domain' do
13 expect(hostname_resolver.resolve(:domain)).to eq(domain)
14 end
15
16 it 'returns fqdn' do
17 expect(hostname_resolver.resolve(:fqdn)).to eq(fqdn)
18 end
19 end
20
21 describe '#resolve' do
22 before do
23 hostname_resolver.instance_variable_set(:@log, log_spy)
24 allow(Socket).to receive(:gethostname).and_return(host)
25 allow(Facter::Util::FileHelper).to receive(:safe_read)
26 .with('/etc/resolv.conf')
27 .and_return("nameserver 10.10.0.10\nnameserver 10.10.1.10\nsearch baz\ndomain baz\n")
28 end
29
30 after do
31 hostname_resolver.invalidate_cache
32 end
33
34 context 'when ruby socket hostname works' do
35 let(:hostname) { 'foo' }
36 let(:domain) { 'bar' }
37 let(:fqdn) { "#{hostname}.#{domain}" }
38
39 context 'when it returns fqdn' do
40 let(:host) { "#{hostname}.#{domain}" }
41
42 it_behaves_like 'detects values'
43 end
44
45 context 'when it returns only the hostname and ruby addrinfo works' do
46 let(:host) { hostname }
47 let(:addr_info) { [['', '', "#{hostname}.#{domain}", '']] }
48
49 before do
50 allow(Socket).to receive(:getaddrinfo).and_return(addr_info)
51 end
52
53 it_behaves_like 'detects values'
54 end
55
56 context 'when it returns only the hostname and ruby addrinfo fails' do
57 let(:host) { hostname }
58 let(:output) { fqdn }
59
60 before do
61 allow(Socket).to receive(:getaddrinfo).and_return([])
62 allow(Facter::Util::Resolvers::Ffi::Hostname).to receive(:getffiaddrinfo).and_return(output)
63 end
64
65 it_behaves_like 'detects values'
66
67 context 'when ffi addrinfo fails' do
68 let(:output) { nil }
69 let(:resolv_conf) { "domain #{domain}" }
70
71 before do
72 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/etc/resolv.conf').and_return(resolv_conf)
73 end
74
75 it_behaves_like 'detects values'
76
77 context 'when /etc/resolv.conf is empty' do
78 let(:resolv_conf) { '' }
79 let(:domain) { nil }
80 let(:fqdn) { hostname }
81
82 it_behaves_like 'detects values'
83 end
84 end
85
86 context 'when FFI is not installed' do
87 before do
88 allow(Socket).to receive(:gethostname).and_return(nil)
89 allow(Facter::Resolvers::Linux::Hostname).to receive(:require).and_call_original
90 allow(Facter::Resolvers::Linux::Hostname).to receive(:require)
91 .with('facter/util/resolvers/ffi/hostname')
92 .and_raise(LoadError, 'cannot load ffi')
93 end
94
95 it 'logs that ffi canot be loaded' do
96 hostname_resolver.resolve(:hostname)
97 expect(log_spy).to have_received(:debug).with('cannot load ffi')
98 end
99
100 it 'does not resolve hostname' do
101 expect(hostname_resolver.resolve(:hostname)).to be_nil
102 end
103
104 it 'does not resolve domain' do
105 expect(hostname_resolver.resolve(:domain)).to be_nil
106 end
107
108 it 'does not resolve fqdn' do
109 expect(hostname_resolver.resolve(:fqdn)).to be_nil
110 end
111 end
112 end
113 end
114
115 context 'when ruby socket hostname fails' do
116 let(:hostname) { 'hostnametest' }
117 let(:domain) { 'domaintest' }
118 let(:fqdn) { "#{hostname}.#{domain}" }
119 let(:host) { '' }
120
121 before do
122 allow(Facter::Util::Resolvers::Ffi::Hostname).to receive(:getffihostname).and_return(ffi_host)
123 end
124
125 context 'when ffi hostname works' do
126 let(:ffi_host) { fqdn }
127
128 it_behaves_like 'detects values'
129 end
130
131 context 'when it returns only the hostname and ruby addrinfo works' do
132 let(:addr_info) { [['', '', "#{hostname}.#{domain}", '']] }
133 let(:ffi_host) { hostname }
134
135 before do
136 allow(Socket).to receive(:getaddrinfo).and_return(addr_info)
137 end
138
139 it_behaves_like 'detects values'
140 end
141
142 context 'when it returns only the hostname and ruby addrinfo fails' do
143 let(:ffi_host) { hostname }
144 let(:output) { fqdn }
145
146 before do
147 allow(Socket).to receive(:getaddrinfo).and_return([])
148 allow(Facter::Util::Resolvers::Ffi::Hostname).to receive(:getffiaddrinfo).and_return(output)
149 end
150
151 it_behaves_like 'detects values'
152
153 context 'when ffi addrinfo fails' do
154 let(:output) { nil }
155 let(:resolv_conf) { "domain #{domain}" }
156
157 before do
158 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/etc/resolv.conf').and_return(resolv_conf)
159 end
160
161 it_behaves_like 'detects values'
162
163 context 'when /etc/resolv.conf is empty' do
164 let(:resolv_conf) { '' }
165 let(:domain) { nil }
166 let(:fqdn) { hostname }
167
168 it_behaves_like 'detects values'
169 end
170 end
171
172 context 'when ffi is not installed' do
173 let(:host) { hostname }
174 let(:resolv_conf) { "domain #{domain}" }
175
176 before do
177 allow(Socket).to receive(:gethostname).and_return(host)
178 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/etc/resolv.conf').and_return(resolv_conf)
179 allow(Facter::Resolvers::Linux::Hostname).to receive(:require).and_call_original
180
181 allow(Facter::Resolvers::Linux::Hostname).to receive(:require)
182 .with('facter/util/resolvers/ffi/hostname')
183 .and_raise(LoadError, 'cannot load ffi')
184 end
185
186 it 'logs that ffi canot be loaded' do
187 hostname_resolver.resolve(:domain)
188 expect(log_spy).to have_received(:debug).with('cannot load ffi')
189 end
190
191 it_behaves_like 'detects values'
192
193 context 'when /etc/resolv.conf is empty' do
194 let(:resolv_conf) { '' }
195 let(:domain) { nil }
196 let(:fqdn) { hostname }
197
198 it_behaves_like 'detects values'
199 end
200 end
201 end
202 end
203
204 context 'when ffi hostname fails to return hostname' do
205 let(:hostname) { nil }
206 let(:domain) { nil }
207 let(:host) { '' }
208 let(:fqdn) { nil }
209
210 before do
211 allow(Facter::Util::Resolvers::Ffi::Hostname).to receive(:getffihostname).and_return('')
212 end
213
214 it_behaves_like 'detects values'
215 end
216
217 context 'when FFI is not installed' do
218 let(:hostname) { nil }
219 let(:domain) { nil }
220 let(:host) { '' }
221 let(:fqdn) { nil }
222
223 before do
224 allow(Facter::Resolvers::Linux::Hostname).to receive(:require).and_call_original
225 allow(Facter::Resolvers::Linux::Hostname).to receive(:require)
226 .with('facter/util/resolvers/ffi/hostname')
227 .and_raise(LoadError, 'cannot load ffi')
228 end
229
230 it 'logs that ffi canot be loaded' do
231 hostname_resolver.resolve(:hostname)
232 expect(log_spy).to have_received(:debug).with('cannot load ffi')
233 end
234
235 it_behaves_like 'detects values'
236 end
237 end
238 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::LoadAverages do
3 let(:load_averages) { { '1m' => 0.00, '5m' => 0.03, '15m' => 0.03 } }
4 let(:no_load_averages) { { '1m' => nil, '5m' => nil, '15m' => nil } }
5
6 after do
7 Facter::Resolvers::Linux::LoadAverages.invalidate_cache
8 end
9
10 context 'when /proc/loadavg is accessible' do
11 before do
12 allow(Facter::Util::FileHelper).to receive(:safe_read)
13 .with('/proc/loadavg')
14 .and_return(load_fixture('loadavg').read)
15 end
16
17 it 'returns load average' do
18 result = Facter::Resolvers::Linux::LoadAverages.resolve(:load_averages)
19
20 expect(result).to eq(load_averages)
21 end
22 end
23
24 context 'when /proc/loadavg is not accessible' do
25 before do
26 allow(Facter::Util::FileHelper).to receive(:safe_read)
27 .with('/proc/loadavg')
28 .and_return('')
29 end
30
31 it 'returns no load averages' do
32 result = Facter::Resolvers::Linux::LoadAverages.resolve(:load_averages)
33
34 expect(result).to eq(no_load_averages)
35 end
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Lscpu do
3 before do
4 Facter::Resolvers::Linux::Lscpu.instance_variable_set(:@log, log_spy)
5 allow(Facter::Core::Execution)
6 .to receive(:execute)
7 .with("lscpu | grep -e 'Thread(s)' -e 'Core(s)'", logger: log_spy)
8 .and_return(lscpu_output)
9 end
10
11 after do
12 Facter::Resolvers::Linux::Lscpu.invalidate_cache
13 end
14
15 let(:log_spy) { instance_spy(Facter::Log) }
16 let(:cores_per_socket) { 1 }
17 let(:threads_per_core) { 2 }
18 let(:lscpu_output) do
19 ["'Thread(s) per core': 2",
20 "'Cores(s) per socket': 1"].join("\n")
21 end
22
23 it 'returns cores per socket' do
24 result = Facter::Resolvers::Linux::Lscpu.resolve(:cores_per_socket)
25
26 expect(result).to eq(cores_per_socket)
27 end
28
29 it 'returns threads per core' do
30 result = Facter::Resolvers::Linux::Lscpu.resolve(:threads_per_core)
31
32 expect(result).to eq(threads_per_core)
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Networking do
3 subject(:networking_linux) { Facter::Resolvers::Linux::Networking }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 describe '#resolve' do
8 before do
9 networking_linux.instance_variable_set(:@log, log_spy)
10 allow(Facter::Core::Execution).to receive(:execute)
11 .with('ip link show', logger: log_spy).and_return(load_fixture('ip_link_show').read)
12 allow(Facter::Util::Linux::SocketParser).to receive(:retrieve_interfaces)
13 .with(log_spy).and_return(socket_interfaces)
14 allow(Facter::Util::Linux::Dhcp).to receive(:dhcp).with('lo', '1', log_spy).and_return('10.32.22.9')
15 allow(Facter::Util::Linux::Dhcp).to receive(:dhcp).with('ens160', '2', log_spy).and_return('10.32.22.10')
16 allow(Facter::Util::Linux::RoutingTable).to receive(:read_routing_table)
17 .with(log_spy).and_return([[{ interface: 'ens192', ip: '10.16.125.217' }], []])
18 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_proc_route)
19 .and_return('ens160')
20 allow(File).to receive(:exist?).with('/proc/net/if_inet6').and_return(true)
21 allow(Facter::Util::FileHelper).to receive(:safe_read)
22 .with('/proc/net/if_inet6', nil).and_return(load_fixture('proc_net_if_inet6').read)
23 end
24
25 after do
26 networking_linux.invalidate_cache
27 end
28
29 let(:socket_interfaces) do
30 {
31 'lo' => {
32 bindings: [
33 { address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }
34 ],
35 bindings6: [
36 { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', network: '::1', scope6: 'host' }
37 ]
38 },
39 'ens160' => {
40 bindings: [
41 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' },
42 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
43 ],
44 bindings6: [
45 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::', network: 'fe80::', scope6: 'link' }
46 ],
47 mac: '00:50:56:9a:61:46'
48 }
49 }
50 end
51
52 let(:result) do
53 {
54 'lo' => {
55 bindings: [
56 { address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }
57 ],
58 bindings6: [
59 { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
60 network: '::1', scope6: 'host', flags: ['permanent'] }
61 ],
62 dhcp: '10.32.22.9',
63 ip: '127.0.0.1',
64 ip6: '::1',
65 mtu: 65_536,
66 netmask: '255.0.0.0',
67 netmask6: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
68 network: '127.0.0.0',
69 network6: '::1',
70 scope6: 'host'
71 },
72 'ens160' => {
73 bindings: [
74 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' },
75 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
76 ],
77 bindings6: [
78 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::',
79 network: 'fe80::', scope6: 'link', flags: ['permanent'] }
80 ],
81 dhcp: '10.32.22.10',
82 ip: '10.16.119.155',
83 ip6: 'fe80::250:56ff:fe9a:8481',
84 mac: '00:50:56:9a:61:46',
85 mtu: 1500,
86 netmask: '255.255.240.0',
87 netmask6: 'ffff:ffff:ffff:ffff::',
88 network: '10.16.112.0',
89 network6: 'fe80::',
90 scope6: 'link'
91 }
92 }
93 end
94
95 it 'returns all the interfaces' do
96 expect(networking_linux.resolve(:interfaces)).to eq(result)
97 end
98
99 it 'returns the primary interface' do
100 expect(networking_linux.resolve(:primary_interface)).to eq('ens160')
101 end
102
103 context 'when caching' do
104 it 'returns from cache' do
105 networking_linux.resolve(:interfaces)
106 networking_linux.resolve(:interfaces)
107
108 expect(Facter::Util::Linux::SocketParser).to have_received(:retrieve_interfaces)
109 .with(log_spy).once
110 end
111 end
112
113 context 'when invalidate caching' do
114 it 'resolved again the fact' do
115 networking_linux.resolve(:interfaces)
116 networking_linux.invalidate_cache
117 networking_linux.resolve(:interfaces)
118
119 expect(Facter::Util::Linux::SocketParser).to have_received(:retrieve_interfaces)
120 .with(log_spy).twice
121 end
122 end
123
124 context 'when 127.0.0.1 is first ip' do
125 let(:socket_interfaces) do
126 {
127 'ens160' => {
128 bindings: [
129 { address: '127.0.0.1', netmask: '255.0.0.0', network: '10.16.112.0' },
130 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
131 ],
132 bindings6: [
133 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::',
134 network: 'fe80::', scope6: 'link' }
135 ],
136 mac: '00:50:56:9a:61:46'
137 }
138 }
139 end
140
141 let(:result_with_127_first) do
142 {
143 bindings: [
144 { address: '127.0.0.1', netmask: '255.0.0.0', network: '10.16.112.0' },
145 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
146 ],
147 bindings6: [
148 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::', network: 'fe80::',
149 scope6: 'link', flags: ['permanent'] }
150 ],
151 dhcp: '10.32.22.10',
152 ip: '10.16.127.70',
153 ip6: 'fe80::250:56ff:fe9a:8481',
154 mac: '00:50:56:9a:61:46',
155 mtu: 1500,
156 netmask: '255.255.240.0',
157 netmask6: 'ffff:ffff:ffff:ffff::',
158 network: '10.16.112.0',
159 network6: 'fe80::',
160 scope6: 'link'
161 }
162 end
163
164 it 'resolves interface ens160' do
165 expect(networking_linux.resolve(:interfaces)['ens160']).to eq(result_with_127_first)
166 end
167 end
168
169 context 'when dhcp is not available on the os' do
170 before do
171 allow(Facter::Util::Linux::Dhcp).to receive(:dhcp).with('lo', '1', log_spy).and_return(nil)
172 end
173
174 it 'does not add dhcp to lo interface' do
175 result = networking_linux.resolve(:interfaces)
176
177 expect(result['lo'][:dhcp]).to be_nil
178 end
179 end
180
181 context 'when ip route show finds an IP, Socket lib did not retrieve' do
182 before do
183 allow(Facter::Util::Linux::RoutingTable).to receive(:read_routing_table)
184 .with(log_spy).and_return([[{ interface: 'ens160', ip: '10.16.125.217' }], []])
185 end
186
187 let(:socket_interfaces) do
188 {
189 'ens160' => {
190 bindings: [
191 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' }
192 ],
193 mac: '00:50:56:9a:61:46'
194 }
195 }
196 end
197
198 it 'adds it to the bindings list' do
199 expected = {
200 bindings: [
201 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' },
202 { address: '10.16.125.217' }
203 ],
204 dhcp: '10.32.22.10',
205 ip: '10.16.119.155',
206 mac: '00:50:56:9a:61:46',
207 mtu: 1500,
208 netmask: '255.255.240.0',
209 network: '10.16.112.0'
210 }
211
212 expect(networking_linux.resolve(:interfaces)['ens160']).to eq(expected)
213 end
214 end
215
216 context 'when asking for primary interface' do
217 before do
218 Facter::Util::Resolvers::Networking::PrimaryInterface.instance_variable_set(:@log, log_spy)
219 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_proc_route).and_return(nil)
220 end
221
222 context 'when primary interface can not be read from /proc/net/route' do
223 before do
224 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_ip_route).and_return('lo')
225 end
226
227 it 'returns primary interface' do
228 expect(networking_linux.resolve(:primary_interface)).to eq('lo')
229 end
230 end
231
232 context 'when primary interface can not be read' do
233 before do
234 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_ip_route).and_return(nil)
235 end
236
237 it 'returns primary interface as the first not ignored ip' do
238 expect(networking_linux.resolve(:primary_interface)).to eq('ens160')
239 end
240 end
241 end
242
243 context 'when only ipv6 is available' do
244 let(:socket_interfaces) do
245 {
246 'lo' => {
247 bindings6: [
248 { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', network: '::1', scope6: 'host' }
249 ]
250 },
251 'ens160' => {
252 bindings6: [
253 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::',
254 network: 'fe80::', scope6: 'link' }
255 ],
256 mac: '00:50:56:9a:61:46'
257 }
258 }
259 end
260
261 let(:result) do
262 {
263 'lo' => {
264 bindings6: [
265 { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
266 network: '::1', scope6: 'host', flags: ['permanent'] }
267 ],
268 ip6: '::1',
269 mtu: 65_536,
270 netmask6: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
271 network6: '::1',
272 scope6: 'host'
273 },
274 'ens160' => {
275 bindings6: [
276 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::',
277 network: 'fe80::', scope6: 'link', flags: ['permanent'] }
278 ],
279 ip6: 'fe80::250:56ff:fe9a:8481',
280 mac: '00:50:56:9a:61:46',
281 mtu: 1500,
282 netmask6: 'ffff:ffff:ffff:ffff::',
283 network6: 'fe80::',
284 scope6: 'link'
285 }
286 }
287 end
288
289 before do
290 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_proc_route).and_return(nil)
291 allow(Facter::Util::Resolvers::Networking::PrimaryInterface).to receive(:read_from_ip_route).and_return(nil)
292 allow(Facter::Util::Linux::Dhcp).to receive(:dhcp).with('lo', '1', log_spy).and_return(nil)
293 allow(Facter::Util::Linux::Dhcp).to receive(:dhcp).with('ens160', '2', log_spy).and_return(nil)
294 allow(File).to receive(:exist?).with('/proc/net/if_inet6').and_return(true)
295 allow(Facter::Util::FileHelper).to receive(:safe_read)
296 .with('/proc/net/if_inet6', nil).and_return(load_fixture('proc_net_if_inet6').read)
297 end
298
299 it 'returns all the interfaces' do
300 expect(networking_linux.resolve(:interfaces)).to eq(result)
301 end
302
303 it 'returns the primary interface' do
304 expect(networking_linux.resolve(:primary_interface)).to be_nil
305 end
306 end
307 end
308 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::LoadAverages do
3 let(:load_averages) { [0.01, 0.02, 0.03] }
4
5 before do
6 allow(Facter::Util::Resolvers::Ffi::LoadAverages).to receive(:read_load_averages)
7 .and_return(load_averages)
8 end
9
10 it 'returns load average' do
11 result = Facter::Resolvers::LoadAverages.resolve(:load_averages)
12
13 expect(result).to eq('15m' => 0.03, '1m' => 0.01, '5m' => 0.02)
14 end
15 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Lpar do
3 subject(:lpar_resolver) { Facter::Resolvers::Lpar }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 lpar_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('/usr/bin/lparstat -i', logger: log_spy)
11 .and_return(load_fixture('lparstat_i').read)
12 lpar_resolver.invalidate_cache
13 end
14
15 it 'returns lpar pratition number' do
16 expect(lpar_resolver.resolve(:lpar_partition_number)) .to eq(15)
17 end
18
19 it 'returns lpar pratition name' do
20 expect(lpar_resolver.resolve(:lpar_partition_name)).to eq('aix61-6')
21 end
22 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::LsbRelease do
3 after do
4 Facter::Resolvers::LsbRelease.invalidate_cache
5 end
6
7 context 'when system is ubuntu' do
8 before do
9 allow(Facter::Core::Execution).to receive(:which)
10 .with('lsb_release').and_return('/usr/bin/lsb_release')
11 allow(Facter::Core::Execution::Popen3).to receive(:popen3e)
12 .with({ 'LANG' => 'C', 'LC_ALL' => 'C' }, 'lsb_release -a')
13 .and_return(["Distributor ID:\tUbuntu\nDescription:\tUbuntu 18.04.1 LTS\nRelease:\t18.04\nCodename:\tbionic\n",
14 '', 0])
15 end
16
17 it 'returns os Distributor ID' do
18 result = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
19
20 expect(result).to eq('Ubuntu')
21 end
22
23 it 'returns distro Description' do
24 result = Facter::Resolvers::LsbRelease.resolve(:description)
25
26 expect(result).to eq('Ubuntu 18.04.1 LTS')
27 end
28
29 it 'returns distro release' do
30 result = Facter::Resolvers::LsbRelease.resolve(:release)
31
32 expect(result).to eq('18.04')
33 end
34
35 it 'returns distro Codename' do
36 result = Facter::Resolvers::LsbRelease.resolve(:codename)
37
38 expect(result).to eq('bionic')
39 end
40 end
41
42 context 'when system is centos' do
43 before do
44 allow(Facter::Core::Execution).to receive(:which)
45 .with('lsb_release').and_return('/usr/bin/lsb_release')
46 allow(Facter::Core::Execution::Popen3).to receive(:popen3e)
47 .with({ 'LANG' => 'C', 'LC_ALL' => 'C' }, 'lsb_release -a')
48 .and_return([load_fixture('centos_lsb_release').read, '', 0])
49 end
50
51 it 'returns distro Distributor ID' do
52 result = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
53
54 expect(result).to eq('CentOS')
55 end
56
57 it 'returns distro Description' do
58 result = Facter::Resolvers::LsbRelease.resolve(:description)
59
60 expect(result).to eq('CentOS Linux release 7.2.1511 (Core)')
61 end
62
63 it 'returns distro release' do
64 result = Facter::Resolvers::LsbRelease.resolve(:release)
65
66 expect(result).to eq('7.2.1511')
67 end
68
69 it 'returns distro lsb release' do
70 result = Facter::Resolvers::LsbRelease.resolve(:lsb_version)
71
72 expect(result).to eq(':core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64')
73 end
74
75 it 'returns distro Codename' do
76 result = Facter::Resolvers::LsbRelease.resolve(:codename)
77
78 expect(result).to eq('Core')
79 end
80 end
81
82 context 'when lsb_release is not installed on system' do
83 before do
84 allow(Facter::Core::Execution).to receive(:which)
85 .with('lsb_release').and_return(nil)
86 end
87
88 it 'returns distro Distributor ID as nil' do
89 result = Facter::Resolvers::LsbRelease.resolve(:distributor_id)
90
91 expect(result).to eq(nil)
92 end
93
94 it 'returns that lsb_release is not installed' do
95 result = Facter::Resolvers::LsbRelease.resolve(:lsb_release_installed)
96
97 expect(result).to be_falsey
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Lspci do
3 subject(:lspci_resolver) { Facter::Resolvers::Lspci }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 lspci_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute).with('lspci', logger: log_spy).and_return(output)
10 end
11
12 after do
13 lspci_resolver.invalidate_cache
14 end
15
16 context 'when lspci fails' do
17 let(:output) { '' }
18
19 it 'returns nil' do
20 expect(lspci_resolver.resolve(:vm)).to be_nil
21 end
22 end
23
24 context 'when lspci detects vmware' do
25 let(:output) { load_fixture('lspci_vmware').read }
26
27 it 'returns hypervisor name' do
28 expect(lspci_resolver.resolve(:vm)).to eq('vmware')
29 end
30 end
31
32 context 'when lspci detects xen' do
33 let(:output) { load_fixture('lspci_aws').read }
34
35 it 'returns hypervisor name' do
36 expect(lspci_resolver.resolve(:vm)).to eq('xenhvm')
37 end
38 end
39
40 context 'when lspci does not detect any hypervisor' do
41 let(:output) { 'lspci output with no hypervisor' }
42
43 it 'returns nil' do
44 expect(lspci_resolver.resolve(:vm)).to be_nil
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::DmiBios do
3 subject(:dmi_resolver) { Facter::Resolvers::Macosx::DmiBios }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:macosx_model) { 'MacBookPro11,4' }
7
8 before do
9 dmi_resolver.instance_variable_set(:@log, log_spy)
10 allow(Facter::Core::Execution).to receive(:execute)
11 .with('sysctl -n hw.model', logger: log_spy)
12 .and_return(macosx_model)
13 end
14
15 context 'when it detects model' do
16 it 'detects model' do
17 expect(Facter::Resolvers::Macosx::DmiBios.resolve(:macosx_model)).to eql(macosx_model)
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::Filesystems do
3 subject(:filesystems_resolver) { Facter::Resolvers::Macosx::Filesystems }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 filesystems_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute).with('mount', logger: log_spy)
10 .and_return(load_fixture('macosx_filesystems').read)
11 end
12
13 describe '#call_the_resolver' do
14 let(:filesystems) { 'apfs,autofs,devfs,vmhgfs' }
15
16 it 'returns systems' do
17 expect(filesystems_resolver.resolve(:macosx_filesystems)).to eq(filesystems)
18 end
19 end
20 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::LoadAverages do
3 subject(:load_averages) { Facter::Resolvers::Macosx::LoadAverages }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:expected_result) { { '1m' => 0.00, '5m' => 0.03, '15m' => 0.03 } }
7
8 before do
9 load_averages.instance_variable_set(:@log, log_spy)
10 allow(Facter::Core::Execution).to receive(:execute)
11 .with('sysctl -n vm.loadavg', logger: log_spy)
12 .and_return('{ 0.00 0.03 0.03 }')
13 end
14
15 it 'returns load average' do
16 expect(load_averages.resolve(:load_averages)).to eq(expected_result)
17 end
18 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::Mountpoints do
3 module Sys
4 class Filesystem
5 class Error < StandardError
6 end
7 end
8 end
9
10 let(:mount) do
11 double(Sys::Filesystem::Mount, mount_type: 'ext4', mount_point: '/proc/a', options: mount_options,
12 name: '/dev/nvme0n1p2', dump_frequency: 0, pass_number: 0)
13 end
14
15 let(:stat) do
16 double(Sys::Filesystem::Stat,
17 path: '/proc/a', base_type: nil, fragment_size: 4096, block_size: 4096, blocks: 113_879_332,
18 blocks_available: 16_596_603, blocks_free: 22_398_776)
19 end
20
21 let(:fact) do
22 { '/proc/a' => { available: '63.31 GiB',
23 available_bytes: 67_979_685_888,
24 capacity: '85.43%',
25 device: '/dev/nvme0n1p2',
26 filesystem: 'ext4',
27 options: fact_options,
28 size: '434.42 GiB',
29 size_bytes: 466_449_743_872,
30 used: '371.10 GiB',
31 used_bytes: 398_470_057_984 } }
32 end
33
34 let(:mount_options) { 'rw' }
35 let(:fact_options) { %w[rw] }
36
37 before do
38 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return([mount])
39 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoint_stats)
40 .with(mount.mount_point).and_return(stat)
41
42 # mock sys/filesystem methods
43 allow(stat).to receive(:bytes_total).and_return(stat.blocks * stat.fragment_size)
44 allow(stat).to receive(:bytes_available).and_return(stat.blocks_available * stat.fragment_size)
45 allow(stat).to receive(:bytes_free).and_return(stat.blocks_free * stat.fragment_size)
46 allow(stat).to receive(:bytes_used).and_return(stat.bytes_total - stat.bytes_free)
47 Facter::Resolvers::Macosx::Mountpoints.invalidate_cache
48 end
49
50 it 'correctly builds the mountpoints fact' do
51 result = Facter::Resolvers::Macosx::Mountpoints.resolve(:mountpoints)
52 expect(result).to match(fact)
53 end
54
55 describe '.root_device' do
56 let(:mount_options) { 'rw,noatime' }
57 let(:fact_options) { %w[rw noatime] }
58
59 let(:mount) do
60 double(Sys::Filesystem::Mount, mount_point: '/', mount_type: 'ext4', options: mount_options,
61 name: '/dev/root')
62 end
63
64 it 'looks up the actual device if /dev/root' do
65 result = Facter::Resolvers::Macosx::Mountpoints.resolve(:mountpoints)
66 expect(result['/'][:device]).to eq('/dev/root')
67 end
68
69 context 'when mountpoint cannot be accessed' do
70 let(:fact) do
71 { '/' => { available: '0 bytes',
72 available_bytes: 0,
73 capacity: '100%',
74 device: '/dev/root',
75 filesystem: 'ext4',
76 options: fact_options,
77 size: '0 bytes',
78 size_bytes: 0,
79 used: '0 bytes',
80 used_bytes: 0 } }
81 end
82
83 before do
84 allow(Facter::Util::Resolvers::FilesystemHelper).to \
85 receive(:read_mountpoint_stats).and_raise(Sys::Filesystem::Error)
86 end
87
88 it 'fallbacks to default values' do
89 result = Facter::Resolvers::Macosx::Mountpoints.resolve(:mountpoints)
90 expect(result).to eq(fact)
91 end
92 end
93 end
94
95 describe '.read_options' do
96 shared_examples_for 'a valid option' do |input, output|
97 let(:mount_options) { input }
98 let(:fact_options) { [output] }
99
100 it "transforms '#{input}' into '#{output}' to match Facter 3 output" do
101 result = Facter::Resolvers::Macosx::Mountpoints.resolve(:mountpoints)
102 expect(result).to match(fact)
103 end
104 end
105
106 it_behaves_like 'a valid option', 'read-only', 'readonly'
107 it_behaves_like 'a valid option', 'asynchronous', 'async'
108 it_behaves_like 'a valid option', 'synchronous', 'noasync'
109 it_behaves_like 'a valid option', 'quotas', 'quota'
110 it_behaves_like 'a valid option', 'rootfs', 'root'
111 it_behaves_like 'a valid option', 'defwrite', 'deferwrites'
112 it_behaves_like 'a valid option', 'nodev', 'nodev'
113 end
114 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::Processors do
3 subject(:processors_resolver) { Facter::Resolvers::Macosx::Processors }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:logicalcount) { 4 }
7 let(:models) do
8 ['Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz', 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz',
9 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz', 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz']
10 end
11 let(:physical_processors) { 1 }
12 let(:speed_expected) { 2_300_000_000 }
13 let(:cores_per_socket) { 4 }
14 let(:threads_per_core) { 1 }
15
16 before do
17 processors_resolver.instance_variable_set(:@log, log_spy)
18
19 allow(Facter::Core::Execution)
20 .to receive(:execute)
21 .with(query_string, logger: log_spy)
22 .and_return(output)
23 end
24
25 after do
26 processors_resolver.invalidate_cache
27 end
28
29 context 'when `sysctl` succeeds' do
30 let(:output) do
31 ['hw.logicalcpu_max: 4',
32 'hw.physicalcpu_max: 1',
33 'machdep.cpu.brand_string: Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz',
34 'hw.cpufrequency_max: 2300000000',
35 'machdep.cpu.core_count: 4',
36 'machdep.cpu.thread_count: 4'].join("\n")
37 end
38
39 let(:query_string) do
40 'sysctl hw.logicalcpu_max '\
41 'hw.physicalcpu_max '\
42 'machdep.cpu.brand_string '\
43 'hw.cpufrequency_max '\
44 'machdep.cpu.core_count machdep.cpu.thread_count'
45 end
46
47 it 'returns number of processors' do
48 expect(processors_resolver.resolve(:logicalcount)).to eq(logicalcount)
49 end
50
51 it 'returns number of physical processors' do
52 expect(processors_resolver.resolve(:physicalcount)).to eq(physical_processors)
53 end
54
55 it 'returns list of models' do
56 expect(processors_resolver.resolve(:models)).to eq(models)
57 end
58
59 it 'returns speed of processors' do
60 expect(processors_resolver.resolve(:speed)).to eq(speed_expected)
61 end
62
63 it 'returns number of cores per socket' do
64 expect(processors_resolver.resolve(:cores_per_socket)).to eq(cores_per_socket)
65 end
66
67 it 'returns number of threads per core' do
68 expect(processors_resolver.resolve(:threads_per_core)).to eq(threads_per_core)
69 end
70 end
71
72 context 'when `sysctl` commands fail' do
73 let(:output) do
74 ['machdep.cpu.core_count: 4', 'machdep.cpu.thread_count: 4'].join("\n")
75 end
76
77 let(:query_string) do
78 'sysctl hw.logicalcpu_max '\
79 'hw.physicalcpu_max '\
80 'machdep.cpu.brand_string '\
81 'hw.cpufrequency_max '\
82 'machdep.cpu.core_count machdep.cpu.thread_count'
83 end
84
85 it 'returns available values' do
86 expect(processors_resolver.resolve(:threads_per_core)).to eq(threads_per_core)
87 end
88 end
89 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::SwapMemory do
3 subject(:swap_memory) { Facter::Resolvers::Macosx::SwapMemory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:available_bytes) { 1_729_363_968 }
7 let(:total_bytes) { 3_221_225_472 }
8 let(:used_bytes) { 1_491_861_504 }
9 let(:capacity) { '46.31%' }
10 let(:encrypted) { true }
11
12 before do
13 swap_memory.instance_variable_set(:@log, log_spy)
14 allow(Facter::Core::Execution).to receive(:execute)
15 .with('sysctl -n vm.swapusage', logger: log_spy)
16 .and_return('total = 3072.00M used = 1422.75M free = 1649.25M (encrypted)')
17 end
18
19 it 'returns available swap memory in bytes' do
20 expect(swap_memory.resolve(:available_bytes)).to eq(available_bytes)
21 end
22
23 it 'returns total swap memory in bytes' do
24 expect(swap_memory.resolve(:total_bytes)).to eq(total_bytes)
25 end
26
27 it 'returns used swap memory in bytes' do
28 expect(swap_memory.resolve(:used_bytes)).to eq(used_bytes)
29 end
30
31 it 'returns capacity of swap memory' do
32 expect(swap_memory.resolve(:capacity)).to eq(capacity)
33 end
34
35 it 'returns true because swap memory is encrypted' do
36 expect(swap_memory.resolve(:encrypted)).to eq(encrypted)
37 end
38 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::SystemMemory do
3 subject(:system_memory) { Facter::Resolvers::Macosx::SystemMemory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6 let(:available_bytes) { 5_590_519_808 }
7 let(:total_bytes) { 34_359_738_368 }
8 let(:used_bytes) { 28_769_218_560 }
9 let(:capacity) { '83.73%' }
10
11 before do
12 system_memory.instance_variable_set(:@log, log_spy)
13 allow(Facter::Core::Execution).to receive(:execute)
14 .with('sysctl -n hw.memsize', logger: log_spy)
15 .and_return('34359738368')
16
17 allow(Facter::Core::Execution).to receive(:execute)
18 .with('vm_stat', logger: log_spy)
19 .and_return(load_fixture('vm_stat').read)
20 end
21
22 it 'returns available system memory in bytes' do
23 expect(system_memory.resolve(:available_bytes)).to eq(available_bytes)
24 end
25
26 it 'returns total system memory in bytes' do
27 expect(system_memory.resolve(:total_bytes)).to eq(total_bytes)
28 end
29
30 it 'returns used system memory in bytes' do
31 expect(system_memory.resolve(:used_bytes)).to eq(used_bytes)
32 end
33
34 it 'returns memory capacity' do
35 expect(system_memory.resolve(:capacity)).to eq(capacity)
36 end
37 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Memory do
3 subject(:resolver) { Facter::Resolvers::Linux::Memory }
4
5 after do
6 Facter::Resolvers::Linux::Memory.invalidate_cache
7 end
8
9 context 'when file /proc/meminfo is readable' do
10 before do
11 allow(Facter::Util::FileHelper).to receive(:safe_read)
12 .with('/proc/meminfo', nil)
13 .and_return(load_fixture(fixture_name).read)
14 end
15
16 context 'when there is swap memory' do
17 let(:total) { 4_036_680 * 1024 }
18 let(:available) { 3_659_324 * 1024 }
19 let(:used) { total - available }
20 let(:swap_total) { 2_097_148 * 1024 }
21 let(:swap_free) { 2_097_148 * 1024 }
22 let(:swap_used) { swap_total - swap_free }
23 let(:fixture_name) { 'meminfo' }
24
25 it 'returns total memory' do
26 expect(resolver.resolve(:total)).to eq(total)
27 end
28
29 it 'returns memfree' do
30 expect(resolver.resolve(:memfree)).to eq(available)
31 end
32
33 it 'returns swap total' do
34 expect(resolver.resolve(:swap_total)).to eq(swap_total)
35 end
36
37 it 'returns swap available' do
38 expect(resolver.resolve(:swap_free)).to eq(swap_free)
39 end
40
41 it 'returns swap capacity' do
42 swap_capacity = format('%<swap_capacity>.2f', swap_capacity: (swap_used / swap_total.to_f * 100)) + '%'
43
44 expect(resolver.resolve(:swap_capacity)).to eq(swap_capacity)
45 end
46
47 it 'returns swap usage' do
48 expect(resolver.resolve(:swap_used_bytes)).to eq(swap_used)
49 end
50
51 it 'returns system capacity' do
52 system_capacity = format('%<capacity>.2f', capacity: (used / total.to_f * 100)) + '%'
53
54 expect(resolver.resolve(:capacity)).to eq(system_capacity)
55 end
56
57 it 'returns system usage' do
58 expect(resolver.resolve(:used_bytes)).to eq(used)
59 end
60 end
61
62 context 'when there is not swap memory' do
63 let(:total) { 4_134_510_592 }
64 let(:available) { 3_665_024 * 1024 }
65 let(:used) { total - available }
66 let(:fixture_name) { 'meminfo2' }
67
68 it 'returns total memory' do
69 expect(resolver.resolve(:total)).to eq(total)
70 end
71
72 it 'returns memfree' do
73 expect(resolver.resolve(:memfree)).to eq(available)
74 end
75
76 it 'returns swap total as nil' do
77 expect(resolver.resolve(:swap_total)).to eq(nil)
78 end
79
80 it 'returns swap available as nil' do
81 expect(resolver.resolve(:swap_free)).to eq(nil)
82 end
83
84 it 'returns swap capacity as nil' do
85 expect(resolver.resolve(:swap_capacity)).to eq(nil)
86 end
87
88 it 'returns swap usage as nil' do
89 expect(resolver.resolve(:swap_used_bytes)).to eq(nil)
90 end
91
92 it 'returns system capacity' do
93 system_capacity = format('%<capacity>.2f', capacity: (used / total.to_f * 100)) + '%'
94
95 expect(resolver.resolve(:capacity)).to eq(system_capacity)
96 end
97
98 it 'returns system usage' do
99 expect(resolver.resolve(:used_bytes)).to eq(used)
100 end
101 end
102
103 context 'when on Rhel 5' do
104 let(:total) { 4_036_680 * 1024 }
105 let(:available) { 3_659_324 * 1024 }
106 let(:used) { total - available }
107 let(:swap_total) { 2_097_148 * 1024 }
108 let(:swap_free) { 2_097_148 * 1024 }
109 let(:swap_used) { swap_total - swap_free }
110 let(:fixture_name) { 'rhel5_memory' }
111
112 it 'returns total memory' do
113 expect(resolver.resolve(:total)).to eq(total)
114 end
115
116 it 'returns memfree' do
117 expect(resolver.resolve(:memfree)).to eq(available)
118 end
119
120 it 'returns swap total' do
121 expect(resolver.resolve(:swap_total)).to eq(swap_total)
122 end
123
124 it 'returns swap available' do
125 expect(resolver.resolve(:swap_free)).to eq(swap_free)
126 end
127
128 it 'returns swap capacity' do
129 swap_capacity = format('%<swap_capacity>.2f', swap_capacity: (swap_used / swap_total.to_f * 100)) + '%'
130
131 expect(resolver.resolve(:swap_capacity)).to eq(swap_capacity)
132 end
133
134 it 'returns swap usage' do
135 expect(resolver.resolve(:swap_used_bytes)).to eq(swap_used)
136 end
137
138 it 'returns system capacity' do
139 system_capacity = format('%<capacity>.2f', capacity: (used / total.to_f * 100)) + '%'
140
141 expect(resolver.resolve(:capacity)).to eq(system_capacity)
142 end
143
144 it 'returns system usage' do
145 expect(resolver.resolve(:used_bytes)).to eq(used)
146 end
147 end
148
149 context 'when there is no available memory' do
150 let(:total) { 4_036_680 * 1024 }
151 let(:buffers) { 4_288 * 1024 }
152 let(:cached) { 298_624 * 1024 }
153 let(:free) { 3_547_792 * 1024 }
154 let(:available) { free + buffers + cached }
155 let(:used) { total - available }
156 let(:swap_total) { 2_097_148 * 1024 }
157 let(:swap_free) { 2_097_148 * 1024 }
158 let(:swap_used) { swap_total - swap_free }
159 let(:fixture_name) { 'meminfo_missing_available' }
160
161 it 'returns total memory' do
162 expect(resolver.resolve(:total)).to eq(total)
163 end
164
165 it 'returns memfree' do
166 expect(resolver.resolve(:memfree)).to eq(available)
167 end
168
169 it 'returns swap total' do
170 expect(resolver.resolve(:swap_total)).to eq(swap_total)
171 end
172
173 it 'returns swap available' do
174 expect(resolver.resolve(:swap_free)).to eq(swap_free)
175 end
176
177 it 'returns swap capacity' do
178 swap_capacity = format('%<swap_capacity>.2f', swap_capacity: (swap_used / swap_total.to_f * 100)) + '%'
179
180 expect(resolver.resolve(:swap_capacity)).to eq(swap_capacity)
181 end
182
183 it 'returns swap usage' do
184 expect(resolver.resolve(:swap_used_bytes)).to eq(swap_used)
185 end
186
187 it 'returns system capacity' do
188 system_capacity = format('%<capacity>.2f', capacity: (used / total.to_f * 100)) + '%'
189
190 expect(resolver.resolve(:capacity)).to eq(system_capacity)
191 end
192
193 it 'returns system usage' do
194 expect(resolver.resolve(:used_bytes)).to eq(used)
195 end
196 end
197 end
198
199 context 'when file /proc/meminfo is not readable' do
200 before do
201 allow(Facter::Util::FileHelper).to receive(:safe_read)
202 .with('/proc/meminfo', nil)
203 .and_return(nil)
204 end
205
206 it 'returns swap available as nil' do
207 expect(resolver.resolve(:swap_free)).to be(nil)
208 end
209
210 it 'returns system usage as nil' do
211 expect(resolver.resolve(:used_bytes)).to be(nil)
212 end
213 end
214 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Mountpoints do
3 let(:mount) do
4 double(Sys::Filesystem::Mount,
5 mount_point: '/', mount_time: nil,
6 mount_type: 'ext4', options: 'rw,noatime', name:
7 '/dev/nvme0n1p2', dump_frequency: 0, pass_number: 0)
8 end
9
10 let(:stat) do
11 double(Sys::Filesystem::Stat,
12 path: '/', base_type: nil, fragment_size: 4096, block_size: 4096, blocks: 113_879_332,
13 blocks_available: -16_596_603, blocks_free: 22_398_776)
14 end
15
16 let(:fact) do
17 [{ available: '63.31 GiB',
18 available_bytes: 67_979_685_888,
19 capacity: '84.64%',
20 device: '/dev/nvme0n1p2',
21 filesystem: 'ext4',
22 options: %w[rw noatime],
23 path: '/',
24 size: '434.42 GiB',
25 size_bytes: 466_449_743_872,
26 used: '348.97 GiB',
27 used_bytes: 374_704_357_376 }]
28 end
29
30 let(:ignored_mounts) do
31 [double(Sys::Filesystem::Mount, mount_type: 'ext4', mount_point: '/proc/a', name: '/dev/ignore', options: 'rw'),
32 double(Sys::Filesystem::Mount, mount_type: 'autofs', mount_point: '/mnt/auto', name: '/dev/ignore', options: 'rw')]
33 end
34
35 before do
36 allow(Facter::Util::FileHelper).to receive(:safe_read)
37 .with('/proc/cmdline')
38 .and_return(load_fixture('cmdline_root_device').read)
39
40 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return([mount])
41 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoint_stats)
42 .with(mount.mount_point).and_return(stat)
43
44 # mock sys/filesystem methods
45 allow(stat).to receive(:bytes_total).and_return(stat.blocks * stat.fragment_size)
46 allow(stat).to receive(:bytes_available).and_return(stat.blocks_available * stat.fragment_size)
47 allow(stat).to receive(:bytes_free).and_return(stat.blocks_free * stat.fragment_size)
48 allow(stat).to receive(:bytes_used).and_return(stat.bytes_total - stat.bytes_free)
49 Facter::Resolvers::Mountpoints.invalidate_cache
50 end
51
52 it 'correctly builds the mountpoints fact' do
53 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
54
55 expect(result).to eq(fact)
56 end
57
58 it 'drops automounts and non-tmpfs mounts under /proc or /sys' do
59 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return(ignored_mounts)
60
61 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
62
63 expect(result).to be_empty
64 end
65
66 context 'when mountpoint cannot be accessed' do
67 subject(:mountpoints) { Facter::Resolvers::Mountpoints }
68
69 let(:expected_fact) do
70 [{ available: '0 bytes',
71 available_bytes: 0,
72 capacity: '100%',
73 device: '/dev/nvme0n1p2',
74 filesystem: 'ext4',
75 options: %w[rw noatime],
76 path: '/',
77 size: '0 bytes',
78 size_bytes: 0,
79 used: '0 bytes',
80 used_bytes: 0 }]
81 end
82 let(:logger) { instance_spy(Facter::Log) }
83
84 before do
85 stub_const('Sys::Filesystem::Error', StandardError)
86 allow(Facter::Util::Resolvers::FilesystemHelper).to \
87 receive(:read_mountpoint_stats).with(mount.mount_point).and_raise(Sys::Filesystem::Error)
88
89 mountpoints.instance_variable_set(:@log, logger)
90 end
91
92 after do
93 Facter.instance_variable_set(:@logger, nil)
94 end
95
96 it 'logs into debug if it cannot receive mountpoints stats' do
97 Facter::Resolvers::Mountpoints.resolve(:mountpoints)
98
99 expect(logger).to have_received(:debug)
100 .with("Could not get stats for mountpoint #{mount.mount_point}, got StandardError")
101 end
102
103 it 'fallbacks to default values' do
104 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
105 expect(result).to eq(expected_fact)
106 end
107 end
108
109 describe '.root_device' do
110 let(:mount) do
111 double(Sys::Filesystem::Mount, mount_point: '/', mount_type: 'ext4', options: 'rw,noatime', name: '/dev/root')
112 end
113
114 it 'looks up the actual device if /dev/root' do
115 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
116
117 expect(result.first[:device]).to eq('/dev/mmcblk0p2')
118 end
119
120 context 'when /proc/cmdline is not accessible' do
121 before do
122 allow(Facter::Util::FileHelper).to receive(:safe_read)
123 .with('/proc/cmdline')
124 .and_return('')
125 end
126
127 it 'returns device as nil' do
128 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
129
130 expect(result.first[:device]).to be(nil)
131 end
132 end
133
134 context 'when root device has partuuid' do
135 let(:log) { instance_spy(Facter::Log) }
136
137 before do
138 allow(Facter::Util::FileHelper).to receive(:safe_read)
139 .with('/proc/cmdline')
140 .and_return(load_fixture('cmdline_root_device_partuuid').read)
141 allow(Facter::Core::Execution).to receive(:execute)
142 .with('blkid', logger: log)
143 .and_return(load_fixture('blkid_output_root_has_partuuid').read)
144 Facter::Resolvers::Mountpoints.instance_variable_set(:@log, log)
145 end
146
147 it 'returns the path instead of the PARTUUID' do
148 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
149
150 expect(result.first[:device]).to eq('/dev/xvda1')
151 end
152
153 context 'when blkid command is not available' do
154 before do
155 allow(Facter::Core::Execution).to receive(:execute)
156 .with('blkid', logger: log)
157 .and_return('blkid: command not found')
158 Facter::Resolvers::Mountpoints.instance_variable_set(:@log, log)
159 end
160
161 it 'returns the partition path as PARTUUID' do
162 result = Facter::Resolvers::Mountpoints.resolve(:mountpoints)
163
164 expect(result.first[:device]).to eq('PARTUUID=a2f52878-01')
165 end
166 end
167 end
168 end
169
170 describe 'resolver key not found' do
171 it 'returns nil when resolver cannot find key' do
172 expect(Facter::Resolvers::Mountpoints.resolve(:inexistent_key)).to be_nil
173 end
174 end
175 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Networking do
3 subject(:networking) { Facter::Resolvers::Networking }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 describe '#resolve' do
8 before do
9 networking.instance_variable_set(:@log, log_spy)
10 allow(Facter::Util::Resolvers::Networking::PrimaryInterface)
11 .to receive(:read_from_route)
12 .and_return(primary)
13 allow(Facter::Core::Execution).to receive(:execute).with('ifconfig -a', logger: log_spy).and_return(interfaces)
14 allow(Facter::Core::Execution)
15 .to receive(:execute).with('ipconfig getoption en0 server_identifier', logger: log_spy).and_return(dhcp)
16 allow(Facter::Core::Execution)
17 .to receive(:execute).with('ipconfig getoption en0.1 server_identifier', logger: log_spy).and_return(dhcp)
18 allow(Facter::Core::Execution)
19 .to receive(:execute).with('ipconfig getoption llw0 server_identifier', logger: log_spy).and_return('')
20 allow(Facter::Core::Execution)
21 .to receive(:execute).with('ipconfig getoption awdl0 server_identifier', logger: log_spy).and_return(dhcp)
22 end
23
24 after do
25 networking.invalidate_cache
26 end
27
28 let(:interfaces) { load_fixture('ifconfig_mac').read }
29 let(:dhcp) { '192.168.143.1' }
30 let(:primary) { 'en0' }
31
32 it 'detects primary interface' do
33 expect(networking.resolve(:primary_interface)).to eq('en0')
34 end
35
36 it 'detects the primary interface dhcp server ip' do
37 expect(networking.resolve(:dhcp)).to eq('192.168.143.1')
38 end
39
40 it 'detects all interfaces' do
41 expected = %w[lo0 gif0 stf0 en0 en0.1 en1 en2 bridge0 p2p0 awdl0 llw0 utun0 utun1 utun2 utun3 ib0 ib1]
42 expect(networking.resolve(:interfaces).keys).to match_array(expected)
43 end
44
45 it 'checks that interface lo0 has the expected keys' do
46 expected = %i[mtu bindings6 bindings ip ip6 netmask netmask6 network network6 scope6]
47 expect(networking.resolve(:interfaces)['lo0'].keys).to match_array(expected)
48 end
49
50 it 'checks that interface lo0 has the expected mtu' do
51 expect(networking.resolve(:interfaces)['lo0']).to include({ mtu: 16_384 })
52 end
53
54 it 'checks that interface lo0 has the expected bindings' do
55 expected = { bindings: [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }] }
56 expect(networking.resolve(:interfaces)['lo0']).to include(expected)
57 end
58
59 it 'checks interface lo0 has the expected bindings6' do
60 expected = { bindings6: [{ address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', network: '::1',
61 scope6: 'host' },
62 { address: 'fe80::1', netmask: 'ffff:ffff:ffff:ffff::', network: 'fe80::',
63 scope6: 'link' }] }
64 expect(networking.resolve(:interfaces)['lo0']).to include(expected)
65 end
66
67 it 'checks that interface lo0 has the expected scope6' do
68 expect(networking.resolve(:interfaces)['lo0']).to include({ scope6: 'host' })
69 end
70
71 it 'detects interface en1' do
72 expected = { mtu: 1500, mac: '82:17:0e:93:9d:00' }
73 expect(networking.resolve(:interfaces)['en1']).to eq(expected)
74 end
75
76 it 'detects interface gif0' do
77 expected = { mtu: 1280 }
78 expect(networking.resolve(:interfaces)['gif0']).to eq(expected)
79 end
80
81 it 'checks that interface en0 has the expected keys' do
82 expected = %i[mtu mac bindings ip netmask network dhcp]
83 expect(networking.resolve(:interfaces)['en0'].keys).to match_array(expected)
84 end
85
86 it 'checks that interface en0 has the expected mtu' do
87 expected = { mtu: 1500 }
88 expect(networking.resolve(:interfaces)['en0']).to include(expected)
89 end
90
91 it 'checks that interface en0 has the expected dhcp' do
92 expected = { dhcp: '192.168.143.1' }
93 expect(networking.resolve(:interfaces)['en0']).to include(expected)
94 end
95
96 it 'checks interface en0 has the expected mac' do
97 expected = { mac: '64:5a:ed:ea:5c:81' }
98 expect(networking.resolve(:interfaces)['en0']).to include(expected)
99 end
100
101 it 'checks that interface en0 has the expected bindings6' do
102 expected = { bindings: [{ address: '192.168.143.212', netmask: '255.255.255.0', network: '192.168.143.0' }] }
103 expect(networking.resolve(:interfaces)['en0']).to include(expected)
104 end
105
106 it 'checks interface bridge0' do
107 expected = { mtu: 1500, mac: '82:17:0e:93:9d:00' }
108 expect(networking.resolve(:interfaces)['bridge0']).to include(expected)
109 end
110
111 it 'checks that interface llw0 has no dhcp' do
112 expected = { dhcp: '192.168.143.1' }
113 expect(networking.resolve(:interfaces)['llw0']).not_to include(expected)
114 end
115
116 it 'checks that interface awdl0 has the expected keys' do
117 expected = %i[mtu mac bindings6 ip6 netmask6 network6 scope6 dhcp]
118 expect(networking.resolve(:interfaces)['awdl0'].keys).to match_array(expected)
119 end
120
121 it 'checks that interface awdl0 has dhcp' do
122 expected = { dhcp: '192.168.143.1' }
123 expect(networking.resolve(:interfaces)['awdl0']).to include(expected)
124 end
125
126 it 'checks interface utun2' do
127 expected = { bindings: [{ address: '10.16.132.213', netmask: '255.255.254.0', network: '10.16.132.0' }] }
128 expect(networking.resolve(:interfaces)['utun2']).to include(expected)
129 end
130
131 it 'checks interface utun3' do
132 expected = { bindings6: [
133 { address: '2001:db8:cafe::132:213', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
134 network: '2001:db8:cafe::132:213', scope6: 'global' }
135 ] }
136 expect(networking.resolve(:interfaces)['utun3']).to include(expected)
137 end
138
139 it 'checks interface ib0 has the expected mac' do
140 expected = { mac: '80:00:02:08:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00' }
141 expect(networking.resolve(:interfaces)['ib0']).to include(expected)
142 end
143
144 context 'when primary interface could not be retrieved' do
145 let(:primary) { nil }
146
147 it 'returns primary interface as from interfaces' do
148 expect(networking.resolve(:primary_interface)).to eq('en0')
149 end
150 end
151
152 context 'when dhcp server ip could not be retrieved' do
153 let(:dhcp) { 'invalid output' }
154
155 it 'returns dhcp server ip as nil' do
156 expect(networking.resolve(:dhcp)).to be(nil)
157 end
158 end
159
160 context 'when interfaces could not be retrieved' do
161 let(:interfaces) { +'invalid output' }
162
163 it 'returns interfaces as nil' do
164 expect(networking.resolve(:interfaces)).to be(nil)
165 end
166 end
167 end
168 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::OpenVz do
3 subject(:openvz_resolver) { Facter::Resolvers::OpenVz }
4
5 let(:vz_dir) { true }
6 let(:list_executable) { false }
7
8 after do
9 openvz_resolver.invalidate_cache
10 end
11
12 before do
13 allow(Dir).to receive(:exist?).with('/proc/vz').and_return(vz_dir)
14 allow(File).to receive(:file?).with('/proc/lve/list').and_return(list_executable)
15 allow(Dir).to receive(:entries).with('/proc/vz').and_return(vz_dir_entries)
16 end
17
18 context 'when /proc/vz dir is empty' do
19 let(:vz_dir_entries) { ['.', '..'] }
20
21 it 'returns nil for vm' do
22 expect(openvz_resolver.resolve(:vm)).to be_nil
23 end
24
25 it 'returns nil for container id' do
26 expect(openvz_resolver.resolve(:id)).to be_nil
27 end
28 end
29
30 context 'when /proc/vz dir is not empty' do
31 let(:vz_dir_entries) { ['.', '..', 'some_file.txt'] }
32
33 before do
34 allow(Facter::Util::FileHelper).to receive(:safe_readlines).with('/proc/self/status', nil).and_return(status_file)
35 end
36
37 context 'when /proc/self/status is nil' do
38 let(:status_file) { nil }
39
40 it 'returns nil for vm' do
41 expect(openvz_resolver.resolve(:vm)).to be_nil
42 end
43
44 it 'returns nil for container id' do
45 expect(openvz_resolver.resolve(:id)).to be_nil
46 end
47 end
48
49 context 'when /proc/self/status is readable and openvz host' do
50 let(:status_file) { load_fixture('proc_self_status_host').readlines }
51
52 it 'returns openvzhn' do
53 expect(openvz_resolver.resolve(:vm)).to eql('openvzhn')
54 end
55
56 it 'returns container id' do
57 expect(openvz_resolver.resolve(:id)).to eql('0')
58 end
59 end
60
61 context 'when /proc/self/status is readable' do
62 let(:status_file) { load_fixture('proc_self_status').readlines }
63
64 it 'returns openvzhn' do
65 expect(openvz_resolver.resolve(:vm)).to eql('openvzve')
66 end
67
68 it 'returns container id' do
69 expect(openvz_resolver.resolve(:id)).to eql('101')
70 end
71 end
72 end
73 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::OsRelease do
3 after do
4 Facter::Resolvers::OsRelease.invalidate_cache
5 end
6
7 before do
8 Facter::Resolvers::OsRelease.invalidate_cache
9 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
10 .with('/etc/os-release')
11 .and_return(os_release_content)
12 end
13
14 context 'when on Ubuntu' do
15 let(:os_release_content) { load_fixture('os_release').readlines }
16
17 it 'returns os NAME' do
18 result = Facter::Resolvers::OsRelease.resolve(:name)
19
20 expect(result).to eq('Ubuntu')
21 end
22
23 it 'returns os PRETTY_NAME' do
24 result = Facter::Resolvers::OsRelease.resolve(:pretty_name)
25
26 expect(result).to eq('Ubuntu 18.04.1 LTS')
27 end
28
29 it 'returns os VERSION_ID' do
30 result = Facter::Resolvers::OsRelease.resolve(:version_id)
31
32 expect(result).to eq('18.04')
33 end
34
35 it 'returns os VERSION_CODENAME' do
36 result = Facter::Resolvers::OsRelease.resolve(:version_codename)
37
38 expect(result).to eq('bionic')
39 end
40
41 it 'returns os id' do
42 result = Facter::Resolvers::OsRelease.resolve(:id)
43
44 expect(result).to eq('')
45 end
46 end
47
48 context 'when /etc/os-release file is not readable' do
49 let(:os_release_content) { [] }
50
51 it 'returns nil' do
52 result = Facter::Resolvers::OsRelease.resolve(:version_codename)
53
54 expect(result).to be(nil)
55 end
56 end
57
58 context 'when on Debian' do
59 let(:os_release_content) { load_fixture('os_release_debian').readlines }
60
61 it 'returns os NAME' do
62 result = Facter::Resolvers::OsRelease.resolve(:name)
63
64 expect(result).to eq('Debian')
65 end
66
67 it 'returns os PRETTY_NAME' do
68 result = Facter::Resolvers::OsRelease.resolve(:pretty_name)
69
70 expect(result).to eq('Debian GNU/Linux 10 (buster)')
71 end
72
73 it 'returns os VERSION_ID with padded 0' do
74 result = Facter::Resolvers::OsRelease.resolve(:version_id)
75
76 expect(result).to eq('10.0')
77 end
78
79 it 'returns os VERSION_CODENAME' do
80 result = Facter::Resolvers::OsRelease.resolve(:version_codename)
81
82 expect(result).to eq('buster')
83 end
84
85 it 'returns os id' do
86 result = Facter::Resolvers::OsRelease.resolve(:id)
87
88 expect(result).to eq('debian')
89 end
90 end
91
92 context 'when on opensuse-leap' do
93 let(:os_release_content) { load_fixture('os_release_opensuse-leap').readlines }
94
95 it 'returns os id' do
96 result = Facter::Resolvers::OsRelease.resolve(:id)
97
98 expect(result).to eq('opensuse')
99 end
100
101 context 'when opensuse identifier is capitalized' do
102 it 'returns os id' do
103 os_release_content[2] = 'ID="Opensuse-Leap"'
104
105 result = Facter::Resolvers::OsRelease.resolve(:id)
106
107 expect(result).to eq('opensuse')
108 end
109 end
110 end
111
112 context 'when on Oracle Linux' do
113 let(:os_release_content) { load_fixture('os_release_oracle_linux').readlines }
114
115 it 'returns os NAME' do
116 result = Facter::Resolvers::OsRelease.resolve(:name)
117
118 expect(result).to eq('OracleLinux')
119 end
120 end
121
122 context 'when on Archlinux' do
123 let(:os_release_content) { load_fixture('os_release_archlinux').readlines }
124
125 it 'returns os NAME' do
126 result = Facter::Resolvers::OsRelease.resolve(:name)
127
128 expect(result).to eq('Archlinux')
129 end
130 end
131
132 context 'when on Manjarolinux' do
133 let(:os_release_content) { load_fixture('os_release_manjarolinux').readlines }
134
135 it 'returns os NAME' do
136 result = Facter::Resolvers::OsRelease.resolve(:name)
137
138 expect(result).to eq('Manjarolinux')
139 end
140
141 it "doesn't pad a non-existent version_id" do
142 result = Facter::Resolvers::OsRelease.resolve(:version_id)
143
144 expect(result).to be_nil
145 end
146 end
147
148 context 'when on VirtuozzoLinux' do
149 let(:os_release_content) { load_fixture('os_release_virtuozzolinux').readlines }
150
151 it 'returns os NAME' do
152 result = Facter::Resolvers::OsRelease.resolve(:name)
153
154 expect(result).to eq('VirtuozzoLinux')
155 end
156 end
157 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Partitions do
3 subject(:resolver) { Facter::Resolvers::Partitions }
4
5 let(:sys_block_path) { '/sys/block' }
6 let(:sys_block_subdirs) { ['.', '..', 'sda'] }
7 let(:logger) { instance_spy(Facter::Log) }
8
9 before do
10 Facter::Resolvers::Partitions.invalidate_cache
11 Facter::Resolvers::Partitions.instance_variable_set(:@log, logger)
12 end
13
14 context 'when /sys/block is not readable' do
15 before do
16 allow(File).to receive(:readable?).with(sys_block_path).and_return(false)
17 end
18
19 it 'returns empty hash' do
20 expect(resolver.resolve(:partitions)).to be(nil)
21 end
22 end
23
24 context 'when /sys/block has no entries' do
25 before do
26 allow(File).to receive(:readable?).with(sys_block_path).and_return(true)
27 allow(Dir).to receive(:entries).with(sys_block_path).and_return(['.', '..'])
28 end
29
30 it 'returns empty hash' do
31 expect(resolver.resolve(:partitions)).to be(nil)
32 end
33 end
34
35 context 'when /sys/block is readable' do
36 before do
37 allow(File).to receive(:readable?).with(sys_block_path).and_return(true)
38 allow(Dir).to receive(:entries).with(sys_block_path).and_return(sys_block_subdirs)
39 allow(Facter::Core::Execution).to receive(:which)
40 .with('blkid').and_return('/usr/bin/blkid')
41 allow(Facter::Core::Execution).to receive(:execute)
42 .with('blkid', logger: logger).and_return(load_fixture('blkid_output').read)
43 allow(Facter::Core::Execution).to receive(:which)
44 .with('lsblk').and_return('/usr/bin/lsblk')
45 allow(Facter::Core::Execution).to receive(:execute)
46 .with('lsblk -fp', logger: logger).and_return(load_fixture('lsblk_output').read)
47 end
48
49 context 'when block has a device subdir' do
50 let(:sda_subdirs) do
51 ['/sys/block/sda/queue',
52 '/sys/block/sda/sda2',
53 '/sys/block/sda/sda2/stat',
54 '/sys/block/sda/sda2/dev',
55 '/sys/block/sda/sda2/uevent',
56 '/sys/block/sda/sda1']
57 end
58
59 before do
60 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/device").and_return(true)
61 allow(Dir).to receive(:[]).with("#{sys_block_path}/sda/**/*").and_return(sda_subdirs)
62 sda_subdirs.each { |subdir| allow(File).to receive(:directory?).with(subdir).and_return(true) }
63 allow(Facter::Util::FileHelper).to receive(:safe_read)
64 .with("#{sys_block_path}/sda/sda2/size", '0').and_return('201213')
65 allow(Facter::Util::FileHelper).to receive(:safe_read)
66 .with("#{sys_block_path}/sda/sda1/size", '0').and_return('234')
67 end
68
69 context 'when there is more than one partition' do
70 it 'checks for blkid only once' do
71 resolver.resolve(:partitions)
72 expect(Facter::Core::Execution).to have_received(:which).with('blkid').once
73 end
74
75 it 'checks for lsblk only once' do
76 resolver.resolve(:partitions)
77 expect(Facter::Core::Execution).to have_received(:which).with('lsblk').once
78 end
79 end
80
81 context 'when device size files are readable' do
82 let(:partitions) do
83 { '/dev/sda1' => { filesystem: 'ext3', label: '/boot', size: '117.00 KiB',
84 size_bytes: 119_808, uuid: '88077904-4fd4-476f-9af2-0f7a806ca25e',
85 partuuid: '00061fe0-01' },
86 '/dev/sda2' => { filesystem: 'LVM2_member', size: '98.25 MiB', size_bytes: 103_021_056,
87 uuid: 'edi7s0-2WVa-ZBan' } }
88 end
89
90 it 'return partitions fact' do
91 expect(resolver.resolve(:partitions)).to eq(partitions)
92 end
93 end
94
95 context 'when device size files are not readable' do
96 let(:partitions_with_no_sizes) do
97 { '/dev/sda1' => { filesystem: 'ext3', label: '/boot', size: '0 bytes',
98 size_bytes: 0, uuid: '88077904-4fd4-476f-9af2-0f7a806ca25e', partuuid: '00061fe0-01' },
99 '/dev/sda2' => { filesystem: 'LVM2_member', size: '0 bytes', size_bytes: 0, uuid: 'edi7s0-2WVa-ZBan' } }
100 end
101
102 before do
103 allow(Facter::Util::FileHelper).to receive(:safe_read)
104 .with("#{sys_block_path}/sda/sda2/size", '0').and_return('')
105 allow(Facter::Util::FileHelper).to receive(:safe_read)
106 .with("#{sys_block_path}/sda/sda1/size", '0').and_return('')
107 end
108
109 it 'return partitions fact with 0 sizes' do
110 result = resolver.resolve(:partitions)
111
112 expect(result).to eq(partitions_with_no_sizes)
113 end
114 end
115 end
116
117 context 'when block has a dm subdir' do
118 before do
119 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/device").and_return(false)
120 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/dm").and_return(true)
121 allow(Facter::Util::FileHelper).to receive(:safe_read)
122 .with("#{sys_block_path}/sda/dm/name").and_return('VolGroup00-LogVol00')
123 allow(Facter::Util::FileHelper).to receive(:safe_read)
124 .with("#{sys_block_path}/sda/size", '0').and_return('201213')
125 end
126
127 context 'when device name file is readable' do
128 let(:partitions) do
129 { '/dev/mapper/VolGroup00-LogVol00' => { filesystem: 'ext3', size: '98.25 MiB', size_bytes: 103_021_056,
130 uuid: '1bd8643b-483a-4fdc-adcd-c586384919a8' } }
131 end
132
133 it 'return partitions fact' do
134 expect(resolver.resolve(:partitions)).to eq(partitions)
135 end
136 end
137
138 context 'when device name file is not readable' do
139 let(:partitions) do
140 { '/dev/sda' => { size: '98.25 MiB', size_bytes: 103_021_056 } }
141 end
142
143 before do
144 allow(Facter::Util::FileHelper).to receive(:safe_read).with("#{sys_block_path}/sda/dm/name").and_return('')
145 end
146
147 it 'return partitions fact with no device name' do
148 result = resolver.resolve(:partitions)
149
150 expect(result).to eq(partitions)
151 end
152 end
153 end
154
155 context 'when block has a loop subdir' do
156 before do
157 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/device").and_return(false)
158 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/dm").and_return(false)
159 allow(File).to receive(:directory?).with("#{sys_block_path}/sda/loop").and_return(true)
160 allow(Facter::Util::FileHelper).to receive(:safe_read)
161 .with("#{sys_block_path}/sda/loop/backing_file").and_return('some_path')
162 allow(Facter::Util::FileHelper).to receive(:safe_read)
163 .with("#{sys_block_path}/sda/size", '0').and_return('201213')
164 end
165
166 context 'when backing_file is readable' do
167 let(:partitions) do
168 { '/dev/sda' => { backing_file: 'some_path', size: '98.25 MiB', size_bytes: 103_021_056 } }
169 end
170
171 it 'returns partitions fact' do
172 expect(resolver.resolve(:partitions)).to eq(partitions)
173 end
174 end
175
176 context 'when backing_file is not readable' do
177 let(:partitions) do
178 { '/dev/sda' => { size: '98.25 MiB', size_bytes: 103_021_056 } }
179 end
180
181 before do
182 allow(Facter::Util::FileHelper).to receive(:safe_read)
183 .with("#{sys_block_path}/sda/loop/backing_file").and_return('')
184 end
185
186 it 'returns partitions fact' do
187 result = resolver.resolve(:partitions)
188
189 expect(result).to eq(partitions)
190 end
191 end
192 end
193 end
194 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Path do
3 describe '#resolve path' do
4 it 'detects path' do
5 expect(Facter::Resolvers::Path.resolve(:path)).to eql(ENV['PATH'])
6 end
7 end
8 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Linux::Processors do
3 after do
4 Facter::Resolvers::Linux::Processors.invalidate_cache
5 end
6
7 context 'when on x86 architecture' do
8 let(:processors) { 4 }
9 let(:models) do
10 ['Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz', 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz',
11 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz', 'Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz']
12 end
13 let(:physical_processors) { 1 }
14
15 context 'when cpuinfo file is readable' do
16 before do
17 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
18 .with('/proc/cpuinfo')
19 .and_return(load_fixture('cpuinfo').readlines)
20 end
21
22 let(:speed) { 2_294_000_000 }
23
24 it 'returns number of processors' do
25 result = Facter::Resolvers::Linux::Processors.resolve(:processors)
26
27 expect(result).to eq(processors)
28 end
29
30 it 'returns list of models' do
31 result = Facter::Resolvers::Linux::Processors.resolve(:models)
32
33 expect(result).to eq(models)
34 end
35
36 it 'returns number of physical processors' do
37 result = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
38
39 expect(result).to eq(physical_processors)
40 end
41
42 it 'returns cpu speed' do
43 result = Facter::Resolvers::Linux::Processors.resolve(:speed)
44
45 expect(result).to eq(speed)
46 end
47 end
48
49 context 'when cpuinfo file is readable but no physical id' do
50 before do
51 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
52 .with('/proc/cpuinfo')
53 .and_return(load_fixture('cpuinfo_wo_physical_id').readlines)
54 allow(Dir).to receive(:entries).with('/sys/devices/system/cpu').and_return(%w[cpu0 cpu1 cpuindex])
55
56 allow(File).to receive(:exist?)
57 .with('/sys/devices/system/cpu/cpu0/topology/physical_package_id')
58 .and_return(true)
59
60 allow(File).to receive(:exist?)
61 .with('/sys/devices/system/cpu/cpu1/topology/physical_package_id')
62 .and_return(true)
63
64 allow(Facter::Util::FileHelper).to receive(:safe_read)
65 .with('/sys/devices/system/cpu/cpu0/topology/physical_package_id')
66 .and_return('0')
67
68 allow(Facter::Util::FileHelper).to receive(:safe_read)
69 .with('/sys/devices/system/cpu/cpu1/topology/physical_package_id')
70 .and_return('1')
71 end
72
73 after do
74 Facter::Resolvers::Linux::Processors.invalidate_cache
75 end
76
77 let(:physical_processors) { 2 }
78
79 it 'returns number of processors' do
80 result = Facter::Resolvers::Linux::Processors.resolve(:processors)
81
82 expect(result).to eq(processors)
83 end
84
85 it 'returns list of models' do
86 result = Facter::Resolvers::Linux::Processors.resolve(:models)
87
88 expect(result).to eq(models)
89 end
90
91 it 'returns number of physical processors' do
92 result = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
93
94 expect(result).to eq(physical_processors)
95 end
96 end
97
98 context 'when cpuinfo is not readable' do
99 before do
100 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
101 .with('/proc/cpuinfo')
102 .and_return([])
103 end
104
105 it 'returns nil' do
106 result = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
107
108 expect(result).to be(nil)
109 end
110 end
111 end
112
113 context 'when on powerpc architecture' do
114 before do
115 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
116 .with('/proc/cpuinfo')
117 .and_return(load_fixture('cpuinfo_powerpc').readlines)
118
119 allow(Dir).to receive(:entries).with('/sys/devices/system/cpu').and_return(%w[cpu0 cpu1 cpuindex])
120 allow(File).to receive(:exist?).with('/sys/devices/system/cpu/cpu0/topology/physical_package_id').and_return(true)
121 allow(File).to receive(:exist?).with('/sys/devices/system/cpu/cpu1/topology/physical_package_id').and_return(true)
122 allow(Facter::Util::FileHelper).to receive(:safe_read)
123 .with('/sys/devices/system/cpu/cpu0/topology/physical_package_id')
124 .and_return('0')
125
126 allow(Facter::Util::FileHelper).to receive(:safe_read)
127 .with('/sys/devices/system/cpu/cpu1/topology/physical_package_id')
128 .and_return('1')
129 end
130
131 let(:speed) { 2_926_000_000 }
132 let(:physical_processors) { 2 }
133
134 let(:models) do
135 ['POWER8 (raw), altivec supported',
136 'POWER8 (raw), altivec supported',
137 'POWER8 (raw), altivec supported',
138 'POWER8 (raw), altivec supported']
139 end
140
141 it 'returns physical_devices_count' do
142 result = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
143
144 expect(result).to eq(physical_processors)
145 end
146
147 it 'returns list of models' do
148 result = Facter::Resolvers::Linux::Processors.resolve(:models)
149
150 expect(result).to eq(models)
151 end
152
153 it 'returns cpu speed' do
154 result = Facter::Resolvers::Linux::Processors.resolve(:speed)
155
156 expect(result).to eq(speed)
157 end
158 end
159
160 context 'when on arm64 architecture' do
161 before do
162 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
163 .with('/proc/cpuinfo')
164 .and_return(load_fixture('cpuinfo_arm64').readlines)
165
166 allow(Dir).to receive(:entries).with('/sys/devices/system/cpu').and_return(%w[cpu0 cpu1 cpuindex])
167 allow(File).to receive(:exist?).with('/sys/devices/system/cpu/cpu0/topology/physical_package_id').and_return(true)
168 allow(File).to receive(:exist?).with('/sys/devices/system/cpu/cpu1/topology/physical_package_id').and_return(true)
169 allow(Facter::Util::FileHelper).to receive(:safe_read)
170 .with('/sys/devices/system/cpu/cpu0/topology/physical_package_id')
171 .and_return('0')
172
173 allow(Facter::Util::FileHelper).to receive(:safe_read)
174 .with('/sys/devices/system/cpu/cpu1/topology/physical_package_id')
175 .and_return('0')
176 end
177
178 let(:physical_processors) { 1 }
179
180 it 'returns physical_devices_count' do
181 result = Facter::Resolvers::Linux::Processors.resolve(:physical_count)
182
183 expect(result).to eq(physical_processors)
184 end
185 end
186 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::RedHatRelease do
3 subject(:redhat_release) { Facter::Resolvers::RedHatRelease }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 after do
8 Facter::Resolvers::RedHatRelease.invalidate_cache
9 end
10
11 context 'when redhat-release has codename' do
12 {
13 fedora: {
14 release_file_content: "Fedora release 32 (Thirty Two)\n",
15 id: 'rhel',
16 name: 'Fedora',
17 version: '32',
18 codename: 'Thirty Two',
19 description: 'Fedora release 32 (Thirty Two)',
20 distributor_id: 'Fedora'
21
22 },
23 el: {
24 release_file_content: "Red Hat Enterprise Linux release 8.0 (Ootpa)\n",
25 id: 'rhel',
26 name: 'RedHat',
27 version: '8.0',
28 codename: 'Ootpa',
29 description: 'Red Hat Enterprise Linux release 8.0 (Ootpa)',
30 distributor_id: 'RedHatEnterprise'
31 },
32 el_server: {
33 release_file_content: "Red Hat Enterprise Linux Server release 5.10 (Tikanga)\n",
34 id: 'rhel',
35 name: 'RedHat',
36 version: '5.10',
37 codename: 'Tikanga',
38 description: 'Red Hat Enterprise Linux Server release 5.10 (Tikanga)',
39 distributor_id: 'RedHatEnterpriseServer'
40 },
41 centos: {
42 release_file_content: "CentOS Linux release 7.2.1511 (Core)\n",
43 id: 'rhel',
44 name: 'CentOS',
45 version: '7.2.1511',
46 codename: 'Core',
47 description: 'CentOS Linux release 7.2.1511 (Core)',
48 distributor_id: 'CentOS'
49 }
50 }.each_pair do |platform, data|
51 context "when #{platform.capitalize}" do
52 before do
53 allow(Facter::Util::FileHelper).to receive(:safe_read)
54 .with('/etc/redhat-release', nil)
55 .and_return(data[:release_file_content])
56 end
57
58 it 'returns os NAME' do
59 expect(redhat_release.resolve(:name)).to eq(data[:name])
60 end
61
62 it 'returns os ID' do
63 expect(redhat_release.resolve(:id)).to eq(data[:id])
64 end
65
66 it 'returns os VERSION_ID' do
67 expect(redhat_release.resolve(:version)).to eq(data[:version])
68 end
69
70 it 'returns os VERSION_CODENAME' do
71 expect(redhat_release.resolve(:codename)).to eq(data[:codename])
72 end
73
74 it 'returns os DESCRIPTION' do
75 expect(redhat_release.resolve(:description)).to eq(data[:description])
76 end
77
78 it 'returns os DISTRIBUTOR_ID' do
79 expect(redhat_release.resolve(:distributor_id)).to eq(data[:distributor_id])
80 end
81 end
82 end
83 end
84
85 context 'when redhat-relase does not have codename' do
86 before do
87 allow(Facter::Util::FileHelper).to receive(:safe_read)
88 .with('/etc/redhat-release', nil)
89 .and_return("Oracle VM server release 3.4.4\n")
90 end
91
92 it 'returns os NAME' do
93 expect(redhat_release.resolve(:name)).to eq('OracleVM')
94 end
95
96 it 'returns os VERSION_ID' do
97 expect(redhat_release.resolve(:version)).to eq('3.4.4')
98 end
99
100 it 'returns os VERSION_CODENAME' do
101 expect(redhat_release.resolve(:codename)).to be_nil
102 end
103 end
104 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::ReleaseFromFirstLine do
3 subject(:release_from_first_line_resolver) { Facter::Resolvers::ReleaseFromFirstLine }
4
5 after do
6 release_from_first_line_resolver.invalidate_cache
7 end
8
9 context 'when release file name is not given' do
10 it 'returns nil' do
11 result = release_from_first_line_resolver.resolve(:release, release_folder: 'folder')
12
13 expect(result).to be_nil
14 end
15 end
16
17 context 'when release file is present' do
18 before do
19 allow(Facter::Util::FileHelper).to receive(:safe_read).with(file_name, nil).and_return(result)
20 end
21
22 context 'when release file can not be read' do
23 let(:file_name) { '/etc/ovs-release' }
24 let(:result) { nil }
25
26 it 'returns nil' do
27 result = release_from_first_line_resolver.resolve(:release, release_file: file_name)
28
29 expect(result).to be_nil
30 end
31 end
32
33 context 'when release file is readable' do
34 let(:file_name) { '/etc/oracle-release' }
35 let(:result) { 'Oracle Linux release 10.5 (something)' }
36
37 it 'returns release value' do
38 result = release_from_first_line_resolver.resolve(:release, release_file: file_name)
39
40 expect(result).to eq('10.5')
41 end
42 end
43
44 context 'when os-release file contains Rawhide' do
45 let(:file_name) { '/etc/os-release' }
46 let(:result) { 'a bunch of data and there is Rawhide' }
47
48 it 'returns nil' do
49 result = release_from_first_line_resolver.resolve(:release, release_file: file_name)
50
51 expect(result).to eq('Rawhide')
52 end
53 end
54
55 context 'when os-release file contains Amazon Linux' do
56 let(:file_name) { '/etc/os-release' }
57 let(:result) { 'some other data and Amazon Linux 15 and that\'s it' }
58
59 it 'returns nil' do
60 result = release_from_first_line_resolver.resolve(:release, release_file: file_name)
61
62 expect(result).to eq('15')
63 end
64 end
65
66 context 'when requesting invalid data' do
67 let(:file_name) { '/etc/oracle-release' }
68 let(:result) { nil }
69
70 it 'returns nil' do
71 result = release_from_first_line_resolver.resolve(:something, release_file: file_name)
72
73 expect(result).to be_nil
74 end
75 end
76 end
77 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Ruby do
3 before do
4 Facter::Resolvers::Ruby.invalidate_cache
5 end
6
7 describe '#resolve ruby facts' do
8 it 'detects ruby sitedir' do
9 expect(Facter::Resolvers::Ruby.resolve(:sitedir)).to eql(RbConfig::CONFIG['sitelibdir'])
10 end
11
12 it 'does not resolve the sitedir fact if sitedir does not exist' do
13 allow(RbConfig::CONFIG).to receive(:[]).with('sitedir').and_return(nil)
14 expect(Facter::Resolvers::Ruby.resolve(:sitedir)).to be(nil)
15 end
16
17 it 'detects ruby platform' do
18 expect(Facter::Resolvers::Ruby.resolve(:platform)).to eql(RUBY_PLATFORM)
19 end
20
21 it 'detects ruby version' do
22 expect(Facter::Resolvers::Ruby.resolve(:version)).to eql(RUBY_VERSION)
23 end
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::SELinux do
3 subject(:selinux_resolver) { Facter::Resolvers::SELinux }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 after do
8 selinux_resolver.invalidate_cache
9 end
10
11 before do
12 selinux_resolver.instance_variable_set(:@log, log_spy)
13 allow(Facter::Core::Execution).to receive(:execute)
14 .with('cat /proc/self/mounts', logger: log_spy)
15 .and_return(load_fixture(file).read)
16 end
17
18 context 'when no selinuxfs is mounted' do
19 let(:file) { 'proc_self_mounts' }
20
21 it 'returns enabled false' do
22 expect(selinux_resolver.resolve(:enabled)).to be(false)
23 end
24
25 it 'returns nil for config_mode' do
26 expect(selinux_resolver.resolve(:config_mode)).to be(nil)
27 end
28 end
29
30 context 'when selinuxfs is mounted' do
31 let(:file) { 'proc_self_mounts_selinux' }
32
33 context 'when config file does not exist' do
34 before do
35 allow(Facter::Util::FileHelper).to receive(:safe_readlines).with('/etc/selinux/config').and_return([])
36 end
37
38 it 'sets enabled to false' do
39 expect(selinux_resolver.resolve(:enabled)).to be(false)
40 end
41
42 it 'returns nil for config_mode' do
43 expect(selinux_resolver.resolve(:config_mode)).to be(nil)
44 end
45 end
46
47 context 'when config file exists' do
48 before do
49 allow(Facter::Util::FileHelper).to receive(:safe_readlines)
50 .with('/etc/selinux/config').and_return(load_fixture('selinux_config').readlines)
51 end
52
53 context 'when policyvers and enforce files are readable' do
54 before do
55 allow(Facter::Util::FileHelper).to receive(:safe_read)
56 .with('/sys/fs/selinux/policyvers', nil).and_return('31')
57 allow(Facter::Util::FileHelper).to receive(:safe_read)
58 .with('/sys/fs/selinux/enforce').and_return('1')
59 end
60
61 it 'returns enabled true' do
62 expect(selinux_resolver.resolve(:enabled)).to be(true)
63 end
64
65 it 'returns config_mode enabled' do
66 expect(selinux_resolver.resolve(:config_mode)).to eql('enabled')
67 end
68
69 it 'returns config_policy targeted' do
70 expect(selinux_resolver.resolve(:config_policy)).to eql('targeted')
71 end
72
73 it 'returns policy_version 31' do
74 expect(selinux_resolver.resolve(:policy_version)).to eql('31')
75 end
76
77 it 'returns enforced true' do
78 expect(selinux_resolver.resolve(:enforced)).to be(true)
79 end
80
81 it 'returns current_mode enforcing' do
82 expect(selinux_resolver.resolve(:current_mode)).to eql('enforcing')
83 end
84 end
85
86 context 'when policyvers and enforce files are not readable' do
87 before do
88 allow(Facter::Util::FileHelper).to receive(:safe_read)
89 .with('/sys/fs/selinux/policyvers', nil).and_return(nil)
90 allow(Facter::Util::FileHelper).to receive(:safe_read)
91 .with('/sys/fs/selinux/enforce').and_return('')
92 end
93
94 it 'returns no policy_version' do
95 expect(selinux_resolver.resolve(:policy_version)).to be(nil)
96 end
97
98 it 'returns enforced false' do
99 expect(selinux_resolver.resolve(:enforced)).to be(false)
100 end
101
102 it 'returns current_mode enforcing on permissive' do
103 expect(selinux_resolver.resolve(:current_mode)).to eql('permissive')
104 end
105 end
106 end
107 end
108 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Disks do
3 subject(:resolver) { Facter::Resolvers::Solaris::Disks }
4
5 before do
6 allow(File).to receive(:executable?).with('/usr/bin/kstat').and_return(status)
7 allow(Facter::Core::Execution)
8 .to receive(:execute)
9 .with('/usr/bin/kstat sderr', logger: resolver.log)
10 .and_return(output)
11 end
12
13 after do
14 resolver.invalidate_cache
15 end
16
17 context 'when kstat is present and can retrieve information' do
18 let(:value) do
19 {
20 'sd0' => {
21 product: 'VMware IDE CDR00Revision',
22 size: '0 bytes',
23 size_bytes: 0,
24 vendor: 'NECVMWar'
25 },
26 'sd1' => {
27 product: 'Virtual disk Revision',
28 size: '20.00 GiB',
29 size_bytes: 21_474_836_480,
30 vendor: 'VMware'
31 }
32 }
33 end
34 let(:status) { true }
35 let(:output) { load_fixture('kstat_sderr').read }
36
37 it 'returns disks info' do
38 expect(resolver.resolve(:disks)).to eq(value)
39 end
40 end
41
42 context 'when kstat is not present' do
43 let(:output) { '' }
44 let(:status) { false }
45
46 it 'returns nil' do
47 expect(resolver.resolve(:disks)).to be_nil
48 end
49 end
50
51 context 'when kstat is present but fails' do
52 let(:output) { '' }
53 let(:status) { true }
54
55 it 'returns nil' do
56 expect(resolver.resolve(:disks)).to be_nil
57 end
58 end
59 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::DmiSparc do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Solaris::DmiSparc }
5
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 resolver.instance_variable_set(:@log, log_spy)
10 allow(File).to receive(:executable?).with('/usr/sbin/prtdiag').and_return(true)
11 allow(Facter::Core::Execution).to receive(:execute)
12 .with('/usr/sbin/prtdiag', logger: log_spy)
13 .and_return(load_fixture('prtdiag').read)
14 allow(File).to receive(:executable?).with('/usr/sbin/sneep').and_return(true)
15 allow(Facter::Core::Execution).to receive(:execute)
16 .with('/usr/sbin/sneep', logger: log_spy).and_return('random_string')
17 end
18
19 after do
20 Facter::Resolvers::Solaris::DmiSparc.invalidate_cache
21 end
22
23 it 'returns manufacturer' do
24 expect(resolver.resolve(:manufacturer)).to eq('Oracle Corporation')
25 end
26
27 it 'returns product_name' do
28 expect(resolver.resolve(:product_name)).to eq('SPARC T7-1')
29 end
30
31 it 'returns serial_number' do
32 expect(resolver.resolve(:serial_number)).to eq('random_string')
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Dmi do
3 describe '#resolve' do
4 subject(:resolver) { Facter::Resolvers::Solaris::Dmi }
5
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 resolver.instance_variable_set(:@log, log_spy)
10 allow(File).to receive(:executable?).with('/usr/sbin/smbios').and_return(true)
11 allow(Facter::Core::Execution).to receive(:execute)
12 .with('/usr/sbin/smbios -t SMB_TYPE_BIOS', logger: log_spy)
13 .and_return(load_fixture('smbios_bios').read)
14 allow(Facter::Core::Execution).to receive(:execute)
15 .with('/usr/sbin/smbios -t SMB_TYPE_SYSTEM', logger: log_spy)
16 .and_return(load_fixture('smbios_system').read)
17 allow(Facter::Core::Execution).to receive(:execute)
18 .with('/usr/sbin/smbios -t SMB_TYPE_CHASSIS', logger: log_spy)
19 .and_return(load_fixture('smbios_chassis').read)
20 end
21
22 after do
23 Facter::Resolvers::Solaris::Dmi.invalidate_cache
24 end
25
26 it 'returns bios_release_date' do
27 expect(resolver.resolve(:bios_release_date)).to eq('12/12/2018')
28 end
29
30 it 'returns bios_vendor' do
31 expect(resolver.resolve(:bios_vendor)).to eq('Phoenix Technologies LTD')
32 end
33
34 it 'returns bios_version' do
35 expect(resolver.resolve(:bios_version)).to eq('6.00')
36 end
37
38 it 'returns chassis_asset_tag' do
39 expect(resolver.resolve(:chassis_asset_tag)).to eq('No Asset Tag')
40 end
41
42 it 'returns chassis_type' do
43 expect(resolver.resolve(:chassis_type)).to eq('0x1 (other)')
44 end
45
46 it 'returns manufacturer' do
47 expect(resolver.resolve(:manufacturer)).to eq('VMware, Inc.')
48 end
49
50 it 'returns product_name' do
51 expect(resolver.resolve(:product_name)).to eq('VMware Virtual Platform')
52 end
53
54 it 'returns serial_number' do
55 expect(resolver.resolve(:serial_number)).to eq('VMware-42 1a 46 19 2d fc 12 90-73 48 ea 8f 1a 37 cb 95')
56 end
57
58 it 'returns product_uuid' do
59 expect(resolver.resolve(:product_uuid)).to eq('421a4619-2dfc-1290-7348-ea8f1a37cb95')
60 end
61 end
62 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Filesystem do
3 subject(:filesystems_resolver) { Facter::Resolvers::Solaris::Filesystem }
4
5 let(:filesystems) { 'hsfs,nfs,pcfs,udfs,ufs' }
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 filesystems_resolver.instance_variable_set(:@log, log_spy)
10 allow(File).to receive(:executable?).with('/usr/sbin/sysdef').and_return(true)
11 allow(Facter::Core::Execution).to receive(:execute)
12 .with('/usr/sbin/sysdef', logger: log_spy)
13 .and_return(load_fixture('solaris_filesystems').read)
14 end
15
16 it 'returns filesystems' do
17 expect(filesystems_resolver.resolve(:file_systems)).to eq(filesystems)
18 end
19 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Ipaddress do
3 subject(:ipaddress) { Facter::Resolvers::Solaris::Ipaddress }
4
5 let(:log) { instance_spy(Facter::Log) }
6
7 describe '#resolve' do
8 after do
9 ipaddress.invalidate_cache
10 end
11
12 before do
13 ipaddress.instance_variable_set(:@log, log)
14 allow(Facter::Core::Execution).to receive(:execute).with('route -n get default | grep interface',
15 logger: log).and_return(route)
16 end
17
18 context 'when returns ip' do
19 let(:route) { ' interface: net0' }
20 let(:ifconfig) { load_fixture('solaris_ifconfig').read }
21
22 before do
23 allow(Facter::Core::Execution).to receive(:execute).with('ifconfig net0', logger: log).and_return(ifconfig)
24 end
25
26 it 'detects ipadress' do
27 expect(ipaddress.resolve(:ip)).to eql('10.16.115.67')
28 end
29 end
30
31 context 'when primary interface could not be retrieved' do
32 let(:route) { 'invalid output' }
33
34 it 'detects that ip is nil' do
35 expect(ipaddress.resolve(:ip)).to be(nil)
36 end
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Ldom do
3 subject(:resolver) { Facter::Resolvers::Solaris::Ldom }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution)
10 .to receive(:execute)
11 .with('/usr/sbin/virtinfo -a -p', logger: log_spy)
12 .and_return(output)
13 end
14
15 after do
16 resolver.invalidate_cache
17 end
18
19 context 'when syscall returns valid output' do
20 let(:output) { load_fixture('virtinfo').read }
21
22 it 'parses chassis_serial' do
23 expect(resolver.resolve(:chassis_serial)).to eq('AK00358110')
24 end
25
26 it 'parses control_domain' do
27 expect(resolver.resolve(:control_domain)).to eq('opdx-a0-sun2')
28 end
29
30 it 'parses domain_name' do
31 expect(resolver.resolve(:domain_name)).to eq('sol11-11')
32 end
33
34 it 'parses domain_uuid' do
35 expect(resolver.resolve(:domain_uuid)).to eq('415dfab4-c373-4ac0-9414-8bf00801fb72')
36 end
37
38 it 'parses role_control' do
39 expect(resolver.resolve(:role_control)).to eq('false')
40 end
41
42 it 'parses role_io' do
43 expect(resolver.resolve(:role_io)).to eq('false')
44 end
45
46 it 'parses role_root' do
47 expect(resolver.resolve(:role_root)).to eq('false')
48 end
49
50 it 'parses role_service' do
51 expect(resolver.resolve(:role_service)).to eq('false')
52 end
53 end
54
55 context 'when syscall returns invalid output' do
56 let(:output) { 'iNvAlId OuTpUt' }
57
58 it 'parses chassis_serial to nil' do
59 expect(resolver.resolve(:chassis_serial)).to be_nil
60 end
61
62 it 'parses control_domain to nil' do
63 expect(resolver.resolve(:control_domain)).to be_nil
64 end
65
66 it 'parses domain_name to nil' do
67 expect(resolver.resolve(:domain_name)).to be_nil
68 end
69
70 it 'parses domain_uuid to nil' do
71 expect(resolver.resolve(:domain_uuid)).to be_nil
72 end
73
74 it 'parses role_control to nil' do
75 expect(resolver.resolve(:role_control)).to be_nil
76 end
77
78 it 'parses role_io to nil' do
79 expect(resolver.resolve(:role_io)).to be_nil
80 end
81
82 it 'parses role_root to nil' do
83 expect(resolver.resolve(:role_root)).to be_nil
84 end
85
86 it 'parses role_service to nil' do
87 expect(resolver.resolve(:role_service)).to be_nil
88 end
89 end
90
91 context 'when syscall has no output' do
92 let(:output) { '' }
93
94 it 'parses chassis_serial to nil' do
95 expect(resolver.resolve(:chassis_serial)).to be_nil
96 end
97
98 it 'parses control_domain to nil' do
99 expect(resolver.resolve(:control_domain)).to be_nil
100 end
101
102 it 'parses domain_name to nil' do
103 expect(resolver.resolve(:domain_name)).to be_nil
104 end
105
106 it 'parses domain_uuid to nil' do
107 expect(resolver.resolve(:domain_uuid)).to be_nil
108 end
109
110 it 'parses role_control to nil' do
111 expect(resolver.resolve(:role_control)).to be_nil
112 end
113
114 it 'parses role_io to nil' do
115 expect(resolver.resolve(:role_io)).to be_nil
116 end
117
118 it 'parses role_root to nil' do
119 expect(resolver.resolve(:role_root)).to be_nil
120 end
121
122 it 'parses role_service to nil' do
123 expect(resolver.resolve(:role_service)).to be_nil
124 end
125 end
126 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Memory do
3 subject(:resolver) { Facter::Resolvers::Solaris::Memory }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution)
10 .to receive(:execute)
11 .with('/usr/bin/kstat -m unix -n system_pages', logger: log_spy)
12 .and_return(kstat_output)
13 allow(Facter::Core::Execution)
14 .to receive(:execute)
15 .with('pagesize', logger: log_spy)
16 .and_return(pagesize)
17 allow(Facter::Core::Execution)
18 .to receive(:execute)
19 .with('/usr/sbin/swap -l', logger: log_spy)
20 .and_return(swap_output)
21 end
22
23 after do
24 resolver.invalidate_cache
25 end
26
27 context 'when everything works fine' do
28 let(:kstat_output) { load_fixture('kstat_sys_pages').read }
29 let(:swap_output) { load_fixture('swap_l').read }
30 let(:pagesize) { '4096' }
31
32 it 'returns values for system' do
33 expect(resolver.resolve(:system)).to eq(
34 available_bytes: 2_627_383_296,
35 capacity: '59.11%',
36 total_bytes: 6_425_141_248,
37 used_bytes: 3_797_757_952
38 )
39 end
40
41 it 'returns values for swap' do
42 expect(resolver.resolve(:swap)).to eq(
43 available_bytes: 1_807_736_832,
44 capacity: '0%',
45 total_bytes: 1_807_736_832,
46 used_bytes: 0
47 )
48 end
49 end
50
51 context 'when there is no output from kstat' do
52 let(:kstat_output) { '' }
53 let(:swap_output) { load_fixture('swap_l').read }
54 let(:pagesize) { '4096' }
55
56 it 'returns nil for system' do
57 expect(resolver.resolve(:system)).to be_nil
58 end
59
60 it 'returns values for swap' do
61 expect(resolver.resolve(:swap)).to eq(
62 available_bytes: 1_807_736_832,
63 capacity: '0%',
64 total_bytes: 1_807_736_832,
65 used_bytes: 0
66 )
67 end
68 end
69
70 context 'when there is no output from swap' do
71 let(:kstat_output) { load_fixture('kstat_sys_pages').read }
72 let(:swap_output) { '' }
73 let(:pagesize) { '4096' }
74
75 it 'returns values for system' do
76 expect(resolver.resolve(:system)).to eq(
77 available_bytes: 2_627_383_296,
78 capacity: '59.11%',
79 total_bytes: 6_425_141_248,
80 used_bytes: 3_797_757_952
81 )
82 end
83
84 it 'returns nil for swap' do
85 expect(resolver.resolve(:swap)).to be_nil
86 end
87 end
88
89 context 'when pagesize does not return a valid value' do
90 let(:kstat_output) { load_fixture('kstat_sys_pages').read }
91 let(:swap_output) { load_fixture('swap_l').read }
92 let(:pagesize) { '-bash: pagesize: command not found' }
93
94 it 'returns nil for system' do
95 expect(resolver.resolve(:system)).to be_nil
96 end
97
98 it 'returns nil for swap' do
99 expect(resolver.resolve(:swap)).to eq(
100 available_bytes: 1_807_736_832,
101 capacity: '0%',
102 total_bytes: 1_807_736_832,
103 used_bytes: 0
104 )
105 end
106 end
107 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Mountpoints do
3 let(:resolver) { Facter::Resolvers::Solaris::Mountpoints }
4 let(:mounts) do
5 [
6 object_double(Sys::Filesystem::Mount,
7 mount_point: '/', mount_time: nil,
8 mount_type: 'zfs', options: 'dev=4490002', name:
9 'rpool/ROOT/solaris', dump_frequency: nil, pass_number: nil),
10 object_double(Sys::Filesystem::Mount,
11 mount_point: '/devices', mount_time: nil,
12 mount_type: 'devfs', options: 'dev=8580000', name:
13 '/devices', dump_frequency: nil, pass_number: nil),
14 object_double(Sys::Filesystem::Mount,
15 mount_point: '/proc', mount_time: nil,
16 mount_type: 'proc', options: 'dev=8600000', name:
17 'proc', dump_frequency: nil, pass_number: nil),
18 object_double(Sys::Filesystem::Mount,
19 mount_point: '/net', mount_time: nil,
20 mount_type: 'autofs', options: 'nosuid,indirect,ignore,nobrowse,dev=8900007', name:
21 '-hosts', dump_frequency: nil, pass_number: nil),
22 object_double(Sys::Filesystem::Mount,
23 mount_point: '/home', mount_time: nil,
24 mount_type: 'autofs', options: 'indirect,ignore,nobrowse,dev=8900008', name:
25 'auto_home', dump_frequency: nil, pass_number: nil),
26 object_double(Sys::Filesystem::Mount,
27 mount_point: '/home/user', mount_time: nil,
28 mount_type: 'zfs', options: 'dev=8900009', name:
29 'rpool/user', dump_frequency: nil, pass_number: nil)
30 ]
31 end
32
33 let(:mount) { mounts.first }
34
35 let(:stat) do
36 object_double('Sys::Filesystem::Stat',
37 path: '/', base_type: 'zfs', fragment_size: 512, block_size: 131_072, blocks: 20_143_706,
38 blocks_available: 11_731_043, blocks_free: 11_731_043)
39 end
40
41 let(:fact) do
42 [{ available: '5.59 GiB',
43 available_bytes: 6_006_294_016,
44 capacity: '41.76%',
45 device: 'rpool/ROOT/solaris',
46 filesystem: 'zfs',
47 options: ['dev=4490002'],
48 path: '/',
49 size: '9.61 GiB',
50 size_bytes: 10_313_577_472,
51 used: '4.01 GiB',
52 used_bytes: 4_307_283_456 }]
53 end
54
55 before do
56 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoint_stats).and_return(stat)
57
58 # mock sys/filesystem methods
59 allow(stat).to receive(:bytes_total).and_return(stat.blocks * stat.fragment_size)
60 allow(stat).to receive(:bytes_available).and_return(stat.blocks_available * stat.fragment_size)
61 allow(stat).to receive(:bytes_free).and_return(stat.blocks_free * stat.fragment_size)
62 allow(stat).to receive(:bytes_used).and_return(stat.bytes_total - stat.bytes_free)
63
64 resolver.invalidate_cache
65 end
66
67 it 'correctly builds the mountpoints fact' do
68 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return([mount])
69 result = resolver.resolve(:mountpoints)
70
71 expect(result).to eq(fact)
72 end
73
74 it 'resolves all applicable mountpoints' do
75 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return(mounts)
76
77 result = resolver.resolve(:mountpoints)
78 expect(result.map { |m| m[:path] }).to eql(%w[/ /devices /proc])
79 end
80
81 it 'does not resolve mounts of type autofs' do
82 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return(mounts)
83
84 result = resolver.resolve(:mountpoints)
85 expect(result).not_to include(hash_including(filesystem: 'autofs'))
86 end
87
88 it 'does not resolve mounts under auto_home' do
89 allow(Facter::Util::Resolvers::FilesystemHelper).to receive(:read_mountpoints).and_return(mounts)
90
91 result = resolver.resolve(:mountpoints)
92 expect(result).not_to include(hash_including(path: '/home/user'))
93 end
94
95 describe 'resolver key not found' do
96 it 'returns nil when resolver cannot find key' do
97 expect(resolver.resolve(:inexistent_key)).to be_nil
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Processors do
3 subject(:resolver) { Facter::Resolvers::Solaris::Processors }
4
5 before do
6 allow(File).to receive(:executable?).with('/usr/bin/kstat').and_return(status)
7 allow(Facter::Core::Execution)
8 .to receive(:execute)
9 .with('/usr/bin/kstat -m cpu_info', logger: resolver.log)
10 .and_return(output)
11 end
12
13 after do
14 resolver.invalidate_cache
15 end
16
17 context 'when kstat is present and can retrieve information' do
18 let(:logicalcount) { 2 }
19 let(:models) do
20 ['Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz', 'Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz']
21 end
22 let(:physical_processors) { 2 }
23 let(:threads_per_core) { 1 }
24 let(:cores_per_socket) { 1 }
25 let(:speed_expected) { 1_995_246_617 }
26 let(:output) { load_fixture('kstat_cpu').read }
27 let(:status) { true }
28
29 it 'returns number of processors' do
30 expect(resolver.resolve(:logical_count)).to eq(logicalcount)
31 end
32
33 it 'returns number of physical processors' do
34 expect(resolver.resolve(:physical_count)).to eq(physical_processors)
35 end
36
37 it 'returns list of models' do
38 expect(resolver.resolve(:models)).to eq(models)
39 end
40
41 it 'returns speed of processors' do
42 expect(resolver.resolve(:speed)).to eq(speed_expected)
43 end
44
45 it 'returns cores per socket' do
46 expect(resolver.resolve(:cores_per_socket)).to eq(cores_per_socket)
47 end
48
49 it 'returns threads per core' do
50 expect(resolver.resolve(:threads_per_core)).to eq(threads_per_core)
51 end
52 end
53
54 context 'when kstat is not present' do
55 let(:output) { '' }
56 let(:status) { false }
57
58 it 'returns nil' do
59 expect(resolver.resolve(:models)).to be_nil
60 end
61 end
62
63 context 'when kstat is present but fails' do
64 let(:output) { '' }
65 let(:status) { true }
66
67 it 'returns nil' do
68 expect(resolver.resolve(:models)).to be_nil
69 end
70 end
71 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::OsRelease do
3 subject(:solaris_release) { Facter::Resolvers::Solaris::OsRelease }
4
5 before do
6 allow(Facter::Util::FileHelper).to receive(:safe_read)
7 .with('/etc/release', nil)
8 .and_return(output)
9 end
10
11 after do
12 solaris_release.invalidate_cache
13 end
14
15 context 'when can resolve os release facts' do
16 let(:output) { load_fixture('os_release_solaris').read }
17
18 it 'returns os FULL' do
19 expect(solaris_release.resolve(:full)).to eq('10_u11')
20 end
21
22 it 'returns os MINOR' do
23 expect(solaris_release.resolve(:minor)).to eq('11')
24 end
25
26 it 'returns os MAJOR' do
27 expect(solaris_release.resolve(:major)).to eq('10')
28 end
29 end
30
31 context 'when os release ends with no minor version' do
32 let(:output) { 'Oracle Solaris 11 X86' }
33
34 it 'returns append 0 to minor version if no minor version is in file but regex pattern matches' do
35 expect(solaris_release.resolve(:full)).to eq('11.0')
36 end
37 end
38
39 context 'when trying to read os release file has exit status == 0 but file is empty' do
40 let(:output) { '' }
41
42 it 'returns result nil if file is empty' do
43 expect(solaris_release.resolve(:full)).to eq(nil)
44 end
45 end
46
47 context 'when the result from the os file release has no valid data' do
48 let(:output) { 'test test' }
49
50 it 'returns nil in case the file returns invalid data' do
51 expect(solaris_release.resolve(:full)).to eq(nil)
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::ZoneName do
3 subject(:solaris_zone) { Facter::Resolvers::Solaris::ZoneName }
4
5 let(:zone_name_output) { 'global' }
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 solaris_zone.instance_variable_set(:@log, log_spy)
10 allow(File).to receive(:executable?)
11 .with('/bin/zonename')
12 .and_return(true)
13 allow(Facter::Core::Execution).to receive(:execute)
14 .with('/bin/zonename', logger: log_spy)
15 .and_return(zone_name_output)
16 end
17
18 after do
19 solaris_zone.invalidate_cache
20 end
21
22 context 'when can resolve zone facts' do
23 it 'returns zone fact' do
24 expect(solaris_zone.resolve(:current_zone_name)).to eq(zone_name_output)
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Solaris::Zone do
3 subject(:solaris_zone) { Facter::Resolvers::Solaris::Zone }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 solaris_zone.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('/usr/sbin/zoneadm list -cp', logger: log_spy)
11 .and_return(output)
12 end
13
14 after do
15 solaris_zone.invalidate_cache
16 end
17
18 context 'when it can resolve zone facts' do
19 let(:output) { '0:global:running:/::solaris:shared:-:none:' }
20 let(:zone) do
21 [{ brand: 'solaris',
22 id: '0',
23 iptype: 'shared',
24 name: 'global',
25 uuid: '',
26 status: 'running',
27 path: '/' }]
28 end
29
30 it 'returns zone fact' do
31 expect(solaris_zone.resolve(:zone)).to eq(zone)
32 end
33 end
34
35 context 'when it can not resolve zone facts' do
36 let(:output) { '' }
37
38 it 'prints debug message' do
39 solaris_zone.resolve(:zone)
40 expect(log_spy).to have_received(:debug)
41 .with('Command /usr/sbin/zoneadm list -cp returned an empty result')
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::SpecificReleaseFile do
3 subject(:specific_release_file_resolver) { Facter::Resolvers::SpecificReleaseFile }
4
5 after do
6 specific_release_file_resolver.invalidate_cache
7 end
8
9 context 'when release file name is not given' do
10 it 'returns nil' do
11 expect(specific_release_file_resolver.resolve(:release, {})).to be_nil
12 end
13 end
14
15 context 'when release file is present' do
16 let(:result) { nil }
17
18 before do
19 allow(Facter::Util::FileHelper).to receive(:safe_read).with(options[:release_file], nil).and_return(result)
20 end
21
22 context 'when release file can not be read' do
23 let(:options) { { release_file: '/etc/alpine-release' } }
24 let(:result) { nil }
25
26 it 'returns nil' do
27 result = specific_release_file_resolver.resolve(:release, options)
28
29 expect(result).to be_nil
30 end
31 end
32
33 context 'when release file is readable' do
34 let(:options) { { release_file: '/etc/devuan_version' } }
35 let(:result) { load_fixture('os_release_devuan').read }
36
37 it 'returns release value' do
38 result = specific_release_file_resolver.resolve(:release, options)
39
40 expect(result).to eq('beowulf')
41 end
42 end
43
44 context 'when requesting invalid data' do
45 let(:options) { { release_file: '/etc/alpine-release' } }
46 let(:result) { nil }
47
48 it 'returns nil' do
49 result = specific_release_file_resolver.resolve(:something, options)
50
51 expect(result).to be_nil
52 end
53 end
54
55 context 'when release file name is not given' do
56 let(:options) do
57 {
58 release_folder: 'folder',
59 no_regex: /regex/
60 }
61 end
62
63 it 'returns nil' do
64 result = specific_release_file_resolver.resolve(:release, options)
65
66 expect(result).to be_nil
67 end
68 end
69
70 context 'when regex is not given' do
71 let(:options) do
72 {
73 release_file: 'folder',
74 no_regex: /regex/
75 }
76 end
77
78 it 'returns nil' do
79 result = specific_release_file_resolver.resolve(:release, options)
80
81 expect(result).to be_nil
82 end
83 end
84
85 context 'when release file and regex are present' do
86 let(:options) do
87 {
88 release_file: '/etc/ovs-release',
89 regex: /^RELEASE=(\d+)/
90 }
91 end
92
93 before do
94 allow(Facter::Util::FileHelper).to receive(:safe_read).with(options[:release_file], nil).and_return(result)
95 end
96
97 context 'when release file can not be read' do
98 let(:result) { nil }
99
100 it 'returns nil' do
101 result = specific_release_file_resolver.resolve(:release, options)
102
103 expect(result).to be_nil
104 end
105 end
106
107 context 'when release file is readable' do
108 let(:result) { load_fixture('os_release_mint').read }
109
110 it 'returns release value' do
111 result = specific_release_file_resolver.resolve(:release, options)[1]
112
113 expect(result).to eq('19')
114 end
115 end
116
117 context 'when requesting invalid data' do
118 let(:result) { load_fixture('os_release_mint').read }
119
120 it 'returns nil' do
121 result = specific_release_file_resolver.resolve(:something, options)
122
123 expect(result).to be_nil
124 end
125 end
126 end
127 end
128 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Ssh do
3 describe '#folders' do
4 let(:ecdsa_content) { load_fixture('ecdsa').read.strip! }
5 let(:rsa_content) { load_fixture('rsa').read.strip! }
6 let(:ed25519_content) { load_fixture('ed25519').read.strip! }
7
8 let(:ecdsa_fingerprint) do
9 Facter::Util::Resolvers::FingerPrint.new(
10 'SSHFP 3 1 fd92cf867fac0042d491eb1067e4f3cabf54039a',
11 'SSHFP 3 2 a51271a67987d7bbd685fa6d7cdd2823a30373ab01420b094480523fabff2a05'
12 )
13 end
14
15 let(:rsa_fingerprint) do
16 Facter::Util::Resolvers::FingerPrint.new(
17 'SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07',
18 'SSHFP 1 2 efaa26ff8169f5ffc372ebcad17aef886f4ccaa727169acdd0379b51c6c77e99'
19 )
20 end
21
22 let(:ed25519_fingerprint) do
23 Facter::Util::Resolvers::FingerPrint.new(
24 'SSHFP 4 1 f5780634d4e34c6ef2411ac439b517bfdce43cf1',
25 'SSHFP 4 2 c1257b3865df22f3349f9ebe19961c8a8edf5fbbe883113e728671b42d2c9723'
26 )
27 end
28
29 let(:ecdsa_result) do
30 Facter::Util::Resolvers::Ssh.new(ecdsa_fingerprint, 'ecdsa-sha2-nistp256', ecdsa_content, 'ecdsa')
31 end
32
33 let(:rsa_result) do
34 Facter::Util::Resolvers::Ssh.new(rsa_fingerprint, 'ssh-rsa', rsa_content, 'rsa')
35 end
36 let(:ed25519_result) do
37 Facter::Util::Resolvers::Ssh.new(ed25519_fingerprint, 'ssh-ed22519', ed25519_content, 'ed25519')
38 end
39
40 let(:paths) { %w[/etc/ssh /usr/local/etc/ssh /etc /usr/local/etc /etc/opt/ssh] }
41 let(:file_names) { %w[ssh_host_rsa_key.pub ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub] }
42
43 before do
44 paths.each { |path| allow(File).to receive(:directory?).with(path).and_return(false) unless path == '/etc' }
45 allow(File).to receive(:directory?).with('/etc').and_return(true)
46
47 allow(Facter::Util::FileHelper).to receive(:safe_read)
48 .with('/etc/ssh_host_ecdsa_key.pub', nil).and_return(ecdsa_content)
49 allow(Facter::Util::FileHelper).to receive(:safe_read)
50 .with('/etc/ssh_host_dsa_key.pub', nil).and_return(nil)
51 allow(Facter::Util::FileHelper).to receive(:safe_read)
52 .with('/etc/ssh_host_rsa_key.pub', nil).and_return(rsa_content)
53 allow(Facter::Util::FileHelper).to receive(:safe_read)
54 .with('/etc/ssh_host_ed25519_key.pub', nil).and_return(ed25519_content)
55
56 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
57 .with('ssh-rsa', load_fixture('rsa_key').read.strip!)
58 .and_return(rsa_result)
59 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
60 .with('ecdsa-sha2-nistp256', load_fixture('ecdsa_key').read.strip!)
61 .and_return(ecdsa_result)
62 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
63 .with('ssh-ed25519', load_fixture('ed25519_key').read.strip!)
64 .and_return(ed25519_result)
65 end
66
67 after do
68 Facter::Resolvers::Ssh.invalidate_cache
69 end
70
71 context 'when ssh_host_dsa_key.pub file is not readable' do
72 it 'returns resolved ssh' do
73 expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([rsa_result, ecdsa_result, ed25519_result])
74 end
75 end
76
77 context 'when ssh_host_ecdsa_key.pub file is also not readable' do
78 before do
79 allow(Facter::Util::FileHelper).to receive(:safe_read)
80 .with('/etc/ssh_host_ecdsa_key.pub', nil).and_return(nil)
81 end
82
83 it 'returns resolved ssh' do
84 expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([rsa_result, ed25519_result])
85 end
86 end
87
88 context 'when ssh fails to be retrieved' do
89 before do
90 paths.each { |path| allow(File).to receive(:directory?).with(path).and_return(false) }
91 end
92
93 it 'returns empty array' do
94 expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([])
95 end
96 end
97 end
98
99 describe 'invalid files' do
100 let(:paths) { %w[/etc/ssh /usr/local/etc/ssh /etc /usr/local/etc /etc/opt/ssh] }
101 let(:file_names) { %w[ssh_host_rsa_key.pub ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub] }
102
103 before do
104 paths.each { |path| allow(File).to receive(:directory?).with(path).and_return(false) unless path == '/etc' }
105 allow(File).to receive(:directory?).with('/etc').and_return(true)
106
107 allow(Facter::Util::FileHelper).to receive(:safe_read)
108 .with('/etc/ssh_host_ecdsa_key.pub', nil).and_return('invalid key')
109 allow(Facter::Util::FileHelper).to receive(:safe_read)
110 .with('/etc/ssh_host_dsa_key.pub', nil).and_return(nil)
111 allow(Facter::Util::FileHelper).to receive(:safe_read)
112 .with('/etc/ssh_host_rsa_key.pub', nil).and_return(nil)
113 allow(Facter::Util::FileHelper).to receive(:safe_read)
114 .with('/etc/ssh_host_ed25519_key.pub', nil).and_return(nil)
115 end
116
117 after do
118 Facter::Resolvers::Ssh.invalidate_cache
119 end
120
121 context 'when reading invalid ssh key' do
122 it 'returns empty array' do
123 expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([])
124 end
125 end
126 end
127 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::SuseRelease do
3 subject(:suse_release) { Facter::Resolvers::SuseRelease }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 allow(Facter::Util::FileHelper).to receive(:safe_read)
9 .with('/etc/SuSE-release', nil)
10 .and_return("openSUSE 11.1 (i586)
11 VERSION = 11.1")
12 end
13
14 it 'returns os NAME' do
15 expect(suse_release.resolve(:name)).to eq('openSUSE')
16 end
17
18 it 'returns os VERSION_ID' do
19 expect(suse_release.resolve(:version)).to eq('11.1')
20 end
21
22 it 'returns the identifier' do
23 expect(suse_release.resolve(:id)).to eq('opensuse')
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::SwVers do
3 subject(:sw_vers) { Facter::Resolvers::SwVers }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 sw_vers.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('sw_vers', logger: log_spy)
11 .and_return("ProductName:\tMac OS X\nProductVersion:\t10.14.1\nBuildVersion:\t18B75\n")
12 end
13
14 it 'returns os ProductName' do
15 expect(sw_vers.resolve(:productname)).to eq('Mac OS X')
16 end
17
18 it 'returns os ProductVersion' do
19 expect(sw_vers.resolve(:productversion)).to eq('10.14.1')
20 end
21
22 it 'returns os BuildVersion' do
23 expect(sw_vers.resolve(:buildversion)).to eq('18B75')
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Macosx::SystemProfiler do
3 subject(:system_profiler) { Facter::Resolvers::Macosx::SystemProfiler }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 let(:sp_hardware_data_type_hash) do
8 {
9 model_name: 'MacBook Pro',
10 model_identifier: 'MacBookPro11,4',
11 processor_name: 'Intel Core i7',
12 processor_speed: '2.8 GHz',
13 number_of_processors: '1',
14 total_number_of_cores: '4',
15 l2_cache_per_core: '256 KB',
16 l3_cache: '6 MB',
17 'hyper-threading_technology': 'Enabled',
18 memory: '16 GB',
19 boot_rom_version: '1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)',
20 smc_version_system: '2.29f24',
21 serial_number_system: '123456789AAA',
22 hardware_uuid: '12345678-1111-2222-AAAA-AABBCCDDEEFF',
23 activation_lock_status: 'Disabled'
24 }
25 end
26
27 let(:sp_software_data_type) do
28 {
29 system_version: 'macOS 10.15.2 (19C57)',
30 kernel_version: 'Darwin 19.2.0',
31 boot_volume: 'Macintosh HD',
32 boot_mode: 'Normal',
33 computer_name: 'Test1’s MacBook Pro',
34 user_name: 'Test1 Test2 (test1.test2)',
35 secure_virtual_memory: 'Enabled',
36 system_integrity_protection: 'Enabled',
37 time_since_boot: '3:28'
38 }
39 end
40
41 let(:sp_ethernet_data_type) do
42 {
43 type: 'Ethernet Controller',
44 bus: 'PCI',
45 vendor_id: '0x8086',
46 device_id: '0x100f',
47 subsystem_vendor_id: '0x1ab8',
48 subsystem_id: '0x0400',
49 revision_id: '0x0000',
50 bsd_name: 'en0',
51 kext_name: 'AppleIntel8254XEthernet.kext',
52 location: '/System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/AppleIntel8254XEthernet.kext',
53 version: '3.1.5'
54 }
55 end
56
57 before do
58 system_profiler.instance_variable_set(:@log, log_spy)
59 end
60
61 context 'when information is obtain from SPHardwareDataType' do
62 before do
63 allow(Facter::Util::Macosx::SystemProfileExecutor)
64 .to receive(:execute)
65 .with('SPHardwareDataType')
66 .and_return(sp_hardware_data_type_hash)
67 end
68
69 it 'returns boot_rom_version' do
70 expect(system_profiler.resolve(:boot_rom_version)).to eq('1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)')
71 end
72
73 it 'returns cores' do
74 expect(system_profiler.resolve(:total_number_of_cores)).to eq('4')
75 end
76
77 it 'returns hardware_uuid' do
78 expect(system_profiler.resolve(:hardware_uuid)).to eq('12345678-1111-2222-AAAA-AABBCCDDEEFF')
79 end
80
81 it 'returns l2_cache_per_core' do
82 expect(system_profiler.resolve(:l2_cache_per_core)).to eq('256 KB')
83 end
84
85 it 'returns l3_cache' do
86 expect(system_profiler.resolve(:l3_cache)).to eq('6 MB')
87 end
88
89 it 'returns memory' do
90 expect(system_profiler.resolve(:memory)).to eq('16 GB')
91 end
92
93 it 'returns model_identifier' do
94 expect(system_profiler.resolve(:model_identifier)).to eq('MacBookPro11,4')
95 end
96
97 it 'returns model_name' do
98 expect(system_profiler.resolve(:model_name)).to eq('MacBook Pro')
99 end
100
101 it 'returns processor_name' do
102 expect(system_profiler.resolve(:processor_name)).to eq('Intel Core i7')
103 end
104
105 it 'returns processor_speed' do
106 expect(system_profiler.resolve(:processor_speed)).to eq('2.8 GHz')
107 end
108
109 it 'returns number_of_processors' do
110 expect(system_profiler.resolve(:number_of_processors)).to eq('1')
111 end
112
113 it 'returns serial_number' do
114 expect(system_profiler.resolve(:serial_number_system)).to eq('123456789AAA')
115 end
116
117 it 'returns smc_version' do
118 expect(system_profiler.resolve(:smc_version_system)).to eq('2.29f24')
119 end
120 end
121
122 context 'when information is obtained from SPSoftwareDataType' do
123 before do
124 allow(Facter::Util::Macosx::SystemProfileExecutor)
125 .to receive(:execute)
126 .with('SPSoftwareDataType')
127 .and_return(sp_software_data_type)
128 end
129
130 it 'returns boot_mode' do
131 expect(system_profiler.resolve(:boot_mode)).to eq('Normal')
132 end
133
134 it 'returns boot_volume' do
135 expect(system_profiler.resolve(:boot_volume)).to eq('Macintosh HD')
136 end
137
138 it 'returns computer_name' do
139 expect(system_profiler.resolve(:computer_name)).to eq('Test1’s MacBook Pro')
140 end
141
142 it 'returns kernel_version' do
143 expect(system_profiler.resolve(:kernel_version)).to eq('Darwin 19.2.0')
144 end
145
146 it 'returns secure_virtual_memory' do
147 expect(system_profiler.resolve(:secure_virtual_memory)).to eq('Enabled')
148 end
149
150 it 'returns system_version' do
151 expect(system_profiler.resolve(:system_version)).to eq('macOS 10.15.2 (19C57)')
152 end
153
154 it 'returns time_since_boot' do
155 expect(system_profiler.resolve(:time_since_boot)).to eq('3:28')
156 end
157
158 it 'returns username' do
159 expect(system_profiler.resolve(:user_name)).to eq('Test1 Test2 (test1.test2)')
160 end
161 end
162
163 context 'when information is obtained from SPEthernetDataType' do
164 before do
165 allow(Facter::Util::Macosx::SystemProfileExecutor)
166 .to receive(:execute)
167 .with('SPEthernetDataType')
168 .and_return(sp_ethernet_data_type)
169 end
170
171 it 'returns type' do
172 expect(system_profiler.resolve(:type)).to eq('Ethernet Controller')
173 end
174
175 it 'returns bus' do
176 expect(system_profiler.resolve(:bus)).to eq('PCI')
177 end
178
179 it 'returns vendor_id' do
180 expect(system_profiler.resolve(:vendor_id)).to eq('0x8086')
181 end
182
183 it 'returns device_id' do
184 expect(system_profiler.resolve(:device_id)).to eq('0x100f')
185 end
186
187 it 'returns subsystem_vendor_id' do
188 expect(system_profiler.resolve(:subsystem_vendor_id)).to eq('0x1ab8')
189 end
190
191 it 'returns subsystem_id' do
192 expect(system_profiler.resolve(:subsystem_id)).to eq('0x0400')
193 end
194
195 it 'returns revision_id' do
196 expect(system_profiler.resolve(:revision_id)).to eq('0x0000')
197 end
198
199 it 'returns bsd_name' do
200 expect(system_profiler.resolve(:bsd_name)).to eq('en0')
201 end
202
203 it 'returns kext_name' do
204 expect(system_profiler.resolve(:kext_name)).to eq('AppleIntel8254XEthernet.kext')
205 end
206
207 it 'returns location' do
208 expect(system_profiler.resolve(:location))
209 .to eq('/System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/AppleIntel8254XEthernet.kext')
210 end
211
212 it 'returns version' do
213 expect(system_profiler.resolve(:version)).to eq('3.1.5')
214 end
215 end
216 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Timezone do
3 describe '#resolve timezone' do
4 it 'detects timezone' do
5 expect(Facter::Resolvers::Timezone.resolve(:timezone)).to eql(Time.now.localtime.strftime('%Z'))
6 end
7 end
8 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Uname do
3 subject(:uname_resolver) { Facter::Resolvers::Uname }
4
5 let(:log_spy) { Facter::Log }
6
7 before do
8 uname_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('uname -m &&
11 uname -n &&
12 uname -p &&
13 uname -r &&
14 uname -s &&
15 uname -v', logger: log_spy)
16 .and_return('x86_64
17 wifi.tsr.corp.puppet.net
18 i386
19 18.2.0
20 Darwin
21 Darwin Kernel Version 18.2.0: Fri Oct 5 19:41:49 PDT 2018; root:xnu-4903.221.2~2/RELEASE_X86_64')
22 end
23
24 it 'returns machine' do
25 expect(uname_resolver.resolve(:machine)).to eq('x86_64')
26 end
27
28 it 'returns nodename' do
29 expect(uname_resolver.resolve(:nodename)).to eq('wifi.tsr.corp.puppet.net')
30 end
31
32 it 'returns processor' do
33 expect(uname_resolver.resolve(:processor)).to eq('i386')
34 end
35
36 it 'returns kernelrelease' do
37 expect(uname_resolver.resolve(:kernelrelease)).to eq('18.2.0')
38 end
39
40 it 'returns kernelname' do
41 expect(uname_resolver.resolve(:kernelname)).to eq('Darwin')
42 end
43
44 it 'returns kernelversion' do
45 expect(uname_resolver.resolve(:kernelversion)).to include('root:xnu-4903.221.2~2/RELEASE_X86_64')
46 end
47 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Uptime do
3 after { Facter::Resolvers::Uptime.invalidate_cache }
4
5 describe 'all uptime stats' do
6 before { allow(Facter::Util::Facts::UptimeParser).to receive(:uptime_seconds_unix).and_return(86_500) }
7
8 it 'returns uptime in days' do
9 expect(Facter::Resolvers::Uptime.resolve(:days)).to eq(1)
10 end
11
12 it 'returns uptime in hours' do
13 expect(Facter::Resolvers::Uptime.resolve(:hours)).to eq(24)
14 end
15
16 it 'returns uptime in seconds' do
17 expect(Facter::Resolvers::Uptime.resolve(:seconds)).to eq(86_500)
18 end
19
20 context 'when we do not input seconds' do
21 it 'returns "uknown" uptime value' do
22 allow(Facter::Util::Facts::UptimeParser).to receive(:uptime_seconds_unix).and_return(nil)
23
24 expect(Facter::Resolvers::Uptime.resolve(:uptime)).to eq('unknown')
25 end
26 end
27 end
28
29 describe 'uptime text description' do
30 context 'when the parsed seconds are less than a day' do
31 it 'returns the hours as a text' do
32 allow(Facter::Util::Facts::UptimeParser).to receive(:uptime_seconds_unix).and_return(21_660)
33
34 expect(Facter::Resolvers::Uptime.resolve(:uptime)).to eq('6:01 hours')
35 end
36 end
37
38 context 'when the parsed seconds are between 1 and 2 days' do
39 it 'returns "1 day" as a text' do
40 allow(Facter::Util::Facts::UptimeParser).to receive(:uptime_seconds_unix).and_return(86_500)
41
42 expect(Facter::Resolvers::Uptime.resolve(:uptime)).to eq('1 day')
43 end
44 end
45
46 context 'when the parsed seconds are more than 2 days' do
47 it 'returns the number of days as a text' do
48 allow(Facter::Util::Facts::UptimeParser).to receive(:uptime_seconds_unix).and_return(186_500)
49
50 expect(Facter::Resolvers::Uptime.resolve(:uptime)).to eq('2 days')
51 end
52 end
53 end
54 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::VirtWhat do
3 subject(:virt_what_resolver) { Facter::Resolvers::VirtWhat }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 after do
8 virt_what_resolver.invalidate_cache
9 end
10
11 before do
12 virt_what_resolver.instance_variable_set(:@log, log_spy)
13 allow(Facter::Core::Execution).to receive(:execute).with('virt-what', logger: log_spy).and_return(content)
14 end
15
16 context 'when virt-what fails' do
17 let(:content) { '' }
18
19 it 'returns nil' do
20 expect(virt_what_resolver.resolve(:vm)).to be_nil
21 end
22 end
23
24 context 'when virt-what detects xen hypervisor' do
25 let(:content) { load_fixture('virt-what-content').read }
26 let(:result) { 'xenhvm' }
27
28 it 'returns virtual fact' do
29 expect(virt_what_resolver.resolve(:vm)).to eq(result)
30 end
31 end
32
33 context 'when virt-what detects vserver' do
34 let(:content) { 'linux_vserver' }
35 let(:result) { 'vserver_host' }
36 let(:proc_status_content) { load_fixture('proc_self_status').readlines }
37
38 before do
39 allow(Facter::Util::FileHelper).to receive(:safe_readlines).with('/proc/self/status', nil)
40 .and_return(proc_status_content)
41 end
42
43 it 'returns virtual fact' do
44 expect(virt_what_resolver.resolve(:vm)).to eq(result)
45 end
46 end
47
48 context 'when virt-what detects kvm' do
49 let(:content) { 'kvm' }
50 let(:result) { 'kvm' }
51
52 it 'returns virtual fact' do
53 expect(virt_what_resolver.resolve(:vm)).to eq(result)
54 end
55 end
56
57 context 'when virt-what detects kvm in RHEL hypervisors redhat first' do
58 let(:content) { "redhat\nkvm" }
59 let(:result) { 'kvm' }
60
61 it 'returns virtual fact' do
62 expect(virt_what_resolver.resolve(:vm)).to eq(result)
63 end
64 end
65
66 context 'when virt-what detects kvm in RHEL hypervisors redhat last' do
67 let(:content) { "kvm\nredhat" }
68 let(:result) { 'kvm' }
69
70 it 'returns virtual fact' do
71 expect(virt_what_resolver.resolve(:vm)).to eq(result)
72 end
73 end
74 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Vmware do
3 subject(:vmware_resolver) { Facter::Resolvers::Vmware }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 vmware_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute).with('vmware -v', logger: log_spy).and_return(output)
10 end
11
12 after do
13 vmware_resolver.invalidate_cache
14 end
15
16 context 'when vmware command exists' do
17 context 'when it returns invalid format' do
18 let(:output) { 'vmware fusion 7.1' }
19
20 it 'returns nil' do
21 expect(vmware_resolver.resolve(:vm)).to be_nil
22 end
23 end
24
25 context 'when it returns valid format' do
26 let(:output) { 'VmWare Fusion' }
27 let(:result) { 'vmware_fusion' }
28
29 it 'returns nil' do
30 expect(vmware_resolver.resolve(:vm)).to eq(result)
31 end
32 end
33 end
34
35 context 'when vmware command do not exists' do
36 let(:output) { '' }
37
38 it 'returns nil' do
39 expect(vmware_resolver.resolve(:vm)).to be_nil
40 end
41 end
42 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::AioAgentVersion do
3 describe '#resolve' do
4 subject(:aio_agent_resolver) { Facter::Resolvers::Windows::AioAgentVersion }
5
6 let(:puppet_version) { '7.0.1' }
7 let(:reg) { instance_spy(Win32::Registry::HKEY_LOCAL_MACHINE) }
8 let(:log) { instance_spy(Facter::Log) }
9
10 before do
11 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('SOFTWARE\\Puppet Labs\\Puppet').and_yield(reg)
12 allow(Facter::Util::FileHelper)
13 .to receive(:safe_read)
14 .with('path_to_puppet/VERSION', nil)
15 .and_return(puppet_version)
16 allow(Facter::Resolvers::BaseResolver).to receive(:log).and_return(log)
17 end
18
19 after do
20 Facter::Resolvers::Windows::AioAgentVersion.invalidate_cache
21 end
22
23 context 'when the registry path is incorrect' do
24 before do
25 allow(Win32::Registry::HKEY_LOCAL_MACHINE)
26 .to receive(:open)
27 .with('SOFTWARE\\Puppet Labs\\Puppet')
28 .and_raise(Win32::Registry::Error)
29 end
30
31 it 'logs debug message specific to none existent path' do
32 aio_agent_resolver.resolve(:aio_agent_version)
33
34 expect(log).to have_received(:debug).with('The registry path SOFTWARE\Puppet Labs\Puppet does not exist')
35 end
36 end
37
38 context 'when windows is 64 bit edition' do
39 before do
40 allow(reg).to receive(:read).with('RememberedInstallDir64').and_return([1, 'path_to_puppet'])
41 end
42
43 it 'returns path from registry specific to 64 bit windows' do
44 expect(aio_agent_resolver.resolve(:aio_agent_version)).to eq(puppet_version)
45 end
46
47 context 'when RememberedInstallDir64 does not contain a value' do
48 before do
49 allow(reg).to receive(:read).with('RememberedInstallDir64').and_return([1, ''])
50 end
51
52 it 'calls file helper with empty path' do
53 expect(aio_agent_resolver.resolve(:aio_agent_version)).to be(nil)
54 end
55 end
56
57 context 'when AIO puppet agent is a dev build' do
58 before do
59 allow(Facter::Util::FileHelper)
60 .to receive(:safe_read)
61 .with('path_to_puppet/VERSION', nil)
62 .and_return('7.0.1.8.g12345678')
63 end
64
65 it 'only shows the first 4 groups of digits' do
66 expect(aio_agent_resolver.resolve(:aio_agent_version)).to eql('7.0.1.8')
67 end
68 end
69 end
70
71 context 'when windows is 32 bit edition' do
72 before do
73 allow(reg).to receive(:read).with('RememberedInstallDir64').and_raise(Win32::Registry::Error)
74 allow(reg).to receive(:read).with('RememberedInstallDir').and_return([1, 'path_to_puppet'])
75 end
76
77 it 'logs debug message for 64 bit register' do
78 aio_agent_resolver.resolve(:aio_agent_version)
79
80 expect(log).to have_received(:debug).with('Could not read Puppet AIO path from 64 bit registry')
81 end
82
83 it 'returns path from registry specific to 32 bit windows' do
84 expect(aio_agent_resolver.resolve(:aio_agent_version)).to eq(puppet_version)
85 end
86
87 context 'when there is no registry entry for 32 bit version' do
88 before do
89 allow(reg).to receive(:read).with('RememberedInstallDir').and_raise(Win32::Registry::Error)
90 end
91
92 it 'logs debug error for 32 bit registry' do
93 aio_agent_resolver.resolve(:aio_agent_version)
94
95 expect(log).to have_received(:debug).with('Could not read Puppet AIO path from 32 bit registry')
96 end
97 end
98 end
99 end
100 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::DMIBios do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 win = double('Facter::Util::Windows::Win32Ole')
7
8 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
9 allow(win).to receive(:return_first).with('SELECT Manufacturer,SerialNumber from Win32_BIOS').and_return(comp)
10 Facter::Resolvers::DMIBios.instance_variable_set(:@log, logger)
11 end
12
13 after do
14 Facter::Resolvers::DMIBios.invalidate_cache
15 end
16
17 describe '#resolve' do
18 let(:comp) do
19 double('WIN32OLE', Manufacturer: 'VMware, Inc.',
20 SerialNumber: 'VMware-42 1a 38 c5 9d 35 5b f1-7a 62 4b 6e cb a0 79 de')
21 end
22
23 it 'detects virtual machine manufacturer' do
24 expect(Facter::Resolvers::DMIBios.resolve(:manufacturer)).to eql('VMware, Inc.')
25 end
26
27 it 'detects virtual machine serial number' do
28 expect(Facter::Resolvers::DMIBios.resolve(:serial_number))
29 .to eql('VMware-42 1a 38 c5 9d 35 5b f1-7a 62 4b 6e cb a0 79 de')
30 end
31 end
32
33 describe '#resolve when WMI query returns nil' do
34 let(:comp) {}
35
36 it 'logs debug message and serial_number is nil' do
37 allow(logger).to receive(:debug)
38 .with('WMI query returned no results for Win32_BIOS with values Manufacturer and SerialNumber.')
39 expect(Facter::Resolvers::DMIBios.resolve(:serial_number)).to be(nil)
40 end
41
42 it 'detects manufacturer as nil' do
43 expect(Facter::Resolvers::DMIBios.resolve(:manufacturer)).to be(nil)
44 end
45 end
46
47 describe '#resolve when WMI query returns nil for Manufacturer and SerialNumber' do
48 let(:comp) do
49 double('WIN32OLE', Manufacturer: nil,
50 SerialNumber: nil)
51 end
52
53 it 'detects SerialNumber as nil' do
54 expect(Facter::Resolvers::DMIBios.resolve(:serial_number)).to be(nil)
55 end
56
57 it 'detects manufacturer as nil' do
58 expect(Facter::Resolvers::DMIBios.resolve(:manufacturer)).to be(nil)
59 end
60 end
61 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::DMIComputerSystem do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 win = double('Facter::Util::Windows::Win32Ole')
7
8 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
9 allow(win).to receive(:return_first).with('SELECT Name,UUID FROM Win32_ComputerSystemProduct').and_return(comp)
10
11 Facter::Resolvers::DMIComputerSystem.instance_variable_set(:@log, logger)
12 end
13
14 after do
15 Facter::Resolvers::DMIComputerSystem.invalidate_cache
16 end
17
18 describe '#resolve' do
19 let(:comp) { double('WIN32OLE', Name: 'VMware7,1', UUID: 'C5381A42-359D-F15B-7A62-4B6ECBA079DE') }
20
21 it 'detects virtual machine name' do
22 expect(Facter::Resolvers::DMIComputerSystem.resolve(:name)).to eql('VMware7,1')
23 end
24
25 it 'detects uuid of virtual machine' do
26 expect(Facter::Resolvers::DMIComputerSystem.resolve(:uuid)).to eql('C5381A42-359D-F15B-7A62-4B6ECBA079DE')
27 end
28 end
29
30 describe '#resolve when WMI query returns nil' do
31 let(:comp) {}
32
33 it 'logs debug message and name is nil' do
34 allow(logger).to receive(:debug)
35 .with('WMI query returned no results for Win32_ComputerSystemProduct with values Name and UUID.')
36 expect(Facter::Resolvers::DMIComputerSystem.resolve(:name)).to be(nil)
37 end
38
39 it 'detects uuid as nil' do
40 expect(Facter::Resolvers::DMIComputerSystem.resolve(:uuid)).to be(nil)
41 end
42 end
43
44 describe '#resolve when WMI query returns nil for Name and UUID' do
45 let(:comp) { double('WIN32OLE', Name: nil, UUID: nil) }
46
47 it 'detects name as nil' do
48 expect(Facter::Resolvers::DMIComputerSystem.resolve(:name)).to be(nil)
49 end
50
51 it 'detects uuid as nil' do
52 expect(Facter::Resolvers::DMIComputerSystem.resolve(:uuid)).to be(nil)
53 end
54 end
55 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Fips do
3 describe '#resolve' do
4 let(:reg) { instance_double('Win32::Registry::HKEY_LOCAL_MACHINE') }
5
6 before do
7 allow(reg).to receive(:close)
8 allow(reg).to receive(:[]).with('Enabled').and_return(is_fips)
9 allow(reg).to receive(:any?).and_yield('Enabled', '1')
10 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open)
11 .with('System\\CurrentControlSet\\Control\\Lsa\\FipsAlgorithmPolicy').and_return(reg)
12 end
13
14 after do
15 Facter::Resolvers::Windows::Fips.invalidate_cache
16 end
17
18 context 'when field exists in registry' do
19 let(:is_fips) { 255 }
20
21 it 'detects that fips is enabled' do
22 expect(Facter::Resolvers::Windows::Fips.resolve(:fips_enabled)).to be(true)
23 end
24 end
25
26 context "when field doesn't exists in registry" do
27 let(:is_fips) { 0 }
28
29 it "detects that fips isn't enabled" do
30 expect(Facter::Resolvers::Windows::Fips.resolve(:fips_enabled)).to be(false)
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::HardwareArchitecture do
3 let(:sys_info_ptr) { double('FFI::MemoryPointer') }
4 let(:sys_info) { double(SystemInfo) }
5 let(:dummyunion) { double(DummyUnionName) }
6 let(:dummystruct) { double(DummyStructName) }
7
8 before do
9 allow(FFI::MemoryPointer).to receive(:new).with(SystemInfo.size).and_return(sys_info_ptr)
10 allow(HardwareFFI).to receive(:GetNativeSystemInfo).with(sys_info_ptr)
11 allow(SystemInfo).to receive(:new).with(sys_info_ptr).and_return(sys_info)
12 allow(sys_info).to receive(:[]).with(:dummyunionname).and_return(dummyunion)
13 allow(dummyunion).to receive(:[]).with(:dummystructname).and_return(dummystruct)
14 allow(dummystruct).to receive(:[]).with(:wProcessorArchitecture).and_return(arch)
15 end
16
17 after do
18 Facter::Resolvers::HardwareArchitecture.invalidate_cache
19 end
20
21 describe '#resolve when processor is amd64' do
22 let(:arch) { HardwareFFI::PROCESSOR_ARCHITECTURE_AMD64 }
23
24 it 'detects hardware' do
25 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql('x86_64')
26 end
27
28 it 'detects architecture' do
29 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('x64')
30 end
31 end
32
33 describe '#resolve when processor is arm' do
34 let(:arch) { HardwareFFI::PROCESSOR_ARCHITECTURE_ARM }
35
36 it 'detects hardware' do
37 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql('arm')
38 end
39
40 it 'detects architecture' do
41 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('arm')
42 end
43 end
44
45 describe '#resolve when processor is ia64' do
46 let(:arch) { HardwareFFI::PROCESSOR_ARCHITECTURE_IA64 }
47
48 it 'detects hardware' do
49 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql('ia64')
50 end
51
52 it 'detects architecture' do
53 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('ia64')
54 end
55 end
56
57 describe '#resolve when processor is intel and level below 5' do
58 before do
59 allow(sys_info).to receive(:[]).with(:wProcessorLevel).and_return(level)
60 end
61
62 let(:arch) { HardwareFFI::PROCESSOR_ARCHITECTURE_INTEL }
63 let(:level) { 4 }
64
65 it 'detects hardware' do
66 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql("i#{level}86")
67 end
68
69 it 'detects architecture' do
70 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('x86')
71 end
72 end
73
74 describe '#resolve when processor is intel and level above 5' do
75 before do
76 allow(sys_info).to receive(:[]).with(:wProcessorLevel).and_return(level)
77 end
78
79 let(:arch) { HardwareFFI::PROCESSOR_ARCHITECTURE_INTEL }
80 let(:level) { 8 }
81
82 it 'detects hardware' do
83 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql('i686')
84 end
85
86 it 'detects architecture' do
87 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('x86')
88 end
89 end
90
91 describe '#resolve when processor unknown' do
92 let(:arch) { nil }
93
94 it 'detects hardware' do
95 expect(Facter::Resolvers::HardwareArchitecture.resolve(:hardware)).to eql('unknown')
96 end
97
98 it 'detects architecture' do
99 expect(Facter::Resolvers::HardwareArchitecture.resolve(:architecture)).to eql('unknown')
100 end
101 end
102 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Identity do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 size_ptr = double('FFI::MemoryPointer', read_uint32: 1)
7 name_ptr = double('FFI::MemoryPointer', read_wide_string_with_length: user_name)
8
9 allow(FFI::MemoryPointer).to receive(:new).with(:win32_ulong, 1).and_return(size_ptr)
10 allow(IdentityFFI).to receive(:GetUserNameExW).with(2, FFI::Pointer::NULL, size_ptr)
11 FFI.define_errno(error_number)
12 allow(FFI::MemoryPointer).to receive(:new).with(:wchar, size_ptr.read_uint32).and_return(name_ptr)
13 allow(IdentityFFI).to receive(:GetUserNameExW).with(2, name_ptr, size_ptr).and_return(error_geting_user?)
14 allow(IdentityFFI).to receive(:IsUserAnAdmin).and_return(admin?)
15
16 Facter::Resolvers::Identity.instance_variable_set(:@log, logger)
17 end
18
19 after do
20 Facter::Resolvers::Identity.invalidate_cache
21 Facter::Resolvers::Identity.instance_variable_set(:@log, nil)
22 end
23
24 describe '#resolve when user is administrator' do
25 let(:user_name) { 'MG93C9IN9WKOITF\Administrator' }
26 let(:error_number) { FFI::ERROR_MORE_DATA }
27 let(:error_geting_user?) { 1 }
28 let(:admin?) { 1 }
29
30 it 'detects user' do
31 expect(Facter::Resolvers::Identity.resolve(:user)).to eql('MG93C9IN9WKOITF\Administrator')
32 end
33
34 it 'detects that user is administrator' do
35 expect(Facter::Resolvers::Identity.resolve(:privileged)).to be(true)
36 end
37 end
38
39 describe '#resolve when user is not administrator' do
40 let(:user_name) { 'MG93C9IN9WKOITF\User' }
41 let(:error_number) { FFI::ERROR_MORE_DATA }
42 let(:error_geting_user?) { 1 }
43 let(:admin?) { 0 }
44
45 it 'detects user' do
46 expect(Facter::Resolvers::Identity.resolve(:user)).to eql('MG93C9IN9WKOITF\User')
47 end
48
49 it 'detects that user is not administrator' do
50 expect(Facter::Resolvers::Identity.resolve(:privileged)).to be(false)
51 end
52 end
53
54 describe '#resolve when' do
55 let(:user_name) { 'MG93C9IN9WKOITF\User' }
56 let(:error_number) { FFI::ERROR_MORE_DATA }
57 let(:error_geting_user?) { 1 }
58 let(:admin?) { nil }
59
60 it 'detects user' do
61 expect(Facter::Resolvers::Identity.resolve(:user)).to eql('MG93C9IN9WKOITF\User')
62 end
63
64 it 'could not determine if user is admin' do
65 expect(Facter::Resolvers::Identity.resolve(:privileged)).to be(nil)
66 end
67 end
68
69 describe '#resolve when error code is different than ERROR_MORE_DATA' do
70 let(:user_name) { '' }
71 let(:error_number) { nil }
72 let(:error_geting_user?) { 1 }
73 let(:admin?) { 0 }
74
75 it 'logs debug message when trying to resolve user' do
76 allow(logger).to receive(:debug)
77 .with("failure resolving identity facts: #{error_number}")
78 expect(Facter::Resolvers::Identity.resolve(:user)).to be(nil)
79 end
80
81 it 'logs debug message when trying to find if user is privileged' do
82 allow(logger).to receive(:debug)
83 .with("failure resolving identity facts: #{error_number}")
84 expect(Facter::Resolvers::Identity.resolve(:privileged)).to be(nil)
85 end
86 end
87
88 describe '#resolve when there is an error getting user name' do
89 let(:user_name) { '' }
90 let(:error_number) { FFI::ERROR_MORE_DATA }
91 let(:error_geting_user?) { 0 }
92 let(:admin?) { 0 }
93
94 it 'logs debug message when trying to resolve user' do
95 allow(logger).to receive(:debug)
96 .with("failure resolving identity facts: #{error_number}")
97 expect(Facter::Resolvers::Identity.resolve(:user)).to be(nil)
98 end
99
100 it 'logs debug message when trying to find if user is privileged' do
101 allow(logger).to receive(:debug)
102 .with("failure resolving identity facts: #{error_number}")
103 expect(Facter::Resolvers::Identity.resolve(:privileged)).to be(nil)
104 end
105 end
106 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Kernel do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 ver_ptr = double('FFI::MemoryPointer')
7 ver = double('OsVersionInfoEx', size: nil)
8
9 allow(FFI::MemoryPointer).to receive(:new).with(OsVersionInfoEx.size).and_return(ver_ptr)
10 allow(OsVersionInfoEx).to receive(:new).with(ver_ptr).and_return(ver)
11
12 allow(ver).to receive(:[]=).with(:dwOSVersionInfoSize, ver.size)
13 allow(KernelFFI).to receive(:RtlGetVersion).with(ver_ptr).and_return(status)
14
15 allow(ver).to receive(:[]).with(:dwMajorVersion).and_return(maj)
16 allow(ver).to receive(:[]).with(:dwMinorVersion).and_return(min)
17 allow(ver).to receive(:[]).with(:dwBuildNumber).and_return(buildnr)
18
19 Facter::Resolvers::Kernel.instance_variable_set(:@log, logger)
20 end
21
22 after do
23 Facter::Resolvers::Kernel.invalidate_cache
24 end
25
26 describe '#resolve' do
27 let(:status) { KernelFFI::STATUS_SUCCESS }
28 let(:maj) { 10 }
29 let(:min) { 0 }
30 let(:buildnr) { 123 }
31
32 it 'detects kernel version' do
33 expect(Facter::Resolvers::Kernel.resolve(:kernelversion)).to eql('10.0.123')
34 end
35
36 it 'detects kernel major version' do
37 expect(Facter::Resolvers::Kernel.resolve(:kernelmajorversion)).to eql('10.0')
38 end
39
40 it 'detects kernel name' do
41 expect(Facter::Resolvers::Kernel.resolve(:kernel)).to eql('windows')
42 end
43 end
44
45 describe '#resolve when RtlGetVersion function fails to get os version information' do
46 let(:status) { 10 }
47 let(:maj) { 10 }
48 let(:min) { 0 }
49 let(:buildnr) { 123 }
50
51 it 'logs debug message and kernel version nil' do
52 allow(logger).to receive(:debug).with('Calling Windows RtlGetVersion failed')
53 expect(Facter::Resolvers::Kernel.resolve(:kernelversion)).to be(nil)
54 end
55
56 it 'detects that kernel major version is nil' do
57 expect(Facter::Resolvers::Kernel.resolve(:kernelmajorversion)).to be(nil)
58 end
59
60 it 'detects that kernel name is nil' do
61 expect(Facter::Resolvers::Kernel.resolve(:kernel)).to be(nil)
62 end
63 end
64 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Memory do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 state_ptr = double('FFI::MemoryPointer', size: nil)
7 state = double('PerformanceInformation', size: nil)
8
9 allow(FFI::MemoryPointer).to receive(:new).with(PerformanceInformation.size).and_return(state_ptr)
10 allow(MemoryFFI).to receive(:GetPerformanceInfo).with(state_ptr, state_ptr.size).and_return(status)
11
12 allow(PerformanceInformation).to receive(:new).with(state_ptr).and_return(state)
13
14 allow(state).to receive(:[]).with(:PhysicalTotal).and_return(total)
15 allow(state).to receive(:[]).with(:PageSize).and_return(page_size)
16 allow(state).to receive(:[]).with(:PhysicalAvailable).and_return(available)
17
18 Facter::Resolvers::Memory.instance_variable_set(:@log, logger)
19 end
20
21 after do
22 Facter::Resolvers::Memory.invalidate_cache
23 end
24
25 describe '#resolve' do
26 let(:status) { 1 }
27 let(:total) { 1_048_313 }
28 let(:page_size) { 4096 }
29 let(:available) { 824_031 }
30
31 it 'detects total bytes' do
32 expect(Facter::Resolvers::Memory.resolve(:total_bytes)).to eq(4_293_890_048)
33 end
34
35 it 'detects available bytes' do
36 expect(Facter::Resolvers::Memory.resolve(:available_bytes)).to eq(3_375_230_976)
37 end
38
39 it 'determines used bytes' do
40 expect(Facter::Resolvers::Memory.resolve(:used_bytes)).to eq(918_659_072)
41 end
42
43 it 'determines capacity' do
44 expect(Facter::Resolvers::Memory.resolve(:capacity)).to eql('21.39%')
45 end
46 end
47
48 describe '#resolve when total bytes is 0' do
49 let(:status) { 1 }
50 let(:total) { 0 }
51 let(:page_size) { 4096 }
52 let(:available) { 23 }
53
54 it 'detects total_bytes as nil' do
55 allow(logger).to receive(:debug)
56 .with('Available or Total bytes are zero could not proceed further')
57 expect(Facter::Resolvers::Memory.resolve(:total_bytes)).to be(nil)
58 end
59
60 it 'detects available bytes as nil' do
61 expect(Facter::Resolvers::Memory.resolve(:available_bytes)).to be(nil)
62 end
63
64 it 'determines used bytes as nil' do
65 expect(Facter::Resolvers::Memory.resolve(:used_bytes)).to be(nil)
66 end
67
68 it 'determines capacity as nil' do
69 expect(Facter::Resolvers::Memory.resolve(:capacity)).to be(nil)
70 end
71 end
72
73 describe '#resolve when available bytes is 0' do
74 let(:status) { 1 }
75 let(:total) { 3242 }
76 let(:page_size) { 4096 }
77 let(:available) { 0 }
78
79 it 'detects total bytes as nil' do
80 allow(logger).to receive(:debug)
81 .with('Available or Total bytes are zero could not proceed further')
82 expect(Facter::Resolvers::Memory.resolve(:total_bytes)).to be(nil)
83 end
84
85 it 'detects available bytes as nil' do
86 expect(Facter::Resolvers::Memory.resolve(:available_bytes)).to be(nil)
87 end
88
89 it 'determines used bytes as nil' do
90 expect(Facter::Resolvers::Memory.resolve(:used_bytes)).to be(nil)
91 end
92
93 it 'determines capacity as nil' do
94 expect(Facter::Resolvers::Memory.resolve(:capacity)).to be(nil)
95 end
96 end
97
98 describe '#resolve when page size is 0' do
99 let(:status) { 1 }
100 let(:total) { 3242 }
101 let(:page_size) { 0 }
102 let(:available) { 4096 }
103
104 it 'detects total bytes as nil' do
105 allow(logger).to receive(:debug)
106 .with('Available or Total bytes are zero could not proceed further')
107 expect(Facter::Resolvers::Memory.resolve(:total_bytes)).to be(nil)
108 end
109
110 it 'detects available bytes as nil' do
111 expect(Facter::Resolvers::Memory.resolve(:available_bytes)).to be(nil)
112 end
113
114 it 'determines used bytes as nil' do
115 expect(Facter::Resolvers::Memory.resolve(:used_bytes)).to be(nil)
116 end
117
118 it 'determines capacity as nil' do
119 expect(Facter::Resolvers::Memory.resolve(:capacity)).to be(nil)
120 end
121 end
122
123 describe '#resolve when GetPerformanceInfo function fails' do
124 let(:status) { FFI::WIN32FALSE }
125 let(:total) { 1_048_313 }
126 let(:page_size) { 4096 }
127 let(:available) { 824_031 }
128
129 it 'logs debug message and detects total bytes as nil' do
130 allow(logger).to receive(:debug).with('Resolving memory facts failed')
131 expect(Facter::Resolvers::Memory.resolve(:total_bytes)).to be(nil)
132 end
133
134 it 'detects available bytes as nil' do
135 expect(Facter::Resolvers::Memory.resolve(:available_bytes)).to be(nil)
136 end
137
138 it 'determines used bytes as nil' do
139 expect(Facter::Resolvers::Memory.resolve(:used_bytes)).to be(nil)
140 end
141
142 it 'determines capacity as nil' do
143 expect(Facter::Resolvers::Memory.resolve(:capacity)).to be(nil)
144 end
145 end
146 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::NetKVM do
3 describe '#resolve' do
4 let(:reg) { instance_spy('Win32::Registry') }
5
6 before do
7 allow(reg).to receive(:keys).and_return(reg_value)
8 allow(reg).to receive(:close)
9 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open)
10 .with('SYSTEM\\CurrentControlSet\\Services')
11 .and_return(reg)
12 end
13
14 after do
15 Facter::Resolvers::NetKVM.invalidate_cache
16 end
17
18 context 'when is not kvm' do
19 let(:reg_value) { { 'puppet' => 'labs' } }
20
21 it 'returns false' do
22 expect(Facter::Resolvers::NetKVM.resolve(:kvm)).to be(false)
23 end
24 end
25
26 context 'when is kvm' do
27 let(:reg_value) { { 'puppet' => 'labs', 'netkvm' => 'info' } }
28
29 it 'returns true' do
30 expect(Facter::Resolvers::NetKVM.resolve(:kvm)).to be(true)
31 end
32 end
33 end
34 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Networking do
3 subject(:resolver) { Facter::Resolvers::Windows::Networking }
4
5 describe '#resolve' do
6 let(:logger) { instance_spy(Facter::Log) }
7 let(:size_ptr) { instance_spy(FFI::MemoryPointer) }
8 let(:adapter_address) { instance_spy(FFI::MemoryPointer) }
9 let(:reg) { instance_spy('Win32::Registry::HKEY_LOCAL_MACHINE') }
10 let(:domain) { '' }
11
12 before do
13 allow(FFI::MemoryPointer).to receive(:new).and_call_original
14 allow(FFI::MemoryPointer).to receive(:new)
15 .with(NetworkingFFI::BUFFER_LENGTH).and_return(size_ptr)
16 allow(FFI::MemoryPointer).to receive(:new)
17 .with(IpAdapterAddressesLh.size, NetworkingFFI::BUFFER_LENGTH)
18 .and_return(adapter_address)
19 allow(NetworkingFFI).to receive(:GetAdaptersAddresses)
20 .with(NetworkingFFI::AF_UNSPEC, 14, FFI::Pointer::NULL, adapter_address, size_ptr)
21 .and_return(error_code)
22
23 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open)
24 .with('SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters')
25 .and_yield(reg)
26 allow(reg).to receive(:[]).with('Domain').and_return(domain)
27 allow(reg).to receive(:close)
28
29 resolver.instance_variable_set(:@log, logger)
30 end
31
32 after do
33 resolver.invalidate_cache
34 end
35
36 context 'when fails to retrieve networking information' do
37 let(:error_code) { NetworkingFFI::ERROR_NO_DATA }
38
39 before do
40 allow(logger).to receive(:debug).with('Unable to retrieve networking facts!')
41 end
42
43 it 'returns interfaces fact as nil' do
44 expect(resolver.resolve(:interfaces)).to be(nil)
45 end
46
47 it 'logs debug message' do
48 resolver.resolve(:interfaces)
49
50 expect(logger).to have_received(:debug).with('Unable to retrieve networking facts!')
51 end
52
53 it 'returns nil for domain' do
54 expect(resolver.resolve(:domain)).to be(nil)
55 end
56 end
57
58 context 'when fails to retrieve networking information after 3 tries' do
59 let(:error_code) { NetworkingFFI::ERROR_BUFFER_OVERFLOW }
60
61 before do
62 allow(FFI::MemoryPointer).to receive(:new).exactly(4).times
63 .with(IpAdapterAddressesLh.size, NetworkingFFI::BUFFER_LENGTH)
64 .and_return(adapter_address)
65 allow(NetworkingFFI)
66 .to receive(:GetAdaptersAddresses)
67 .exactly(3).times
68 .with(NetworkingFFI::AF_UNSPEC, 14, FFI::Pointer::NULL, adapter_address, size_ptr)
69 .and_return(error_code)
70 end
71
72 it 'returns nil' do
73 expect(resolver.resolve(:interfaces)).to be(nil)
74 end
75 end
76
77 context 'when it succeeded to retrieve networking information but all interface are down' do
78 let(:error_code) { NetworkingFFI::ERROR_SUCCES }
79 let(:adapter) { instance_double('FFI::MemoryPointer') }
80 let(:next_adapter) { instance_spy(FFI::Pointer) }
81
82 before do
83 allow(IpAdapterAddressesLh).to receive(:read_list).with(adapter_address).and_yield(adapter)
84 allow(IpAdapterAddressesLh).to receive(:new).with(next_adapter).and_return(adapter)
85 allow(adapter).to receive(:[]).with(:OperStatus).and_return(NetworkingFFI::IF_OPER_STATUS_DOWN)
86 allow(adapter).to receive(:[]).with(:Next).and_return(next_adapter)
87 allow(adapter).to receive(:to_ptr).and_return(FFI::Pointer::NULL)
88 end
89
90 it 'returns nil' do
91 expect(resolver.resolve(:interfaces)).to be(nil)
92 end
93 end
94
95 context "when it succeeded to retrieve networking information but the interface hasn't got an address" do
96 let(:error_code) { NetworkingFFI::ERROR_SUCCES }
97 let(:adapter) do
98 OpenStruct.new(OperStatus: NetworkingFFI::IF_OPER_STATUS_UP, IfType: NetworkingFFI::IF_TYPE_ETHERNET_CSMACD,
99 DnsSuffix: dns_ptr, FriendlyName: friendly_name_ptr, Flags: 0, Mtu: 1500,
100 FirstUnicastAddress: ptr)
101 end
102 let(:dns_ptr) { instance_spy(FFI::Pointer) }
103 let(:friendly_name_ptr) { instance_spy(FFI::Pointer) }
104 let(:ptr) { instance_spy(FFI::Pointer) }
105 let(:unicast) { OpenStruct.new(Address: ptr, Next: ptr, to_ptr: FFI::Pointer::NULL) }
106
107 before do
108 allow(IpAdapterAddressesLh).to receive(:read_list).with(adapter_address).and_yield(adapter)
109 allow(IpAdapterUnicastAddressLH).to receive(:read_list).with(ptr).and_yield(unicast)
110 allow(NetworkUtils).to receive(:address_to_string).with(ptr).and_return(nil)
111 allow(IpAdapterUnicastAddressLH).to receive(:new).with(ptr).and_return(unicast)
112 allow(NetworkUtils).to receive(:find_mac_address).with(adapter).and_return('00:50:56:9A:F8:6B')
113 allow(friendly_name_ptr).to receive(:read_wide_string_without_length).and_return('Ethernet0')
114 allow(dns_ptr).to receive(:read_wide_string_without_length).and_return('domain')
115 end
116
117 it 'returns interfaces' do
118 expected = {
119 'Ethernet0' => {
120 dhcp: nil,
121 mac: '00:50:56:9A:F8:6B',
122 mtu: 1500
123 }
124 }
125 expect(resolver.resolve(:interfaces)).to eql(expected)
126 end
127
128 it 'returns domain' do
129 expect(resolver.resolve(:domain)).to eq('domain')
130 end
131
132 it 'returns nil for mtu fact as primary interface is nil' do
133 expect(resolver.resolve(:mtu)).to be(nil)
134 end
135
136 it 'returns nil for dhcp fact as primary interface is nil' do
137 expect(resolver.resolve(:dhcp)).to be(nil)
138 end
139
140 it 'returns nil for mac fact as primary interface is nil' do
141 expect(resolver.resolve(:mac)).to be(nil)
142 end
143 end
144
145 context 'when it succeeded to retrieve networking information but the interface has an address' do
146 let(:error_code) { NetworkingFFI::ERROR_SUCCES }
147 let(:adapter) do
148 OpenStruct.new(OperStatus: NetworkingFFI::IF_OPER_STATUS_UP, IfType: NetworkingFFI::IF_TYPE_ETHERNET_CSMACD,
149 DnsSuffix: dns_ptr, FriendlyName: friendly_name_ptr, Flags: 0, Mtu: 1500,
150 FirstUnicastAddress: ptr, Next: ptr, to_ptr: FFI::Pointer::NULL)
151 end
152 let(:ptr) { instance_spy(FFI::Pointer) }
153 let(:dns_ptr) { instance_spy(FFI::Pointer) }
154 let(:friendly_name_ptr) { instance_spy(FFI::Pointer) }
155 let(:unicast) { OpenStruct.new(Address: address, Next: ptr, to_ptr: FFI::Pointer::NULL, OnLinkPrefixLength: 24) }
156 let(:address) { OpenStruct.new(lpSockaddr: ptr) }
157 let(:sock_address) { OpenStruct.new(sa_family: NetworkingFFI::AF_INET) }
158 let(:binding) do
159 {
160 address: '10.16.127.3',
161 netmask: '255.255.255.0',
162 network: '10.16.127.0'
163 }
164 end
165
166 before do
167 allow(IpAdapterAddressesLh).to receive(:read_list).with(adapter_address).and_yield(adapter)
168 allow(IpAdapterUnicastAddressLH).to receive(:read_list).with(ptr).and_yield(unicast)
169 allow(NetworkUtils).to receive(:address_to_string).with(address).and_return('10.16.127.3')
170 allow(SockAddr).to receive(:new).with(ptr).and_return(sock_address)
171 allow(NetworkUtils).to receive(:ignored_ip_address).with('10.16.127.3').and_return(false)
172 allow(IpAdapterUnicastAddressLH).to receive(:new).with(ptr).and_return(unicast)
173 allow(NetworkUtils).to receive(:find_mac_address).with(adapter).and_return('00:50:56:9A:F8:6B')
174 allow(IpAdapterAddressesLh).to receive(:new).with(ptr).and_return(adapter)
175 allow(dns_ptr).to receive(:read_wide_string_without_length).and_return('10.122.0.2')
176 allow(friendly_name_ptr).to receive(:read_wide_string_without_length).and_return('Ethernet0')
177 end
178
179 it 'returns interface' do
180 result = {
181 'Ethernet0' => {
182 bindings: [binding],
183 dhcp: nil,
184 ip: '10.16.127.3',
185 mac: '00:50:56:9A:F8:6B',
186 mtu: 1500,
187 netmask: '255.255.255.0',
188 network: '10.16.127.0'
189 }
190 }
191 expect(resolver.resolve(:interfaces)).to eql(result)
192 end
193 end
194
195 context 'when it succeeded to retrieve networking information but the interface has an ipv6 address' do
196 let(:error_code) { NetworkingFFI::ERROR_SUCCES }
197 let(:adapter) do
198 OpenStruct.new(OperStatus: NetworkingFFI::IF_OPER_STATUS_UP, IfType: NetworkingFFI::IF_TYPE_ETHERNET_CSMACD,
199 DnsSuffix: dns_ptr, FriendlyName: friendly_name_ptr, Flags: 0, Mtu: 1500,
200 FirstUnicastAddress: ptr, Next: ptr, to_ptr: FFI::Pointer::NULL)
201 end
202 let(:ptr) { FFI::Pointer.new }
203 let(:dns_ptr) { FFI::Pointer.new }
204 let(:friendly_name_ptr) { FFI::Pointer.new }
205 let(:unicast) { OpenStruct.new(Address: address, Next: ptr, to_ptr: FFI::Pointer::NULL, OnLinkPrefixLength: 24) }
206 let(:address) { OpenStruct.new(lpSockaddr: ptr) }
207 let(:sock_address) { OpenStruct.new(sa_family: NetworkingFFI::AF_INET6) }
208 let(:binding) do
209 {
210 address: 'fe80::7ca0:ab22:703a:b329',
211 netmask: 'ffff:ff00::',
212 network: 'fe80::',
213 scope6: 'link'
214 }
215 end
216 let(:domain) { 'my_domain' }
217 let(:wchar_null) { "\0".encode(Encoding::UTF_16LE) }
218 let(:invalid_chars) { (+"\xf0\xdc").force_encoding(Encoding::UTF_16LE) }
219
220 def stub_utf16le_bytes(ptr, str)
221 i = 0
222 str.encode(Encoding::UTF_16LE).each_char do |ch|
223 allow(ptr).to receive(:get_bytes).with(i, 2).and_return(ch)
224 i += 2
225 end
226 allow(ptr).to receive(:get_bytes).with(i, 2).and_return(wchar_null)
227 end
228
229 before do
230 allow(IpAdapterAddressesLh).to receive(:read_list).with(adapter_address).and_yield(adapter)
231 allow(IpAdapterUnicastAddressLH).to receive(:read_list).with(ptr).and_yield(unicast)
232 allow(NetworkUtils).to receive(:address_to_string).with(address).and_return('fe80::7ca0:ab22:703a:b329')
233 allow(SockAddr).to receive(:new).with(ptr).and_return(sock_address)
234 allow(NetworkUtils).to receive(:ignored_ip_address).with('fe80::7ca0:ab22:703a:b329')
235 allow(IpAdapterUnicastAddressLH).to receive(:new).with(ptr).and_return(unicast)
236 allow(NetworkUtils).to receive(:find_mac_address).with(adapter).and_return('00:50:56:9A:F8:6B')
237 allow(IpAdapterAddressesLh).to receive(:new).with(ptr).and_return(adapter)
238
239 stub_utf16le_bytes(dns_ptr, '')
240 stub_utf16le_bytes(friendly_name_ptr, 'Ethernet0')
241 end
242
243 it 'returns interface' do
244 result = {
245 'Ethernet0' => {
246 bindings6: [binding],
247 dhcp: nil,
248 ip6: 'fe80::7ca0:ab22:703a:b329',
249 mac: '00:50:56:9A:F8:6B',
250 mtu: 1500,
251 netmask6: 'ffff:ff00::',
252 network6: 'fe80::',
253 scope6: 'link'
254 }
255 }
256 expect(resolver.resolve(:interfaces)).to eql(result)
257 end
258
259 it 'returns domain' do
260 expect(resolver.resolve(:domain)).to eql(domain)
261 end
262
263 it 'replaces invalid characters in the friendly name' do
264 stub_utf16le_bytes(friendly_name_ptr, invalid_chars)
265
266 resolved_interface = resolver.resolve(:interfaces).keys.first
267 expect(resolved_interface).to eq("\uFFFD")
268 end
269 end
270 end
271 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Processors do
3 subject(:resolver) { Facter::Resolvers::Processors }
4
5 let(:logger) { instance_spy(Facter::Log) }
6
7 before do
8 win = double('Facter::Util::Windows::Win32Ole')
9 query_string = 'SELECT Name,'\
10 'Architecture,'\
11 'NumberOfLogicalProcessors,'\
12 'NumberOfCores FROM Win32_Processor'
13
14 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
15 allow(win).to receive(:exec_query)
16 .with(query_string)
17 .and_return(proc)
18 resolver.instance_variable_set(:@log, logger)
19 end
20
21 after do
22 resolver.invalidate_cache
23 end
24
25 describe '#resolve' do
26 let(:proc) do
27 [
28 double(
29 'proc',
30 Name: 'Pretty_Name',
31 Architecture: 0,
32 NumberOfLogicalProcessors: 2,
33 NumberOfCores: 2
34 )
35 ]
36 end
37
38 it 'detects models of processors' do
39 expect(resolver.resolve(:models)).to eql(['Pretty_Name'])
40 end
41
42 it 'detects isa' do
43 expect(resolver.resolve(:isa)).to eql('x86')
44 end
45
46 it 'counts proccesors' do
47 expect(resolver.resolve(:count)).to be(2)
48 end
49
50 it 'counts physical processors' do
51 expect(resolver.resolve(:physicalcount)).to be(1)
52 end
53
54 it 'counts number of cores per socket' do
55 expect(resolver.resolve(:cores_per_socket)).to be(2)
56 end
57
58 it 'counts number of threads per core' do
59 expect(resolver.resolve(:threads_per_core)).to be(1)
60 end
61 end
62
63 describe '#resolve when number of logical processors is 0' do
64 let(:proc) do
65 [double('proc', Name: 'Pretty_Name', Architecture: 0, NumberOfLogicalProcessors: 0,
66 NumberOfCores: 2),
67 double('proc', Name: 'Awesome_Name', Architecture: 10, NumberOfLogicalProcessors: 0,
68 NumberOfCores: 2)]
69 end
70
71 it 'detects models' do
72 expect(resolver.resolve(:models)).to eql(%w[Pretty_Name Awesome_Name])
73 end
74
75 it 'detects isa' do
76 expect(resolver.resolve(:isa)).to eql('x86')
77 end
78
79 it 'counts proccesors' do
80 expect(resolver.resolve(:count)).to be(2)
81 end
82
83 it 'counts physical processors' do
84 expect(resolver.resolve(:physicalcount)).to be(2)
85 end
86
87 it 'counts number of cores per socket' do
88 expect(resolver.resolve(:cores_per_socket)).to be(2)
89 end
90
91 it 'counts number of threads per core' do
92 expect(resolver.resolve(:threads_per_core)).to be(1)
93 end
94 end
95
96 describe '#resolve logs a debug message when is an unknown architecture' do
97 let(:proc) do
98 [
99 double(
100 'proc',
101 Name: 'Pretty_Name',
102 Architecture: 10,
103 NumberOfLogicalProcessors: 2,
104 NumberOfCores: 2
105 )
106 ]
107 end
108
109 it 'logs that is unknown architecture' do
110 allow(logger).to receive(:debug)
111 .with('Unable to determine processor type: unknown architecture')
112 expect(resolver.resolve(:isa)).to be(nil)
113 end
114 end
115
116 describe '#resolve when WMI query returns nil' do
117 let(:proc) { nil }
118
119 it 'logs that query failed and isa nil' do
120 allow(logger).to receive(:debug)
121 .with('WMI query returned no results'\
122 'for Win32_Processor with values Name, Architecture and NumberOfLogicalProcessors.')
123 expect(resolver.resolve(:isa)).to be(nil)
124 end
125
126 it 'detects that models fact is nil' do
127 expect(resolver.resolve(:models)).to be(nil)
128 end
129
130 it 'detects that count fact is nil' do
131 expect(resolver.resolve(:count)).to be(nil)
132 end
133
134 it 'detects that physicalcount fact is nil' do
135 expect(resolver.resolve(:physicalcount)).to be(nil)
136 end
137
138 it 'counts number of cores per socket' do
139 expect(resolver.resolve(:cores_per_socket)).to be(nil)
140 end
141
142 it 'counts number of threads per core' do
143 expect(resolver.resolve(:threads_per_core)).to be(nil)
144 end
145 end
146
147 describe '#resolve when WMI query returns nil for Name, Architecture and NumberOfLogicalProcessors' do
148 let(:proc) do
149 [
150 double(
151 'proc',
152 Name: nil,
153 Architecture: nil,
154 NumberOfLogicalProcessors: nil,
155 NumberOfCores: 2
156 )
157 ]
158 end
159
160 it 'detects that isa is nil' do
161 allow(logger).to receive(:debug)
162 .with('Unable to determine processor type: unknown architecture')
163 expect(resolver.resolve(:isa)).to be(nil)
164 end
165
166 it 'detects that models is an array' do
167 expect(resolver.resolve(:models)).to eql([nil])
168 end
169
170 it 'detects count fact' do
171 expect(resolver.resolve(:count)).to be(1)
172 end
173
174 it 'detects physicalcount' do
175 expect(resolver.resolve(:physicalcount)).to be(1)
176 end
177
178 it 'counts number of cores per socket' do
179 expect(resolver.resolve(:cores_per_socket)).to be(2)
180 end
181
182 it 'counts number of threads per core' do
183 expect(resolver.resolve(:threads_per_core)).to be(1)
184 end
185 end
186 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::ProductRelease do
3 describe '#resolve' do
4 let(:reg) { instance_double('Win32::Registry::HKEY_LOCAL_MACHINE') }
5 let(:ed) { 'ServerStandard' }
6 let(:install) { 'Server' }
7 let(:prod) { 'Windows Server 2022 Standard' }
8 let(:release) { '1809' }
9 let(:display_version) { '21H2' }
10
11 before do
12 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open)
13 .with('SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
14 .and_return(reg)
15 allow(reg).to receive(:each)
16 .and_yield('EditionID', ed)
17 .and_yield('InstallationType', install)
18 .and_yield('ProductName', prod)
19 .and_yield('ReleaseId', release)
20 .and_yield('DisplayVersion', display_version)
21
22 allow(reg).to receive(:[]).with('EditionID').and_return(ed)
23 allow(reg).to receive(:[]).with('InstallationType').and_return(install)
24 allow(reg).to receive(:[]).with('ProductName').and_return(prod)
25 allow(reg).to receive(:[]).with('ReleaseId').and_return(release)
26 allow(reg).to receive(:[]).with('DisplayVersion').and_return(display_version)
27
28 allow(reg).to receive(:close)
29 end
30
31 after do
32 Facter::Resolvers::ProductRelease.invalidate_cache
33 end
34
35 context 'when all fields exist in registry' do
36 it 'detects edition id' do
37 expect(Facter::Resolvers::ProductRelease.resolve(:edition_id)).to eql(ed)
38 end
39
40 it 'detects installation type' do
41 expect(Facter::Resolvers::ProductRelease.resolve(:installation_type)).to eql(install)
42 end
43
44 it 'detects product name' do
45 expect(Facter::Resolvers::ProductRelease.resolve(:product_name)).to eql(prod)
46 end
47
48 it 'detects release id' do
49 expect(Facter::Resolvers::ProductRelease.resolve(:release_id)).to eql(display_version)
50 end
51
52 it 'detects display version' do
53 expect(Facter::Resolvers::ProductRelease.resolve(:display_version)).to eql(display_version)
54 end
55 end
56
57 context "when InstallationType doesn't exist in registry" do
58 let(:install) { nil }
59
60 it 'detects edition id' do
61 expect(Facter::Resolvers::ProductRelease.resolve(:edition_id)).to eql(ed)
62 end
63
64 it 'detects installation type as nil' do
65 expect(Facter::Resolvers::ProductRelease.resolve(:installation_type)).to be(nil)
66 end
67
68 it 'detects product name' do
69 expect(Facter::Resolvers::ProductRelease.resolve(:product_name)).to eql(prod)
70 end
71
72 it 'detects release id' do
73 expect(Facter::Resolvers::ProductRelease.resolve(:release_id)).to eql(display_version)
74 end
75
76 it 'detects display version' do
77 expect(Facter::Resolvers::ProductRelease.resolve(:display_version)).to eql(display_version)
78 end
79 end
80
81 context 'when DisplayVersion registry key is not available' do
82 before do
83 allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open)
84 .with('SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
85 .and_return(reg)
86 allow(reg).to receive(:each)
87 .and_yield('ReleaseId', release)
88
89 allow(reg).to receive(:[]).with('ReleaseId').and_return(release)
90
91 allow(reg).to receive(:close)
92 end
93
94 it 'detects release id' do
95 expect(Facter::Resolvers::ProductRelease.resolve(:release_id)).to eql(release)
96 end
97
98 it 'detects display version' do
99 expect(Facter::Resolvers::ProductRelease.resolve(:display_version)).to be(nil)
100 end
101 end
102 end
103 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Ssh do
3 describe '#resolve' do
4 before do
5 allow(ENV).to receive(:[]).with('programdata').and_return(programdata_dir)
6 allow(File).to receive(:directory?).with("#{programdata_dir}/ssh").and_return(dir_exists)
7 end
8
9 after do
10 Facter::Resolvers::Windows::Ssh.invalidate_cache
11 end
12
13 context 'when programdata enviroment variable is set' do
14 let(:programdata_dir) { 'C:/ProgramData' }
15 let(:dir_exists) { true }
16
17 before do
18 allow(Facter::Util::FileHelper).to receive(:safe_read).with("#{programdata_dir}/ssh/ssh_host_ecdsa_key.pub")
19 .and_return(ecdsa_content)
20 allow(Facter::Util::FileHelper).to receive(:safe_read).with("#{programdata_dir}/ssh/ssh_host_ed25519_key.pub")
21 .and_return(ed25519_content)
22 allow(Facter::Util::FileHelper).to receive(:safe_read).with("#{programdata_dir}/ssh/ssh_host_dsa_key.pub")
23 .and_return('')
24 allow(Facter::Util::FileHelper).to receive(:safe_read).with("#{programdata_dir}/ssh/ssh_host_rsa_key.pub")
25 .and_return(rsa_content)
26 end
27
28 context 'when ecdsa, ed25519 and rsa files exists' do
29 before do
30 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
31 .with('ssh-rsa', load_fixture('rsa_key').read.strip!)
32 .and_return(rsa_result)
33 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
34 .with('ecdsa-sha2-nistp256', load_fixture('ecdsa_key').read.strip!)
35 .and_return(ecdsa_result)
36 allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
37 .with('ssh-ed25519', load_fixture('ed25519_key').read.strip!)
38 .and_return(ed25519_result)
39 end
40
41 let(:ecdsa_exists) { true }
42 let(:rsa_exists) { true }
43 let(:ed25519_exists) { true }
44 let(:ecdsa_content) { load_fixture('ecdsa').read.strip! }
45 let(:rsa_content) { load_fixture('rsa').read.strip! }
46 let(:ed25519_content) { load_fixture('ed25519').read.strip! }
47
48 let(:ecdsa_fingerprint) do
49 Facter::Util::Resolvers::FingerPrint.new(
50 'SSHFP 3 1 fd92cf867fac0042d491eb1067e4f3cabf54039a',
51 'SSHFP 3 2 a51271a67987d7bbd685fa6d7cdd2823a30373ab01420b094480523fabff2a05'
52 )
53 end
54
55 let(:rsa_fingerprint) do
56 Facter::Util::Resolvers::FingerPrint.new(
57 'SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07',
58 'SSHFP 1 2 efaa26ff8169f5ffc372ebcad17aef886f4ccaa727169acdd0379b51c6c77e99'
59 )
60 end
61
62 let(:ed25519_fingerprint) do
63 Facter::Util::Resolvers::FingerPrint.new(
64 'SSHFP 4 1 f5780634d4e34c6ef2411ac439b517bfdce43cf1',
65 'SSHFP 4 2 c1257b3865df22f3349f9ebe19961c8a8edf5fbbe883113e728671b42d2c9723'
66 )
67 end
68
69 let(:ecdsa_result) do
70 Facter::Util::Resolvers::Ssh.new(ecdsa_fingerprint, 'ecdsa-sha2-nistp256', ecdsa_content, 'ecdsa')
71 end
72
73 let(:rsa_result) do
74 Facter::Util::Resolvers::Ssh.new(rsa_fingerprint, 'ssh-rsa', rsa_content, 'rsa')
75 end
76
77 let(:ed25519_result) do
78 Facter::Util::Resolvers::Ssh.new(ed25519_fingerprint, 'ssh-ed22519', ed25519_content, 'ed25519')
79 end
80
81 it 'returns ssh fact' do
82 expect(Facter::Resolvers::Windows::Ssh.resolve(:ssh)).to eq([rsa_result, ecdsa_result, ed25519_result])
83 end
84 end
85
86 context 'when files are not readable' do
87 let(:ecdsa_content) { '' }
88 let(:rsa_content) { '' }
89 let(:ed25519_content) { '' }
90
91 it 'returns nil' do
92 expect(Facter::Resolvers::Windows::Ssh.resolve(:ssh)).to eq(nil)
93 end
94 end
95 end
96
97 context 'when programdata enviroment variable is not set' do
98 let(:programdata_dir) { '' }
99 let(:dir_exists) { false }
100
101 it 'returns nil' do
102 expect(Facter::Resolvers::Windows::Ssh.resolve(:ssh)).to eq(nil)
103 end
104 end
105 end
106 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::System32 do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 allow(ENV).to receive(:[]).with('SystemRoot').and_return(win_path)
7
8 bool_ptr = double('FFI::MemoryPointer', read_win32_bool: is_wow)
9 allow(FFI::MemoryPointer).to receive(:new).with(:win32_bool, 1).and_return(bool_ptr)
10 allow(System32FFI).to receive(:GetCurrentProcess).and_return(2)
11 allow(System32FFI).to receive(:IsWow64Process).with(2, bool_ptr).and_return(bool)
12
13 Facter::Resolvers::System32.instance_variable_set(:@log, logger)
14 end
15
16 after do
17 Facter::Resolvers::System32.invalidate_cache
18 end
19
20 describe '#resolve when is wow 64 process' do
21 let(:win_path) { 'C:\\Windows' }
22 let(:bool) { 1 }
23 let(:is_wow) { true }
24
25 it 'detects sysnative dir' do
26 expect(Facter::Resolvers::System32.resolve(:system32)).to eql("#{win_path}\\sysnative")
27 end
28 end
29
30 describe '#resolve when it is not wow 64 process' do
31 let(:win_path) { 'C:\\Windows' }
32 let(:bool) { 1 }
33 let(:is_wow) { false }
34
35 it 'detects system32 dir' do
36 expect(Facter::Resolvers::System32.resolve(:system32)).to eql("#{win_path}\\system32")
37 end
38 end
39
40 describe '#resolve when env variable is not set' do
41 let(:win_path) { '' }
42 let(:bool) { 1 }
43 let(:is_wow) { false }
44
45 it 'detects system32 dir is nil and prints debug message' do
46 allow(logger).to receive(:debug).with('Unable to find correct value for SystemRoot'\
47 ' enviroment variable')
48 expect(Facter::Resolvers::System32.resolve(:system32)).to be(nil)
49 end
50 end
51
52 describe '#resolve when env variable is found as nil' do
53 let(:win_path) { nil }
54 let(:bool) { 1 }
55 let(:is_wow) { false }
56
57 it 'detects system32 dir is nil and prints debug message' do
58 allow(logger).to receive(:debug).with('Unable to find correct value for SystemRoot'\
59 ' enviroment variable')
60 expect(Facter::Resolvers::System32.resolve(:system32)).to be(nil)
61 end
62 end
63
64 describe '#resolve when IsWow64Process fails' do
65 let(:win_path) { 'C:\\Windows' }
66 let(:bool) { 0 }
67 let(:is_wow) { false }
68
69 it 'detects system32 dir is nil and prints debug message' do
70 allow(logger).to receive(:debug).with('IsWow64Process failed')
71 expect(Facter::Resolvers::System32.resolve(:system32)).to be(nil)
72 end
73 end
74 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Timezone do
3 before do
4 Facter::Resolvers::Windows::Timezone.invalidate_cache
5 end
6
7 describe '#resolve timezone' do
8 it 'resolves timezone with codepage from api' do
9 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api).and_return('850')
10
11 expect(Facter::Resolvers::Windows::Timezone.resolve(:timezone).encoding.to_s).to eql('UTF-8')
12 end
13
14 it 'resolves timezone with codepage from registry' do
15 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api).and_return('')
16 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_registry).and_return('850')
17
18 expect(Facter::Resolvers::Windows::Timezone.resolve(:timezone).encoding.to_s).to eql('UTF-8')
19 end
20
21 it 'resolves timezone with default codepage' do
22 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api).and_return('')
23 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_registry).and_return('something_invalid')
24
25 expect(Facter::Resolvers::Windows::Timezone.resolve(:timezone).encoding).to eql(Time.now.zone.encoding)
26 end
27
28 it 'detects timezone' do
29 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage).and_return('850')
30
31 timezone_result = Time.now.zone.force_encoding('CP850')
32 expect(Facter::Resolvers::Windows::Timezone.resolve(:timezone)).to eql(timezone_result)
33 end
34 end
35
36 describe '#codepage' do
37 it 'gets codepage from api' do
38 expect(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api)
39 Facter::Resolvers::Windows::Timezone.resolve(:timezone)
40 end
41
42 it 'gets codepage from registry' do
43 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api).and_return('')
44 expect(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_registry)
45 Facter::Resolvers::Windows::Timezone.resolve(:timezone)
46 end
47
48 it 'gets invalid codepage' do
49 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_api).and_return('')
50 allow(Facter::Resolvers::Windows::Timezone).to receive(:codepage_from_registry).and_return('something_invalid')
51
52 expect { Facter::Resolvers::Windows::Timezone.resolve(:timezone) }.not_to raise_error
53 end
54 end
55 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Uptime do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 win = double('Facter::Util::Windows::Win32Ole')
7
8 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
9 allow(win).to receive(:return_first).with('SELECT LocalDateTime,LastBootUpTime FROM Win32_OperatingSystem')
10 .and_return(comp)
11
12 Facter::Resolvers::Windows::Uptime.instance_variable_set(:@log, logger)
13 end
14
15 after do
16 Facter::Resolvers::Windows::Uptime.invalidate_cache
17 end
18
19 describe '#resolve' do
20 context 'when system is up for 1 hour' do
21 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
22 let(:local_time) { '20010203040506+0700' }
23 let(:last_bootup_time) { '20010203030506+0700' }
24
25 it 'resolves uptime' do
26 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to eql('1:00 hours')
27 end
28
29 it 'resolves seconds' do
30 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(3600)
31 end
32
33 it 'resolves hours' do
34 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(1)
35 end
36
37 it 'resolves days' do
38 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(0)
39 end
40 end
41
42 context 'when system is up for 1 hour and 45 minutes' do
43 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
44 let(:local_time) { '20010203045006+0700' }
45 let(:last_bootup_time) { '20010203030506+0700' }
46
47 it 'resolves uptime' do
48 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to eql('1:45 hours')
49 end
50
51 it 'resolves seconds' do
52 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(6300)
53 end
54
55 it 'resolves hours' do
56 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(1)
57 end
58
59 it 'resolves days' do
60 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(0)
61 end
62 end
63
64 context 'when system is up for 1 hour, 45 minutes and 20 seconds' do
65 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
66 let(:local_time) { '20010203045026+0700' }
67 let(:last_bootup_time) { '20010203030506+0700' }
68
69 it 'resolves uptime' do
70 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to eql('1:45 hours')
71 end
72
73 it 'resolves seconds' do
74 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(6320)
75 end
76
77 it 'resolves hours' do
78 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(1)
79 end
80
81 it 'resolves days' do
82 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(0)
83 end
84 end
85
86 context 'when system is up for 1 day' do
87 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
88 let(:local_time) { '20010204040506+0700' }
89 let(:last_bootup_time) { '20010203040506+0700' }
90
91 it 'resolves uptime' do
92 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(1)
93 end
94
95 it 'resolves seconds' do
96 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(86_400)
97 end
98
99 it 'resolves hours' do
100 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(24)
101 end
102
103 it 'resolvese uptime' do
104 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to eql('1 day')
105 end
106 end
107
108 context 'when system is up for more than 1 day' do
109 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
110 let(:local_time) { '20010204040506+0700' }
111 let(:last_bootup_time) { '20010201120506+0700' }
112
113 it 'resolves uptime days' do
114 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(2)
115 end
116
117 it 'resolves seconds' do
118 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(230_400)
119 end
120
121 it 'resolves hours' do
122 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(64)
123 end
124
125 it 'resolves total uptime' do
126 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to eql('2 days')
127 end
128 end
129
130 context 'when local time is behind last bootup time' do
131 let(:comp) { double('WIN32OLE', LocalDateTime: local_time, LastBootUpTime: last_bootup_time) }
132 let(:local_time) { '20010201110506+0700' }
133 let(:last_bootup_time) { '20010201120506+0700' }
134
135 before do
136 allow(logger).to receive(:debug).with('Unable to determine system uptime!')
137 end
138
139 it 'logs that is unable to determine system uptime and all facts are nil' do
140 Facter::Resolvers::Windows::Uptime.resolve(:days)
141
142 expect(logger).to have_received(:debug).with('Unable to determine system uptime!')
143 end
144
145 it 'uptime fact is nil' do
146 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to be(nil)
147 end
148 end
149
150 context 'when WMI query returns nil' do
151 let(:comp) { nil }
152
153 it 'logs that query failed and days nil' do
154 allow(logger).to receive(:debug)
155 .with('WMI query returned no results'\
156 'for Win32_OperatingSystem with values LocalDateTime and LastBootUpTime.')
157 allow(logger).to receive(:debug)
158 .with('Unable to determine system uptime!')
159 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(nil)
160 end
161
162 it 'detects uptime fact is nil' do
163 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to be(nil)
164 end
165
166 it 'detects uptime.seconds fact is nil' do
167 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(nil)
168 end
169
170 it 'detects uptime.hours fact is nil' do
171 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(nil)
172 end
173 end
174
175 context 'when WMI query returns nil for LocalDateTime and LastBootUpTime' do
176 let(:comp) { double('WIN32OLE', LocalDateTime: nil, LastBootUpTime: nil) }
177
178 it 'logs that is unable to determine system uptime and days fact is nil' do
179 allow(logger).to receive(:debug)
180 .with('Unable to determine system uptime!')
181 expect(Facter::Resolvers::Windows::Uptime.resolve(:days)).to be(nil)
182 end
183
184 it 'detects uptime fact is nil' do
185 expect(Facter::Resolvers::Windows::Uptime.resolve(:uptime)).to be(nil)
186 end
187
188 it 'detects uptime.seconds fact is nil' do
189 expect(Facter::Resolvers::Windows::Uptime.resolve(:seconds)).to be(nil)
190 end
191
192 it 'detects uptime.hours fact is nil' do
193 expect(Facter::Resolvers::Windows::Uptime.resolve(:hours)).to be(nil)
194 end
195 end
196 end
197 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Windows::Virtualization do
3 let(:logger) { instance_spy(Facter::Log) }
4 let(:win32ole) { instance_spy('WIN32OLE') }
5 let(:win32ole2) { instance_spy('WIN32OLE') }
6 let(:win) { instance_spy('Facter::Util::Windows::Win32Ole') }
7
8 before do
9 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
10 allow(win).to receive(:exec_query).with('SELECT Manufacturer,Model,OEMStringArray FROM Win32_ComputerSystem')
11 .and_return(query_result)
12 Facter::Resolvers::Windows::Virtualization.instance_variable_set(:@log, logger)
13 Facter::Resolvers::Windows::Virtualization.invalidate_cache
14 end
15
16 describe '#resolve VirtualBox' do
17 before do
18 allow(win32ole).to receive(:Model).and_return(model)
19 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
20 allow(win32ole).to receive(:OEMStringArray).and_return(vbox_version)
21 allow(win32ole2).to receive(:Model).and_return(model)
22 allow(win32ole2).to receive(:Manufacturer).and_return(manufacturer)
23 allow(win32ole2).to receive(:OEMStringArray).and_return(vbox_revision)
24 end
25
26 let(:query_result) { [win32ole, win32ole2] }
27 let(:model) { 'VirtualBox' }
28 let(:manufacturer) {}
29 let(:vbox_version) { 'vboxVer_6.0.4' }
30 let(:vbox_revision) { 'vboxRev_128413' }
31
32 it 'detects virtual machine model' do
33 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('virtualbox')
34 end
35
36 it 'detects that is virtual' do
37 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
38 end
39
40 it 'detects oem_strings facts' do
41 expect(Facter::Resolvers::Windows::Virtualization.resolve(:oem_strings)).to eql([vbox_version, vbox_revision])
42 end
43 end
44
45 describe '#resolve Vmware' do
46 before do
47 allow(win32ole).to receive(:Model).and_return(model)
48 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
49 allow(win32ole).to receive(:OEMStringArray).and_return('')
50 end
51
52 let(:query_result) { [win32ole] }
53 let(:model) { 'VMware' }
54 let(:manufacturer) {}
55
56 it 'detects virtual machine model' do
57 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('vmware')
58 end
59
60 it 'detects that is virtual' do
61 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
62 end
63 end
64
65 describe '#resolve KVM' do
66 before do
67 allow(win32ole).to receive(:Model).and_return(model)
68 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
69 allow(win32ole).to receive(:OEMStringArray).and_return('')
70 end
71
72 let(:query_result) { [win32ole] }
73 let(:model) { 'KVM10' }
74 let(:manufacturer) {}
75
76 it 'detects virtual machine model' do
77 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('kvm')
78 end
79
80 it 'detects that is virtual' do
81 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
82 end
83 end
84
85 describe '#resolve Openstack VM' do
86 before do
87 allow(win32ole).to receive(:Model).and_return(model)
88 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
89 allow(win32ole).to receive(:OEMStringArray).and_return('')
90 end
91
92 let(:query_result) { [win32ole] }
93 let(:model) { 'OpenStack' }
94 let(:manufacturer) {}
95
96 it 'detects virtual machine model' do
97 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('openstack')
98 end
99
100 it 'detects that is virtual' do
101 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
102 end
103 end
104
105 describe '#resolve Microsoft VM' do
106 before do
107 allow(win32ole).to receive(:Model).and_return(model)
108 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
109 allow(win32ole).to receive(:OEMStringArray).and_return('')
110 end
111
112 let(:query_result) { [win32ole] }
113 let(:model) { 'Virtual Machine' }
114 let(:manufacturer) { 'Microsoft' }
115
116 it 'detects virtual machine model' do
117 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('hyperv')
118 end
119
120 it 'detects that is virtual' do
121 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
122 end
123 end
124
125 describe '#resolve Xen VM' do
126 before do
127 allow(win32ole).to receive(:Model).and_return(model)
128 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
129 allow(win32ole).to receive(:OEMStringArray).and_return('')
130 end
131
132 let(:query_result) { [win32ole] }
133 let(:model) { '' }
134 let(:manufacturer) { 'Xen' }
135
136 it 'detects virtual machine model' do
137 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('xen')
138 end
139
140 it 'detects that is virtual' do
141 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
142 end
143 end
144
145 describe '#resolve Amazon EC2 VM' do
146 before do
147 allow(win32ole).to receive(:Model).and_return(model)
148 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
149 allow(win32ole).to receive(:OEMStringArray).and_return('')
150 end
151
152 let(:query_result) { [win32ole] }
153 let(:model) { '' }
154 let(:manufacturer) { 'Amazon EC2' }
155
156 it 'detects virtual machine model' do
157 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('kvm')
158 end
159
160 it 'detects that is virtual' do
161 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(true)
162 end
163 end
164
165 describe '#resolve Physical Machine' do
166 before do
167 allow(win32ole).to receive(:Model).and_return(model)
168 allow(win32ole).to receive(:Manufacturer).and_return(manufacturer)
169 allow(win32ole).to receive(:OEMStringArray).and_return('')
170 end
171
172 let(:query_result) { [win32ole] }
173 let(:model) { '' }
174 let(:manufacturer) { '' }
175
176 it 'detects virtual machine model' do
177 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('physical')
178 end
179
180 it 'detects that is not virtual' do
181 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(false)
182 end
183 end
184
185 describe '#resolve should cache facts in the same run' do
186 let(:query_result) { nil }
187
188 it 'detects virtual machine model' do
189 Facter::Resolvers::Windows::Virtualization.instance_variable_set(:@fact_list, { virtual: 'physical' })
190
191 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('physical')
192 end
193
194 it 'detects that is virtual' do
195 Facter::Resolvers::Windows::Virtualization.instance_variable_set(:@fact_list, { is_virtual: false })
196
197 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(false)
198 end
199 end
200
201 describe '#resolve when WMI query returns nil' do
202 let(:query_result) { nil }
203
204 it 'logs that query failed and virtual nil' do
205 allow(logger).to receive(:debug)
206 .with('WMI query returned no results'\
207 ' for Win32_ComputerSystem with values Manufacturer, Model and OEMStringArray.')
208 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to be(nil)
209 end
210
211 it 'detects that is_virtual nil' do
212 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(nil)
213 end
214 end
215
216 describe '#resolve when WMI query returns nil for Model and Manufacturer' do
217 before do
218 allow(win32ole).to receive(:Model).and_return(nil)
219 allow(win32ole).to receive(:Manufacturer).and_return(nil)
220 allow(win32ole).to receive(:OEMStringArray).and_return('')
221 end
222
223 let(:query_result) { [win32ole] }
224
225 it 'detects that is physical' do
226 expect(Facter::Resolvers::Windows::Virtualization.resolve(:virtual)).to eql('physical')
227 end
228
229 it 'detects that is_virtual is false' do
230 expect(Facter::Resolvers::Windows::Virtualization.resolve(:is_virtual)).to be(false)
231 end
232 end
233 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::WinOsDescription do
3 let(:logger) { instance_spy(Facter::Log) }
4
5 before do
6 win = double('Facter::Util::Windows::Win32Ole')
7
8 allow(Facter::Util::Windows::Win32Ole).to receive(:new).and_return(win)
9 allow(win).to receive(:return_first).with('SELECT ProductType,OtherTypeDescription FROM Win32_OperatingSystem')
10 .and_return(comp)
11 Facter::Resolvers::WinOsDescription.instance_variable_set(:@log, logger)
12 end
13
14 after do
15 Facter::Resolvers::WinOsDescription.invalidate_cache
16 end
17
18 describe '#resolve when query fails' do
19 let(:comp) { nil }
20
21 it 'logs debug message and facts are nil' do
22 allow(logger).to receive(:debug)
23 .with('WMI query returned no results for Win32_OperatingSystem'\
24 'with values ProductType and OtherTypeDescription.')
25
26 expect(Facter::Resolvers::WinOsDescription.resolve(:full)).to be(nil)
27 end
28 end
29
30 describe '#resolve' do
31 let(:comp) { double('Facter::Util::Windows::Win32Ole', ProductType: prod, OtherTypeDescription: type) }
32 let(:prod) { 1 }
33 let(:type) {}
34
35 it 'returns consumerrel true' do
36 expect(Facter::Resolvers::WinOsDescription.resolve(:consumerrel)).to be(true)
37 end
38
39 it 'returns description as nil' do
40 expect(Facter::Resolvers::WinOsDescription.resolve(:description)).to be(nil)
41 end
42 end
43
44 describe '#resolve when product type is nil' do
45 let(:comp) { double('Facter::Util::Windows::Win32Ole', ProductType: prod, OtherTypeDescription: type) }
46 let(:prod) { nil }
47 let(:type) { 'description' }
48
49 it 'returns consumerrel false' do
50 expect(Facter::Resolvers::WinOsDescription.resolve(:consumerrel)).to be(false)
51 end
52
53 it 'returns description' do
54 expect(Facter::Resolvers::WinOsDescription.resolve(:description)).to eql('description')
55 end
56 end
57 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Wpar do
3 let(:log_spy) { instance_spy(Facter::Log) }
4
5 before do
6 Facter::Resolvers::Wpar.instance_variable_set(:@log, log_spy)
7 allow(Facter::Core::Execution).to receive(:execute)
8 .with('/usr/bin/lparstat -W', logger: log_spy)
9 .and_return(open3_result)
10 end
11
12 after do
13 Facter::Resolvers::Wpar.invalidate_cache
14 end
15
16 describe '#oslevel 6.1+' do
17 let(:open3_result) { load_fixture('lparstat_w').read }
18
19 it 'returns wpar_key' do
20 expect(Facter::Resolvers::Wpar.resolve(:wpar_key)).to eq(13)
21 end
22
23 it 'returns wpar_configured_id' do
24 expect(Facter::Resolvers::Wpar.resolve(:wpar_configured_id)).to eq(14)
25 end
26 end
27
28 describe '#oslevel 6.0' do
29 let(:open3_result) { '' }
30
31 it 'does not return wpar_key' do
32 expect(Facter::Resolvers::Wpar.resolve(:wpar_key)).to be_nil
33 end
34
35 it 'does not return wpar_configured_id' do
36 expect(Facter::Resolvers::Wpar.resolve(:wpar_configured_id)).to be_nil
37 end
38 end
39 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Xen do
3 subject(:xen_resolver) { Facter::Resolvers::Xen }
4
5 let(:proc_xen_file) { false }
6 let(:xvda1_file) { false }
7 let(:log_spy) { instance_spy(Facter::Log) }
8 let(:domains) { '' }
9
10 before do
11 xen_resolver.instance_variable_set(:@log, log_spy)
12 allow(File).to receive(:exist?).with('/dev/xen/evtchn').and_return(evtchn_file)
13 allow(File).to receive(:exist?).with('/proc/xen').and_return(proc_xen_file)
14 allow(File).to receive(:exist?).with('/dev/xvda1').and_return(xvda1_file)
15 allow(File).to receive(:exist?).with('/usr/lib/xen-common/bin/xen-toolstack').and_return(false)
16 allow(File).to receive(:exist?).with('/usr/sbin/xl').and_return(false)
17 allow(File).to receive(:exist?).with('/usr/sbin/xm').and_return(true)
18 allow(Facter::Core::Execution).to receive(:execute).with('/usr/sbin/xm list', logger: log_spy).and_return(domains)
19
20 xen_resolver.invalidate_cache
21 end
22
23 after do
24 xen_resolver.invalidate_cache
25 end
26
27 context 'when xen is privileged' do
28 context 'when /dev/xen/evtchn exists' do
29 let(:domains) { load_fixture('xen_domains').read }
30 let(:evtchn_file) { true }
31
32 it 'returns xen0' do
33 expect(xen_resolver.resolve(:vm)).to eq('xen0')
34 end
35
36 it 'detects xen as privileged' do
37 expect(xen_resolver.resolve(:privileged)).to be_truthy
38 end
39
40 it 'does not check other files' do
41 expect(File).not_to have_received(:exist?).with('/proc/xen')
42 end
43
44 it 'returns domains' do
45 expect(xen_resolver.resolve(:domains)).to eq(%w[win linux])
46 end
47 end
48
49 context 'when /dev/xen/evtchn does not exist' do
50 let(:evtchn_file) { false }
51
52 before do
53 allow(Facter::Util::FileHelper)
54 .to receive(:safe_read)
55 .with('/proc/xen/capabilities', nil)
56 .and_return('control_d')
57 end
58
59 it 'detects xen as privileged' do
60 expect(xen_resolver.resolve(:privileged)).to be_truthy
61 end
62 end
63 end
64
65 context 'when xen is unprivileged' do
66 let(:evtchn_file) { false }
67 let(:xvda1_file) { true }
68
69 before do
70 allow(Facter::Util::FileHelper)
71 .to receive(:safe_read)
72 .with('/proc/xen/capabilities', nil)
73 .and_return(nil)
74 end
75
76 it 'returns xenu' do
77 expect(xen_resolver.resolve(:vm)).to eq('xenu')
78 end
79
80 it 'detects xen as unprivileged' do
81 expect(xen_resolver.resolve(:privileged)).to be_falsey
82 end
83
84 it 'does not detect domains' do
85 expect(xen_resolver.resolve(:domains)).to be_nil
86 end
87 end
88 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::ZFS do
3 subject(:zfs_resolver) { Facter::Resolvers::ZFS }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 zfs_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('zfs upgrade -v', logger: log_spy)
11 .and_return(output)
12 end
13
14 after do
15 zfs_resolver.invalidate_cache
16 end
17
18 context 'when zfs command is found' do
19 let(:output) { load_fixture('zfs').read }
20
21 it 'returns zfs version fact' do
22 expect(zfs_resolver.resolve(:zfs_version)).to eq('6')
23 end
24
25 it 'returns zfs featurenumbers fact' do
26 expect(zfs_resolver.resolve(:zfs_featurenumbers)).to eq('1,2,3,4,5,6')
27 end
28 end
29
30 context 'when zfs command is not found' do
31 let(:output) { '' }
32
33 it 'returns nil for zfs version fact' do
34 expect(zfs_resolver.resolve(:zfs_version)).to eq(nil)
35 end
36
37 it 'returns nil for zfs featurenumbers fact' do
38 expect(zfs_resolver.resolve(:zfs_featurenumbers)).to eq(nil)
39 end
40 end
41 end
0 # frozen_string_literal: true
1
2 describe Facter::Resolvers::Zpool do
3 subject(:zpool_resolver) { Facter::Resolvers::Zpool }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 before do
8 zpool_resolver.instance_variable_set(:@log, log_spy)
9 allow(Facter::Core::Execution).to receive(:execute)
10 .with('zpool upgrade -v', logger: log_spy)
11 .and_return(output)
12 end
13
14 after do
15 Facter::Resolvers::Zpool.invalidate_cache
16 end
17
18 context 'when zpool command is found' do
19 let(:output) { load_fixture('zpool').read }
20
21 it 'return zpool version fact' do
22 expect(zpool_resolver.resolve(:zpool_version)).to eq('34')
23 end
24
25 it 'return zpool featurenumbers fact' do
26 expect(zpool_resolver.resolve(:zpool_featurenumbers)).to eq('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,'\
27 '18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34')
28 end
29
30 context 'when zpool has featureflags' do
31 let(:output) { load_fixture('zpool-with-featureflags').read }
32 let(:zpool_featureflags) do
33 'async_destroy,empty_bpobj,lz4_compress,multi_vdev_crash_dump,spacemap_histogram,enabled_txg,' \
34 'hole_birth,extensible_dataset,embedded_data,bookmarks,filesystem_limits,large_blocks,large_dnode,' \
35 'sha512,skein,device_removal,obsolete_counts,zpool_checkpoint,spacemap_v2'
36 end
37
38 it 'returns zpool version fact' do
39 result = Facter::Resolvers::Zpool.resolve(:zpool_version)
40 expect(result).to eq('5000')
41 end
42
43 it 'returns zpool featureflags fact' do
44 result = Facter::Resolvers::Zpool.resolve(:zpool_featureflags)
45 expect(result).to eq(zpool_featureflags)
46 end
47 end
48 end
49
50 context 'when zpool command is not found' do
51 let(:output) { 'zpool command not found' }
52
53 it 'returns nil for zpool version fact' do
54 expect(zpool_resolver.resolve(:zpool_version)).to eq(nil)
55 end
56
57 it 'returns nil for zpool featureversion fact' do
58 expect(zpool_resolver.resolve(:zpool_featurenumbers)).to eq(nil)
59 end
60 end
61 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Aix::ODMQuery do
3 let(:odm_query) { Facter::Util::Aix::ODMQuery.new }
4 let(:log_spy) { instance_spy(Facter::Log) }
5
6 before do
7 odm_query.instance_variable_set(:@log, log_spy)
8 stub_const('Facter::Util::Aix::ODMQuery::REPOS', ['CuAt'])
9 end
10
11 it 'creates a query' do
12 odm_query.equals('name', '12345')
13
14 expect(Facter::Core::Execution).to receive(:execute).with("odmget -q \"name='12345'\" CuAt", logger: log_spy)
15 odm_query.execute
16 end
17
18 it 'can chain conditions' do
19 odm_query.equals('field1', 'value').like('field2', 'value*')
20
21 expect(Facter::Core::Execution).to receive(:execute)
22 .with("odmget -q \"field1='value' AND field2 like 'value*'\" CuAt", logger: log_spy)
23 odm_query.execute
24 end
25 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Facts do
3 subject(:facts_util) { Facter::Util::Facts }
4
5 describe '#discover_family' do
6 it "discovers Fedora's family" do
7 expect(facts_util.discover_family('rhel fedora')).to eq('RedHat')
8 end
9
10 it "discovers Centos's family" do
11 expect(facts_util.discover_family('rhel fedora centos')).to eq('RedHat')
12 end
13
14 it "discovers PSBM's family" do
15 expect(facts_util.discover_family('PSBM')).to eq('RedHat')
16 end
17
18 it "discovers Virtuozzo's family" do
19 expect(facts_util.discover_family('VirtuozzoLinux')).to eq('RedHat')
20 end
21
22 it "discovers SLED's family" do
23 expect(facts_util.discover_family('SLED')).to eq('Suse')
24 end
25
26 it "discovers KDE's family" do
27 expect(facts_util.discover_family('KDE')).to eq('Debian')
28 end
29
30 it "discovers HuaweiOS's family" do
31 expect(facts_util.discover_family('HuaweiOS')).to eq('Debian')
32 end
33
34 it "discovers Gentoo's family" do
35 expect(facts_util.discover_family('gentoo')).to eq('Gentoo')
36 end
37
38 it "discovers Manjaro's family" do
39 expect(facts_util.discover_family('Manjaro')).to eq('Archlinux')
40 end
41
42 it "discovers Mandriva's family" do
43 expect(facts_util.discover_family('Mandriva')).to eq('Mandrake')
44 end
45 end
46
47 describe '#release_hash_from_string' do
48 it 'returns full release' do
49 expect(facts_util.release_hash_from_string('6.2')['full']).to eq('6.2')
50 end
51
52 it 'returns major release' do
53 expect(facts_util.release_hash_from_string('6.2')['major']).to eq('6')
54 end
55
56 it 'returns valid minor release' do
57 expect(facts_util.release_hash_from_string('6.2.1')['minor']).to eq('2')
58 end
59
60 it 'returns minor release as nil' do
61 expect(facts_util.release_hash_from_string('6')['minor']).to be_nil
62 end
63
64 it 'returns nil if data is nil' do
65 expect(facts_util.release_hash_from_string(nil)).to be_nil
66 end
67 end
68
69 describe '#release_hash_from_matchdata' do
70 let(:match_data) do
71 'RELEASE=4.3' =~ /^RELEASE=(\d+.\d+.*)/
72 Regexp.last_match
73 end
74
75 it 'returns full release' do
76 expect(facts_util.release_hash_from_matchdata(match_data)['full']).to eq('4.3')
77 end
78
79 it 'returns major release' do
80 expect(facts_util.release_hash_from_matchdata(match_data)['major']).to eq('4')
81 end
82
83 it 'returns valid minor release' do
84 expect(facts_util.release_hash_from_matchdata(match_data)['minor']).to eq('3')
85 end
86
87 it 'returns nil if data is nil' do
88 expect(facts_util.release_hash_from_matchdata(nil)).to be_nil
89 end
90
91 context 'when minor version is unavailable' do
92 let(:match_data) do
93 'RELEASE=4' =~ /^RELEASE=(\d+)/
94 Regexp.last_match
95 end
96
97 it 'returns minor release as nil' do
98 expect(facts_util.release_hash_from_matchdata(match_data)['minor']).to be_nil
99 end
100 end
101 end
102 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Facts::Posix::VirtualDetector do
3 subject(:detector) { Facter::Util::Facts::Posix::VirtualDetector }
4
5 describe '#platform' do
6 let(:logger_mock) { instance_spy(Facter::Log) }
7
8 before do
9 Facter::Util::Facts::Posix::VirtualDetector.class_variable_set(:@@fact_value, nil)
10 allow(Facter::Log).to receive(:new).and_return(logger_mock)
11 end
12
13 context 'when in docker' do
14 let(:vm) { 'docker' }
15
16 before do
17 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(vm)
18 end
19
20 it 'calls Facter::Resolvers::Containers' do
21 detector.platform
22 expect(Facter::Resolvers::Containers).to have_received(:resolve).with(:vm)
23 end
24
25 it 'returns container type' do
26 expect(detector.platform).to eq(vm)
27 end
28 end
29
30 context 'when FreeBSD' do
31 let(:value) { 'jail' }
32
33 before do
34 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
35 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return('jail')
36 end
37
38 it 'calls Facter::Resolvers::Freebsd::Virtual' do
39 detector.platform
40
41 expect(Facter::Resolvers::Freebsd::Virtual).to have_received(:resolve).with(:vm)
42 end
43
44 it 'returns jail' do
45 expect(detector.platform).to eq(value)
46 end
47 end
48
49 context 'when gce' do
50 let(:value) { 'gce' }
51
52 before do
53 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
54 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
55 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('Google Engine')
56 end
57
58 it 'calls Facter::Resolvers::Linux::DmiBios' do
59 detector.platform
60
61 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_vendor)
62 end
63
64 it 'returns gce' do
65 expect(detector.platform).to eq(value)
66 end
67 end
68
69 context 'when xen-hvm' do
70 let(:value) { 'xenhvm' }
71
72 before do
73 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
74 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
75 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
76 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(value)
77 end
78
79 it 'calls Facter::Resolvers::VirtWhat' do
80 detector.platform
81
82 expect(Facter::Resolvers::VirtWhat). to have_received(:resolve).with(:vm)
83 end
84
85 it 'returns xen' do
86 expect(detector.platform).to eq(value)
87 end
88 end
89
90 context 'when vmware' do
91 let(:value) { 'vmware_fusion' }
92
93 before do
94 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
95 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
96 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
97 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
98 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(value)
99 end
100
101 it 'calls Facter::Resolvers::Vmware' do
102 detector.platform
103
104 expect(Facter::Resolvers::Vmware).to have_received(:resolve).with(:vm)
105 end
106
107 it 'returns vmware' do
108 expect(detector.platform).to eq(value)
109 end
110 end
111
112 context 'when openVz' do
113 let(:value) { 'openvzve' }
114
115 before do
116 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
117 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
118 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
119 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
120 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
121 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(value)
122 end
123
124 it 'calls Facter::Resolvers::OpenVz' do
125 detector.platform
126
127 expect(Facter::Resolvers::OpenVz).to have_received(:resolve).with(:vm)
128 end
129
130 it 'returns openvz' do
131 expect(detector.platform).to eq(value)
132 end
133 end
134
135 context 'when vserver' do
136 let(:value) { 'vserver_host' }
137
138 before do
139 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
140 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
141 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
142 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
143 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
144 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
145 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(value)
146 end
147
148 it 'calls Facter::Resolvers::VirtWhat' do
149 detector.platform
150
151 expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vserver)
152 end
153
154 it 'returns vserver' do
155 expect(detector.platform).to eq(value)
156 end
157 end
158
159 context 'when xen' do
160 let(:value) { 'xen0' }
161
162 before do
163 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
164 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
165 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
166 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
167 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
168 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
169 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(nil)
170 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(value)
171 end
172
173 it 'calls Facter::Resolvers::Xen' do
174 detector.platform
175
176 expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:vm)
177 end
178
179 it 'returns xen' do
180 expect(detector.platform).to eq(value)
181 end
182 end
183
184 context 'when bochs' do
185 let(:value) { 'bochs' }
186
187 before do
188 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
189 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
190 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
191 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
192 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
193 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(nil)
194 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
195 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
196 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('Bochs Machine')
197 end
198
199 it 'calls Facter::Resolvers::Linux::DmiBios with bios_vendor' do
200 detector.platform
201
202 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:bios_vendor).twice
203 end
204
205 it 'calls Facter::Resolvers::Linux::DmiBios with product_name' do
206 detector.platform
207
208 expect(Facter::Resolvers::Linux::DmiBios).to have_received(:resolve).with(:product_name)
209 end
210
211 it 'returns bochs' do
212 expect(detector.platform).to eq(value)
213 end
214 end
215
216 context 'when hyper-v' do
217 let(:value) { 'hyperv' }
218
219 before do
220 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
221 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
222 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
223 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
224 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
225 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(nil)
226 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
227 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
228 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return(nil)
229 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return(value)
230 end
231
232 it 'calls Facter::Resolvers::Lspci' do
233 detector.platform
234
235 expect(Facter::Resolvers::Lspci).to have_received(:resolve).with(:vm)
236 end
237
238 it 'returns hyper-v' do
239 expect(detector.platform).to eq(value)
240 end
241 end
242
243 context 'when all resolvers return nil ' do
244 let(:vm) { 'physical' }
245
246 before do
247 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
248 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
249 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
250 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
251 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
252 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(nil)
253 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
254 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return(nil)
255 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return(nil)
256 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return(nil)
257 end
258
259 it 'returns physical' do
260 expect(detector.platform).to eq(vm)
261 end
262 end
263
264 context 'when product name is not found in the HYPERVISORS_HASH' do
265 let(:vm) { 'physical' }
266
267 before do
268 allow(Facter::Resolvers::Containers).to receive(:resolve).with(:vm).and_return(nil)
269 allow(Facter::Resolvers::Freebsd::Virtual).to receive(:resolve).with(:vm).and_return(nil)
270 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(nil)
271 allow(Facter::Resolvers::Vmware).to receive(:resolve).with(:vm).and_return(nil)
272 allow(Facter::Resolvers::OpenVz).to receive(:resolve).with(:vm).and_return(nil)
273 allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vserver).and_return(nil)
274 allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return(nil)
275 allow(Facter::Resolvers::Lspci).to receive(:resolve).with(:vm).and_return(nil)
276 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:bios_vendor).and_return('unknown')
277 allow(Facter::Resolvers::Linux::DmiBios).to receive(:resolve).with(:product_name).and_return('unknown')
278 end
279
280 it 'returns virtual fact as physical' do
281 expect(detector.platform).to eq(vm)
282 end
283 end
284 end
285 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Facts::UnitConverter do
3 subject(:converter) { Facter::Util::Facts::UnitConverter }
4
5 describe '#bytes_to_mb' do
6 it 'converts bytes to mega bytes' do
7 expect(converter.bytes_to_mb(256_586_343)).to eq(244.6998052597046)
8 end
9
10 it 'returns nil if value is nil' do
11 expect(converter.bytes_to_mb(nil)).to be(nil)
12 end
13
14 it 'converts bytes if value is string' do
15 expect(converter.bytes_to_mb('2343455')).to eq(2.2348928451538086)
16 end
17
18 it 'returns 0 if value is 0' do
19 expect(converter.bytes_to_mb(0)).to eq(0.0)
20 end
21 end
22
23 describe '#hertz_to_human_readable' do
24 it 'returns nil if value is 0' do
25 expect(converter.hertz_to_human_readable(0)).to be_nil
26 end
27
28 it 'returns nil if value is string' do
29 expect(converter.hertz_to_human_readable('test')).to be_nil
30 end
31
32 it 'converts to GHz' do
33 expect(converter.hertz_to_human_readable(2_300_000_000)).to eql('2.30 GHz')
34 end
35
36 it 'converts to MHz' do
37 expect(converter.hertz_to_human_readable(800_000_000)).to eql('800.00 MHz')
38 end
39
40 it 'handles small-ish number correctly' do
41 expect(converter.hertz_to_human_readable(42)).to eql('42.00 Hz')
42 end
43
44 it 'converts to Hz even if argument is string' do
45 expect(converter.hertz_to_human_readable('2400')).to eql('2.40 kHz')
46 end
47
48 it 'rounds down when 3rd decimal is lower than 5' do
49 expect(converter.hertz_to_human_readable(2_363_000_000)).to eql('2.36 GHz')
50 end
51
52 it 'rounds up when 3rd decimal is equal with 5' do
53 expect(converter.hertz_to_human_readable(2_365_000_000)).to eql('2.37 GHz')
54 end
55
56 it 'rounds up when 3rd decimal is greater than 5' do
57 expect(converter.hertz_to_human_readable(2_367_000_000)).to eql('2.37 GHz')
58 end
59 end
60
61 describe '#bytes_to_human_readable' do
62 it 'returns nil if bytes variable is nil' do
63 expect(converter.bytes_to_human_readable(nil)).to be(nil)
64 end
65
66 it 'returns next unit if result is 1024 after conversion' do
67 expect(converter.bytes_to_human_readable(1_048_575.7)).to eql('1.00 MiB')
68 end
69
70 it 'returns bytes if bytes variable is less than 1024' do
71 expect(converter.bytes_to_human_readable(1023)).to eql('1023 bytes')
72 end
73
74 it 'returns 1 Kib if bytes variable equals 1024' do
75 expect(converter.bytes_to_human_readable(1024)).to eql('1.00 KiB')
76 end
77
78 it 'returns bytes if number exceeds etta bytes' do
79 expect(converter.bytes_to_human_readable(3_296_472_651_763_232_323_235)).to eql('3296472651763232323235 bytes')
80 end
81 end
82
83 describe '#pad_number' do
84 it 'appends a 0 when conversion has one decimal digit' do
85 expect(converter.send(:pad_number, 10.0)).to eql('10.00')
86 end
87
88 it 'leaves the value unmodified if it has two decimals' do
89 expect(converter.send(:pad_number, 10.23)).to eql('10.23')
90 end
91 end
92 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Facts::UptimeParser do
3 describe '.uptime_seconds_unix' do
4 let(:uptime_proc_file_cmd) { '/bin/cat /proc/uptime' }
5 let(:kern_boottime_cmd) { 'sysctl -n kern.boottime' }
6 let(:uptime_cmd) { 'uptime' }
7 let(:log_spy) { instance_spy(Facter::Log) }
8
9 before do
10 Facter::Util::Facts::UptimeParser.instance_variable_set(:@log, log_spy)
11 end
12
13 context 'when a /proc/uptime file exists' do
14 let(:proc_uptime_value) { '2672.69 20109.75' }
15
16 it 'returns the correct result' do
17 allow(Facter::Core::Execution)
18 .to receive(:execute)
19 .with(uptime_proc_file_cmd, logger: log_spy)
20 .and_return(proc_uptime_value)
21
22 expect(Facter::Util::Facts::UptimeParser.uptime_seconds_unix).to eq(2672)
23 end
24 end
25
26 context 'when a sysctl kern.boottime command is available only' do
27 let!(:time_now) { Time.parse('2019-10-10 11:00:00 +0100') }
28 let(:kern_boottime_value) do
29 '{ sec = 60, usec = 0 } Tue Oct 10 10:59:00 2019'
30 end
31
32 it 'returns the correct result' do
33 allow(Time).to receive(:at).with(60) { Time.parse('2019-10-10 10:59:00 +0100') }
34 allow(Time).to receive(:now) { time_now }
35
36 allow(Facter::Core::Execution)
37 .to receive(:execute)
38 .with(uptime_proc_file_cmd, logger: log_spy)
39 .and_return('')
40
41 allow(Facter::Core::Execution)
42 .to receive(:execute)
43 .with(kern_boottime_cmd, logger: log_spy)
44 .and_return(kern_boottime_value)
45
46 expect(Facter::Util::Facts::UptimeParser.uptime_seconds_unix).to eq(60)
47 end
48 end
49
50 context 'when the uptime executable is available only' do
51 before do
52 allow(Facter::Core::Execution)
53 .to receive(:execute)
54 .with(uptime_proc_file_cmd, logger: log_spy)
55 .and_return('')
56
57 allow(Facter::Core::Execution)
58 .to receive(:execute)
59 .with(kern_boottime_cmd, logger: log_spy)
60 .and_return('')
61 end
62
63 shared_examples 'uptime executable regex expectation' do |cmd_output, result|
64 it 'returns the correct result' do
65 allow(Facter::Core::Execution)
66 .to receive(:execute)
67 .with(uptime_cmd, logger: log_spy)
68 .and_return(cmd_output)
69
70 expect(Facter::Util::Facts::UptimeParser.uptime_seconds_unix).to eq(result)
71 end
72 end
73
74 context 'when the output matches days, hours and minutes regex' do
75 include_examples(
76 'uptime executable regex expectation',
77 '10:00AM up 2 days, 1:00, 1 user, load average: 1.00, 0.75, 0.66',
78 176_400
79 )
80 end
81
82 context 'when the output matches days and hours regex' do
83 include_examples(
84 'uptime executable regex expectation',
85 '10:00AM up 2 days, 1 hr(s), 1 user, load average: 1.00, 0.75, 0.66',
86 176_400
87 )
88 end
89
90 context 'when the output matches days and minutes regex' do
91 include_examples(
92 'uptime executable regex expectation',
93 '10:00AM up 2 days, 60 min(s), 1 user, load average: 1.00, 0.75, 0.66',
94 176_400
95 )
96 end
97
98 context 'when the output matches days regex' do
99 include_examples(
100 'uptime executable regex expectation',
101 '10:00AM up 2 days, 1 user, load average: 1.00, 0.75, 0.66',
102 172_800
103 )
104 end
105
106 context 'when the output matches hours and minutes regex' do
107 include_examples(
108 'uptime executable regex expectation',
109 '10:00AM up 49:00, 1 user, load average: 1.00, 0.75, 0.66',
110 176_400
111 )
112 end
113
114 context 'when the output matches hours regex' do
115 include_examples(
116 'uptime executable regex expectation',
117 '10:00AM up 49 hr(s), 1 user, load average: 1.00, 0.75, 0.66',
118 176_400
119 )
120 end
121
122 context 'when the output matches minutes regex' do
123 include_examples(
124 'uptime executable regex expectation',
125 '10:00AM up 2940 mins, 1 user, load average: 1.00, 0.75, 0.66',
126 176_400
127 )
128 end
129
130 context 'when the output does not match any conditional regex' do
131 include_examples(
132 'uptime executable regex expectation',
133 'running for 2 days',
134 0
135 )
136 end
137 end
138 end
139 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Facts::WindowsReleaseFinder do
3 let(:input) { { consumerrel: cons, description: desc, kernel_version: k_version, version: version } }
4
5 describe '#find windows release when version nil' do
6 let(:cons) { false }
7 let(:desc) {}
8 let(:k_version) {}
9 let(:version) { nil }
10
11 it 'returns nil' do
12 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to be(nil)
13 end
14 end
15
16 describe '#find windows release when version is 10' do
17 let(:cons) { true }
18 let(:desc) {}
19 let(:k_version) { '10.0.123' }
20 let(:version) { '10.0' }
21
22 it 'returns 10' do
23 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('10')
24 end
25 end
26
27 describe '#find windows release when version is 11' do
28 let(:cons) { true }
29 let(:desc) {}
30 let(:k_version) { '10.0.22000' }
31 let(:version) { '10.0' }
32
33 it 'returns 11' do
34 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('11')
35 end
36 end
37
38 describe '#find windows release when version is 2022' do
39 let(:cons) { false }
40 let(:desc) {}
41 let(:k_version) { '10.0.20348' }
42 let(:version) { '10.0' }
43
44 it 'returns 2022' do
45 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2022')
46 end
47 end
48
49 describe '#find windows release when version is 2019' do
50 let(:cons) { false }
51 let(:desc) {}
52 let(:k_version) { '10.0.17623' }
53 let(:version) { '10.0' }
54
55 it 'returns 2019' do
56 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2019')
57 end
58 end
59
60 describe '#find windows release when version is 2016' do
61 let(:cons) { false }
62 let(:desc) {}
63 let(:k_version) { '10.0.176' }
64 let(:version) { '10.0' }
65
66 it 'returns 2016' do
67 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2016')
68 end
69 end
70
71 describe '#find windows release when version is 8.1' do
72 let(:cons) { true }
73 let(:desc) {}
74 let(:k_version) {}
75 let(:version) { '6.3' }
76
77 it 'returns 8.1' do
78 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('8.1')
79 end
80 end
81
82 describe '#find windows release when version is 2012 R2' do
83 let(:cons) { false }
84 let(:desc) {}
85 let(:k_version) {}
86 let(:version) { '6.3' }
87
88 it 'returns 2012 R2' do
89 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2012 R2')
90 end
91 end
92
93 describe '#find windows release when version is XP' do
94 let(:cons) { true }
95 let(:desc) {}
96 let(:k_version) {}
97 let(:version) { '5.2' }
98
99 it 'returns XP' do
100 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('XP')
101 end
102 end
103
104 describe '#find windows release when version is 2003' do
105 let(:cons) { false }
106 let(:desc) {}
107 let(:k_version) {}
108 let(:version) { '5.2' }
109
110 it 'returns 2003' do
111 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2003')
112 end
113 end
114
115 describe '#find windows release when version is 2003 R2' do
116 let(:cons) { false }
117 let(:desc) { 'R2' }
118 let(:k_version) {}
119 let(:version) { '5.2' }
120
121 it 'returns 2003 R2' do
122 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('2003 R2')
123 end
124 end
125
126 describe '#find windows release when version is 4.2' do
127 let(:cons) { false }
128 let(:desc) { 'R2' }
129 let(:k_version) {}
130 let(:version) { '4.2' }
131
132 it 'returns 4.2' do
133 expect(Facter::Util::Facts::WindowsReleaseFinder.find_release(input)).to eql('4.2')
134 end
135 end
136 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::FileHelper do
3 subject(:file_helper) { Facter::Util::FileHelper }
4
5 let(:path) { '/Users/admin/file.txt' }
6 let(:entries) { ['file.txt', 'a'] }
7 let(:content) { 'file content' }
8 let(:error_message) do
9 "Facter::Util::FileHelper - #{Facter::CYAN}File at: /Users/admin/file.txt is not accessible.#{Facter::RESET}"
10 end
11 let(:array_content) { ['line 1', 'line 2', 'line 3'] }
12 let(:logger_double) { instance_spy(Facter::Log) }
13
14 before do
15 Facter::Log.class_variable_set(:@@logger, logger_double)
16 allow(Facter).to receive(:debugging?).and_return(true)
17 allow(Facter::OptionStore).to receive(:color).and_return(true)
18 end
19
20 shared_context 'when file is readable' do
21 before do
22 allow(File).to receive(:readable?).with(path).and_return(true)
23 end
24 end
25
26 shared_context 'when file is not readable' do
27 before do
28 allow(File).to receive(:readable?).with(path).and_return(false)
29 end
30 end
31
32 describe '#safe_read' do
33 before do
34 allow(File).to receive(:read).with(path, anything).and_return(content)
35 end
36
37 context 'when successfully read the file content' do
38 include_context 'when file is readable'
39
40 it 'returns the file content' do
41 expect(file_helper.safe_read(path)).to eq(content)
42 end
43
44 it 'returns the file content as UTF-8' do
45 expect(file_helper.safe_read(path).encoding.name).to eq('UTF-8')
46 end
47
48 it 'File.readable? is called with the correct path' do
49 file_helper.safe_read(path)
50
51 expect(File).to have_received(:readable?).with(path)
52 end
53
54 it 'File.read is called with the correct path' do
55 file_helper.safe_read(path)
56
57 expect(File).to have_received(:read).with(path, anything)
58 end
59
60 it "doesn't log anything" do
61 file_helper.safe_read(path)
62
63 expect(logger_double).not_to have_received(:debug)
64 end
65 end
66
67 context 'when failed to read the file content' do
68 include_context 'when file is not readable'
69
70 it 'returns empty string by default' do
71 expect(file_helper.safe_read(path)).to eq('')
72 end
73
74 it 'returns nil' do
75 expect(file_helper.safe_read(path, nil)).to eq(nil)
76 end
77
78 it 'File.readable? is called with the correct path' do
79 file_helper.safe_read(path)
80
81 expect(File).to have_received(:readable?).with(path)
82 end
83
84 it 'File.read is not called' do
85 file_helper.safe_read(path)
86
87 expect(File).not_to have_received(:read)
88 end
89
90 it 'logs a debug message' do
91 file_helper.safe_read(path)
92
93 expect(logger_double).to have_received(:debug)
94 .with(error_message)
95 end
96 end
97 end
98
99 describe '#dir_children' do
100 context 'with ruby < 2.5' do
101 before do
102 allow(Dir).to receive(:entries).with(File.dirname(path)).and_return(entries + ['.', '..'])
103 stub_const('RUBY_VERSION', '2.4.5')
104 end
105
106 it 'delegates to Dir.entries' do
107 file_helper.dir_children(File.dirname(path))
108 expect(Dir).to have_received(:entries)
109 end
110
111 it 'correctly resolves entries' do
112 expect(file_helper.dir_children(File.dirname(path))).to eq(entries)
113 end
114 end
115
116 context 'with ruby >= 2.5', if: RUBY_VERSION.to_f >= 2.5 do
117 before do
118 allow(Dir).to receive(:children).with(File.dirname(path)).and_return(entries)
119 stub_const('RUBY_VERSION', '2.5.1')
120 end
121
122 it 'delegates to Dir.children' do
123 file_helper.dir_children(File.dirname(path))
124 expect(Dir).to have_received(:children)
125 end
126
127 it 'correctly resolves entries' do
128 expect(file_helper.dir_children(File.dirname(path))).to eq(entries)
129 end
130 end
131 end
132
133 describe '#safe_read_lines' do
134 before do
135 allow(File).to receive(:readlines).with(path, anything).and_return(array_content)
136 end
137
138 context 'when successfully read the file lines' do
139 include_context 'when file is readable'
140
141 it 'returns the file content in an array of lines' do
142 expect(file_helper.safe_readlines(path)).to eq(array_content)
143 end
144
145 it 'File.readable? is called with the correct path' do
146 file_helper.safe_readlines(path)
147
148 expect(File).to have_received(:readable?).with(path)
149 end
150
151 it 'File.readlines is called with the correct path' do
152 file_helper.safe_readlines(path)
153
154 expect(File).to have_received(:readlines).with(path, anything)
155 end
156
157 it "doesn't log anything" do
158 file_helper.safe_readlines(path)
159
160 expect(logger_double).not_to have_received(:debug)
161 end
162 end
163
164 context 'when failed to read the file lines' do
165 include_context 'when file is not readable'
166
167 it 'returns empty array by default' do
168 expect(file_helper.safe_readlines(path)).to eq([])
169 end
170
171 it 'returns nil' do
172 expect(file_helper.safe_readlines(path, nil)).to eq(nil)
173 end
174
175 it 'File.readable? is called with the correct path' do
176 file_helper.safe_readlines(path)
177
178 expect(File).to have_received(:readable?).with(path)
179 end
180
181 it 'File.readlines is not called' do
182 file_helper.safe_readlines(path)
183
184 expect(File).not_to have_received(:readlines)
185 end
186
187 it 'logs a debug message' do
188 file_helper.safe_read(path)
189
190 expect(logger_double).to have_received(:debug).with(error_message)
191 end
192 end
193 end
194 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Linux::Dhcp do
3 subject(:dhcp_search) { Facter::Util::Linux::Dhcp }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 describe '#dhcp' do
8 context 'when lease file has the dhcp' do
9 before do
10 allow(Facter::Util::FileHelper).to receive(:safe_read)
11 .with('/run/systemd/netif/leases/2', nil).and_return(load_fixture('dhcp_lease').read)
12 end
13
14 it 'returns dhcp ip' do
15 expect(dhcp_search.dhcp('ens160', 2, log_spy)).to eq('10.32.22.10')
16 end
17 end
18
19 context 'when dhclient lease file has the dhcp' do
20 before do
21 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/systemd/netif/leases/1', nil).and_return(nil)
22 allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(true)
23 allow(Dir).to receive(:entries).with('/var/lib/dhclient/').and_return(%w[dhclient.lo.leases dhclient.leases])
24 allow(Facter::Util::FileHelper).to receive(:safe_read)
25 .with('/var/lib/dhclient/dhclient.lo.leases', nil).and_return(load_fixture('dhclient_lease').read)
26 end
27
28 it 'returns dhcp ip' do
29 expect(dhcp_search.dhcp('lo', 1, log_spy)).to eq('10.32.22.9')
30 end
31 end
32
33 context 'when dhcp is available in the internal leases' do
34 let(:network_manager_files) do
35 %w[NetworkManager-intern.conf
36 secret_key
37 internal-fdgh45-345356fg-dfg-dsfge5er4-sdfghgf45ty-lo.lease
38 timestamps
39 NetworkManager.state
40 seen-bssids]
41 end
42
43 before do
44 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/systemd/netif/leases/1', nil).and_return(nil)
45 allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(false)
46 allow(File).to receive(:readable?).with('/var/lib/dhcp/').and_return(false)
47 allow(File).to receive(:readable?).with('/var/lib/dhcp3/').and_return(false)
48 allow(File).to receive(:readable?).with('/var/lib/NetworkManager/').and_return(true)
49 allow(Dir).to receive(:entries).with('/var/lib/NetworkManager/').and_return(network_manager_files)
50 allow(Facter::Util::FileHelper).to receive(:safe_read)
51 .with('/var/lib/NetworkManager/internal-fdgh45-345356fg-dfg-dsfge5er4-sdfghgf45ty-lo.lease', nil)
52 .and_return(load_fixture('dhcp_internal_lease').read)
53 allow(File).to receive(:readable?).with('/var/db/').and_return(false)
54 end
55
56 it 'returns dhcp ip' do
57 expect(dhcp_search.dhcp('lo', 1, log_spy)).to eq('35.32.82.9')
58 end
59 end
60
61 context 'when dhcp is available through the dhcpcd command' do
62 before do
63 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/systemd/netif/leases/1', nil).and_return(nil)
64 allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(false)
65 allow(File).to receive(:readable?).with('/var/lib/dhcp/').and_return(false)
66 allow(File).to receive(:readable?).with('/var/lib/dhcp3/').and_return(false)
67 allow(File).to receive(:readable?).with('/var/lib/NetworkManager/').and_return(false)
68 allow(File).to receive(:readable?).with('/var/db/').and_return(false)
69
70 allow(Facter::Core::Execution).to receive(:which)
71 .with('dhcpcd').and_return('/usr/bin/dhcpcd')
72 allow(Facter::Core::Execution).to receive(:execute)
73 .with('/usr/bin/dhcpcd -U ens160', logger: log_spy).and_return(load_fixture('dhcpcd').read)
74
75 dhcp_search.instance_eval { @dhcpcd_command = nil }
76 end
77
78 it 'returns dhcp ip' do
79 expect(dhcp_search.dhcp('ens160', 1, log_spy)).to eq('10.32.22.9')
80 end
81 end
82
83 context 'when dhcp is not available through the dhcpcd command' do
84 before do
85 allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/systemd/netif/leases/1', nil).and_return(nil)
86 allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(false)
87 allow(File).to receive(:readable?).with('/var/lib/dhcp/').and_return(false)
88 allow(File).to receive(:readable?).with('/var/lib/dhcp3/').and_return(false)
89 allow(File).to receive(:readable?).with('/var/lib/NetworkManager/').and_return(false)
90 allow(File).to receive(:readable?).with('/var/db/').and_return(false)
91
92 allow(Facter::Core::Execution).to receive(:which)
93 .with('dhcpcd').and_return(nil)
94
95 dhcp_search.instance_eval { @dhcpcd_command = nil }
96 end
97
98 it 'returns nil' do
99 expect(dhcp_search.dhcp('ens160', 1, log_spy)).to eq(nil)
100 end
101 end
102 end
103 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Linux::IfInet6 do
3 subject(:if_inet6) { Facter::Util::Linux::IfInet6 }
4
5 let(:simple) do
6 {
7 'ens160' => { 'fe80::250:56ff:fe9a:8481' => ['permanent'] },
8 'lo' => { '::1' => ['permanent'] }
9 }
10 end
11 let(:complex) do
12 {
13 'temporary' => { '2001:db8::1' => ['temporary'] },
14 'noad' => { '2001:db8::2' => ['noad'] },
15 'optimistic' => { '2001:db8::3' => ['optimistic'] },
16 'dadfailed' => { '2001:db8::4' => ['dadfailed'] },
17 'homeaddress' => { '2001:db8::5' => ['homeaddress'] },
18 'deprecated' => { '2001:db8::6' => ['deprecated'] },
19 'tentative' => { '2001:db8::7' => ['tentative'] },
20 'permanent' => { '2001:db8::8' => ['permanent'] },
21 'everything' => { '2001:db8::9' => %w[temporary noad optimistic dadfailed
22 homeaddress deprecated tentative permanent] },
23 'lo' => { '::1' => ['permanent'] }
24 }
25 end
26
27 describe '#read_flags' do
28 context 'when only ipv6 link-local and lo ipv6 present' do
29 before do
30 allow(File).to receive(:exist?).with('/proc/net/if_inet6').and_return(true)
31 allow(Facter::Util::FileHelper).to receive(:safe_read)
32 .with('/proc/net/if_inet6', nil).and_return(load_fixture('proc_net_if_inet6').read)
33 end
34
35 it { expect(if_inet6.read_flags).to eq(simple) }
36 end
37
38 context 'when multiple IPv6 addresses present with different flags' do
39 before do
40 allow(File).to receive(:exist?).with('/proc/net/if_inet6').and_return(true)
41 allow(Facter::Util::FileHelper).to receive(:safe_read)
42 .with('/proc/net/if_inet6', nil).and_return(load_fixture('proc_net_if_inet6_complex').read)
43 end
44
45 it { expect(if_inet6.read_flags).to eq(complex) }
46 end
47 end
48 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Linux::RoutingTable do
3 subject(:routing_parser) { Facter::Util::Linux::RoutingTable }
4
5 let(:log_spy) { instance_spy(Facter::Log) }
6
7 describe '#read_routing_table' do
8 context 'when ip route show finds an IP, Socket lib did not retrieve' do
9 before do
10 allow(Facter::Core::Execution).to receive(:execute)
11 .with('ip route show', logger: log_spy).and_return(load_fixture('ip_route_show').read)
12 allow(Facter::Core::Execution).to receive(:execute)
13 .with('ip -6 route show', logger: log_spy).and_return(load_fixture('ip_-6_route_show').read)
14 end
15
16 it 'returns the ipv4 info' do
17 expected = [{ interface: 'ens192', ip: '10.16.125.217' }]
18
19 expect(routing_parser.read_routing_table(log_spy)[0]).to eq(expected)
20 end
21
22 it 'returns no ipv6 info' do
23 expect(routing_parser.read_routing_table(log_spy)[1]).to eq([])
24 end
25 end
26 end
27 end
0 # frozen_string_literal: true
1
2 def ifaddr_obj(name, ip, mac, netmask, ipv4_type)
3 addr_info = instance_spy(AddrInfo, getnameinfo: [mac], inspect_sockaddr: "hwaddr=#{mac}",
4 ip_address: ip, ip?: true, ipv4?: ipv4_type)
5 netmask = instance_spy(AddrInfo, ip_address: netmask)
6 instance_spy(Ifaddr, name: name, addr: addr_info, netmask: netmask)
7 end
8
9 describe Facter::Util::Linux::SocketParser do
10 subject(:socket_parser) { Facter::Util::Linux::SocketParser }
11
12 let(:log_spy) { instance_spy(Facter::Log) }
13 let(:ifaddrs) do
14 [
15 ifaddr_obj('lo', '127.0.0.1', '00:00:00:00:00:00', '255.0.0.0', true),
16 ifaddr_obj('lo', '::1', '00:00:00:00:00:00', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', false),
17 ifaddr_obj('ens160', '10.16.119.155', '00:50:56:9a:61:46', '255.255.240.0', true),
18 ifaddr_obj('ens160', '10.16.127.70', '00:50:56:9a:61:46', '255.255.240.0', true),
19 ifaddr_obj('ens160', 'fe80::250:56ff:fe9a:8481', '00:50:56:9a:61:46', 'ffff:ffff:ffff:ffff::', false),
20 ifaddr_obj('ib0', '192.168.2.12', '80:00:02:08:fa:81:00:00:00:00:00:00:00:23:7d:ff:ff:94:73:fd', '255.255.255.0', true) # rubocop:disable Layout/LineLength
21 ]
22 end
23
24 describe '#retrieve_interfaces' do
25 before do
26 allow(Socket).to receive(:getifaddrs).and_return(ifaddrs)
27 allow(Socket).to receive(:const_defined?).with(:PF_LINK).and_return(true)
28 allow(Facter::Core::Execution).to receive(:execute)
29 .with('ip -o link show', logger: log_spy).and_return(load_fixture('ip_link_show_all').read)
30 end
31
32 let(:result) do
33 {
34 'lo' => {
35 bindings: [
36 { address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }
37 ],
38 bindings6: [
39 { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', network: '::1', scope6: 'host' }
40 ]
41 },
42 'ens160' => {
43 bindings: [
44 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' },
45 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
46 ],
47 bindings6: [
48 { address: 'fe80::250:56ff:fe9a:8481', netmask: 'ffff:ffff:ffff:ffff::', network: 'fe80::', scope6: 'link' }
49 ],
50 mac: '00:50:56:9a:61:46'
51 },
52 'ib0' => {
53 bindings: [
54 { address: '192.168.2.12', netmask: '255.255.255.0', network: '192.168.2.0' }
55 ],
56 mac: '80:00:02:08:fa:81:00:00:00:00:00:00:00:23:7d:ff:ff:94:73:fd'
57 }
58 }
59 end
60
61 it 'returns all the interfaces' do
62 expect(socket_parser.retrieve_interfaces(log_spy)).to eq(result)
63 end
64
65 context 'when bonded interfaces are present' do
66 let(:ifaddrs) do
67 [
68 ifaddr_obj('lo', '127.0.0.1', '00:00:00:00:00:00', '255.0.0.0', true),
69 ifaddr_obj('lo', '::1', '00:00:00:00:00:00', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', false),
70 ifaddr_obj('ens160', '10.16.119.155', '00:50:56:9a:61:46', '255.255.240.0', true),
71 ifaddr_obj('eth2', '10.16.127.70', '08:00:27:29:dc:a5', '255.255.240.0', true),
72 ifaddr_obj('eth3', '11.11.0.1', '08:00:27:29:dc:a5', '255.255.0.0', true),
73 ifaddr_obj('bond0', '11.0.0.3', '08:00:27:29:dc:a5', '255.255.0.0', true)
74 ]
75 end
76
77 before do
78 allow(Facter::Core::Execution).to receive(:execute)
79 .with('ip -o link show', logger: log_spy).and_return(load_fixture('ip_link_show_all').read)
80 allow(Facter::Util::FileHelper).to receive(:safe_read)
81 .with('/proc/net/bonding/bond0', nil).and_return(load_fixture('bond_interface_data').read)
82 end
83
84 it 'retrieves eth2 interface' do
85 expected = {
86 bindings: [
87 { address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }
88 ],
89 mac: '08:00:27:29:dc:a5'
90 }
91
92 expect(socket_parser.retrieve_interfaces(log_spy)['eth2']).to eq(expected)
93 end
94
95 it 'uses the mac from /proc/net/bonding/bond0 for eth3' do
96 result = socket_parser.retrieve_interfaces(log_spy)
97
98 expect(result['eth3'][:mac]).to eq('08:00:27:d5:44:7e')
99 end
100
101 it 'retrieves bond0 interface' do
102 expected = {
103 bindings: [
104 { address: '11.0.0.3', netmask: '255.255.0.0', network: '11.0.0.0' }
105 ],
106 mac: '08:00:27:29:dc:a5'
107 }
108
109 expect(socket_parser.retrieve_interfaces(log_spy)['bond0']).to eq(expected)
110 end
111 end
112
113 context 'when the defined constant is Socket::PF_PACKET, not Socket::PF_LINK' do
114 let(:ifaddrs) do
115 [
116 ifaddr_obj('ens160', '10.16.119.155', '00:50:56:9a:61:46', '255.255.240.0', true)
117 ]
118 end
119
120 before do
121 allow(Socket).to receive(:const_defined?).with(:PF_LINK).and_return(false)
122 allow(Socket).to receive(:const_defined?).with(:PF_PACKET).and_return(true)
123 end
124
125 it 'manages to retrieve mac for ens160' do
126 expect(socket_parser.retrieve_interfaces(log_spy)['ens160'][:mac]).to eq('00:50:56:9a:61:46')
127 end
128 end
129
130 context 'when Ifaddr.addr throws an error' do
131 before do
132 allow(ifaddrs[4]).to receive(:addr).and_raise(SocketError)
133 end
134
135 it 'does not solve ens160 ipv6 binding' do
136 expect(socket_parser.retrieve_interfaces(log_spy)['ens160']).not_to have_key(:bindings6)
137 end
138
139 it 'resolves the mac and ipv4 binding for ens160' do
140 expect(socket_parser.retrieve_interfaces(log_spy)['ens160'].keys).to match_array(%i[mac bindings])
141 end
142 end
143
144 context 'when Ifaddr.addr.ip_address throws SocketError' do
145 before do
146 allow(ifaddrs[0]).to receive(:addr) do
147 double.tap do |addr_returned_object|
148 allow(addr_returned_object).to receive(:getnameinfo).and_return(['00:00:00:00:00:00'])
149 allow(addr_returned_object).to receive(:ip?).and_return(true)
150 allow(addr_returned_object).to receive(:ip_address).and_raise(SocketError)
151 end
152 end
153 end
154
155 it 'does not solve lo ipv4 binding' do
156 expect(socket_parser.retrieve_interfaces(log_spy)['lo']).not_to have_key(:bindings)
157 end
158
159 it 'resolves the mac and ipv6 binding for lo' do
160 expect(socket_parser.retrieve_interfaces(log_spy)['lo'].keys).to match_array([:bindings6])
161 end
162 end
163
164 context 'when Ifaddr.netmask.ip_address throws SocketError' do
165 before do
166 allow(ifaddrs[2]).to receive(:netmask) do
167 double.tap do |addr_returned_object|
168 allow(addr_returned_object).to receive(:ip_address).and_raise(SocketError)
169 end
170 end
171 end
172
173 it 'does not solve ens160 first ipv4 binding' do
174 expected = [{ address: '10.16.127.70', netmask: '255.255.240.0', network: '10.16.112.0' }]
175
176 expect(socket_parser.retrieve_interfaces(log_spy)['ens160'][:bindings]).to eq(expected)
177 end
178 end
179
180 context 'when Ifaddr.addr.getnameinfo throws SocketError' do
181 let(:ifaddrs) do
182 [
183 ifaddr_obj('ens160', '10.16.119.155', '00:50:56:9a:61:46', '255.255.240.0', true)
184
185 ]
186 end
187
188 before do
189 allow(Socket).to receive(:const_defined?).with(:PF_LINK).and_return(true)
190 allow(ifaddrs[0]).to receive(:addr) do
191 double.tap do |addr_returned_object|
192 allow(addr_returned_object).to receive(:getnameinfo).and_raise(SocketError)
193 allow(addr_returned_object).to receive(:ip?).and_return(true)
194 allow(addr_returned_object).to receive(:ip_address).and_return('10.16.119.155')
195 allow(addr_returned_object).to receive(:ipv4?).and_return(true)
196 end
197 end
198 end
199
200 it 'does not retrieve mac for ens160' do
201 expected = {
202 'ens160' => {
203 bindings: [
204 { address: '10.16.119.155', netmask: '255.255.240.0', network: '10.16.112.0' }
205 ]
206 }
207 }
208
209 expect(socket_parser.retrieve_interfaces(log_spy)).to eq(expected)
210 end
211 end
212
213 context 'when Ifaddr.addr.inspect_sockaddr throws SocketError' do
214 let(:ifaddrs) do
215 [
216 ifaddr_obj('ens160', 'fe80::250:56ff:fe9a:8481', '00:50:56:9a:61:46', 'ffff:ffff:ffff:ffff::', false)
217 ]
218 end
219
220 before do
221 allow(Socket).to receive(:const_defined?).with(:PF_LINK).and_return(false)
222 allow(Socket).to receive(:const_defined?).with(:PF_PACKET).and_return(true)
223 allow(ifaddrs[0]).to receive(:addr) do
224 double.tap do |addr_returned_object|
225 allow(addr_returned_object).to receive(:inspect_sockaddr) do
226 double.tap do |inspect_sockaddr_obj|
227 allow(inspect_sockaddr_obj).to receive(:nil?).and_return(false)
228 allow(inspect_sockaddr_obj).to receive(:match).with(/hwaddr=([\h:]+)/).and_raise(SocketError)
229 end
230 end
231 allow(addr_returned_object).to receive(:ip?).and_return(true)
232 allow(addr_returned_object).to receive(:ip_address).and_return('::1')
233 allow(addr_returned_object).to receive(:ipv4?).and_return(false)
234 end
235 end
236 end
237
238 it 'does not retrieve mac for ens160' do
239 expected = {
240 'ens160' => {
241 bindings6: [
242 { address: '::1', netmask: 'ffff:ffff:ffff:ffff::', network: '::', scope6: 'host' }
243 ]
244 }
245 }
246
247 expect(socket_parser.retrieve_interfaces(log_spy)).to eq(expected)
248 end
249 end
250
251 context 'when Ifaddr.addr.getnameinfo returns ip instead of mac' do
252 let(:ifaddrs) do
253 [
254 ifaddr_obj('ens160', 'fe80::250:56ff:fe9a:8481', 'fe80::250:56ff:fe9a:8481', 'ffff:ffff:ffff:ffff::', false)
255 ]
256 end
257
258 it 'does not retrieve_interfaces mac' do
259 expect(socket_parser.retrieve_interfaces(log_spy)['ens160']).not_to have_key(:mac)
260 end
261 end
262
263 context 'when Socket.getifaddrs throws SocketError' do
264 before do
265 allow(Socket).to receive(:getifaddrs).and_raise(SocketError)
266 end
267
268 it 'raises SocketError' do
269 expect { socket_parser.retrieve_interfaces(log_spy) }.to raise_error(SocketError)
270 end
271 end
272 end
273 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Macosx::SystemProfileExecutor do
3 subject(:system_profiler_executor) { Facter::Util::Macosx::SystemProfileExecutor }
4
5 describe '#execute' do
6 before do
7 allow(Facter::Core::Execution).to receive(:execute)
8 end
9
10 it 'calls Facter::Core::Execution.execute' do
11 system_profiler_executor.execute('SPEthernetDataType')
12
13 expect(Facter::Core::Execution).to have_received(:execute)
14 end
15
16 context 'when executing system_profiler with SPEthernetDataType argument' do
17 let(:eth_data_hash) do
18 {
19 type: 'Ethernet Controller',
20 bus: 'PCI',
21 vendor_id: '0x8086',
22 device_id: '0x100f',
23 subsystem_vendor_id: '0x1ab8',
24 subsystem_id: '0x0400',
25 revision_id: '0x0000',
26 bsd_name: 'en0',
27 kext_name: 'AppleIntel8254XEthernet.kext',
28 location: '/System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/AppleIntel8254XEthernet.kext',
29 version: '3.1.5'
30 }
31 end
32
33 before do
34 allow(Facter::Core::Execution)
35 .to receive(:execute)
36 .with('system_profiler SPEthernetDataType', any_args)
37 .and_return(load_fixture('system_profile_sp_ethernet_data_type').read)
38 end
39
40 it 'returns a hash with the information' do
41 ethernet_data_type_hash = system_profiler_executor.execute('SPEthernetDataType')
42
43 expect(ethernet_data_type_hash).to eq(eth_data_hash)
44 end
45 end
46 end
47 end
0 # frozen_string_literal: true
1
2 describe 'Facter::Util::Resolvers::Http' do
3 subject(:aws_token) { Facter::Util::Resolvers::AwsToken }
4
5 before do
6 Facter::Util::Resolvers::AwsToken.reset
7 end
8
9 after do
10 Facter::Util::Resolvers::AwsToken.reset
11 end
12
13 describe '#get' do
14 it 'calls Facter::Util::Resolvers::Http.put_request' do
15 allow(Facter::Util::Resolvers::Http).to receive(:put_request)
16 aws_token.get
17 expect(Facter::Util::Resolvers::Http).to have_received(:put_request)
18 end
19
20 it 'does make a second request if token is still available' do
21 allow(Facter::Util::Resolvers::Http).to receive(:put_request).and_return('token')
22 aws_token.get(1000)
23 aws_token.get(1000)
24 expect(Facter::Util::Resolvers::Http).to have_received(:put_request).once
25 end
26
27 it 'makes a second request if token is expired' do
28 allow(Facter::Util::Resolvers::Http).to receive(:put_request).and_return('token')
29 aws_token.get(1)
30 Timecop.travel(Time.now + 2)
31 aws_token.get(1)
32 expect(Facter::Util::Resolvers::Http).to have_received(:put_request).twice
33 end
34 end
35 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::Ffi::LoadAverages do
3 describe '#load_averages' do
4 let(:memory_pointer) { instance_spy(FFI::MemoryPointer) }
5
6 before do
7 allow(Facter::Util::Resolvers::Ffi::LoadAverages).to receive(:getloadavg).and_return(response)
8 allow(FFI::MemoryPointer).to receive(:new).with(:double, 3).and_return(memory_pointer)
9 allow(memory_pointer).to receive(:read_array_of_double).with(response).and_return(averages)
10 end
11
12 context 'when averages are returned' do
13 let(:response) { 3 }
14 let(:averages) { [0.19482421875, 0.2744140625, 0.29296875] }
15
16 it 'returns load average' do
17 expect(Facter::Util::Resolvers::Ffi::LoadAverages.read_load_averages).to eq(averages)
18 end
19 end
20
21 context 'when averages are not returned' do
22 let(:response) { -1 }
23 let(:averages) { nil }
24
25 it 'does not return load average' do
26 expect(Facter::Util::Resolvers::Ffi::LoadAverages.read_load_averages).to eq(averages)
27 end
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::FilesystemHelper do
3 describe '.compute_capacity' do
4 it 'returns an integer if full' do
5 capacity = Facter::Util::Resolvers::FilesystemHelper.send(:compute_capacity, 100, 100)
6 expect(capacity).to eq('100%')
7 end
8
9 it 'returns an integer if empty' do
10 capacity = Facter::Util::Resolvers::FilesystemHelper.send(:compute_capacity, 0, 100)
11 expect(capacity).to eq('0%')
12 end
13
14 it 'returns a ratio with 2 decimals otherwise' do
15 capacity = Facter::Util::Resolvers::FilesystemHelper.send(:compute_capacity, 421, 10_000)
16 expect(capacity).to eq('4.21%')
17 end
18 end
19
20 describe '#read_mountpoints' do
21 before do
22 mount = OpenStruct.new
23 mount.name = +'test_name'.encode('ASCII-8BIT')
24 mount.mount_type = +'test_type'.encode('ASCII-8BIT')
25 mount.mount_point = +'test_mount_point'.encode('ASCII-8BIT')
26 mount.options = +'test_options'.encode('ASCII-8BIT')
27
28 mounts = [mount]
29 allow(Sys::Filesystem).to receive(:mounts).and_return(mounts)
30 end
31
32 let(:mount_points) { Facter::Util::Resolvers::FilesystemHelper.read_mountpoints }
33
34 it 'converts name from ASCII-8BIT to UTF-8' do
35 expect(mount_points.first.name.encoding.name). to eq('UTF-8')
36 end
37
38 it 'converts mount_type from ASCII-8BIT to UTF-8' do
39 expect(mount_points.first.mount_type.encoding.name). to eq('UTF-8')
40 end
41
42 it 'converts mount_point from ASCII-8BIT to UTF-8' do
43 expect(mount_points.first.mount_point.encoding.name). to eq('UTF-8')
44 end
45
46 it 'converts options from ASCII-8BIT to UTF-8' do
47 expect(mount_points.first.options.encoding.name). to eq('UTF-8')
48 end
49 end
50 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::Http do
3 subject(:http) { Facter::Util::Resolvers::Http }
4
5 let(:url) { 'http://169.254.169.254/meta-data/' }
6 let(:log_spy) { instance_spy(Facter::Log) }
7
8 before do
9 http.instance_variable_set(:@log, log_spy)
10 allow(Gem).to receive(:win_platform?).and_return(false)
11 end
12
13 RSpec.shared_examples 'a http request' do
14 context 'when success' do
15 before do
16 stub_request(http_verb, url).to_return(status: 200, body: 'success')
17 end
18
19 it 'returns the body of the response' do
20 expect(http.send(client_method, url)).to eql('success')
21 end
22 end
23
24 context 'when setting specific headers' do
25 let(:headers) { { 'Content-Type' => 'application/json' } }
26
27 before do
28 stub_request(http_verb, url).with(headers: headers).and_return(body: 'success')
29 end
30
31 it 'adds them to the request' do
32 expect(http.send(client_method, url, headers)).to eql('success')
33 end
34 end
35
36 context 'when server response with error' do
37 before do
38 stub_request(http_verb, url).to_return(status: 500, body: 'Internal Server Error')
39 end
40
41 it 'returns empty string' do
42 expect(http.send(client_method, url)).to eql('')
43 end
44
45 it 'logs error code' do
46 http.send(client_method, url)
47 expect(log_spy).to have_received(:debug).with("Request to #{url} failed with error code 500")
48 end
49 end
50
51 context 'when timeout is reached' do
52 before do
53 stub_request(http_verb, url).to_timeout
54 end
55
56 it 'returns empty string' do
57 expect(http.send(client_method, url)).to eql('')
58 end
59
60 it 'logs error message' do
61 http.send(client_method, url)
62 expect(log_spy).to have_received(:debug)
63 .with("Trying to connect to #{url} but got: execution expired")
64 end
65 end
66
67 context 'when http request raises error' do
68 before do
69 stub_request(http_verb, url).to_raise(StandardError.new('some error'))
70 end
71
72 it 'returns empty string' do
73 expect(http.send(client_method, url)).to eql('')
74 end
75
76 it 'logs error message' do
77 http.send(client_method, url)
78 expect(log_spy).to have_received(:debug).with("Trying to connect to #{url} but got: some error")
79 end
80 end
81
82 context 'when options[:http_debug] is set to true' do
83 let(:net_http_class) { class_spy(Net::HTTP) }
84 let(:net_http_instance) { instance_spy(Net::HTTP) }
85
86 before do
87 stub_const('Net::HTTP', net_http_class)
88 Facter::Options[:http_debug] = true
89 end
90
91 it 'sets Net::Http to write request and responses to stderr' do
92 allow(net_http_class).to receive(:new).and_return(net_http_instance)
93 http.send(client_method, url)
94 expect(net_http_instance).to have_received(:set_debug_output).with($stderr)
95 end
96 end
97 end
98
99 RSpec.shared_examples 'a http request on windows' do
100 it_behaves_like 'a http request'
101
102 context 'when host is unreachable ' do
103 before do
104 allow(Socket).to receive(:tcp)
105 .with('169.254.169.254', 80, { connect_timeout: 0.6 })
106 .and_raise(Errno::ETIMEDOUT)
107 end
108
109 it 'returns empty string' do
110 expect(http.send(client_method, url)).to eql('')
111 end
112
113 it 'logs error message' do
114 http.send(client_method, url)
115 expect(log_spy).to have_received(:debug)
116 .with(/((Operation|Connection) timed out)|(A connection attempt.*)/)
117 end
118 end
119
120 context 'when timeout is configured' do
121 let(:socket_spy) { class_spy(Socket) }
122
123 before do
124 stub_const('Socket', socket_spy)
125 stub_request(http_verb, url)
126 allow(Socket).to receive(:tcp).with('169.254.169.254', 80, { connect_timeout: 10 })
127 end
128
129 it 'uses the desired value' do
130 http.send(client_method, url, {}, { connection: 10 })
131 expect(socket_spy).to have_received(:tcp).with('169.254.169.254', 80, { connect_timeout: 10 })
132 end
133 end
134 end
135
136 describe '#get_request' do
137 let(:http_verb) { :get }
138 let(:client_method) { :get_request }
139
140 it_behaves_like 'a http request'
141 end
142
143 describe '#put_request' do
144 let(:http_verb) { :put }
145 let(:client_method) { :put_request }
146
147 it_behaves_like 'a http request'
148 end
149
150 context 'when windows' do
151 before do
152 # The Windows implementation of sockets does not respect net/http
153 # timeouts, so the http client checks if the target is reachable using Socket.tcp
154 allow(Gem).to receive(:win_platform?).and_return(true)
155 allow(Socket).to receive(:tcp).with('169.254.169.254', 80, { connect_timeout: 0.6 })
156 end
157
158 describe '#get_request' do
159 let(:http_verb) { :get }
160 let(:client_method) { :get_request }
161
162 it_behaves_like 'a http request on windows'
163 end
164
165 describe '#put_request' do
166 let(:http_verb) { :put }
167 let(:client_method) { :put_request }
168
169 it_behaves_like 'a http request on windows'
170 end
171 end
172 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::Networking do
3 subject(:networking_helper) { Facter::Util::Resolvers::Networking }
4
5 let(:ipv4) { '192.168.1.3' }
6 let(:ipv6) { 'fe80::1' }
7 let(:mask4_length) { 10 }
8 let(:mask6_length) { 57 }
9
10 describe '#build_binding' do
11 context 'when input is ipv4 address' do
12 let(:netmask) { IPAddr.new('255.255.240.0/255.255.240.0').to_s }
13 let(:network) { IPAddr.new('10.16.112.0/255.255.240.0').to_s }
14 let(:addr) { '10.16.121.248' }
15
16 it 'returns ipv4 binding' do
17 expect(networking_helper.build_binding(addr, 20)).to eql(address: addr, netmask: netmask, network: network)
18 end
19 end
20
21 context "when mask's length is nil" do
22 it 'returns nil' do
23 expect(networking_helper.build_binding(ipv4, nil)).to be(nil)
24 end
25 end
26
27 context 'when input is ipv6 address' do
28 let(:network) do
29 IPAddr.new('fe80:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000').to_s
30 end
31 let(:netmask) do
32 IPAddr.new('ffff:ffff:ffff:ffff:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000').to_s
33 end
34 let(:addr) { 'fe80::dc20:a2b9:5253:9b46' }
35
36 it 'returns ipv6 binding' do
37 expect(networking_helper.build_binding(addr, 64)).to eql(address: addr, netmask: netmask, network: network,
38 scope6: 'link')
39 end
40 end
41 end
42
43 describe '#get_scope' do
44 context "when address's scope is link" do
45 let(:address) { 'fe80::b13f:903e:5f5:3b52' }
46
47 it 'returns scope6' do
48 expect(networking_helper.get_scope(address)).to eql('link')
49 end
50 end
51
52 context "when address's scope is global" do
53 let(:address) { '::ffff:192.0.2.128' }
54
55 it 'returns scope6' do
56 expect(networking_helper.get_scope(address)).to eql('global')
57 end
58 end
59
60 context "when address's scope is ipv4 compatible" do
61 let(:address) { '::192.0.2.128' }
62
63 it 'returns scope6' do
64 expect(networking_helper.get_scope(address)).to eql('compat,global')
65 end
66 end
67
68 context "when address's scope is site" do
69 let(:address) { 'fec0::' }
70
71 it 'returns scope6' do
72 expect(networking_helper.get_scope(address)).to eql('site')
73 end
74 end
75 end
76
77 describe '#ignored_ip_address' do
78 context 'when input is empty' do
79 it 'returns true' do
80 expect(networking_helper.ignored_ip_address('')).to be(true)
81 end
82 end
83
84 context 'when input starts with 127.' do
85 it 'returns true' do
86 expect(networking_helper.ignored_ip_address('127.255.0.2')).to be(true)
87 end
88 end
89
90 context 'when input is a valid ipv4 address' do
91 it 'returns false' do
92 expect(networking_helper.ignored_ip_address('169.255.0.2')).to be(false)
93 end
94 end
95
96 context 'when input starts with fe80' do
97 it 'returns true' do
98 expect(networking_helper.ignored_ip_address('fe80::')).to be(true)
99 end
100 end
101
102 context 'when input equal with ::1' do
103 it 'returns true' do
104 expect(networking_helper.ignored_ip_address('::1')).to be(true)
105 end
106 end
107
108 context 'when input is a valid ipv6 address' do
109 it 'returns false' do
110 expect(networking_helper.ignored_ip_address('fe70::7d01:99a1:3900:531b')).to be(false)
111 end
112 end
113 end
114
115 describe '#expand_main_bindings' do
116 let(:networking_facts) do
117 {
118 primary_interface: 'en0',
119 interfaces:
120 { 'alw0' => {
121 mtu: 1484,
122 mac: '2e:ba:e4:83:4b:b7',
123 bindings6:
124 [{ address: 'fe80::2cba:e4ff:fe83:4bb7',
125 netmask: 'ffff:ffff:ffff:ffff::',
126 network: 'fe80::' }]
127 },
128 'bridge0' => { mtu: 1500, mac: '82:17:0e:93:9d:00' },
129 'en0' =>
130 { mtu: 1500,
131 mac: '64:5a:ed:ea:5c:81',
132 bindings:
133 [{ address: '192.168.1.2',
134 netmask: '255.255.255.0',
135 network: '192.168.1.0' }] },
136 'lo0' =>
137 { mtu: 16_384,
138 bindings:
139 [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
140 bindings6:
141 [{ address: '::1',
142 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
143 network: '::1' },
144 { address: 'fe80::1',
145 netmask: 'ffff:ffff:ffff:ffff::',
146 network: 'fe80::' }] } }
147 }
148 end
149
150 it 'expands the ipv4 binding' do
151 expected = {
152 mtu: 1500,
153 mac: '64:5a:ed:ea:5c:81',
154 bindings:
155 [{ address: '192.168.1.2',
156 netmask: '255.255.255.0',
157 network: '192.168.1.0' }],
158 ip: '192.168.1.2',
159 netmask: '255.255.255.0',
160 network: '192.168.1.0'
161 }
162
163 networking_helper.expand_main_bindings(networking_facts)
164
165 expect(networking_facts[:interfaces]['en0']).to eq(expected)
166 end
167
168 it 'expands the ipv6 binding' do
169 expected = {
170 mtu: 1484,
171 mac: '2e:ba:e4:83:4b:b7',
172 bindings6:
173 [{ address: 'fe80::2cba:e4ff:fe83:4bb7',
174 netmask: 'ffff:ffff:ffff:ffff::',
175 network: 'fe80::' }],
176 ip6: 'fe80::2cba:e4ff:fe83:4bb7',
177 netmask6: 'ffff:ffff:ffff:ffff::',
178 network6: 'fe80::',
179 scope6: 'link'
180 }
181
182 networking_helper.expand_main_bindings(networking_facts)
183
184 expect(networking_facts[:interfaces]['alw0']).to eq(expected)
185 end
186
187 it 'expands both the ipv6 and ipv4 binding' do
188 expected = {
189 mtu: 16_384,
190 bindings:
191 [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
192 bindings6:
193 [{ address: '::1',
194 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
195 network: '::1' },
196 { address: 'fe80::1',
197 netmask: 'ffff:ffff:ffff:ffff::',
198 network: 'fe80::' }],
199 ip: '127.0.0.1',
200 ip6: '::1',
201 netmask: '255.0.0.0',
202 netmask6: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
203 network: '127.0.0.0',
204 network6: '::1',
205 scope6: 'host'
206 }
207
208 networking_helper.expand_main_bindings(networking_facts)
209
210 expect(networking_facts[:interfaces]['lo0']).to eq(expected)
211 end
212
213 it 'checks networking_facts have the expected keys' do
214 expected = %i[mtu mac ip netmask network interfaces primary_interface]
215
216 networking_helper.expand_main_bindings(networking_facts)
217
218 expect(networking_facts.keys).to match_array(expected)
219 end
220
221 it 'exoands the primary interface' do
222 expected = {
223 mtu: 1500,
224 mac: '64:5a:ed:ea:5c:81',
225 ip: '192.168.1.2',
226 netmask: '255.255.255.0',
227 network: '192.168.1.0'
228 }
229
230 networking_helper.expand_main_bindings(networking_facts)
231
232 expect(networking_facts).to include(expected)
233 end
234
235 context 'when there is a global ip address' do
236 let(:networking_facts) do
237 {
238 interfaces:
239 { 'lo0' =>
240 { mtu: 16_384,
241 bindings6:
242 [{ address: '::1',
243 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
244 network: '::1' },
245 { address: 'fe87::1',
246 netmask: 'ffff:ffff:ffff:ffff::',
247 network: 'fe87::' }] } }
248 }
249 end
250
251 it 'expands the correct binding' do
252 expected = {
253 ip6: 'fe87::1',
254 netmask6: 'ffff:ffff:ffff:ffff::',
255 network6: 'fe87::',
256 scope6: 'link'
257 }
258
259 networking_helper.expand_main_bindings(networking_facts)
260
261 expect(networking_facts[:interfaces]['lo0']).to include(expected)
262 end
263 end
264 end
265
266 describe '#calculate_mask_length' do
267 context 'when ip v4' do
268 let(:netmask) { '255.0.0.0' }
269
270 it 'returns 8 as mask length' do
271 mask_length = Facter::Util::Resolvers::Networking.calculate_mask_length(netmask)
272
273 expect(mask_length).to be(8)
274 end
275 end
276
277 context 'when ip v6' do
278 let(:netmask) { '::ffff:ffff:ffff:ffff:ffff:ffff' }
279
280 it 'returns 10 as mask length' do
281 mask_length = Facter::Util::Resolvers::Networking.calculate_mask_length(netmask)
282
283 expect(mask_length).to be(96)
284 end
285 end
286 end
287 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::Networking::PrimaryInterface do
3 subject(:primary_interface) { Facter::Util::Resolvers::Networking::PrimaryInterface }
4
5 describe '#read_from_route' do
6 before do
7 allow(Facter::Core::Execution).to receive(:execute)
8 .and_return(load_fixture('route_n_get_default').read)
9 end
10
11 it 'parses output from `route -n get default`' do
12 allow(Facter::Core::Execution).to receive(:which).with('route').and_return('/some/path')
13 expect(primary_interface.read_from_route).to eq('net0')
14 end
15
16 it 'returns nil if route command does not exist' do
17 allow(Facter::Core::Execution).to receive(:which).with('route').and_return(nil)
18 expect(primary_interface.read_from_route).to be_nil
19 end
20 end
21
22 describe '#read_from_proc_route' do
23 before do
24 allow(Facter::Util::FileHelper)
25 .to receive(:safe_read)
26 .with('/proc/net/route', '') \
27 .and_return(output)
28 end
29
30 context 'when proc net route has output' do
31 let(:output) { load_fixture('proc_net_route').read }
32
33 it 'parses output /proc/net/route file' do
34 expect(primary_interface.read_from_proc_route).to eq('ens160')
35 end
36 end
37
38 context 'when proc net route has no output' do
39 let(:output) { load_fixture('proc_net_route_empty').read }
40
41 it 'parses output /proc/net/route file' do
42 expect(primary_interface.read_from_proc_route).to eq(nil)
43 end
44 end
45
46 context 'when proc net route has blackhole default' do
47 let(:output) { load_fixture('proc_net_route_blackhole').read }
48
49 it 'parses output /proc/net/route file' do
50 expect(primary_interface.read_from_proc_route).to eq(nil)
51 end
52 end
53 end
54
55 describe '#read_from_ip_route' do
56 before do
57 allow(Facter::Core::Execution).to receive(:execute)
58 .and_return(load_fixture('ip_route_show_default').read)
59 end
60
61 it 'parses output from `ip route show default`' do
62 allow(Facter::Core::Execution).to receive(:which).with('ip').and_return('/some/path')
63 expect(primary_interface.read_from_ip_route).to eq('ens160')
64 end
65
66 it 'returns nil if route command does not exist' do
67 allow(Facter::Core::Execution).to receive(:which).with('ip').and_return(nil)
68 expect(primary_interface.read_from_ip_route).to be_nil
69 end
70 end
71
72 describe '#find_in_interfaces' do
73 let(:interfaces) do
74 { 'lo' =>
75 { bindings: [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
76 bindings6: [{
77 address: '::1',
78 netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
79 network: '::1',
80 scope6: 'host'
81 }],
82 scope6: 'host',
83 mtu: 65_536 },
84 'ens160' =>
85 { bindings: [{ address: '10.16.124.1', netmask: '255.255.240.0', network: '10.16.112.0' }],
86 dhcp: '10.32.22.9',
87 bindings6: [{
88 address: 'fe80::250:56ff:fe9a:41fc',
89 netmask: 'ffff:ffff:ffff:ffff::',
90 network: 'fe80::',
91 scope6: 'link'
92 }],
93 scope6: 'link',
94 mac: '00:50:56:9a:41:fc',
95 mtu: 1500 } }
96 end
97
98 it 'parses interfaces to find primary interface' do
99 expect(primary_interface.find_in_interfaces(interfaces)).to eq('ens160')
100 end
101 end
102 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::SshHelper do
3 subject(:ssh_helper) { Facter::Util::Resolvers::SshHelper }
4
5 describe '#create_ssh' do
6 let(:fingerprint) { instance_spy(Facter::Util::Resolvers::FingerPrint) }
7 let(:key) { load_fixture('rsa_key').read.strip }
8
9 before do
10 allow(Facter::Util::Resolvers::FingerPrint).to receive(:new).and_return(fingerprint)
11 end
12
13 it 'returns a ssh object' do
14 expect(ssh_helper.create_ssh('ssh-rsa', key)).to be_an_instance_of(Facter::Util::Resolvers::Ssh).and \
15 have_attributes(name: 'rsa', type: 'ssh-rsa', fingerprint: fingerprint)
16 end
17 end
18 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Resolvers::UptimeHelper do
3 subject(:helper) { Facter::Util::Resolvers::UptimeHelper }
4
5 context 'when the uptime is less than 1 minutes' do
6 let(:expected_result) { { days: 0, hours: 0, seconds: 20, uptime: '0:00 hours' } }
7
8 it 'returns response hash' do
9 expect(helper.create_uptime_hash(20)).to eq(expected_result)
10 end
11 end
12
13 context 'when the uptime is more than 1 minute and less than 1 hour' do
14 let(:expected_result) { { days: 0, hours: 0, seconds: 620, uptime: '0:10 hours' } }
15
16 it 'returns response hash' do
17 expect(helper.create_uptime_hash(620)).to eq(expected_result)
18 end
19 end
20
21 context 'when the uptime is more than 1 hour but less than 1 day' do
22 let(:expected_result) { { days: 0, hours: 3, seconds: 11_420, uptime: '3:10 hours' } }
23
24 it 'returns response hash' do
25 expect(helper.create_uptime_hash(11_420)).to eq(expected_result)
26 end
27 end
28
29 context 'when the uptime is 1 day' do
30 let(:expected_result) { { days: 1, hours: 27, seconds: 97_820, uptime: '1 day' } }
31
32 it 'returns response hash' do
33 expect(helper.create_uptime_hash(97_820)).to eq(expected_result)
34 end
35 end
36
37 context 'when the uptime is more than 2 day' do
38 let(:expected_result) { { days: 2, hours: 51, seconds: 184_220, uptime: '2 days' } }
39
40 it 'returns response hash' do
41 expect(helper.create_uptime_hash(184_220)).to eq(expected_result)
42 end
43 end
44 end
0 # frozen_string_literal: true
1
2 describe NetworkUtils do
3 describe '#address_to_strig' do
4 let(:logger) { instance_spy(Facter::Log) }
5 let(:addr) { instance_spy('SocketAddress') }
6 let(:size) { instance_spy(FFI::MemoryPointer) }
7 let(:buffer) { instance_spy(FFI::MemoryPointer) }
8 let(:length) { 32 }
9
10 before do
11 allow(addr).to receive(:[]).with(:lpSockaddr).and_return(address)
12 allow(FFI::MemoryPointer).to receive(:new).with(NetworkingFFI::INET6_ADDRSTRLEN + 1).and_return(size)
13 allow(FFI::MemoryPointer).to receive(:new).with(:wchar, NetworkingFFI::INET6_ADDRSTRLEN + 1).and_return(buffer)
14 allow(addr).to receive(:[]).with(:lpSockaddr).and_return(address)
15 allow(addr).to receive(:[]).with(:iSockaddrLength).and_return(length)
16 allow(NetworkingFFI).to receive(:WSAAddressToStringW)
17 .with(address, length, FFI::Pointer::NULL, buffer, size).and_return(error)
18 allow(NetworkUtils).to receive(:extract_address).with(buffer).and_return('10.123.0.2')
19
20 NetworkUtils.instance_variable_set(:@log, logger)
21 end
22
23 context 'when lpSockaddr is null' do
24 let(:address) { FFI::Pointer::NULL }
25 let(:error) { 0 }
26
27 it 'returns nil' do
28 expect(NetworkUtils.address_to_string(addr)).to be(nil)
29 end
30 end
31
32 context 'when error code is zero' do
33 let(:address) { instance_spy(FFI::MemoryPointer) }
34 let(:error) { 0 }
35
36 it 'returns an address' do
37 expect(NetworkUtils.address_to_string(addr)).to eql('10.123.0.2')
38 end
39 end
40
41 context 'when error code is not zero' do
42 let(:address) { instance_spy(FFI::MemoryPointer) }
43 let(:error) { 1 }
44
45 it 'returns nil and logs debug message' do
46 allow(logger).to receive(:debug).with('address to string translation failed!')
47 expect(NetworkUtils.address_to_string(addr)).to be(nil)
48 end
49 end
50 end
51
52 describe '#extract_address' do
53 context 'when address is ipv6' do
54 let(:addr) { 'fe80::38bf:8f11:6227:9e6b%6' }
55 let(:input) { double(FFI::Pointer) }
56
57 before do
58 allow(input).to receive(:read_wide_string_without_length).and_return(addr)
59 end
60
61 it 'returns address without interface' do
62 expect(NetworkUtils.extract_address(input)).to eql('fe80::38bf:8f11:6227:9e6b')
63 end
64 end
65 end
66
67 describe '#find_mac_address' do
68 context 'with a char array' do
69 let(:adapter) do
70 Ps = Struct.new(:PhysicalAddress, :PhysicalAddressLength)
71 Ps.new([0, 80, 86, 154, 248, 107, 0, 0], 6)
72 end
73
74 it 'returns mac address' do
75 expect(NetworkUtils.find_mac_address(adapter)).to eql('00:50:56:9A:F8:6B')
76 end
77 end
78 end
79 end
0 # frozen_string_literal: true
1
2 describe Facter::Util::Windows::Win32Ole do
3 before do
4 result = double(WIN32OLE)
5 allow(WIN32OLE).to receive(:new).with('WbemScripting.SWbemLocator').and_return(result)
6 allow(result).to receive(:ConnectServer).with('.', 'root\\cimv2').and_return(result)
7 allow(result).to receive(:Security_).and_return(result)
8 allow(result).to receive(:ImpersonationLevel=).and_return(result)
9 allow(result).to receive(:execquery).with(query).and_return(query_result)
10 end
11
12 describe '#return_first when query result is nil' do
13 let(:query) { 'query' }
14 let(:query_result) {}
15
16 it 'returns nil' do
17 win = Facter::Util::Windows::Win32Ole.new
18 output = win.return_first(query)
19 expect(output).to eq(nil)
20 end
21 end
22
23 describe '#return_first' do
24 let(:query) { 'query' }
25 let(:query_result) { ['something'] }
26
27 it 'returns first element' do
28 win = Facter::Util::Windows::Win32Ole.new
29 output = win.return_first(query)
30 expect(output).to eq('something')
31 end
32 end
33 end
0 # frozen_string_literal: true
1
2 describe Facter do
3 describe '#reported and gemspec files version' do
4 it 'checks that reported and facter.gemspec versions are the same' do
5 gemspec_file_path = File.join(File.dirname(__FILE__), '..', '..', 'facter.gemspec')
6 gemspec_facter_version = Gem::Specification.load(gemspec_file_path).version.to_s
7
8 expect(gemspec_facter_version).to eq(Facter::VERSION)
9 end
10
11 it 'checks that reported and facter-ng.gemspec versions are the same' do
12 gemspec_file_path = File.join(File.dirname(__FILE__), '..', '..', 'agent', 'facter-ng.gemspec')
13 gemspec_facter_version = Gem::Specification.load(gemspec_file_path).version.to_s
14
15 expect(gemspec_facter_version).to eq(Facter::VERSION)
16 end
17 end
18 end
0 # Comment line
1 # Another comment line
2 #
3 # This is a test
4 %defaultvfs jfs2 nfs
5 #
6 cdrfs 5 none none
7 procfs 6 none none
8 sfs 16 none none
9 namefs 1 none none
10 ahafs 39 none none
0 /dev/mapper/VolGroup00-LogVol01: TYPE="swap"
1 /dev/mapper/VolGroup00-LogVol00: UUID="1bd8643b-483a-4fdc-adcd-c586384919a8" TYPE="ext3"
2 /dev/sda1: LABEL="/boot" UUID="88077904-4fd4-476f-9af2-0f7a806ca25e" TYPE="ext3" SEC_TYPE="ext2" PARTUUID="00061fe0-01 "
3 /dev/hdc: LABEL="VMware Tools" TYPE="iso9660"
4 /dev/VolGroup00/LogVol00: UUID="1bd8643b-483a-4fdc-adcd-c586384919a8" TYPE="ext3"
5 /dev/VolGroup00/LogVol01: TYPE="swap"
6 /dev/cdrom: LABEL="VMware Tools" TYPE="iso9660"
0 /dev/xvda1: LABEL="cloudimg-rootfs" UUID="f387d281-b162-4d60-84b5-e7e94687e6b8" TYPE="ext4" PARTUUID="a2f52878-01"
1 /dev/loop0: TYPE="squashfs"
2 /dev/loop1: TYPE="squashfs"
3 /dev/loop2: TYPE="squashfs"
4 /dev/loop3: TYPE="squashfs"
5 /dev/loop4: TYPE="squashfs"
0 Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)
1
2 Bonding Mode: IEEE 802.3ad Dynamic link aggregation
3 Transmit Hash Policy: layer2 (0)
4 MII Status: up
5 MII Polling Interval (ms): 100
6 Up Delay (ms): 0
7 Down Delay (ms): 0
8 Peer Notification Delay (ms): 0
9
10 802.3ad info
11 LACP rate: fast
12 Min links: 0
13 Aggregator selection policy (ad_select): stable
14 System priority: 65535
15 System MAC address: 08:00:27:29:dc:a5
16 Active Aggregator Info:
17 Aggregator ID: 1
18 Number of ports: 1
19 Actor Key: 9
20 Partner Key: 1
21 Partner Mac Address: 00:00:00:00:00:00
22
23 Slave Interface: eth2
24 MII Status: up
25 Speed: 1000 Mbps
26 Duplex: full
27 Link Failure Count: 1
28 Permanent HW addr: 08:00:27:29:dc:a5
29 Slave queue ID: 0
30 Aggregator ID: 1
31 Actor Churn State: none
32 Partner Churn State: churned
33 Actor Churned Count: 0
34 Partner Churned Count: 2
35 details actor lacp pdu:
36 system priority: 65535
37 system mac address: 08:00:27:29:dc:a5
38 port key: 9
39 port priority: 255
40 port number: 1
41 port state: 207
42 details partner lacp pdu:
43 system priority: 65535
44 system mac address: 00:00:00:00:00:00
45 oper key: 1
46 port priority: 255
47 port number: 1
48 port state: 3
49
50 Slave Interface: eth3
51 MII Status: up
52 Speed: 1000 Mbps
53 Duplex: full
54 Link Failure Count: 1
55 Permanent HW addr: 08:00:27:d5:44:7e
56 Slave queue ID: 0
57 Aggregator ID: 2
58 Actor Churn State: churned
59 Partner Churn State: churned
60 Actor Churned Count: 2
61 Partner Churned Count: 2
62 details actor lacp pdu:
63 system priority: 65535
64 system mac address: 08:00:27:29:dc:a5
65 port key: 9
66 port priority: 255
67 port number: 2
68 port state: 199
69 details partner lacp pdu:
70 system priority: 65535
71 system mac address: 00:00:00:00:00:00
72 oper key: 1
73 port priority: 255
74 port number: 1
75 port state: 3
0 [
1 {
2 "Linux": [
3 "Debian": [
4 "Elementary",
5 "Ubuntu",
6 "Raspbian"
7 ]
8 },
9 {
10 "El": [
11 "Fedora",
12 "Amzn",
13 "Centos"
14 ]
15 },
16 {
17 "Sles": [
18 "Opensuse"
19 ]
20 }
21 ]
22 },
23 {
24 "Solaris": [
25 "Bsd"
26 ]
27 },
28 "Macosx",
29 "Windows",
30 "Aix"
31 ]
0 LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64
1 Distributor ID: CentOS
2 Description: CentOS Linux release 7.2.1511 (Core)
3 Release: 7.2.1511
4 Codename: Core
0 12:pids:/init.scope
1 11:cpu,cpuacct:/init.scope
2 10:rdma:/
0 mem=512M console=ttyS2,115200n8 root=/dev/mmcblk0p2 rw rootwait
0 BOOT_IMAGE=/boot/vmlinuz-5.4.0-1024-aws root=PARTUUID=a2f52878-01 ro console=tty1 console=ttyS0 nvme_core.io_timeout=4294967295 panic=-1
0 processor : 0
1 vendor_id : GenuineIntel
2 cpu family : 6
3 model : 79
4 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
5 stepping : 1
6 microcode : 0xb00002e
7 cpu MHz : 2294.686
8 cache size : 46080 KB
9 physical id : 0
10 siblings : 4
11 core id : 0
12 cpu cores : 4
13 apicid : 0
14 initial apicid : 0
15 fpu : yes
16 fpu_exception : yes
17 cpuid level : 20
18 wp : yes
19 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
20 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
21 bogomips : 4589.37
22 clflush size : 64
23 cache_alignment : 64
24 address sizes : 43 bits physical, 48 bits virtual
25 power management:
26
27 processor : 1
28 vendor_id : GenuineIntel
29 cpu family : 6
30 model : 79
31 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
32 stepping : 1
33 microcode : 0xb00002e
34 cpu MHz : 2294.686
35 cache size : 46080 KB
36 physical id : 0
37 siblings : 4
38 core id : 1
39 cpu cores : 4
40 apicid : 1
41 initial apicid : 1
42 fpu : yes
43 fpu_exception : yes
44 cpuid level : 20
45 wp : yes
46 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
47 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
48 bogomips : 4589.37
49 clflush size : 64
50 cache_alignment : 64
51 address sizes : 43 bits physical, 48 bits virtual
52 power management:
53
54 processor : 2
55 vendor_id : GenuineIntel
56 cpu family : 6
57 model : 79
58 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
59 stepping : 1
60 microcode : 0xb00002e
61 cpu MHz : 2294.686
62 cache size : 46080 KB
63 physical id : 0
64 siblings : 4
65 core id : 2
66 cpu cores : 4
67 apicid : 2
68 initial apicid : 2
69 fpu : yes
70 fpu_exception : yes
71 cpuid level : 20
72 wp : yes
73 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
74 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
75 bogomips : 4589.37
76 clflush size : 64
77 cache_alignment : 64
78 address sizes : 43 bits physical, 48 bits virtual
79 power management:
80
81 processor : 3
82 vendor_id : GenuineIntel
83 cpu family : 6
84 model : 79
85 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
86 stepping : 1
87 microcode : 0xb00002e
88 cpu MHz : 2294.686
89 cache size : 46080 KB
90 physical id : 0
91 siblings : 4
92 core id : 3
93 cpu cores : 4
94 apicid : 3
95 initial apicid : 3
96 fpu : yes
97 fpu_exception : yes
98 cpuid level : 20
99 wp : yes
100 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
101 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
102 bogomips : 4589.37
103 clflush size : 64
104 cache_alignment : 64
105 address sizes : 43 bits physical, 48 bits virtual
106 power management:
0 processor : 0
1 BogoMIPS : 200.00
2 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid
3 CPU implementer : 0x43
4 CPU architecture: 8
5 CPU variant : 0x1
6 CPU part : 0x0a1
7 CPU revision : 1
8
9 processor : 1
10 BogoMIPS : 200.00
11 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid
12 CPU implementer : 0x43
13 CPU architecture: 8
14 CPU variant : 0x1
15 CPU part : 0x0a1
16 CPU revision : 1
0 processor : 0
1 cpu : POWER8 (raw), altivec supported
2 clock : 2926.000000MHz
3 revision : 2.0 (pvr 004d 0200)
4
5 processor : 1
6 cpu : POWER8 (raw), altivec supported
7 clock : 2926.000000MHz
8 revision : 2.0 (pvr 004d 0200)
9
10 processor : 2
11 cpu : POWER8 (raw), altivec supported
12 clock : 2926.000000MHz
13 revision : 2.0 (pvr 004d 0200)
14
15 processor : 3
16 cpu : POWER8 (raw), altivec supported
17 clock : 2926.000000MHz
18 revision : 2.0 (pvr 004d 0200)
19
20 timebase : 512000000
21 platform : pSeries
22 model : IBM pSeries (emulated by qemu)
23 machine : CHRP IBM pSeries (emulated by qemu)
0 processor : 0
1 vendor_id : GenuineIntel
2 cpu family : 6
3 model : 79
4 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
5 stepping : 1
6 microcode : 0xb00002e
7 cpu MHz : 2294.686
8 cache size : 46080 KB
9 siblings : 4
10 core id : 0
11 cpu cores : 4
12 apicid : 0
13 initial apicid : 0
14 fpu : yes
15 fpu_exception : yes
16 cpuid level : 20
17 wp : yes
18 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
19 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
20 bogomips : 4589.37
21 clflush size : 64
22 cache_alignment : 64
23 address sizes : 43 bits physical, 48 bits virtual
24 power management:
25
26 processor : 1
27 vendor_id : GenuineIntel
28 cpu family : 6
29 model : 79
30 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
31 stepping : 1
32 microcode : 0xb00002e
33 cpu MHz : 2294.686
34 cache size : 46080 KB
35 siblings : 4
36 core id : 1
37 cpu cores : 4
38 apicid : 1
39 initial apicid : 1
40 fpu : yes
41 fpu_exception : yes
42 cpuid level : 20
43 wp : yes
44 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
45 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
46 bogomips : 4589.37
47 clflush size : 64
48 cache_alignment : 64
49 address sizes : 43 bits physical, 48 bits virtual
50 power management:
51
52 processor : 2
53 vendor_id : GenuineIntel
54 cpu family : 6
55 model : 79
56 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
57 stepping : 1
58 microcode : 0xb00002e
59 cpu MHz : 2294.686
60 cache size : 46080 KB
61 siblings : 4
62 core id : 2
63 cpu cores : 4
64 apicid : 2
65 initial apicid : 2
66 fpu : yes
67 fpu_exception : yes
68 cpuid level : 20
69 wp : yes
70 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
71 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
72 bogomips : 4589.37
73 clflush size : 64
74 cache_alignment : 64
75 address sizes : 43 bits physical, 48 bits virtual
76 power management:
77
78 processor : 3
79 vendor_id : GenuineIntel
80 cpu family : 6
81 model : 79
82 model name : Intel(R) Xeon(R) CPU E5-2697 v4 @ 2.30GHz
83 stepping : 1
84 microcode : 0xb00002e
85 cpu MHz : 2294.686
86 cache size : 46080 KB
87 siblings : 4
88 core id : 3
89 cpu cores : 4
90 apicid : 3
91 initial apicid : 3
92 fpu : yes
93 fpu_exception : yes
94 cpuid level : 20
95 wp : yes
96 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid rtm rdseed adx smap xsaveopt arat flush_l1d arch_capabilities
97 bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
98 bogomips : 4589.37
99 clflush size : 64
100 cache_alignment : 64
101 address sizes : 43 bits physical, 48 bits virtual
102 power management:
0 Filesystem 512-blocks Used Available Capacity Mounted on
1 /dev/hd4 4194304 780504 3413800 19% /
2 /dev/hd2 10485760 4530640 5955120 44% /usr
3 /dev/hd3 423201 3233 419968 1% /var
4 /proc - - - - /proc
5 joe:/var/share 143650816 10343368 133307448 8% /tmp/salam
0 lease {
1 interface "lo";
2 option routers 10.32.112.1;
3 option dhcp-message-type 5;
4 option dhcp-server-identifier 10.32.22.9;
5 option domain-name-servers 10.240.0.10,10.240.1.10;
6 }
0 default-duid "\000\004N&+>\315WL\311\2756\\\036P\3448\017";
1 lease {
2 interface "eth0";
3 fixed-address 10.0.0.5;
4 server-name "AMS072031502021";
5 option subnet-mask 255.255.255.0;
6 option routers 10.0.0.1;
7 option dhcp-lease-time 4294967295;
8 option dhcp-message-type 5;
9 option domain-name-servers 168.63.129.16;
10 option dhcp-server-identifier 168.63.129.16;
11 option dhcp-renewal-time 4294967295;
12 option dhcp-rebinding-time 4294967295;
13 option unknown-245 a8:3f:81:10;
14 option rfc3442-classless-static-routes 0,10,0,0,1,32,168,63,129,16,10,0,0,1,32,169,254,169,254,10,0,0,1;
15 option domain-name "peq1diqrkypunc0cz32ezmxjye.ax.internal.cloudapp.net";
16 renew never;
17 rebind never;
18 expire never;
19 }
20 lease {
21 interface "eth0";
22 fixed-address 10.0.0.5;
23 server-name "AMS072031502021";
24 option subnet-mask 255.255.255.0;
25 option dhcp-lease-time 4294967295;
26 option routers 10.0.0.1;
27 option dhcp-message-type 5;
28 option dhcp-server-identifier 168.63.129.16;
29 option domain-name-servers 168.63.129.16;
30 option dhcp-renewal-time 4294967295;
31 option rfc3442-classless-static-routes 0,10,0,0,1,32,168,63,129,16,10,0,0,1,32,169,254,169,254,10,0,0,1;
32 option unknown-245 a8:3f:81:10;
33 option dhcp-rebinding-time 4294967295;
34 option domain-name "peq1diqrkypunc0cz32ezmxjye.ax.internal.cloudapp.net";
35 renew 0 2156/10/17 20:41:21;
36 rebind 0 2156/10/17 20:41:21;
37 expire 0 2156/10/17 20:41:21;
38 }
0 # This is private data. Do not parse.
1 ADDRESS=11.22.36.241
2 NETMASK=255.255.240.0
3 ROUTER=99.26.666.1
4 SERVER_ADDRESS=35.32.82.9
5 NEXT_SERVER=05.3.3.100
6 T1=150
7 T2=262
8 LIFETIME=300
9 DNS=78.250.0.0 90.230.1.10
10 NTP=55.2.25.65 12.0.21.19 88.2.45.18 96.45.22.39
11 DOMAINNAME=bakery.puppetlabs.net
12 CLIENTID=61952356faeffac
0 # This is private data. Do not parse.
1 ADDRESS=10.16.122.112
2 NETMASK=255.255.240.0
3 ROUTER=10.16.112.1
4 SERVER_ADDRESS=10.32.22.10
5 NEXT_SERVER=10.32.23.146
6 T1=450
7 T2=787
8 LIFETIME=900
9 DNS=10.240.0.10 10.240.1.10
10 NTP=10.32.22.9 10.0.22.10 10.0.22.11
11 DOMAINNAME=delivery.puppetlabs.net
12 CLIENTID=ff9f6e852400020000ab113d3dd2dee03bb312
0 broadcast_address='10.16.127.255'
1 dhcp_lease_time='300'
2 dhcp_message_type='5'
3 dhcp_server_identifier='10.32.22.9'
4 domain_name='delivery.puppetlabs.net'
5 domain_name_servers='10.240.0.10 10.240.1.10'
6 filename='pxelinux.0'
7 ip_address='10.16.116.66'
8 network_number='10.16.112.0'
9 ntp_servers='10.32.22.9 10.0.22.10 10.32.22.10 10.0.22.11'
10 routers='10.16.112.1'
11 subnet_cidr='20'
12 subnet_mask='255.255.240.0'
13 ens192: dhcp6_dump: No such file or directory
0 # dmidecode 3.1
1 Getting SMBIOS data from sysfs.
2 SMBIOS 2.5 present.
3 10 structures occupying 449 bytes.
4 Table at 0x000E1000.
5
6 Handle 0x0000, DMI type 0, 20 bytes
7 BIOS Information
8 Vendor: innotek GmbH
9 Version: VirtualBox
10 Release Date: 12/01/2006
11 Address: 0xE0000
12 Runtime Size: 128 kB
13 ROM Size: 128 kB
14 Characteristics:
15 ISA is supported
16 PCI is supported
17 Boot from CD is supported
18 Selectable boot is supported
19 8042 keyboard services are supported (int 9h)
20 CGA/mono video services are supported (int 10h)
21 ACPI is supported
22
23 Handle 0x0001, DMI type 1, 27 bytes
24 System Information
25 Manufacturer: innotek GmbH
26 Product Name: VirtualBox
27 Version: 1.2
28 Serial Number: 0
29 UUID: 784198EC-5827-476E-9E24-B0E98E8958F5
30 Wake-up Type: Power Switch
31 SKU Number: Not Specified
32 Family: Virtual Machine
33
34 Handle 0x0008, DMI type 2, 15 bytes
35 Base Board Information
36 Manufacturer: Oracle Corporation
37 Product Name: VirtualBox
38 Version: 1.2
39 Serial Number: 0
40 Asset Tag: Not Specified
41 Features:
42 Board is a hosting board
43 Location In Chassis: Not Specified
44 Chassis Handle: 0x0003
45 Type: Motherboard
46 Contained Object Handles: 0
47
48 Handle 0x0003, DMI type 3, 13 bytes
49 Chassis Information
50 Manufacturer: Oracle Corporation
51 Type: Other
52 Lock: Not Present
53 Version: Not Specified
54 Serial Number: Not Specified
55 Asset Tag: Not Specified
56 Boot-up State: Safe
57 Power Supply State: Safe
58 Thermal State: Safe
59 Security Status: None
60
61 Handle 0x0007, DMI type 126, 42 bytes
62 Inactive
63
64 Handle 0x0005, DMI type 126, 15 bytes
65 Inactive
66
67 Handle 0x0006, DMI type 126, 28 bytes
68 Inactive
69
70 Handle 0x0002, DMI type 11, 7 bytes
71 OEM Strings
72 String 1: vboxVer_6.1.4
73 String 2: vboxRev_136177
74
75 Handle 0x0008, DMI type 128, 8 bytes
76 OEM-specific Type
77 Header and Data:
78 80 08 08 00 3D A0 2A 00
79
80 Handle 0xFEFF, DMI type 127, 4 bytes
81 End Of Table
0 Getting SMBIOS data from sysfs.
1 SMBIOS 2.7 present.
2 242 structures occupying 9908 bytes.
3 Table at 0x000E0010.
4
5 Handle 0x0000, DMI type 0, 24 bytes
6 BIOS Information
7 Vendor: Phoenix Technologies LTD
8 Version: 6.00
9 Release Date: 12/12/2018
10 Address: 0xEA490
11 Runtime Size: 88944 bytes
12 ROM Size: 64 kB
13 Characteristics:
14 ISA is supported
15 PCI is supported
16 PC Card (PCMCIA) is supported
17 PNP is supported
18 APM is supported
19 BIOS is upgradeable
20 BIOS shadowing is allowed
21 ESCD support is available
22 Boot from CD is supported
23 Selectable boot is supported
24 EDD is supported
25 Print screen service is supported (int 5h)
26 8042 keyboard services are supported (int 9h)
27 Serial services are supported (int 14h)
28 Printer services are supported (int 17h)
29 CGA/mono video services are supported (int 10h)
30 ACPI is supported
31 Smart battery is supported
32 BIOS boot specification is supported
33 Function key-initiated network boot is supported
34 Targeted content distribution is supported
35 BIOS Revision: 4.6
36 Firmware Revision: 0.0
37
38 Handle 0x0001, DMI type 1, 27 bytes
39 System Information
40 Manufacturer: VMware, Inc.
41 Product Name: VMware Virtual Platform
42 Version: None
43 Serial Number: VMware-42 1a ee 9a 8e 13 ee e5-fc ec 82 3d cd 74 76 94
44 UUID: 9aee1a42-138e-e5ee-fcec-823dcd747694
45 Wake-up Type: Power Switch
46 SKU Number: Not Specified
47 Family: Not Specified
48
49 Handle 0x0002, DMI type 2, 15 bytes
50 Base Board Information
51 Manufacturer: Intel Corporation
52 Product Name: 440BX Desktop Reference Platform
53 Version: None
54 Serial Number: None
55 Asset Tag: Not Specified
56 Features: None
57 Location In Chassis: Not Specified
58 Chassis Handle: 0x0000
59 Type: Unknown
60 Contained Object Handles: 0
61
62 Handle 0x0003, DMI type 3, 21 bytes
63 Chassis Information
64 Manufacturer: No Enclosure
65 Type: Other
66 Lock: Not Present
67 Version: N/A
68 Serial Number: None
69 Asset Tag: No Asset Tag
70 Boot-up State: Safe
71 Power Supply State: Safe
72 Thermal State: Safe
73 Security Status: None
74 OEM Information: 0x00001234
75 Height: Unspecified
76 Number Of Power Cords: Unspecified
77 Contained Elements: 0
78
79 Handle 0x0004, DMI type 4, 42 bytes
80 Processor Information
81 Socket Designation: CPU #000
82 Type: Central Processor
83 Family: Unknown
84 Manufacturer: GenuineIntel
85 ID: 54 06 05 00 FF FB 8B 0F
86 Version: Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz
87 Voltage: 3.3 V
88 External Clock: Unknown
89 Max Speed: 30000 MHz
90 Current Speed: 2000 MHz
91 Status: Populated, Enabled
92 Upgrade: ZIF Socket
93 L1 Cache Handle: 0x0016
94 L2 Cache Handle: 0x0018
95 L3 Cache Handle: Not Provided
96 Serial Number: Not Specified
97 Asset Tag: Not Specified
98 Part Number: Not Specified
99 Core Count: 1
100 Core Enabled: 1
101 Characteristics:
102 64-bit capable
103 Execute Protection
104
105 Handle 0x0004, DMI type 4, 42 bytes
106 Processor Information
107 Socket Designation: CPU #001
108 Type: Central Processor
109 Family: Unknown
110 Manufacturer: GenuineIntel
111 ID: 54 06 00 00 FF FB 8B 0F
112 Version: Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz
113 Voltage: 3.3 V
114 External Clock: Unknown
115 Max Speed: 30000 MHz
116 Current Speed: 2000 MHz
117 Status: Populated, Enabled
118 Upgrade: ZIF Socket
119 L1 Cache Handle: 0x0016
120 L2 Cache Handle: 0x0018
121 L3 Cache Handle: Not Provided
122 Serial Number: Not Specified
123 Asset Tag: Not Specified
124 Part Number: Not Specified
125 Core Count: 1
126 Core Enabled: 1
127 Characteristics:
128 64-bit capable
129 Execute Protection
0 13:name=systemd:/docker/ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90
1 12:pids:/docker/ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90
2 11:hugetlb:/docker/ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90
0 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNG0AgjrPXt5/osLsmECV/qISOwaRmDW1yNSHZiAJvZ6p6ZUilg5vqtSskaUsT5XN8J2amVktN6wOHDwtWiEbpM=
0 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNG0AgjrPXt5/osLsmECV/qISOwaRmDW1yNSHZiAJvZ6p6ZUilg5vqtSskaUsT5XN8J2amVktN6wOHDwtWiEbpM=
0 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC0rUF3Qt67/maEmCEn3VEp86x/xvmP09b79/7yi8liN
0 AAAAC3NzaC1lZDI1NTE5AAAAIC0rUF3Qt67/maEmCEn3VEp86x/xvmP09b79/7yi8liN
0 ---
1 testsfact:
2 time: 2020-04-28 01:44:08.148119000 +01:01
0 ---
1 one:
2 test:
3 a: &a1
4 - foo
5 two:
6 TEST:
7 A: *a1
0 ---
1 testsfact:
2 date: 2020-04-28
0 ---
1 testsfact:
2 time: 2020-04-28 01:44:08.148119000
0 nodev sysfs
1 nodev rootfs
2 nodev ramfs
3 nodev bdev
4 nodev proc
5 nodev cpuset
6 nodev cgroup
7 nodev cgroup2
8 nodev tmpfs
9 nodev devtmpfs
10 nodev configfs
11 nodev debugfs
12 nodev tracefs
13 nodev securityfs
14 nodev sockfs
15 nodev dax
16 nodev bpf
17 nodev pipefs
18 nodev hugetlbfs
19 nodev devpts
20 ext3
21 ext2
22 fuseblk
23 ext4
24 nodev autofs
25 nodev mqueue
26 nodev pstore
27 xfs
28 nodev rpc_pipefs
0 Device 1K-blocks Used Avail Capacity
1 /dev/ada0p2.eli 2097152 0 2097152 0%
2 /dev/ada1p2.eli 2097152 0 2097152 0%
0 {"__version": "1", "processes": {"runnable":2,"waiting":14,"swapped-out":0}
1 , "memory": {"available-memory":62672436,"free-memory":2633264,"total-page-faults":1972}
2 , "paging-rates": {"page-reactivated":0,"paged-in":1,"paged-out":0,"freed":1831,"scanned":841}
3 , "device": [{"name":"ad0","transfers":"0"}, {"name":"pa0","transfers":"0"}], "fault-rates": {"interrupts":3495,"system-calls":17391,"context-switches":11777}
4 , "cpu-statistics": {"user":" 5","system":" 3","idle":"92"}
5 }
0 {
1 "instance":
2 {
3 "attributes":{},
4 "cpuPlatform":"Intel Broadwell",
5 "description":"",
6 "disks":[{"deviceName":"instance-3","index":0,"interface":"SCSI","mode":"READ_WRITE","type":"PERSISTENT"}],
7 "guestAttributes":{},
8 "hostname":"instance-3.c.facter-performance-history.internal",
9 "id":2206944706428651580,
10 "image":"projects/ubuntu-os-cloud/global/images/ubuntu-2004-focal-v20200810",
11 "legacyEndpointAccess":{"0.1":0,"v1beta1":0},
12 "licenses":[{"id":"2211838267635035815"}],
13 "machineType":"projects/728618928092/machineTypes/n1-standard-2",
14 "maintenanceEvent":"NONE",
15 "name":"instance-3",
16 "networkInterfaces":[
17 {
18 "accessConfigs":[{"externalIp":"34.89.230.102","type":"ONE_TO_ONE_NAT"}],
19 "dnsServers":["169.254.169.254"],
20 "forwardedIps":[],
21 "gateway":"10.156.0.1",
22 "ip":"10.156.0.4",
23 "ipAliases":[],
24 "mac":"42:01:0a:9c:00:04",
25 "mtu":1460,
26 "network":"projects/728618928092/networks/default",
27 "subnetmask":"255.255.240.0",
28 "targetInstanceIps":[]}],
29 "preempted":"FALSE",
30 "remainingCpuTime":-1,
31 "scheduling":{"automaticRestart":"TRUE","onHostMaintenance":"MIGRATE","preemptible":"FALSE"},
32 "serviceAccounts":
33 {
34 "728618928092-compute@developer.gserviceaccount.com":
35 {
36 "aliases":["default"],
37 "email":"728618928092-compute@developer.gserviceaccount.com",
38 "scopes":["https://www.googleapis.com/auth/devstorage.read_only",
39 "https://www.googleapis.com/auth/logging.write",
40 "https://www.googleapis.com/auth/monitoring.write",
41 "https://www.googleapis.com/auth/servicecontrol",
42 "https://www.googleapis.com/auth/service.management.readonly",
43 "https://www.googleapis.com/auth/trace.append"]
44 },
45 "default":
46 {
47 "aliases":["default"],
48 "email":"728618928092-compute@developer.gserviceaccount.com",
49 "scopes":["https://www.googleapis.com/auth/devstorage.read_only",
50 "https://www.googleapis.com/auth/logging.write",
51 "https://www.googleapis.com/auth/monitoring.write",
52 "https://www.googleapis.com/auth/servicecontrol",
53 "https://www.googleapis.com/auth/service.management.readonly",
54 "https://www.googleapis.com/auth/trace.append"]}
55 },
56 "tags":[],
57 "virtualClock":{"driftToken":"0"},
58 "zone":"projects/728618928092/zones/europe-west3-c"
59 },
60 "oslogin":{"authenticate":{"sessions":{}}},
61 "project":
62 {
63 "attributes":
64 {
65 "ssh-keys":"john_doe:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDA9D8Op48TtEiDmb+Gtna3Bs9B google-ssh {\"userName\":\"john.doe@puppet.com\",\"expireOn\":\"2020-08-13T12:17:19+0000\"}\n"
66 },
67 "numericProjectId":728618928092,
68 "projectId":"facter-performance-history"
69 }
70 }
0 lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
1 options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
2 inet 127.0.0.1 netmask 0xff000000
3 inet6 ::1 prefixlen 128
4 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
5 nd6 options=201<PERFORMNUD,DAD>
6 gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
7 stf0: flags=0<> mtu 1280
8 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
9 options=400<CHANNEL_IO>
10 ether 64:5a:ed:ea:5c:81
11 inet 192.168.143.212 netmask 0xffffff00 broadcast 192.168.143.255
12 media: autoselect
13 status: active
14 en0.1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
15 options=3<RXCSUM,TXCSUM>
16 ether 08:00:27:f5:23:f7
17 inet 0.0.0.0 netmask 0xff000000 broadcast 255.255.255.255
18 groups: vlan
19 vlan: 1 vlanpcp: 0 parent interface: em0
20 media: autoselect
21 status: active
22 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
23 en1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
24 options=460<TSO4,TSO6,CHANNEL_IO>
25 ether 82:17:0e:93:9d:00
26 media: autoselect <full-duplex>
27 status: inactive
28 en2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
29 options=460<TSO4,TSO6,CHANNEL_IO>
30 ether 82:17:0e:93:9d:01
31 media: autoselect <full-duplex>
32 status: inactive
33 bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
34 options=63<RXCSUM,TXCSUM,TSO4,TSO6>
35 ether 82:17:0e:93:9d:00
36 Configuration:
37 id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
38 maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
39 root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
40 ipfilter disabled flags 0x0
41 member: en1 flags=3<LEARNING,DISCOVER>
42 ifmaxaddr 0 port 5 priority 0 path cost 0
43 member: en2 flags=3<LEARNING,DISCOVER>
44 ifmaxaddr 0 port 6 priority 0 path cost 0
45 media: <unknown type>
46 status: inactive
47 p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
48 options=400<CHANNEL_IO>
49 ether 06:5a:ed:ea:5c:81
50 media: autoselect
51 status: inactive
52 awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484
53 options=400<CHANNEL_IO>
54 ether 2e:ba:e4:83:4b:b7
55 inet6 fe80::2cba:e4ff:fe83:4bb7%awdl0 prefixlen 64 scopeid 0x9
56 nd6 options=201<PERFORMNUD,DAD>
57 media: autoselect
58 status: active
59 llw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
60 options=400<CHANNEL_IO>
61 ether 2e:ba:e4:83:4b:b7
62 inet6 fe80::2cba:e4ff:fe83:4bb7%llw0 prefixlen 64 scopeid 0xa
63 nd6 options=201<PERFORMNUD,DAD>
64 media: aumm,l.j hn toselect
65 status: active
66 utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380
67 inet6 fe80::ba29:a797:6aa:9eb%utun0 prefixlen 64 scopeid 0xb
68 nd6 options=201<PERFORMNUD,DAD>
69 utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000
70 inet6 fe80::64c6:1885:bec0:c316%utun1 prefixlen 64 scopeid 0xc
71 nd6 options=201<PERFORMNUD,DAD>
72 utun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
73 inet 10.16.132.213 --> 10.16.132.213 netmask 0xfffffe00
74 utun3: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
75 inet6 2001:db8:cafe::132:213 --> 2001:db8:cafe::132:213 prefixlen 128
76 ib0: flags=4099<UP,BROADCAST,MULTICAST> mtu 4092
77 infiniband 80:00:02:08:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00 txqueuelen 256 (InfiniBand)
78 RX packets 0 bytes 0 (0.0 B)
79 RX errors 0 dropped 0 overruns 0 frame 0
80 TX packets 0 bytes 0 (0.0 B)
81 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
82
83 ib1: flags=4099<UP,BROADCAST,MULTICAST> mtu 4092
84 infiniband 80:00:02:09:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00 txqueuelen 256 (InfiniBand)
85 RX packets 0 bytes 0 (0.0 B)
86 RX errors 0 dropped 0 overruns 0 frame 0
87 TX packets 0 bytes 0 (0.0 B)
88 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
89
0 fe80::/64 dev ens160 proto kernel metric 256 pref medium
0 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
3 link/ether 00:50:56:9a:cb:a4 brd ff:ff:ff:ff:ff:ff
0 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000\ link/ether 00:50:56:9a:51:af brd ff:ff:ff:ff:ff:ff
2 3: ib0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 4092 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 256\ link/infiniband 80:00:02:08:fa:81:00:00:00:00:00:00:00:23:7d:ff:ff:94:73:fd brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
3 4: eth2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff
4 5: eth3: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff
5 6: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff
0 default via 10.16.112.1 dev ens192 proto dhcp src 10.16.125.217 metric 100
1 10.16.112.0/20 dev ens192 proto kernel scope link src 10.16.125.217
2 10.16.112.1 dev ens192 proto dhcp scope link src 10.16.125.217 metric 100
0 10.16.112.0/20 dev ens160 proto kernel scope link src 10.16.124.1
1 default via 10.16.112.1 dev ens160
0 <mesh>
1 <class id="0xffffffff81b732b0">
2 <name>FD</name>
3 </class>
4 <class id="0xffffffff81aff518">
5 <name>RAID</name>
6 </class>
7 <class id="0xffffffff81afc368">
8 <name>DEV</name>
9 <geom id="0xfffff800170b3e00">
10 <class ref="0xffffffff81afc368"/>
11 <name>ada0p2.eli</name>
12 <rank>4</rank>
13 <consumer id="0xfffff80018274780">
14 <geom ref="0xfffff800170b3e00"/>
15 <provider ref="0xfffff800170b3a00"/>
16 <mode>r0w0e0</mode>
17 </consumer>
18 </geom>
19 <geom id="0xfffff80003953700">
20 <class ref="0xffffffff81afc368"/>
21 <name>ada0p3.eli</name>
22 <rank>4</rank>
23 <consumer id="0xfffff800038cd700">
24 <geom ref="0xfffff80003953700"/>
25 <provider ref="0xfffff80003948700"/>
26 <mode>r0w0e0</mode>
27 </consumer>
28 </geom>
29 <geom id="0xfffff80007e01e00">
30 <class ref="0xffffffff81afc368"/>
31 <name>gpt/gptboot0</name>
32 <rank>4</rank>
33 <consumer id="0xfffff80003a1cb00">
34 <geom ref="0xfffff80007e01e00"/>
35 <provider ref="0xfffff80007e01b00"/>
36 <mode>r0w0e0</mode>
37 </consumer>
38 </geom>
39 <geom id="0xfffff80003a11100">
40 <class ref="0xffffffff81afc368"/>
41 <name>ada0p3</name>
42 <rank>3</rank>
43 <consumer id="0xfffff8000398b700">
44 <geom ref="0xfffff80003a11100"/>
45 <provider ref="0xfffff80003a11000"/>
46 <mode>r0w0e0</mode>
47 </consumer>
48 </geom>
49 <geom id="0xfffff80003a11300">
50 <class ref="0xffffffff81afc368"/>
51 <name>ada0p2</name>
52 <rank>3</rank>
53 <consumer id="0xfffff8000398b800">
54 <geom ref="0xfffff80003a11300"/>
55 <provider ref="0xfffff80003a11200"/>
56 <mode>r0w0e0</mode>
57 </consumer>
58 </geom>
59 <geom id="0xfffff80003a11900">
60 <class ref="0xffffffff81afc368"/>
61 <name>ada0p1</name>
62 <rank>3</rank>
63 <consumer id="0xfffff8000398b880">
64 <geom ref="0xfffff80003a11900"/>
65 <provider ref="0xfffff80003a11400"/>
66 <mode>r0w0e0</mode>
67 </consumer>
68 </geom>
69 <geom id="0xfffff800038d6e00">
70 <class ref="0xffffffff81afc368"/>
71 <name>ada0</name>
72 <rank>2</rank>
73 <consumer id="0xfffff8000398ba80">
74 <geom ref="0xfffff800038d6e00"/>
75 <provider ref="0xfffff80003a11b00"/>
76 <mode>r0w0e0</mode>
77 </consumer>
78 </geom>
79 </class>
80 <class id="0xffffffff82a08f70">
81 <name>ELI</name>
82 <geom id="0xfffff800170e6c00">
83 <class ref="0xffffffff82a08f70"/>
84 <name>ada0p2.eli</name>
85 <rank>3</rank>
86 <config>
87 <KeysTotal>1</KeysTotal>
88 <KeysAllocated>1</KeysAllocated>
89 <Flags>ONETIME, W-DETACH, W-OPEN</Flags>
90 <Version>7</Version>
91 <Crypto>hardware</Crypto>
92 <KeyLength>128</KeyLength>
93 <EncryptionAlgorithm>AES-XTS</EncryptionAlgorithm>
94 <State>ACTIVE</State>
95 </config>
96 <consumer id="0xfffff80018274600">
97 <geom ref="0xfffff800170e6c00"/>
98 <provider ref="0xfffff80003a11200"/>
99 <mode>r1w1e1</mode>
100 <config>
101 </config>
102 </consumer>
103 <provider id="0xfffff800170b3a00">
104 <geom ref="0xfffff800170e6c00"/>
105 <mode>r1w1e0</mode>
106 <name>ada0p2.eli</name>
107 <mediasize>2147483648</mediasize>
108 <sectorsize>4096</sectorsize>
109 <stripesize>0</stripesize>
110 <stripeoffset>0</stripeoffset>
111 <config>
112 </config>
113 </provider>
114 </geom>
115 <geom id="0xfffff80003a11a00">
116 <class ref="0xffffffff82a08f70"/>
117 <name>ada0p3.eli</name>
118 <rank>3</rank>
119 <config>
120 <KeysTotal>119</KeysTotal>
121 <KeysAllocated>119</KeysAllocated>
122 <Flags>BOOT, GELIBOOT</Flags>
123 <UsedKey>0</UsedKey>
124 <Version>7</Version>
125 <Crypto>hardware</Crypto>
126 <KeyLength>256</KeyLength>
127 <EncryptionAlgorithm>AES-XTS</EncryptionAlgorithm>
128 <State>ACTIVE</State>
129 </config>
130 <consumer id="0xfffff8000398b600">
131 <geom ref="0xfffff80003a11a00"/>
132 <provider ref="0xfffff80003a11000"/>
133 <mode>r1w1e1</mode>
134 <config>
135 </config>
136 </consumer>
137 <provider id="0xfffff80003948700">
138 <geom ref="0xfffff80003a11a00"/>
139 <mode>r1w1e1</mode>
140 <name>ada0p3.eli</name>
141 <mediasize>509961302016</mediasize>
142 <sectorsize>4096</sectorsize>
143 <stripesize>0</stripesize>
144 <stripeoffset>0</stripeoffset>
145 <config>
146 </config>
147 </provider>
148 </geom>
149 </class>
150 <class id="0xffffffff81a7dc30">
151 <name>MD</name>
152 </class>
153 <class id="0xffffffff82802b28">
154 <name>ZFS::VDEV</name>
155 <geom id="0xfffff80003ba5a00">
156 <class ref="0xffffffff82802b28"/>
157 <name>zfs::vdev</name>
158 <rank>4</rank>
159 <consumer id="0xfffff8000500ee00">
160 <geom ref="0xfffff80003ba5a00"/>
161 <provider ref="0xfffff80003948700"/>
162 <mode>r1w1e1</mode>
163 </consumer>
164 </geom>
165 </class>
166 <class id="0xffffffff82802650">
167 <name>ZFS::ZVOL</name>
168 </class>
169 <class id="0xffffffff81b613d8">
170 <name>SWAP</name>
171 <geom id="0xfffff80018b1d400">
172 <class ref="0xffffffff81b613d8"/>
173 <name>swap</name>
174 <rank>4</rank>
175 <consumer id="0xfffff80018275500">
176 <geom ref="0xfffff80018b1d400"/>
177 <provider ref="0xfffff800170b3a00"/>
178 <mode>r1w1e0</mode>
179 </consumer>
180 </geom>
181 </class>
182 <class id="0xffffffff81afe340">
183 <name>PART</name>
184 <geom id="0xfffff80003a11800">
185 <class ref="0xffffffff81afe340"/>
186 <name>ada0</name>
187 <rank>2</rank>
188 <config>
189 <scheme>GPT</scheme>
190 <entries>128</entries>
191 <first>40</first>
192 <last>1000215175</last>
193 <fwsectors>63</fwsectors>
194 <fwheads>16</fwheads>
195 <state>OK</state>
196 <modified>false</modified>
197 </config>
198 <consumer id="0xfffff8000398ba00">
199 <geom ref="0xfffff80003a11800"/>
200 <provider ref="0xfffff80003a11b00"/>
201 <mode>r2w2e4</mode>
202 <config>
203 </config>
204 </consumer>
205 <provider id="0xfffff80003a11000">
206 <geom ref="0xfffff80003a11800"/>
207 <mode>r1w1e1</mode>
208 <name>ada0p3</name>
209 <mediasize>509961306112</mediasize>
210 <sectorsize>512</sectorsize>
211 <stripesize>4096</stripesize>
212 <stripeoffset>0</stripeoffset>
213 <config>
214 <start>4196352</start>
215 <end>1000214527</end>
216 <index>3</index>
217 <type>freebsd-zfs</type>
218 <offset>2148532224</offset>
219 <length>509961306112</length>
220 <label>zfs0</label>
221 <rawtype>516e7cba-6ecf-11d6-8ff8-00022d09712b</rawtype>
222 <rawuuid>504f1547-c135-11e8-bd11-7d7cd061b26f</rawuuid>
223 <efimedia>HD(3,GPT,504f1547-c135-11e8-bd11-7d7cd061b26f,0x400800,0x3b5e0800)</efimedia>
224 </config>
225 </provider>
226 <provider id="0xfffff80003a11200">
227 <geom ref="0xfffff80003a11800"/>
228 <mode>r1w1e1</mode>
229 <name>ada0p2</name>
230 <mediasize>2147483648</mediasize>
231 <sectorsize>512</sectorsize>
232 <stripesize>4096</stripesize>
233 <stripeoffset>0</stripeoffset>
234 <config>
235 <start>2048</start>
236 <end>4196351</end>
237 <index>2</index>
238 <type>freebsd-swap</type>
239 <offset>1048576</offset>
240 <length>2147483648</length>
241 <label>swap0</label>
242 <rawtype>516e7cb5-6ecf-11d6-8ff8-00022d09712b</rawtype>
243 <rawuuid>5048d40d-c135-11e8-bd11-7d7cd061b26f</rawuuid>
244 <efimedia>HD(2,GPT,5048d40d-c135-11e8-bd11-7d7cd061b26f,0x800,0x400000)</efimedia>
245 </config>
246 </provider>
247 <provider id="0xfffff80003a11400">
248 <geom ref="0xfffff80003a11800"/>
249 <mode>r0w0e0</mode>
250 <name>ada0p1</name>
251 <mediasize>524288</mediasize>
252 <sectorsize>512</sectorsize>
253 <stripesize>4096</stripesize>
254 <stripeoffset>0</stripeoffset>
255 <config>
256 <start>40</start>
257 <end>1063</end>
258 <index>1</index>
259 <type>freebsd-boot</type>
260 <offset>20480</offset>
261 <length>524288</length>
262 <label>gptboot0</label>
263 <rawtype>83bd6b9d-7f41-11dc-be0b-001560b84f0f</rawtype>
264 <rawuuid>503d3458-c135-11e8-bd11-7d7cd061b26f</rawuuid>
265 <efimedia>HD(1,GPT,503d3458-c135-11e8-bd11-7d7cd061b26f,0x28,0x400)</efimedia>
266 </config>
267 </provider>
268 </geom>
269 </class>
270 <class id="0xffffffff81afd290">
271 <name>LABEL</name>
272 <geom id="0xfffff80007e01c00">
273 <class ref="0xffffffff81afd290"/>
274 <name>ada0p1</name>
275 <rank>3</rank>
276 <config>
277 </config>
278 <consumer id="0xfffff8000398b780">
279 <geom ref="0xfffff80007e01c00"/>
280 <provider ref="0xfffff80003a11400"/>
281 <mode>r0w0e0</mode>
282 <config>
283 </config>
284 </consumer>
285 <provider id="0xfffff80007e01b00">
286 <geom ref="0xfffff80007e01c00"/>
287 <mode>r0w0e0</mode>
288 <name>gpt/gptboot0</name>
289 <mediasize>524288</mediasize>
290 <sectorsize>512</sectorsize>
291 <stripesize>4096</stripesize>
292 <stripeoffset>0</stripeoffset>
293 <config>
294 <index>0</index>
295 <length>524288</length>
296 <seclength>1024</seclength>
297 <offset>0</offset>
298 <secoffset>0</secoffset>
299 </config>
300 </provider>
301 </geom>
302 </class>
303 <class id="0xffffffff81afd0c0">
304 <name>VFS</name>
305 </class>
306 <class id="0xffffffff81afc868">
307 <name>Flashmap</name>
308 </class>
309 <class id="0xffffffff81afc678">
310 <name>DISK</name>
311 <geom id="0xfffff80003a11c00">
312 <class ref="0xffffffff81afc678"/>
313 <name>ada0</name>
314 <rank>1</rank>
315 <config>
316 </config>
317 <provider id="0xfffff80003a11b00">
318 <geom ref="0xfffff80003a11c00"/>
319 <mode>r2w2e4</mode>
320 <name>ada0</name>
321 <mediasize>512110190592</mediasize>
322 <sectorsize>512</sectorsize>
323 <stripesize>4096</stripesize>
324 <stripeoffset>0</stripeoffset>
325 <config>
326 <fwheads>16</fwheads>
327 <fwsectors>63</fwsectors>
328 <rotationrate>0</rotationrate>
329 <ident>S250NXAG959927J</ident>
330 <lunid>50025388400c2138</lunid>
331 <descr>Samsung SSD 850 PRO 512GB</descr>
332 </config>
333 </provider>
334 </geom>
335 </class>
336 </mesh>
0 module: cpu_info instance: 0
1 name: cpu_info0 class: misc
2 brand Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz
3 cache_id 0
4 chip_id 0
5 clock_MHz 1995
6 clog_id 0
7 core_id 0
8 cpu_type i386
9 crtime 37.957590729
10 cstates_count 1014040:1014042
11 cstates_nsec 7731043379931:134295045809
12 current_clock_Hz 1995246617
13 current_cstate 1
14 current_pstate 0
15 family 6
16 fpu_type i387 compatible
17 implementation x86 (chipid 0x0 GenuineIntel 50654 family 6 model 85 step 4 clock 2000 MHz)
18 lgroup_id 0
19 max_ncpu_per_chip 1
20 max_ncpu_per_core 1
21 max_pwrcap 0
22 model 85
23 ncore_per_chip 1
24 ncpu_per_chip 1
25 pg_id 18446744073709551615
26 pkg_core_id 0
27 pstates_count null
28 pstates_nsec null
29 snaptime 7827.384789463
30 socket_type Unknown
31 state on-line
32 state_begin 1586929325
33 stepping 4
34 supported_frequencies_Hz 1995246617
35 supported_max_cstates 1
36 supported_max_pstates 0
37 vendor_id GenuineIntel
38
39 module: cpu_info instance: 1
40 name: cpu_info1 class: misc
41 brand Intel(r) Xeon(r) Gold 6138 CPU @ 2.00GHz
42 cache_id 2
43 chip_id 2
44 clock_MHz 1995
45 clog_id 0
46 core_id 2
47 cpu_type i386
48 crtime 38.360143483
49 cstates_count 652423:652424
50 cstates_nsec 7734973017129:130932431358
51 current_clock_Hz 1995246617
52 current_cstate 0
53 current_pstate 0
54 family 6
55 fpu_type i387 compatible
56 implementation x86 (chipid 0x2 GenuineIntel 50654 family 6 model 85 step 4 clock 2000 MHz)
57 lgroup_id 0
58 max_ncpu_per_chip 1
59 max_ncpu_per_core 1
60 max_pwrcap 0
61 model 85
62 ncore_per_chip 1
63 ncpu_per_chip 1
64 pg_id 18446744073709551615
65 pkg_core_id 0
66 pstates_count null
67 pstates_nsec null
68 snaptime 7827.385273786
69 socket_type Unknown
70 state on-line
71 state_begin 1586929326
72 stepping 4
73 supported_frequencies_Hz 1995246617
74 supported_max_cstates 1
75 supported_max_pstates 0
76 vendor_id GenuineIntel
0 module: sderr instance: 0
1 name: sd0,err class: device_error
2 Device Not Ready 0
3 Hard Errors 0
4 Illegal Request 1
5 Media Error 0
6 No Device 0
7 Non-Aligned Writes 0
8 Predictive Failure Analysis 0
9 Product VMware IDE CDR00Revision
10 Recoverable 0
11 Revision 1.00
12 Serial No
13 Size 0
14 Soft Errors 0
15 Transport Errors 0
16 Vendor NECVMWar
17 crtime 36.863598884
18 snaptime 10853.889452697
19
20 module: sderr instance: 1
21 name: sd1,err class: device_error
22 Device Not Ready 0
23 Hard Errors 0
24 Illegal Request 8
25 Media Error 0
26 No Device 0
27 Non-Aligned Writes 0
28 Predictive Failure Analysis 0
29 Product Virtual disk Revision
30 Recoverable 0
31 Revision 1.0
32 Serial No
33 Size 21474836480
34 Soft Errors 0
35 Transport Errors 0
36 Vendor VMware
37 crtime 36.668316603
38 snaptime 10853.889846855
0 module: unix instance: 0
1 name: system_pages class: pages
2 availrmem 672943
3 crtime 0
4 desfree 12254
5 desscan 25
6 econtig 4230422528
7 fastscan 784319
8 freemem 641451
9 kernelbase 0
10 lotsfree 24509
11 minfree 6127
12 nalloc 42231683
13 nalloc_calls 21934
14 nfree 40207768
15 nfree_calls 14661
16 nscan 0
17 pagesfree 641451
18 pageslocked 895695
19 pagestotal 1568638
20 physmem 1568638
21 pp_kernel 914701
22 slowscan 100
23 snaptime 28502.92289964
0 0.00 0.03 0.03 1/165 21302
0 Partition Name : aix61-6
1 Partition Number : 15
2 Sub Processor Mode : -
0 WPAR Key : 13
1 WPAR Configured ID : 14
0 NAME FSTYPE LABEL UUID MOUNTPOINT
1 /dev/sda
2 |-/dev/sda1 xfs 5ba36204-050f-4ab6 /boot
3 `-/dev/sda2 LVM2_member edi7s0-2WVa-ZBan
4 |-/dev/mapper/rhel-root xfs cb455c09-da6b-44e6 /
5 `-/dev/mapper/rhel-swap swap 0fd5997d-eb82-493d [SWAP]
6 /dev/sr0
0 LOGICAL VOLUME: hd5 VOLUME GROUP: rootvg
1 LV IDENTIFIER: 00fa684e00004c00000001715c6c0708.1 PERMISSION: read/write
2 VG STATE: active/complete LV STATE: closed/syncd
3 TYPE: boot WRITE VERIFY: off
4 MAX LPs: 512 PP SIZE: 32 megabyte(s)
5 COPIES: 1 SCHED POLICY: parallel
6 LPs: 1 PPs: 1
7 STALE PPs: 0 BB POLICY: non-relocatable
8 INTER-POLICY: minimum RELOCATABLE: no
9 INTRA-POLICY: edge UPPER BOUND: 32
10 MOUNT POINT: N/A LABEL: primary_bootlv
11 MIRROR WRITE CONSISTENCY: on/ACTIVE
12 EACH LP COPY ON A SEPARATE PV ?: yes
0 00:00.0 Host bridge: Intel Corporation FX PMC [Natoma] (rev 02)
1 00:02.0 VGA compatible controller: Cirrus Logic GD 5446
2 00:03.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)
0 00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
1 00:11.0 PCI bridge: VMware PCI bridge (rev 02)
2 00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01)
3 00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01)
4 00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)
5 03:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
0 PHYSICAL VOLUME: hdisk0 VOLUME GROUP: rootvg
1 PV IDENTIFIER: 00f9280c31fc0ae6 VG IDENTIFIER 00f9280c00004c00000001737084c416
2 PV STATE: active
3 STALE PARTITIONS: 0 ALLOCATABLE: yes
4 PP SIZE: 32 megabyte(s) LOGICAL VOLUMES: 12
5 TOTAL PPs: 959 (30688 megabytes) VG DESCRIPTORS: 2
6 FREE PPs: 1 (32 megabytes) HOT SPARE: no
7 USED PPs: 958 (30656 megabytes) MAX REQUEST: 256 kilobytes
8 FREE DISTRIBUTION: 00..00..00..00..01
9 USED DISTRIBUTION: 192..192..191..192..191
10 MIRROR POOL: None
0 hdisk0 testid0998 rootvg active
0 12:pids:/init.scope
1 11:cpu,cpuacct:/init.scope
2 10:rdma:/
3 /lxc/lxc_container/
0 /dev/disk1s5 on / (apfs, local, read-only, journaled)
1 devfs on /dev (devfs, local, nobrowse)
2 /dev/disk1s1 on /System/Volumes/Data (apfs, local, journaled, nobrowse)
3 /dev/disk1s4 on /private/var/vm (apfs, local, journaled, nobrowse)
4 map auto_home on /System/Volumes/Data/home (autofs, automounted, nobrowse)
5 .host:/VMware Shared Folders on /Volumes/VMware Shared Folders (vmhgfs)
0 MemTotal: 4036680 kB
1 MemFree: 3547792 kB
2 MemAvailable: 3659324 kB
3 Buffers: 4288 kB
4 Cached: 298624 kB
5 SwapCached: 0 kB
6 Active: 81968 kB
7 Inactive: 255516 kB
8 Active(anon): 35056 kB
9 Inactive(anon): 264 kB
10 Active(file): 46912 kB
11 Inactive(file): 255252 kB
12 Unevictable: 0 kB
13 Mlocked: 0 kB
14 SwapTotal: 2097148 kB
15 SwapFree: 2097148 kB
16 Dirty: 216 kB
17 Writeback: 0 kB
18 AnonPages: 34656 kB
19 Mapped: 84464 kB
20 Shmem: 680 kB
21 KReclaimable: 29072 kB
22 Slab: 80056 kB
23 SReclaimable: 29072 kB
24 SUnreclaim: 50984 kB
25 KernelStack: 2688 kB
26 PageTables: 1704 kB
27 NFS_Unstable: 0 kB
28 Bounce: 0 kB
29 WritebackTmp: 0 kB
30 CommitLimit: 4115488 kB
31 Committed_AS: 274552 kB
32 VmallocTotal: 34359738367 kB
33 VmallocUsed: 0 kB
34 VmallocChunk: 0 kB
35 Percpu: 1728 kB
36 HardwareCorrupted: 0 kB
37 AnonHugePages: 0 kB
38 ShmemHugePages: 0 kB
39 ShmemPmdMapped: 0 kB
40 CmaTotal: 0 kB
41 CmaFree: 0 kB
42 HugePages_Total: 0
43 HugePages_Free: 0
44 HugePages_Rsvd: 0
45 HugePages_Surp: 0
46 Hugepagesize: 2048 kB
47 Hugetlb: 0 kB
48 DirectMap4k: 100224 kB
49 DirectMap2M: 4093952 kB
50 DirectMap1G: 2097152 kB
0 MemTotal: 4037608 kB
1 MemFree: 3384496 kB
2 MemAvailable: 3665024 kB
3 Buffers: 2088 kB
4 Cached: 445204 kB
5 SwapCached: 0 kB
6 Active: 206832 kB
7 Inactive: 273488 kB
8 Active(anon): 33224 kB
9 Inactive(anon): 284 kB
10 Active(file): 173608 kB
11 Inactive(file): 273204 kB
12 Unevictable: 0 kB
13 Mlocked: 0 kB
14 SwapTotal: 0 kB
15 SwapFree: 0 kB
16 Dirty: 48 kB
17 Writeback: 0 kB
18 AnonPages: 33124 kB
19 Mapped: 42864 kB
20 Shmem: 480 kB
21 Slab: 111772 kB
22 SReclaimable: 71384 kB
23 SUnreclaim: 40388 kB
24 KernelStack: 2112 kB
25 PageTables: 5772 kB
26 NFS_Unstable: 0 kB
27 Bounce: 0 kB
28 WritebackTmp: 0 kB
29 CommitLimit: 2018804 kB
30 Committed_AS: 214484 kB
31 VmallocTotal: 34359738367 kB
32 VmallocUsed: 0 kB
33 VmallocChunk: 0 kB
34 HardwareCorrupted: 0 kB
35 AnonHugePages: 0 kB
36 ShmemHugePages: 0 kB
37 ShmemPmdMapped: 0 kB
38 HugePages_Total: 0
39 HugePages_Free: 0
40 HugePages_Rsvd: 0
41 HugePages_Surp: 0
42 Hugepagesize: 2048 kB
43 DirectMap4k: 96256 kB
44 DirectMap2M: 4098048 kB
0 MemTotal: 4036680 kB
1 MemFree: 3547792 kB
2 Buffers: 4288 kB
3 Cached: 298624 kB
4 SwapCached: 0 kB
5 Active: 81968 kB
6 Inactive: 255516 kB
7 Active(anon): 35056 kB
8 Inactive(anon): 264 kB
9 Active(file): 46912 kB
10 Inactive(file): 255252 kB
11 Unevictable: 0 kB
12 Mlocked: 0 kB
13 SwapTotal: 2097148 kB
14 SwapFree: 2097148 kB
15 Dirty: 216 kB
16 Writeback: 0 kB
17 AnonPages: 34656 kB
18 Mapped: 84464 kB
19 Shmem: 680 kB
20 KReclaimable: 29072 kB
21 Slab: 80056 kB
22 SReclaimable: 29072 kB
23 SUnreclaim: 50984 kB
24 KernelStack: 2688 kB
25 PageTables: 1704 kB
26 NFS_Unstable: 0 kB
27 Bounce: 0 kB
28 WritebackTmp: 0 kB
29 CommitLimit: 4115488 kB
30 Committed_AS: 274552 kB
31 VmallocTotal: 34359738367 kB
32 VmallocUsed: 0 kB
33 VmallocChunk: 0 kB
34 Percpu: 1728 kB
35 HardwareCorrupted: 0 kB
36 AnonHugePages: 0 kB
37 ShmemHugePages: 0 kB
38 ShmemPmdMapped: 0 kB
39 CmaTotal: 0 kB
40 CmaFree: 0 kB
41 HugePages_Total: 0
42 HugePages_Free: 0
43 HugePages_Rsvd: 0
44 HugePages_Surp: 0
45 Hugepagesize: 2048 kB
46 Hugetlb: 0 kB
47 DirectMap4k: 100224 kB
48 DirectMap2M: 4093952 kB
49 DirectMap1G: 2097152 kB
0 node mounted mounted over vfs date options
1 -------- --------------- --------------- ------ ------------ ---------------
2 /dev/hd4 / jfs2 Mar 22 08:05 rw,log=/dev/hd8
3 /dev/hd2 /usr jfs2 Mar 22 08:05 rw,log=/dev/hd8
4 /proc /proc procfs Mar 22 08:05 rw
5 /dev/hd10opt /opt jfs2 Mar 22 08:05 rw,log=/dev/hd8
6 /dev/hd3 /var x Mar 22 08:05 rw,nodev,log=/dev/hd3
7 joe /var/share /tmp/salam nfs3 Nov 12 04:40
0 Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll
1 en0 1500 link#2 a.c6.24.39.41.3 162325 0 60252 0 0
2 en0 1500 10.32.77 10.32.77.30 162325 0 60252 0 0
3 lo0 16896 link#1 36891 0 36891 0 0
4 lo0 16896 127 127.0.0.1 36891 0 36891 0 0
5 lo0 16896 ::1%1 36891 0 36891 0 0
0 Routing tables
1 Destination Gateway Flags Refs Use If Exp Groups
2
3 Route Tree for Protocol Family 2 (Internet):
4 default 10.32.77.1 UG 1 12696 en0 - -
5 10.32.77.0 10.32.77.40 UHSb 0 0 en0 - - =>
6 10.32.77/24 10.32.77.40 U 0 639 en0 - -
7 10.32.77.40 127.0.0.1 UGHS 0 16 lo0 - -
8 10.32.77.255 10.32.77.40 UHSb 0 1 en0 - -
9 127/8 127.0.0.1 U 1 7504 lo0 - -
10
11 Route Tree for Protocol Family 24 (Internet v6):
12 ::1%1 ::1%1 UH 2 798 lo0 - -
0 export NIM_NAME=aix7
1 export NIM_MASTER_HOSTNAME=test
2 export NIM_MASTER_PORT=1000
3 export NIM_REGISTRATION_PORT=1000
4 export NIM_SHELL="nimsh"
5 export NIM_MASTERID=00000000
6 export NIM_CONFIGURATION=master
0 export NIM_NAME=aix7
1 export NIM_MASTER_HOSTNAME=test
2 export NIM_MASTER_PORT=1000
3 export NIM_REGISTRATION_PORT=1000
4 export NIM_SHELL="nimsh"
5 export NIM_MASTERID=00000000
6 export NIM_CONFIGURATION=testtest
0 export NIM_NAME=aix7
1 export NIM_MASTER_HOSTNAME=test
2 export NIM_MASTER_PORT=1000
3 export NIM_REGISTRATION_PORT=1000
4 export NIM_SHELL="nimsh"
5 export NIM_MASTERID=00000000
0 [
1 {
2 "Linux": [
3 {
4 "Debian": [
5 "Elementary",
6 "Ubuntu",
7 "Raspbian"
8 ]
9 },
10 {
11 "El": [
12 "Fedora",
13 "Amzn",
14 "Centos"
15 ]
16 },
17 {
18 "Sles": [
19 "Opensuse"
20 ]
21 }
22 ]
23 },
24 {
25 "Solaris": [
26 "Bsd"
27 ]
28 },
29 "Macosx",
30 "Windows",
31 "Aix"
32 ]
0 NAME="Ubuntu Linux"
1 VERSION="18.04.1 LTS (Bionic Beaver)"
2 ID=
3 ID_LIKE=debian
4 PRETTY_NAME="Ubuntu 18.04.1 LTS"
5 VERSION_ID="18.04"
6 HOME_URL="https://www.ubuntu.com/"
7 SUPPORT_URL="https://help.ubuntu.com/"
8 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
9 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
10 VERSION_CODENAME=bionic
11 UBUNTU_CODENAME=bionic
0 NAME="Arch Linux"
1 PRETTY_NAME="Arch Linux"
2 ID=arch
3 BUILD_ID=rolling
4 ANSI_COLOR="38;2;23;147;209"
5 HOME_URL="https://archlinux.org/"
6 DOCUMENTATION_URL="https://wiki.archlinux.org/"
7 SUPPORT_URL="https://bbs.archlinux.org/"
8 BUG_REPORT_URL="https://bugs.archlinux.org/"
9 LOGO=archlinux
0 PRETTY_NAME="Debian GNU/Linux 10 (buster)"
1 NAME="Debian GNU/Linux"
2 VERSION_ID="10"
3 VERSION="10 (buster)"
4 VERSION_CODENAME=buster
5 ID=debian
6 HOME_URL="https://www.debian.org/"
7 SUPPORT_URL="https://www.debian.org/support"
8 BUG_REPORT_URL="https://bugs.debian.org/"
0 NAME="Manjaro Linux"
1 ID=manjaro
2 ID_LIKE=arch
3 BUILD_ID=rolling
4 PRETTY_NAME="Manjaro Linux"
5 ANSI_COLOR="32;1;24;144;200"
6 HOME_URL="https://manjaro.org/"
7 DOCUMENTATION_URL="https://wiki.manjaro.org/"
8 SUPPORT_URL="https://manjaro.org/"
9 BUG_REPORT_URL="https://bugs.manjaro.org/"
10 LOGO=manjarolinux
0 DISTR_NAME="Linuxmint"
1 RELEASE=19.3
0 NAME="openSUSE Leap"
1 VERSION="15.1"
2 ID="opensuse-leap"
3 ID_LIKE="suse opensuse"
4 VERSION_ID="15.1"
5 PRETTY_NAME="openSUSE Leap 15.1"
6 ANSI_COLOR="0;32"
7 CPE_NAME="cpe:/o:opensuse:leap:15.1"
8 BUG_REPORT_URL="https://bugs.opensuse.org"
9 HOME_URL="https://www.opensuse.org/"
0 NAME="Oracle Linux Server"
1 VERSION="7.2"
2 ID="ol"
3 VERSION_ID="7.2"
4 PRETTY_NAME="Oracle Linux Server 7.2"
5 ANSI_COLOR="0;31"
6 CPE_NAME="cpe:/o:oracle:linux:7:2:server"
7 HOME_URL="https://linux.oracle.com/"
8 BUG_REPORT_URL="https://bugzilla.oracle.com/"
9
10 ORACLE_BUGZILLA_PRODUCT="Oracle Linux 7"
11 ORACLE_BUGZILLA_PRODUCT_VERSION=7.2
12 ORACLE_SUPPORT_PRODUCT="Oracle Linux"
13 ORACLE_SUPPORT_PRODUCT_VERSION=7.2
0 Oracle Solaris 10 1/13 s10x_u11wos_24a X86
1 Copyright (c) 1983, 2013, Oracle and/or its affiliates. All rights reserved.
2 Assembled 17 January 2013
0 NAME="Virtuozzo"
1 VERSION="7.5.1"
2 ID="virtuozzo"
3 ID_LIKE="rhel fedora"
4 VERSION_ID="7"
5 PRETTY_NAME="Virtuozzo release 7.5.1"
6 ANSI_COLOR="0;31"
7 CPE_NAME="cpe:/o:virtuozzoproject:vz:7"
8 HOME_URL="http://www.virtuozzo.com"
9 BUG_REPORT_URL="https://bugs.openvz.org/"
0 route to: default
1 destination: default
2 mask: default
3 gateway: 10.160.24.1
4 interface: en0
5 flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
6 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
7 0 0 0 0 0 0 1500 0
0 CuDv:
1 name = "hd5"
2 status = 0
3 chgstatus = 1
4 ddins = ""
5 location = ""
6 parent = "rootvg"
7 connwhere = ""
8 PdDvLn = "logical_volume/lvsubclass/lvtype"
9
10 CuDv:
11 name = "hd6"
12 status = 0
13 chgstatus = 1
14 ddins = ""
15 location = ""
16 parent = "rootvg"
17 connwhere = ""
18 PdDvLn = "logical_volume/lvsubclass/lvtype"
0 00000000000000000000000000000001 01 80 10 80 lo
1 fe80000000000000025056fffe9a8481 02 40 20 80 ens160
0 00000000000000000000000000000001 01 80 10 80 lo
1 20010db8000000000000000000000001 02 40 20 01 temporary
2 20010db8000000000000000000000002 02 40 20 02 noad
3 20010db8000000000000000000000003 02 40 20 04 optimistic
4 20010db8000000000000000000000004 02 40 20 08 dadfailed
5 20010db8000000000000000000000005 02 40 20 10 homeaddress
6 20010db8000000000000000000000006 02 40 20 20 deprecated
7 20010db8000000000000000000000007 02 40 20 40 tentative
8 20010db8000000000000000000000008 02 40 20 80 permanent
9 20010db8000000000000000000000009 02 40 20 ff everything
0 Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
1 ens160 00000000 0170100A 0003 0 0 0 00000000 0 0 0
2 ens160 0070100A 00000000 0001 0 0 0 00F0FFFF 0 0 0
0 Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
1 * 00000000 00000000 0001 0 0 0 00000000 0 0 0
0 Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
0 'sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
1 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
2 udev /dev devtmpfs rw,nosuid,relatime,size=3023708k,nr_inodes=755927,mode=755 0 0
3 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
4 tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=610440k,mode=755 0 0
5 /dev/mapper/localhost--vg-root / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0'
0 sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
1 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
2 udev /dev devtmpfs rw,nosuid,relatime,size=3023708k,nr_inodes=755927,mode=755 0 0
3 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
4 selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
5 /dev/mapper/localhost--vg-root / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
0 Name: cat
1 Umask: 0002
2 State: R (running)
3 Tgid: 14437
4 Ngid: 0
5 Pid: 14437
6 PPid: 14122
7 TracerPid: 0
8 Uid: 1000 1000 1000 1000
9 Gid: 1000 1000 1000 1000
10 FDSize: 256
11 Groups: 4 20 24 25 27 29 30 44 46 108 114 1000
12 NStgid: 14437
13 NSpid: 14437
14 NSpgid: 14437
15 envID: 101
16 NSsid: 14122
17 VxID: 0
0 Name: cat
1 Umask: 0002
2 State: R (running)
3 Tgid: 14437
4 Ngid: 0
5 Pid: 14437
6 PPid: 14122
7 TracerPid: 0
8 Uid: 1000 1000 1000 1000
9 Gid: 1000 1000 1000 1000
10 FDSize: 256
11 Groups: 4 20 24 25 27 29 30 44 46 108 114 1000
12 NStgid: 14437
13 NSpid: 14437
14 NSpgid: 14437
15 envID: 0
16 NSsid: 14122
17 VxID: 0
0 CuAt:
1 name = "proc0"
2 attribute = "type"
3 value = "PowerPC_POWER8"
4 type = "R"
5 generic = "D"
6 rep = "sl"
7 nls_index = 49
8
9 CuAt:
10 name = "proc0"
11 attribute = "frequency"
12 value = "3425000000"
13 type = "R"
14 generic = "D"
15 rep = "s"
16 nls_index = 69
17
18 CuAt:
19 name = "proc0"
20 attribute = "smt_threads"
21 value = "8"
22 type = "R"
23 generic = "D"
24 rep = ""
25 nls_index = 82
26
27 CuAt:
28 name = "proc0"
29 attribute = "smt_enabled"
30 value = "true"
31 type = "R"
32 generic = "D"
33 rep = ""
34 nls_index = 83
0 CuDv:
1 name = "proc0"
2 status = 1
3 chgstatus = 2
4 PdDvLn = "processor/sys/proc_rspc"
5
6 CuDv:
7 name = "proc8"
8 status = 0
9 chgstatus = 2
10 PdDvLn = "processor/sys/proc_rspc"
0 PdDv:
1 type = "proc_rspc"
2 class = "processor"
3 Stop = ""
4 inventory_only = 0
5 uniquetype = "processor/sys/proc_rspc"
0 System Configuration: Oracle Corporation sun4v SPARC T7-1
1 Memory size: 8192 Megabytes
2
3 ================================ Virtual CPUs ================================
4
5
6 CPU ID Frequency Implementation Status
7 ------ --------- ---------------------- -------
8 0 4133 MHz SPARC-M7 on-line
9 1 4133 MHz SPARC-M7 on-line
10 2 4133 MHz SPARC-M7 on-line
11 3 4133 MHz SPARC-M7 on-line
12 4 4133 MHz SPARC-M7 on-line
13 5 4133 MHz SPARC-M7 on-line
14 6 4133 MHz SPARC-M7 on-line
15 7 4133 MHz SPARC-M7 on-line
16
17 ======================================== IO Devices =======================================
18 Slot + Bus Name + Model Max Speed Cur Speed
19 Status Type Path /Width /Width
20 -------------------------------------------------------------------------------------------
0 MemTotal: 4036680 kB
1 MemFree: 3547792 kB
2 MemAvailable: 3659324 kB
3 Buffers: 4288 kB
4 Cached: 298624 kB
5 SwapCached: 0 kB
6 Active: 81968 kB
7 Inactive: 255516 kB
8 SwapTotal: 2097148 kB
9 SwapFree: 2097148 kB
10 Dirty: 216 kB
11 Writeback: 0 kB
12 AnonPages: 34656 kB
13 Mapped: 84464 kB
14 Slab: 80056 kB
15 PageTables: 1704 kB
16 NFS_Unstable: 0 kB
17 Bounce: 0 kB
18 WritebackTmp: 0 kB
19 CommitLimit: 4115488 kB
20 Committed_AS: 274552 kB
21 VmallocTotal: 34359738367 kB
22 VmallocUsed: 0 kB
23 VmallocChunk: 0 kB
24 Hugepagesize: 2048 kB
0 route to: default
1 destination: default
2 mask: default
3 gateway: 10.16.112.1
4 interface: net0
5 flags: <UP,GATEWAY,DONE,STATIC>
6 recvpipe sendpipe ssthresh rtt,ms rttvar,ms hopcount mtu expire
7 0 0 0 0 0 0 1500 0
0 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDb4P6/Iw6/WoW1rMbIdpq7qSd8ktGgta2CC3CGrx4AXxjbofyegH+B9wadIrmyNcHmLFAneAmeb+H/dewRMa5Hb7tMN8z+/X9MRzlMaZDGo+1CeXQkJz7SywJrMXb00VcfSmGKe+aHr4OSdN7cXsAR3BzjUSjXdCMkNcp7bw0gcQOmMtqpxFXzbLyFeQgaXtDGwVeByzE9//mhdNv5+pcg3Jx0aC2ED9yrq78HpKYyvLx0t7SpE2i9y+meJxrk/SqlL2BJHAugRwXJkEexPr2hOoulc+PYcbEs8uGiliQEK6aqAtJYVgzcnrDajsElGkQnN9/A9uXY8N0VTLPbYBIz6v+XdWiSK+NA1uzb/GN1XnTvCP0XDTtpzBsMG/m9C02Hq6xQDDWsPp+xv2VfYlVCrlJVf/CHeKEvy78WS+HMu3McvzKmP1Topow+utEqk4qfNxEs7FwwbdDcI02dsWJWJ+1FeCz+ZiscJI0LN0NKvM3xCLGt4hf6s9/JD9b6lO8=
0 AAAAB3NzaC1yc2EAAAADAQABAAABgQDb4P6/Iw6/WoW1rMbIdpq7qSd8ktGgta2CC3CGrx4AXxjbofyegH+B9wadIrmyNcHmLFAneAmeb+H/dewRMa5Hb7tMN8z+/X9MRzlMaZDGo+1CeXQkJz7SywJrMXb00VcfSmGKe+aHr4OSdN7cXsAR3BzjUSjXdCMkNcp7bw0gcQOmMtqpxFXzbLyFeQgaXtDGwVeByzE9//mhdNv5+pcg3Jx0aC2ED9yrq78HpKYyvLx0t7SpE2i9y+meJxrk/SqlL2BJHAugRwXJkEexPr2hOoulc+PYcbEs8uGiliQEK6aqAtJYVgzcnrDajsElGkQnN9/A9uXY8N0VTLPbYBIz6v+XdWiSK+NA1uzb/GN1XnTvCP0XDTtpzBsMG/m9C02Hq6xQDDWsPp+xv2VfYlVCrlJVf/CHeKEvy78WS+HMu3McvzKmP1Topow+utEqk4qfNxEs7FwwbdDcI02dsWJWJ+1FeCz+ZiscJI0LN0NKvM3xCLGt4hf6s9/JD9b6lO8=
0 # This file controls the state of SELinux on the system.
1 # SELINUX= can take one of these three values:
2 # enforcing - SELinux security policy is enforced.
3 # permissive - SELinux prints warnings instead of enforcing.
4 # disabled - No SELinux policy is loaded.
5 SELINUX=enabled
6 # SELINUXTYPE= can take one of three two values:
7 # targeted - Targeted processes are protected,
8 # minimum - Modification of targeted policy. Only selected processes are protected.
9 # mls - Multi Level Security protection.
10 SELINUXTYPE=targeted
0 ID SIZE TYPE
1 0 64 SMB_TYPE_BIOS (BIOS information)
2
3 Vendor: Phoenix Technologies LTD
4 Version String: 6.00
5 Release Date: 12/12/2018
6 Address Segment: 0xea49
7 ROM Size: 65536 bytes
8 Image Size: 88944 bytes
9 Characteristics: 0x7c09df90
10 SMB_BIOSFL_ISA (ISA is supported)
11 SMB_BIOSFL_PCI (PCI is supported)
12 SMB_BIOSFL_PCMCIA (PCMCIA is supported)
13 SMB_BIOSFL_PLUGNPLAY (Plug and Play is supported)
14 SMB_BIOSFL_APM (APM is supported)
15 SMB_BIOSFL_FLASH (BIOS is Flash Upgradeable)
16 SMB_BIOSFL_SHADOW (BIOS shadowing is allowed)
17 SMB_BIOSFL_ESCD (ESCD support is available)
18 SMB_BIOSFL_CDBOOT (Boot from CD is supported)
19 SMB_BIOSFL_SELBOOT (Selectable Boot supported)
20 SMB_BIOSFL_EDD (EDD Spec is supported)
21 SMB_BIOSFL_I5_PRINT (int 0x5 print screen svcs)
22 SMB_BIOSFL_I9_KBD (int 0x9 8042 keyboard svcs)
23 SMB_BIOSFL_I14_SER (int 0x14 serial svcs)
24 SMB_BIOSFL_I17_PRINTER (int 0x17 printer svcs)
25 SMB_BIOSFL_I10_CGA (int 0x10 CGA svcs)
26 Characteristics Extension Byte 1: 0x81
27 SMB_BIOSXB1_ACPI (ACPI is supported)
28 SMB_BIOSXB1_SMBAT (Smart Battery is supported)
29 Characteristics Extension Byte 2: 0x7
30 SMB_BIOSXB2_BBOOT (BIOS Boot Specification supported)
31 SMB_BIOSXB2_FKNETSVC (F-key Network Svc boot supported)
32 SMB_BIOSXB2_ETCDIST (Enable Targeted Content Distrib.)
33 Version Number: 0.0
34 Embedded Ctlr Firmware Version Number: 0.0
0 ID SIZE TYPE
1 3 55 SMB_TYPE_CHASSIS (system enclosure or chassis)
2
3 Manufacturer: No Enclosure
4 Version: N/A
5 Serial Number: None
6 Asset Tag: No Asset Tag
7
8 OEM Data: 0x1234
9 Lock Present: N
10 Chassis Type: 0x1 (other)
11 Boot-Up State: 0x3 (safe)
12 Power Supply State: 0x3 (safe)
13 Thermal State: 0x3 (safe)
14 Chassis Height: 0u
15 Power Cords: 0
16 Element Records: 0
0 ID SIZE TYPE
1 1 123 SMB_TYPE_SYSTEM (system information)
2
3 Manufacturer: VMware, Inc.
4 Product: VMware Virtual Platform
5 Version: None
6 Serial Number: VMware-42 1a 46 19 2d fc 12 90-73 48 ea 8f 1a 37 cb 95
7
8 UUID: 421a4619-2dfc-1290-7348-ea8f1a37cb95
9 Wake-Up Event: 0x6 (power switch)
10 SKU Number:
11 Family:
0 *
1 *
2 *
3 * Devices
4 *
5 scsi_vhci, instance #0
6 pci, instance #0
7 pci15ad,1976 (driver not attached)
8 pci8086,7191, instance #0
9 isa, instance #0
10 i8042, instance #0
11 keyboard, instance #0
12 mouse, instance #0
13 motherboard (driver not attached)
14 lp, instance #0 (driver not attached)
15 asy, instance #0 (driver not attached)
16 asy, instance #1 (driver not attached)
17 fdc (driver not attached)
18 pit_beep, instance #0
19 pci-ide, instance #0
20 ide, instance #0
21 sd, instance #0
22 ide, instance #1 (driver not attached)
23 pci15ad,1976 (driver not attached)
24 pci15ad,740 (driver not attached)
25 display, instance #0
26 pci15ad,1976, instance #0
27 sd, instance #1
28 pci15ad,790, instance #1
29 pci15ad,7a0, instance #0
30 pci15ad,7b0, instance #0
31 pci15ad,7a0, instance #1
32 pci15ad,7a0, instance #2
33 pci15ad,7a0, instance #3
34 pci15ad,7a0, instance #4
35 fw, instance #0
36 sb, instance #1
37 cpu, instance #0 (driver not attached)
38 cpu, instance #1 (driver not attached)
39 fcoe, instance #0
40 iscsi, instance #0
41 options, instance #0
42 *
43 * Loadable Objects
44 *
45 * Loadable Object Path = /platform/i86pc/kernel
46 *
47 drv/amd64/xsvc
48 drv/amd64/vga_arbiter
49 amd64/unix
50 cpu/amd64/cpu.generic
51 cpu/amd64/cpu_ms.AuthenticAMD
52 cpu/amd64/cpu_ms.AuthenticAMD.15
53 cpu/amd64/cpu_ms.GenuineIntel
54 cpu/amd64/cpu_ms.GenuineIntel.6.45
55 hard link: cpu/amd64/cpu_ms.GenuineIntel.6.62
56 cpu/amd64/cpu_ms.GenuineIntel.6.46
57 hard link: cpu/amd64/cpu_ms.GenuineIntel.6.47
58 drv/amd64/acpinex
59 dacf/amd64/consconfig_dacf
60 drv/amd64/amd_iommu
61 drv/amd64/acpippm
62 drv/amd64/cpudrv
63 drv/amd64/intel_iommu
64 drv/amd64/isa
65 drv/amd64/npe
66 drv/amd64/pci
67 drv/amd64/ppm
68 misc/amd64/gfx_private
69 tod/amd64/zvtod
70 drv/amd64/cpc
71 hard link: sys/amd64/cpc
72 misc/amd64/drmach_acpi
73 drv/amd64/dr
74 misc/amd64/cpr
75 kmdb/amd64/zvpsm
76 kmdb/amd64/pcplusmp
77 kmdb/amd64/unix
78 kmdb/amd64/uppc
79 kmdb/amd64/apix
80 kmdb/amd64/iommu
81 drv/amd64/pci-ide
82 drv/amd64/zvpci_stub
83 drv/amd64/zvmm
84 *
85 * Loadable Object Path = /kernel
86 *
87 misc/amd64/dadk
88 drv/amd64/cmdk
89 misc/amd64/gda
90 misc/amd64/strategy
91 drv/amd64/cxge
92 drv/amd64/cxgen
93 drv/amd64/nge
94 drv/amd64/rum
95 fs/amd64/hsfs
96 drv/amd64/ahci
97 misc/qlc/amd64/qlc_fw_8100
98 drv/amd64/rdsv3
99 misc/amd64/nfssrv
100 drv/amd64/fcip
101 crypto/amd64/rsa
102 drv/amd64/imraid_sas
103 drv/amd64/ibp
104 crypto/amd64/sha1
105 hard link: misc/amd64/sha1
106 crypto/amd64/arcfour
107 crypto/amd64/blowfish
108 crypto/amd64/aes
109 crypto/amd64/camellia
110 crypto/amd64/des
111 crypto/amd64/ecc
112 crypto/amd64/md5
113 hard link: misc/amd64/md5
114 crypto/amd64/intelrd
115 drv/amd64/cryptoadm
116 misc/amd64/bignum
117 misc/amd64/kcf
118 crypto/amd64/sha2
119 hard link: misc/amd64/sha2
120 crypto/amd64/sha3
121 hard link: misc/amd64/sha3
122 crypto/amd64/swrand
123 drv/amd64/sdpib
124 hard link: strmod/amd64/sdpib
125 socketmod/amd64/socksdp
126 drv/amd64/lmrc
127 fs/amd64/udfs
128 drv/amd64/ntxn
129 drv/amd64/uvfs
130 hard link: fs/amd64/uvfs
131 drv/amd64/bge
132 misc/amd64/fssnap_if
133 fs/amd64/ufs
134 drv/amd64/fcoe
135 fs/amd64/nfs
136 hard link: sys/amd64/nfs
137 misc/amd64/klmmod
138 misc/amd64/klmops
139 misc/amd64/nfs_dlboot
140 drv/amd64/usbftdi
141 drv/amd64/qlcnic
142 drv/amd64/fcsm
143 sched/amd64/RT_DPTBL
144 sched/amd64/FX_DPTBL
145 pcbe/amd64/uncore.GenuineIntel.6.45
146 pcbe/amd64/pcbe.AuthenticAMD.21
147 pcbe/amd64/pcbe.GenuineIntel.15
148 pcbe/amd64/pcbe.GenuineIntel.6.26
149 pcbe/amd64/pcbe.GenuineIntel.6.60
150 pcbe/amd64/pcbe.GenuineIntel.6
151 pcbe/amd64/pcbe.GenuineIntel.6.78
152 pcbe/amd64/pcbe.GenuineIntel.6.15
153 drv/amd64/uncore
154 pcbe/amd64/pcbe.AuthenticAMD
155 pcbe/amd64/pcbe.GenuineIntel.6.42
156 drv/amd64/zcons
157 fs/amd64/pcfs
158 drv/amd64/pool
0 net0: flags=100001004843<UP,BROADCAST,RUNNING,MULTICAST,DHCP,IPv4,PHYSRUNNING> mtu 1500 index 2
1 inet 10.16.115.67 netmask fffff000 broadcast 10.16.127.255
2 ether 0:50:56:9a:45:1c
0 size inuse free pin virtual mmode
1 memory 2097152 884743 1212409 381480 454093 Ded
2 pg space 131072 2737
3
4 work pers clnt other
5 pin 245356 0 2108 134016
6 in use 454093 0 430650
7
8 PageSize PoolSize inuse pgsp pin virtual
9 s 4 KB - 638055 2737 198376 207405
10 m 64 KB - 15418 0 11444 15418
11 L 16 MB - 0 0 0 0
12 S 16 GB - 0 0 0 0
0 swapfile dev swaplo blocks free
1 /dev/zvol/dsk/rpool/swap 279,1 8 2097144 2097144
2 /dev/zvol/dsk/rpool/swap_test 279,3 8 1433592 1433592
0 Ethernet Cards:
1
2 ethernet:
3
4 Type: Ethernet Controller
5 Bus: PCI
6 Vendor ID: 0x8086
7 Device ID: 0x100f
8 Subsystem Vendor ID: 0x1ab8
9 Subsystem ID: 0x0400
10 Revision ID: 0x0000
11 BSD name: en0
12 Kext name: AppleIntel8254XEthernet.kext
13 Location: /System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/AppleIntel8254XEthernet.kext
14 Version: 3.1.5
0 Hardware:
1
2 Hardware Overview:
3
4 Model Name: MacBook Pro
5 Model Identifier: MacBookPro11,4
6 Processor Name: Intel Core i7
7 Processor Speed: 2.8 GHz
8 Number of Processors: 1
9 Total Number of Cores: 4
10 L2 Cache (per Core): 256 KB
11 L3 Cache: 6 MB
12 Hyper-Threading Technology: Enabled
13 Memory: 16 GB
14 Boot ROM Version: 1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)
15 SMC Version (system): 2.29f24
16 Serial Number (system): 123456789AAA
17 Hardware UUID: 12345678-1111-2222-AAAA-AABBCCDDEEFF
18 Activation Lock Status: Disabled
0 Software:
1
2 System Software Overview:
3
4 System Version: macOS 10.15.2 (19C57)
5 Kernel Version: Darwin 19.2.0
6 Boot Volume: Macintosh HD
7 Boot Mode: Normal
8 Computer Name: Test1’s MacBook Pro
9 User Name: Test1 Test2 (test1.test2)
10 Secure Virtual Memory: Enabled
11 System Integrity Protection: Enabled
12 Time since boot: 3:28
0 Hardware:
1
2 Hardware Overview:
3
4 Model Name: MacBook Pro
5 Model Identifier: MacBookPro11,4
6 Processor Name: Intel Core i7
7 Processor Speed: 2.8 GHz
8 Number of Processors: 1
9 Total Number of Cores: 4
10 L2 Cache (per Core): 256 KB
11 L3 Cache: 6 MB
12 Hyper-Threading Technology: Enabled
13 Memory: 16 GB
14 Boot ROM Version: 1037.60.58.0.0 (iBridge: 17.16.12551.0.0,0)
15 SMC Version (system): 2.29f24
16 Serial Number (system): 123456789AAA
17 Hardware UUID: 12345678-1111-2222-AAAA-AABBCCDDEEFF
18 Activation Lock Status: Disabled
19
20 Software:
21
22 System Software Overview:
23
24 System Version: macOS 10.15.2 (19C57)
25 Kernel Version: Darwin 19.2.0
26 Boot Volume: Macintosh HD
27 Boot Mode: Normal
28 Computer Name: Test1’s MacBook Pro
29 User Name: Test1 Test2 (test1.test2)
30 Secure Virtual Memory: Enabled
31 System Integrity Protection: Enabled
32 Time since boot: 3:28
0 xen
1 xen-hvm
2 aws
0 VERSION 1.0
1 DOMAINROLE|impl=LDoms|control=false|io=false|service=false|root=false
2 DOMAINNAME|name=sol11-11
3 DOMAINUUID|uuid=415dfab4-c373-4ac0-9414-8bf00801fb72
4 DOMAINCONTROL|name=opdx-a0-sun2
5 DOMAINCHASSIS|serialno=AK00358110
0 Mach Virtual Memory Statistics: (page size of 4096 bytes)
1 Pages free: 1364873.
2 Pages active: 2653007.
3 Pages inactive: 1583485.
4 Pages speculative: 1061442.
5 Pages throttled: 0.
6 Pages wired down: 986842.
7 Pages purgeable: 207521.
8 "Translation faults": 2444018907.
9 Pages copy-on-write: 202802362.
10 Pages zero filled: 1466949525.
11 Pages reactivated: 1421652.
12 Pages purged: 277081.
13 File-backed pages: 1562816.
14 Anonymous pages: 3735118.
15 Pages stored in compressor: 1909929.
16 Pages occupied by compressor: 737949.
17 Decompressions: 8791424.
18 Compressions: 13081516.
19 Pageins: 8432517.
20 Pageouts: 5901.
21 Swapins: 10474828.
22 Swapouts: 10972484.
0 Name ID Mem VCPUs State Time(s)
1 Domain-0 0 750 4 r----- 11794.3
2 win 1 1019 1 r----- 0.3
3 linux 2 2048 2 r----- 5624.2
0 The following filesystem versions are supported:
1
2 VER DESCRIPTION
3 --- --------------------------------------------------------
4 1 Initial ZFS filesystem version
5 2 Enhanced directory entries
6 3 Case insensitive and SMB credentials support
7 4 userquota, groupquota properties
8 5 System attributes
9 6 Multilevel file system support
10
11 For more information on a particular version, including supported releases,
12 see the ZFS Administration Guide.
0 This system is currently running ZFS pool version 34.
1
2 The following versions are supported:
3
4 VER DESCRIPTION
5 --- --------------------------------------------------------
6 1 Initial ZFS version
7 2 Ditto blocks (replicated metadata)
8 3 Hot spares and double parity RAID-Z
9 4 zpool history
10 5 Compression using the gzip algorithm
11 6 bootfs pool property
12 7 Separate intent log devices
13 8 Delegated administration
14 9 refquota and refreservation properties
15 10 Cache devices
16 11 Improved scrub performance
17 12 Snapshot properties
18 13 snapused property
19 14 passthrough-x aclinherit
20 15 user/group space accounting
21 16 stmf property support
22 17 Triple-parity RAID-Z
23 18 Snapshot user holds
24 19 Log device removal
25 20 Compression using zle (zero-length encoding)
26 21 Deduplication
27 22 Received properties
28 23 Slim ZIL
29 24 System attributes
30 25 Improved scrub stats
31 26 Improved snapshot deletion performance
32 27 Improved snapshot creation performance
33 28 Multiple vdev replacements
34 29 RAID-Z/mirror hybrid allocator
35 30 Encryption
36 31 Improved 'zfs list' performance
37 32 One MB blocksize
38 33 Improved share support
39 34 Sharing with inheritance
40
41 For more information on a particular version, including supported releases,
42 see the ZFS Administration Guide.
0 This system supports ZFS pool feature flags.
1
2 The following features are supported:
3
4 FEAT DESCRIPTION
5 -------------------------------------------------------------
6 async_destroy (read-only compatible)
7 Destroy filesystems asynchronously.
8 empty_bpobj (read-only compatible)
9 Snapshots use less space.
10 lz4_compress
11 LZ4 compression algorithm support.
12 multi_vdev_crash_dump
13 Crash dumps to multiple vdev pools.
14 spacemap_histogram (read-only compatible)
15 Spacemaps maintain space histograms.
16 enabled_txg (read-only compatible)
17 Record txg at which a feature is enabled
18 hole_birth
19 Retain hole birth txg for more precise zfs send
20 extensible_dataset
21 Enhanced dataset functionality, used by other features.
22 embedded_data
23 Blocks which compress very well use even less space.
24 bookmarks (read-only compatible)
25 "zfs bookmark" command
26 filesystem_limits (read-only compatible)
27 Filesystem and snapshot limits.
28 large_blocks
29 Support for blocks larger than 128KB.
30 large_dnode
31 Variable on-disk size of dnodes.
32 sha512
33 SHA-512/256 hash algorithm.
34 skein
35 Skein hash algorithm.
36 device_removal
37 Top-level vdevs can be removed, reducing logical pool size.
38 obsolete_counts (read-only compatible)
39 Reduce memory used by removed devices when their blocks are freed or remapped.
40 zpool_checkpoint (read-only compatible)
41 Pool state can be checkpointed, allowing rewind later.
42 spacemap_v2 (read-only compatible)
43 Space maps representing large segments are more efficient.
44
45 The following legacy versions are also supported:
46
47 VER DESCRIPTION
48 --- --------------------------------------------------------
49 1 Initial ZFS version
50 2 Ditto blocks (replicated metadata)
51 3 Hot spares and double parity RAID-Z
52 4 zpool history
53 5 Compression using the gzip algorithm
54 6 bootfs pool property
55 7 Separate intent log devices
56 8 Delegated administration
57 9 refquota and refreservation properties
58 10 Cache devices
59 11 Improved scrub performance
60 12 Snapshot properties
61 13 snapused property
62 14 passthrough-x aclinherit
63 15 user/group space accounting
64 16 stmf property support
65 17 Triple-parity RAID-Z
66 18 Snapshot user holds
67 19 Log device removal
68 20 Compression using zle (zero-length encoding)
69 21 Deduplication
70 22 Received properties
71 23 Slim ZIL
72 24 System attributes
73 25 Improved scrub stats
74 26 Improved snapshot deletion performance
75 27 Improved snapshot creation performance
76 28 Multiple vdev replacements
77
78 For more information on a particular version, including supported releases,
79 see the ZFS Administration Guide.
80
0 # frozen_string_literal: true
1
2 require 'facter/framework/cli/cli_launcher'
3
4 describe CliLauncher do
5 subject(:cli_launcher) { CliLauncher }
6
7 let(:args) { [] }
8
9 describe '#prepare_arguments' do
10 let(:task_list) do
11 { 'help' => Thor::Command.new('help', 'description', 'long_description', 'usage'),
12 'query' => Thor::Command.new('query', 'description', 'long_description', 'usage'),
13 'version' => Thor::Command.new('version', 'description', 'long_description', 'usage'),
14 'list_block_groups' => Thor::Command.new('list_block_groups', 'description', 'long_description', 'usage'),
15 'list_cache_groups' => Thor::Command.new('list_cache_groups', 'description', 'long_description', 'usage') }
16 end
17
18 let(:map) do
19 { '-h' => :help, '--version' => :version, '--list-block-groups' => :list_block_groups,
20 '--list-cache-groups' => :list_cache_groups }
21 end
22
23 before do
24 allow(Facter::Cli).to receive(:all_tasks).and_return(task_list)
25 allow(Facter::Cli).to receive(:instance_variable_get).with(:@map).and_return(map)
26 end
27
28 context 'when arguments should be reordered' do
29 let(:args) { %w[--debug --list-cache-groups --list-block-groups] }
30 let(:expected_arguments) { %w[--list-cache-groups --list-block-groups --debug] }
31
32 it 'reorders arguments' do
33 prepare_arguments = CliLauncher.prepare_arguments(args)
34
35 expect(prepare_arguments).to eq(expected_arguments)
36 end
37 end
38
39 context 'when arguments should not be reordered' do
40 let(:args) { %w[--list-cache-groups --list-block-groups --debug] }
41
42 it 'does not reorder arguments' do
43 prepare_arguments = CliLauncher.prepare_arguments(args)
44
45 expect(prepare_arguments).to eq(args)
46 end
47 end
48
49 context 'when default task should be added' do
50 let(:args) { %w[fact1 fact2] }
51 let(:expected_args) { %w[query fact1 fact2] }
52
53 it 'adds default (query) task' do
54 prepare_arguments = CliLauncher.prepare_arguments(args)
55 expect(prepare_arguments).to eq(expected_args)
56 end
57 end
58 end
59
60 describe '#start' do
61 context 'when no errors' do
62 before do
63 allow(Facter::Cli).to receive(:start)
64 end
65
66 it 'calls Facter::Cli.start' do
67 CliLauncher.start(args)
68
69 expect(Facter::Cli).to have_received(:start).with(args, debug: true)
70 end
71 end
72
73 context 'when errors' do
74 before do
75 allow(Facter::OptionsValidator).to receive(:write_error_and_exit)
76 allow(Facter::Cli).to receive(:start).with(any_args).and_raise(Thor::UnknownArgumentError.new({}, {}))
77 end
78
79 it 'calls Facter::OptionsValidator.write_error_and_exit' do
80 CliLauncher.start(args)
81
82 expect(Facter::OptionsValidator).to have_received(:write_error_and_exit)
83 end
84 end
85 end
86 end
0 # frozen_string_literal: true
1
2 describe Facter::ConfigReader do
3 subject(:config_reader) { Facter::ConfigReader }
4
5 let(:linux_default_path) { File.join('/', 'etc', 'puppetlabs', 'facter', 'facter.conf') }
6 let(:windows_default_path) { File.join('C:', 'ProgramData', 'PuppetLabs', 'facter', 'etc', 'facter.conf') }
7
8 before do
9 allow(OsDetector.instance).to receive(:identifier).and_return(os)
10 end
11
12 describe '#init' do
13 before do
14 allow(File).to receive(:readable?).and_return(false)
15 end
16
17 context 'without config_path sent' do
18 context 'with os linux' do
19 let(:os) { :linux }
20
21 before do
22 stub_const('RUBY_PLATFORM', 'linux')
23 end
24
25 it 'calls refresh_config with linux path' do
26 config_reader.init
27 expect(File).to have_received(:readable?).with(linux_default_path)
28 end
29 end
30
31 context 'with os windows' do
32 let(:os) { :windows }
33
34 before do
35 stub_const('RUBY_PLATFORM', 'windows')
36 end
37
38 it 'calls refresh_config with windows path' do
39 config_reader.init
40 expect(File).to have_received(:readable?).with(windows_default_path)
41 end
42 end
43
44 context 'with JRUBY' do
45 let(:os) { :linux }
46
47 before do
48 stub_const('RUBY_PLATFORM', 'java')
49 end
50
51 it 'load no config' do
52 config_reader.init
53 expect(File).to have_received(:readable?).with('')
54 end
55 end
56 end
57
58 context 'with config_path sent' do
59 let(:os) { :linux }
60
61 it 'calls refresh_config with custom path' do
62 config_reader.init('/path/to/config/file')
63 expect(File).to have_received(:readable?).with('/path/to/config/file')
64 end
65 end
66 end
67
68 describe '#block_list' do
69 let(:os) { :linux }
70
71 before do
72 allow(File).to receive(:readable?).and_return(true)
73 allow(Hocon).to receive(:load).and_return(config)
74 end
75
76 context 'with empty config file' do
77 let(:config) { {} }
78
79 it 'returns nil' do
80 config_reader.init
81
82 expect(config_reader.block_list).to eq(nil)
83 end
84 end
85
86 context 'with blocklist in config file' do
87 let(:config) { { 'facts' => { 'blocklist' => %w[group1 fact1] } } }
88
89 it 'returns blocklisted facts' do
90 config_reader.init
91 expect(config_reader.block_list).to eq(%w[group1 fact1])
92 end
93 end
94 end
95
96 describe '#ttls' do
97 let(:os) { :linux }
98
99 before do
100 allow(File).to receive(:readable?).and_return(true)
101 allow(Hocon).to receive(:load).and_return(config)
102 end
103
104 context 'with empty config file' do
105 let(:config) { {} }
106
107 it 'returns nil' do
108 config_reader.init
109
110 expect(config_reader.ttls).to eq(nil)
111 end
112 end
113
114 context 'with ttls in config file' do
115 let(:config) { { 'facts' => { 'ttls' => [{ 'fact_name' => '10 days' }] } } }
116
117 it 'returns blocklisted facts' do
118 config_reader.init
119
120 expect(config_reader.ttls).to eq([{ 'fact_name' => '10 days' }])
121 end
122 end
123 end
124
125 describe '#global' do
126 let(:os) { :linux }
127
128 before do
129 allow(File).to receive(:readable?).and_return(true)
130 allow(Hocon).to receive(:load).and_return(config)
131 end
132
133 context 'with empty config file' do
134 let(:config) { {} }
135
136 it 'returns nil' do
137 config_reader.init
138
139 expect(config_reader.global).to eq(nil)
140 end
141 end
142
143 context 'with invalid config file' do
144 let(:config) { 'some corrupt information' }
145 let(:log) { instance_spy(Facter::Log) }
146
147 before do
148 allow(Facter::Log).to receive(:new).and_return(log)
149 allow(Hocon).to receive(:load).and_raise(StandardError)
150 allow(log).to receive(:warn)
151 end
152
153 it 'loggs a warning' do
154 config_reader.init
155
156 expect(log).to have_received(:warn).with(/Facter failed to read config file/)
157 end
158 end
159
160 context 'with global section in config file' do
161 let(:config) { { 'global' => 'global_config' } }
162
163 it 'returns blocklisted facts' do
164 config_reader.init
165
166 expect(config_reader.global).to eq('global_config')
167 end
168 end
169 end
170
171 describe '#cli' do
172 let(:os) { :linux }
173
174 before do
175 allow(File).to receive(:readable?).and_return(true)
176 allow(Hocon).to receive(:load).and_return(config)
177 end
178
179 context 'with empty config file' do
180 let(:config) { {} }
181
182 it 'returns nil' do
183 config_reader.init
184
185 expect(config_reader.cli).to eq(nil)
186 end
187 end
188
189 context 'with cli section in config file' do
190 let(:config) { { 'cli' => 'cli_config' } }
191
192 it 'returns blocklisted facts' do
193 config_reader.init
194
195 expect(config_reader.cli).to eq('cli_config')
196 end
197 end
198 end
199
200 describe '#fact-groups' do
201 let(:os) { :linux }
202
203 before do
204 allow(File).to receive(:readable?).and_return(true)
205 allow(Hocon).to receive(:load).and_return(config)
206 end
207
208 context 'with empty config file' do
209 let(:config) { {} }
210
211 it 'returns nil' do
212 config_reader.init
213
214 expect(config_reader.fact_groups).to eq(nil)
215 end
216 end
217
218 context 'with fact-groups in config file' do
219 let(:config) { { 'fact-groups' => { 'cached-custom-facts' => ['my_custom_fact'] } } }
220
221 it 'returns fact-groups' do
222 config_reader.init
223
224 expect(config_reader.fact_groups).to eq('cached-custom-facts' => ['my_custom_fact'])
225 end
226 end
227 end
228 end
0 # frozen_string_literal: true
1
2 describe Facter::FactGroups do
3 subject(:fact_groups) { Facter::FactGroups }
4
5 let(:fg) { fact_groups.new }
6 let(:config_reader) { class_spy(Facter::ConfigReader) }
7
8 before do
9 allow(Facter::ConfigReader).to receive(:init).and_return(config_reader)
10 allow(config_reader).to receive(:block_list).and_return([])
11 allow(config_reader).to receive(:ttls).and_return([])
12 allow(config_reader).to receive(:fact_groups).and_return({})
13 end
14
15 describe '#initialize' do
16 it 'merges groups from facter.conf' do
17 allow(config_reader).to receive(:fact_groups).and_return('foo' => 'bar')
18 fct_grp = fact_groups.new
19
20 expect(fct_grp.groups).to include('foo' => 'bar')
21 end
22
23 it 'merges external facts' do
24 external_path = '/path/to/external'
25 allow(Facter::Options).to receive(:external_dir).and_return([external_path])
26 allow(Dir).to receive(:exist?).with(external_path).and_return true
27 allow(Dir).to receive(:entries).with(external_path).and_return(['.', '..', 'external.sh'])
28 fct_grp = fact_groups.new
29
30 expect(fct_grp.instance_variable_get(:@groups)).to include('external.sh' => nil)
31 end
32
33 it 'merges groups from facter.conf with default group override' do
34 stub_const('Facter::Config::FACT_GROUPS', { 'kernel' => %w[kernel kernelversion] })
35 allow(config_reader).to receive(:fact_groups).and_return('kernel' => 'foo')
36 fct_grp = fact_groups.new
37
38 expect(fct_grp.groups).to eq('kernel' => 'foo')
39 end
40 end
41
42 describe '#blocked_facts' do
43 context 'with block_list' do
44 before do
45 stub_const('Facter::Config::FACT_GROUPS', 'blocked_group' => %w[fact1 fact2])
46 allow(config_reader).to receive(:block_list).and_return(%w[blocked_group blocked_fact])
47 end
48
49 it 'returns a list of blocked facts' do
50 blk_list = fact_groups.new
51
52 expect(blk_list.blocked_facts).to eq(%w[fact1 fact2 blocked_fact])
53 end
54 end
55
56 context 'with blocked `legacy` group' do
57 before do
58 stub_const('Facter::Config::FACT_GROUPS', 'legacy' => %w[legacy_fact1 legacy_fact2])
59 allow(config_reader).to receive(:block_list).and_return(%w[legacy])
60 end
61
62 # legacy facts are excluded because they are blocked by another mechanism that takes into account the fact's type
63 it 'excludes legacy facts' do
64 blk_list = fact_groups.new
65
66 expect(blk_list.blocked_facts).to be_empty
67 end
68 end
69
70 context 'without block_list' do
71 before do
72 allow(config_reader).to receive(:block_list).and_return([])
73 allow(Facter::ConfigReader).to receive(:new).and_return(config_reader)
74 allow(config_reader).to receive(:block_list).and_return(nil)
75 end
76
77 it 'finds no block group file' do
78 blk_list = fact_groups.new
79
80 expect(blk_list.blocked_facts).to eq([])
81 end
82 end
83 end
84
85 describe '#get_fact_group' do
86 before do
87 stub_const('Facter::Config::FACT_GROUPS', 'operating system' => %w[os os.name])
88 allow(config_reader).to receive(:ttls).and_return(['operating system' => '30 minutes'])
89 end
90
91 it 'returns group' do
92 expect(fg.get_fact_group('os')).to eq('operating system')
93 end
94
95 it 'returns nil' do
96 expect(fg.get_fact_group('memory')).to be_nil
97 end
98 end
99
100 describe '#get_group_ttls' do
101 let(:ttls) { ['operating system' => '30 minutes'] }
102
103 before do
104 stub_const('Facter::Config::FACT_GROUPS', 'operating system' => %w[os os.name])
105 allow(config_reader).to receive(:ttls).and_return(ttls)
106 end
107
108 it 'returns group' do
109 expect(fg.get_group_ttls('operating system')).to eq(1800)
110 end
111
112 it 'returns nil' do
113 expect(fg.get_group_ttls('memory')).to be_nil
114 end
115
116 context 'when ttls has short names for units' do
117 let(:ttls) { ['operating system' => '10000000000000 ns', 'memory' => '10000', 'hostname' => '30 h'] }
118
119 it 'returns os ttl in seconds' do
120 expect(fg.get_group_ttls('operating system')).to eq(10_000)
121 end
122
123 it 'returns memory ttl in seconds' do
124 expect(fg.get_group_ttls('memory')).to eq(10)
125 end
126
127 it 'returns hostname ttl in seconds' do
128 expect(fg.get_group_ttls('hostname')).to eq(108_000)
129 end
130 end
131
132 context 'when ttls has hour instead of hour' do
133 let(:ttls) { ['operating system' => '1 hour', 'memory' => '1 day', 'hostname' => '30 invalid_unit'] }
134 let(:logger) { instance_spy(Facter::Log) }
135
136 before do
137 allow(Facter::Log).to receive(:new).and_return(logger)
138 end
139
140 it 'logs an error message' do
141 fg.get_group_ttls('hostname')
142 expect(logger).to have_received(:error).with('Could not parse time unit invalid_units '\
143 "(try #{Facter::FactGroups::STRING_TO_SECONDS.keys.reject(&:empty?).join(', ')})").twice
144 end
145
146 it 'returns os ttl in seconds' do
147 expect(fg.get_group_ttls('operating system')).to eq(3600)
148 end
149
150 it 'returns memory ttl in seconds' do
151 expect(fg.get_group_ttls('memory')).to eq(86_400)
152 end
153 end
154 end
155 end
0 # frozen_string_literal: true
1
2 describe Facter::ExternalFactManager do
3 describe '#resolve' do
4 let(:custom_fact_name) { 'my_custom_fact' }
5 let(:custom_fact_value) { 'custom_fact_value' }
6 let(:custom_fact) { Facter::Util::Fact.new(custom_fact_name) }
7 let(:searched_fact) { Facter::SearchedFact.new(custom_fact_name, nil, '', :custom) }
8 let(:custom_fact_manager) { Facter::ExternalFactManager.new }
9
10 before do
11 allow(LegacyFacter).to receive(:[]).with(custom_fact_name).and_return(custom_fact)
12 allow(custom_fact).to receive(:value).and_return(custom_fact_value)
13 end
14
15 it 'resolves one custom fact' do
16 resolved_facts = custom_fact_manager.resolve_facts([searched_fact])
17 expect(resolved_facts.length).to eq(1)
18 end
19
20 it 'resolves custom fact with name my_custom_fact' do
21 resolved_facts = custom_fact_manager.resolve_facts([searched_fact])
22 expect(resolved_facts.first.name).to eq(custom_fact_name)
23 end
24
25 it 'resolves custom fact with value custom_fact_value' do
26 resolved_facts = custom_fact_manager.resolve_facts([searched_fact])
27 expect(resolved_facts.first.value).to eq(custom_fact_value)
28 end
29 end
30 end
0 # frozen_string_literal: true
1
2 describe Facter::InternalFactManager do
3 let(:internal_fact_manager) { Facter::InternalFactManager.new }
4 let(:os_name_class_spy) { class_spy(Facts::Linux::Os::Name) }
5 let(:os_name_instance_spy) { instance_spy(Facts::Linux::Os::Name) }
6
7 describe '#resolve_facts' do
8 it 'resolved one core fact' do
9 resolved_fact = mock_resolved_fact('os', 'Debian', nil)
10 allow(os_name_class_spy).to receive(:new).and_return(os_name_instance_spy)
11 allow(os_name_instance_spy).to receive(:call_the_resolver).and_return(resolved_fact)
12 searched_fact = instance_spy(Facter::SearchedFact, name: 'os', fact_class: os_name_class_spy,
13 user_query: '', type: :core)
14
15 resolved_facts = internal_fact_manager.resolve_facts([searched_fact])
16
17 expect(resolved_facts).to eq([resolved_fact])
18 end
19
20 it 'resolved one legacy fact' do
21 networking_interface_class_spy = class_spy(Facts::Windows::NetworkInterfaces)
22 windows_networking_interface = instance_spy(Facts::Windows::NetworkInterfaces)
23 resolved_fact = mock_resolved_fact('network_Ethernet0', '192.168.5.121', nil, :legacy)
24 allow(networking_interface_class_spy).to receive(:new).and_return(windows_networking_interface)
25 allow(windows_networking_interface).to receive(:call_the_resolver).and_return(resolved_fact)
26 searched_fact = instance_spy(Facter::SearchedFact, name: 'network_.*', fact_class: networking_interface_class_spy,
27 user_query: '', type: :core)
28
29 resolved_facts = internal_fact_manager.resolve_facts([searched_fact])
30
31 expect(resolved_facts).to eq([resolved_fact])
32 end
33
34 context 'when resolved fact is of type nil' do
35 let(:searched_fact) do
36 instance_spy(Facter::SearchedFact, name: 'missing_fact', fact_class: nil,
37 user_query: '', type: :nil)
38 end
39 let(:resolved_fact) { instance_spy(Facter::ResolvedFact) }
40
41 before do
42 allow(Facter::ResolvedFact).to receive(:new).and_return(resolved_fact)
43 end
44
45 it 'resolved one nil fact' do
46 resolved_facts = internal_fact_manager.resolve_facts([searched_fact])
47
48 expect(resolved_facts).to eq([resolved_fact])
49 end
50 end
51
52 context 'when there are multiple search facts pointing to the same fact' do
53 before do
54 resolved_fact = mock_resolved_fact('os', 'Debian', nil)
55
56 allow(os_name_class_spy).to receive(:new).and_return(os_name_instance_spy)
57 allow(os_name_instance_spy).to receive(:call_the_resolver).and_return(resolved_fact)
58
59 searched_fact = instance_spy(Facter::SearchedFact, name: 'os.name', fact_class: os_name_class_spy,
60 user_query: '', type: :core)
61
62 searched_fact_with_alias = instance_spy(Facter::SearchedFact, name: 'operatingsystem',
63 fact_class: os_name_class_spy,
64 user_query: '', type: :core)
65
66 internal_fact_manager.resolve_facts([searched_fact, searched_fact_with_alias])
67 end
68
69 it 'resolves the fact for all searched facts' do
70 expect(os_name_instance_spy).to have_received(:call_the_resolver).twice
71 end
72 end
73
74 context 'when fact throws exception' do
75 let(:searched_fact) do
76 instance_spy(Facter::SearchedFact,
77 name: 'os',
78 fact_class: os_name_class_spy,
79
80 user_query: '',
81 type: :core)
82 end
83
84 let(:logger_double) { instance_spy(Logger) }
85
86 before do
87 allow(os_name_class_spy).to receive(:new).and_return(os_name_instance_spy)
88
89 exception = StandardError.new('error_message')
90 exception.set_backtrace(%w[backtrace])
91 allow(os_name_instance_spy).to receive(:call_the_resolver).and_raise(exception)
92
93 allow(logger_double).to receive(:error)
94 Facter::Log.class_variable_set(:@@logger, logger_double)
95 end
96
97 after do
98 Facter::Log.class_variable_set(:@@logger, Logger.new(STDOUT))
99 end
100
101 it 'does not store the fact value' do
102 resolved_facts = internal_fact_manager.resolve_facts([searched_fact])
103
104 expect(resolved_facts).to match_array []
105 end
106
107 it 'logs backtrace as error with --trace option' do
108 allow(Facter::Options).to receive(:[])
109 allow(Facter::Options).to receive(:[]).with(:trace).and_return(true)
110 internal_fact_manager.resolve_facts([searched_fact])
111
112 expect(logger_double)
113 .to have_received(:error)
114 .with("Facter::InternalFactManager - #{colorize("error_message\nbacktrace", Facter::RED)}")
115 end
116
117 it 'logs error message as error without --trace option' do
118 internal_fact_manager.resolve_facts([searched_fact])
119
120 expect(logger_double)
121 .to have_received(:error)
122 .with("Facter::InternalFactManager - #{colorize('error_message', Facter::RED)}")
123 end
124 end
125 end
126 end
0 # frozen_string_literal: true
1
2 describe Facter::ClassDiscoverer do
3 describe '#discover_classes' do
4 let(:result) { [Facts::Windows::NetworkInterfaces, Facts::Windows::FipsEnabled] }
5
6 before do
7 Singleton.__init__(Facter::ClassDiscoverer)
8 end
9
10 it 'loads all classes' do
11 allow(Module).to receive(:const_get).with('Facts::Windows').and_return(Facts::Windows)
12 allow(Facts::Windows).to receive(:constants).and_return(%i[NetworkInterfaces FipsEnabled])
13
14 expect(Facter::ClassDiscoverer.instance.discover_classes('Windows')).to eq(result)
15 end
16
17 it 'loads no classes' do
18 allow(Module).to receive(:const_get).and_return([])
19
20 expect(Facter::ClassDiscoverer.instance.discover_classes('Debian')).to eq([])
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facter::ExternalFactLoader do
3 let(:collection) { double(LegacyFacter::Util::Collection) }
4 let(:external_fact) { Facter::Util::Fact.new('external_fact', options: { fact_type: :external }) }
5 let(:custom_fact) { Facter::Util::Fact.new('custom_fact', options: { fact_type: :custom }) }
6
7 before do
8 allow(LegacyFacter).to receive(:collection).and_return(collection)
9 allow(collection).to receive(:external_facts).and_return({})
10 allow(collection).to receive(:custom_facts).and_return([])
11 end
12
13 describe '#custom_facts' do
14 context 'when one custom fact is loaded' do
15 before do
16 allow(collection).to receive(:custom_facts).and_return({ 'custom_fact' => custom_fact })
17 allow(Facter::Options).to receive(:custom_dir?).and_return(true)
18 allow(Facter::Options).to receive(:custom_dir).and_return(['custom_fact_dir'])
19 end
20
21 it 'returns one custom fact' do
22 external_fact_loader = Facter::ExternalFactLoader.new
23 expect(external_fact_loader.custom_facts.size).to eq(1)
24 end
25
26 it 'returns custom fact with name custom_fact' do
27 external_fact_loader = Facter::ExternalFactLoader.new
28 expect(external_fact_loader.custom_facts.first.name).to eq('custom_fact')
29 end
30 end
31
32 context 'when no custom facts are loaded' do
33 before do
34 allow(collection).to receive(:custom_facts).and_return([])
35 end
36
37 it 'return no custom facts' do
38 external_fact_loader = Facter::ExternalFactLoader.new
39 expect(external_fact_loader.custom_facts).to eq([])
40 end
41 end
42 end
43
44 describe '#external_facts' do
45 context 'when loads one external fact' do
46 before do
47 allow(collection).to receive(:external_facts).and_return({ 'external_fact' => external_fact })
48 allow(Facter::Options).to receive(:external_dir?).and_return(true)
49 allow(Facter::Options).to receive(:external_dir).and_return(['external_fact_dir'])
50 end
51
52 it 'returns one external fact' do
53 external_fact_loader = Facter::ExternalFactLoader.new
54 expect(external_fact_loader.external_facts.size).to eq(1)
55 end
56
57 it 'returns external fact with name external_fact' do
58 external_fact_loader = Facter::ExternalFactLoader.new
59 expect(external_fact_loader.external_facts.first.name).to eq('external_fact')
60 end
61 end
62
63 context 'when loads no external facts' do
64 before do
65 allow(collection).to receive(:external_facts).and_return([])
66 end
67
68 it 'return no external facts' do
69 external_fact_loader = Facter::ExternalFactLoader.new
70 expect(external_fact_loader.external_facts).to eq([])
71 end
72 end
73 end
74 end
0 # frozen_string_literal: true
1
2 describe Facter::FactLoader do
3 describe '#load' do
4 let(:internal_fact_loader_double) { instance_spy(Facter::InternalFactLoader) }
5 let(:external_fact_loader_double) { instance_spy(Facter::ExternalFactLoader) }
6
7 let(:ubuntu_os_name) { instance_spy(Facts::Linux::Os::Name) }
8 let(:networking_class) { instance_spy(Facts::Windows::NetworkInterfaces) }
9
10 let(:loaded_fact_os_name) { instance_spy(Facter::LoadedFact, name: 'os.name', klass: ubuntu_os_name, type: :core) }
11 let(:loaded_fact_os_name_legacy) do
12 instance_spy(Facter::LoadedFact, name: 'operatingsystem', klass: ubuntu_os_name,
13 type: :legacy)
14 end
15 let(:loaded_fact_networking) do
16 instance_spy(Facter::LoadedFact, name: 'network_.*', klass: networking_class, type: :legacy)
17 end
18 let(:loaded_fact_custom_fact) { instance_spy(Facter::LoadedFact, name: 'custom_fact', klass: nil, type: :custom) }
19
20 let(:loaded_env_custom_fact) do
21 instance_spy(Facter::LoadedFact, name: 'os.name', klass: nil,
22 type: :external, is_env: true)
23 end
24
25 before do
26 Singleton.__init__(Facter::FactLoader)
27
28 allow(Facter::InternalFactLoader).to receive(:new).and_return(internal_fact_loader_double)
29 allow(Facter::ExternalFactLoader).to receive(:new).and_return(external_fact_loader_double)
30 end
31
32 it 'loads all internal facts' do
33 options = { user_query: true }
34
35 facts_to_load = [loaded_fact_os_name, loaded_fact_networking]
36
37 allow(internal_fact_loader_double).to receive(:facts).and_return(facts_to_load)
38 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
39 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
40
41 loaded_facts = Facter::FactLoader.instance.load(options[:user_query], options)
42 expect(loaded_facts).to eq(facts_to_load)
43 end
44
45 it 'loads core facts' do
46 options = {}
47
48 facts_to_load = [loaded_fact_os_name]
49
50 allow(internal_fact_loader_double).to receive(:core_facts).and_return(facts_to_load)
51 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
52 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
53
54 loaded_facts = Facter::FactLoader.instance.load(nil, options)
55 expect(loaded_facts).to eq(facts_to_load)
56 end
57
58 it 'blocks one internal fact' do
59 options = { blocked_facts: ['os.name'] }
60
61 facts_to_load = [loaded_fact_os_name, loaded_fact_os_name_legacy]
62
63 allow(internal_fact_loader_double).to receive(:core_facts).and_return(facts_to_load)
64 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
65 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
66
67 loaded_facts = Facter::FactLoader.instance.load(nil, options)
68 expect(loaded_facts.size).to eq(0)
69 end
70
71 it 'blocks one legacy fact' do
72 options = { blocked_facts: ['operatingsystem'] }
73
74 facts_to_load = [loaded_fact_os_name, loaded_fact_os_name_legacy]
75
76 allow(internal_fact_loader_double).to receive(:core_facts).and_return(facts_to_load)
77 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
78 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
79
80 loaded_facts = Facter::FactLoader.instance.load(nil, options)
81 expect(loaded_facts.size).to eq(1)
82 end
83
84 context 'when blocking custom facts' do
85 before do
86 facts_to_load = [loaded_fact_custom_fact]
87
88 allow(internal_fact_loader_double).to receive(:core_facts).and_return([])
89 allow(external_fact_loader_double).to receive(:custom_facts).and_return(facts_to_load)
90 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
91 end
92
93 it 'blocks one custom fact' do
94 options = { custom_facts: true, blocked_facts: ['custom_fact'] }
95 loaded_facts = Facter::FactLoader.instance.load(nil, options)
96
97 expect(loaded_facts.size).to eq(0)
98 end
99 end
100
101 it 'loads the same amount of core facts everytime' do
102 options = { user_query: true }
103
104 facts_to_load = [loaded_fact_os_name, loaded_fact_networking]
105
106 allow(internal_fact_loader_double).to receive(:facts).and_return(facts_to_load)
107 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
108 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
109
110 loaded_facts1 = Facter::FactLoader.instance.load(options[:user_query], options)
111 loaded_facts2 = Facter::FactLoader.instance.load(options[:user_query], options)
112 expect(loaded_facts1).to eq(loaded_facts2)
113 end
114
115 it 'loads the same amount of custom facts everytime' do
116 options = { blocked_facts: ['os.name'], custom_facts: true }
117
118 facts_to_load = [loaded_fact_os_name]
119
120 allow(internal_fact_loader_double).to receive(:core_facts).and_return(facts_to_load)
121 allow(external_fact_loader_double).to receive(:custom_facts).and_return(facts_to_load)
122 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
123
124 loaded_facts1 = Facter::FactLoader.instance.load(nil, options)
125 loaded_facts2 = Facter::FactLoader.instance.load(nil, options)
126 expect(loaded_facts1).to eq(loaded_facts2)
127 end
128
129 it 'env facts override core facts' do
130 options = { external_facts: true }
131
132 allow(internal_fact_loader_double).to receive(:core_facts).and_return([loaded_fact_os_name])
133 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
134 allow(external_fact_loader_double).to receive(:external_facts).and_return([loaded_env_custom_fact])
135
136 loaded_facts = Facter::FactLoader.instance.load(nil, options)
137 expect(loaded_facts).to be_an_instance_of(Array).and contain_exactly(
138 loaded_env_custom_fact
139 )
140 end
141
142 context 'when blocking legacy group' do
143 before do
144 facts_to_load = [loaded_fact_os_name_legacy]
145
146 allow(internal_fact_loader_double).to receive(:core_facts).and_return(facts_to_load)
147 allow(external_fact_loader_double).to receive(:custom_facts).and_return([])
148 allow(external_fact_loader_double).to receive(:external_facts).and_return([])
149 end
150
151 let(:options) { { block_list: 'legacy' } }
152
153 it 'blocks legacy facts' do
154 loaded_facts = Facter::FactLoader.instance.load(nil, options)
155
156 expect(loaded_facts).to be_empty
157 end
158 end
159 end
160 end
0 # frozen_string_literal: true
1
2 describe Facter::InternalFactLoader do
3 describe '#initialize' do
4 subject(:internal_fact_loader) { Facter::InternalFactLoader.new }
5
6 let(:os_detector_mock) { instance_spy(OsDetector) }
7 let(:class_discoverer_mock) { instance_spy(Facter::ClassDiscoverer) }
8
9 before do
10 allow(os_detector_mock).to receive(:hierarchy).and_return([:Linux])
11 allow(OsDetector).to receive(:instance).and_return(os_detector_mock)
12 end
13
14 context 'when loading one legacy fact' do
15 before do
16 allow(os_detector_mock).to receive(:hierarchy).and_return([:Windows])
17 allow(OsDetector).to receive(:instance).and_return(os_detector_mock)
18
19 allow(class_discoverer_mock)
20 .to receive(:discover_classes)
21 .with(:Windows)
22 .and_return([Facts::Windows::NetworkInterfaces])
23 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
24
25 stub_const('Facts::Windows::NetworkInterfaces::FACT_NAME', 'network_.*')
26 end
27
28 it 'loads no core facts' do
29 expect(internal_fact_loader.core_facts).to be_empty
30 end
31
32 it 'loads one legacy fact' do
33 expect(internal_fact_loader.legacy_facts.size).to eq(1)
34 end
35
36 it 'loads one fact with :legacy type' do
37 expect(internal_fact_loader.legacy_facts.first.type).to eq(:legacy)
38 end
39 end
40
41 context 'when loading one core fact' do
42 before do
43 allow(class_discoverer_mock)
44 .to receive(:discover_classes)
45 .with(:Linux)
46 .and_return([Facts::Linux::Path])
47 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
48
49 stub_const('Facts::Linux::Path::FACT_NAME', 'path')
50 end
51
52 it 'loads no legacy facts' do
53 expect(internal_fact_loader.legacy_facts).to be_empty
54 end
55
56 it 'loads one core fact' do
57 expect(internal_fact_loader.core_facts.size).to eq(1)
58 end
59
60 it 'loads one fact with :core type' do
61 expect(internal_fact_loader.core_facts.first.type).to eq(:core)
62 end
63 end
64
65 context 'when loading one legacy fact and one core fact' do
66 before do
67 allow(os_detector_mock).to receive(:hierarchy).and_return([:Windows])
68 allow(OsDetector).to receive(:instance).and_return(os_detector_mock)
69
70 allow(class_discoverer_mock)
71 .to receive(:discover_classes)
72 .with(:Windows)
73 .and_return([Facts::Windows::NetworkInterfaces, Facts::Windows::Path])
74 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
75
76 stub_const('Facts::Windows::NetworkInterface::FACT_NAME', 'network_.*')
77 stub_const('Facts::Windows::OsName::FACT_NAME', 'path')
78 end
79
80 it 'loads two facts' do
81 expect(internal_fact_loader.facts.size).to eq(2)
82 end
83
84 it 'loads one legacy fact' do
85 expect(internal_fact_loader.facts.count { |lf| lf.type == :legacy }).to eq(1)
86 end
87
88 it 'loads one core fact' do
89 expect(internal_fact_loader.facts.count { |lf| lf.type == :core }).to eq(1)
90 end
91 end
92
93 context 'when loading no facts' do
94 before do
95 allow(class_discoverer_mock)
96 .to receive(:discover_classes)
97 .with(:Linux)
98 .and_return([])
99 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
100 end
101
102 it 'loads no facts' do
103 expect(internal_fact_loader.facts).to be_empty
104 end
105 end
106
107 context 'when loading hierarchy of facts' do
108 before do
109 allow(os_detector_mock).to receive(:hierarchy).and_return(%i[Linux Rhel])
110 allow(OsDetector).to receive(:instance).and_return(os_detector_mock)
111
112 allow(class_discoverer_mock)
113 .to receive(:discover_classes)
114 .with(:Linux)
115 .and_return([Facts::Linux::Os::Family])
116 allow(class_discoverer_mock)
117 .to receive(:discover_classes)
118 .with(:Rhel)
119 .and_return([Facts::Rhel::Os::Family])
120 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
121
122 stub_const('Facts::Linux::Path::FACT_NAME', 'os.family')
123 stub_const('Facts::Rhel::Path::FACT_NAME', 'os.family')
124 end
125
126 it 'loads one fact' do
127 core_facts = internal_fact_loader.facts.select { |loaded_fact| loaded_fact.type == :core }
128
129 expect(core_facts.size).to eq(1)
130 end
131
132 it 'loads os.family fact' do
133 core_facts = internal_fact_loader.facts.select { |loaded_fact| loaded_fact.type == :core }
134
135 expect(core_facts.first.name).to eq('os.family')
136 end
137
138 it 'loads only Rhel path' do
139 core_facts = internal_fact_loader.facts.select { |loaded_fact| loaded_fact.type == :core }
140
141 expect(core_facts.first.klass).to eq(Facts::Rhel::Os::Family)
142 end
143 end
144
145 context 'when loading fact with aliases' do
146 before do
147 allow(class_discoverer_mock)
148 .to receive(:discover_classes)
149 .with(:Linux)
150 .and_return([Facts::Linux::Os::Name])
151 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
152
153 stub_const('Facts::Linux::Os::Name::FACT_NAME', 'os.name')
154 stub_const('Facts::Linux::Os::Name::ALIASES', 'operatingsystem')
155 end
156
157 it 'loads two facts' do
158 expect(internal_fact_loader.facts.size).to eq(2)
159 end
160
161 it 'loads one core fact' do
162 expect(internal_fact_loader.core_facts.size).to eq(1)
163 end
164
165 it 'loads one legacy fact' do
166 expect(internal_fact_loader.legacy_facts.size).to eq(1)
167 end
168
169 it 'loads a core fact with the fact name' do
170 expect(internal_fact_loader.core_facts.first.name).to eq('os.name')
171 end
172
173 it 'loads a legacy fact with the alias name' do
174 expect(internal_fact_loader.legacy_facts.first.name).to eq('operatingsystem')
175 end
176 end
177
178 context 'when loading wildcard facts' do
179 before do
180 allow(os_detector_mock).to receive(:hierarchy).and_return([:Windows])
181 allow(OsDetector).to receive(:instance).and_return(os_detector_mock)
182
183 allow(class_discoverer_mock)
184 .to receive(:discover_classes)
185 .with(:Windows)
186 .and_return([Facts::Windows::NetworkInterfaces])
187 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
188
189 stub_const('Facts::Windows::NetworkInterfaces::FACT_NAME', 'network_.*')
190 end
191
192 it 'loads one fact' do
193 expect(internal_fact_loader.facts.size).to eq(1)
194 end
195
196 it 'loads one legacy fact' do
197 expect(internal_fact_loader.legacy_facts.size).to eq(1)
198 end
199
200 it 'contains a wildcard at the end' do
201 expect(internal_fact_loader.legacy_facts.first.name).to end_with('.*')
202 end
203
204 it 'loads no core facts' do
205 expect(internal_fact_loader.core_facts).to be_empty
206 end
207 end
208
209 context 'when loading legacy fact without wildcard' do
210 before do
211 allow(class_discoverer_mock)
212 .to receive(:discover_classes)
213 .with(:Linux)
214 .and_return([Facts::Debian::Lsbdistid])
215 allow(Facter::ClassDiscoverer).to receive(:instance).and_return(class_discoverer_mock)
216
217 stub_const('Facts::Debian::Lsbdistid::FACT_NAME', 'lsbdistid')
218 end
219
220 it 'loads one fact' do
221 expect(internal_fact_loader.facts.size).to eq(1)
222 end
223
224 it 'loads one legacy fact' do
225 expect(internal_fact_loader.legacy_facts.size).to eq(1)
226 end
227
228 it 'does not contain a wildcard at the end' do
229 expect(internal_fact_loader.legacy_facts.first.name).not_to end_with('.*')
230 end
231
232 it 'loads no core facts' do
233 expect(internal_fact_loader.core_facts).to be_empty
234 end
235 end
236 end
237 end
0 # frozen_string_literal: true
1
2 describe Facter::FactManager do
3 let(:internal_manager) { instance_spy(Facter::InternalFactManager) }
4 let(:external_manager) { instance_spy(Facter::ExternalFactManager) }
5 let(:cache_manager) { instance_spy(Facter::CacheManager) }
6 let(:fact_loader) { instance_double(Facter::FactLoader) }
7 let(:logger) { instance_spy(Facter::Log) }
8
9 def stub_query_parser(withs, returns)
10 allow(Facter::QueryParser).to receive(:parse).with(*withs).and_return(returns)
11 end
12
13 def stub_internal_manager(withs, returns)
14 allow(internal_manager).to receive(:resolve_facts).with(withs).and_return(returns)
15 end
16
17 def stub_external_manager(withs, returns)
18 allow(external_manager).to receive(:resolve_facts).with(withs).and_return(returns)
19 end
20
21 def stub_cache_manager(withs, returns)
22 allow(cache_manager).to receive(:resolve_facts).with(withs).and_return([withs, Array(returns)])
23 allow(cache_manager).to receive(:cache_facts)
24 end
25
26 before do
27 Singleton.__init__(Facter::FactManager)
28 Singleton.__init__(Facter::FactLoader)
29
30 allow(Facter::Log).to receive(:new).and_return(logger)
31 allow(Facter::InternalFactManager).to receive(:new).and_return(internal_manager)
32 allow(Facter::ExternalFactManager).to receive(:new).and_return(external_manager)
33 allow(Facter::CacheManager).to receive(:new).and_return(cache_manager)
34 allow(Facter::FactLoader).to receive(:new).and_return(fact_loader)
35 end
36
37 describe '#resolve_facts' do
38 let(:os) { 'os' }
39 let(:os_klass) { instance_double(Facts::Linux::Os::Name) }
40 let(:user_query) { [] }
41 let(:loaded_facts) do
42 [
43 instance_double(Facter::LoadedFact, name: 'os.name', klass: os_klass, type: :core),
44 instance_double(Facter::LoadedFact, name: 'custom_fact', klass: nil, type: :custom)
45 ]
46 end
47
48 let(:searched_facts) do
49 [
50 instance_double(
51 Facter::SearchedFact,
52 name: os, fact_class: os_klass,
53 user_query: '', type: :core
54 ),
55 instance_double(
56 Facter::SearchedFact,
57 name: 'my_custom_fact', fact_class: nil,
58 user_query: '', type: :custom
59 )
60 ]
61 end
62
63 let(:resolved_fact) { mock_resolved_fact(os, 'Ubuntu', os) }
64
65 before do
66 allow(Facter::FactLoader.instance).to receive(:load).and_return(loaded_facts)
67 stub_query_parser([user_query, loaded_facts], searched_facts)
68 stub_internal_manager(searched_facts, [resolved_fact])
69 stub_external_manager(searched_facts, nil)
70 stub_cache_manager(searched_facts, [])
71 end
72
73 it 'resolved all facts' do
74 resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)
75
76 expect(resolved_facts).to eq([resolved_fact])
77 end
78 end
79
80 describe '#resolve_fact' do
81 context 'with custom fact' do
82 let(:user_query) { 'custom_fact' }
83 let(:fact_name) { 'custom_fact' }
84 let(:custom_fact) { instance_double(Facter::LoadedFact, name: fact_name, klass: nil, type: :custom) }
85 let(:loaded_facts) { [custom_fact] }
86
87 let(:searched_facts) do
88 [
89 instance_double(
90 Facter::SearchedFact,
91 name: fact_name, fact_class: nil,
92 user_query: '', type: :custom
93 )
94 ]
95 end
96
97 let(:resolved_fact) { mock_resolved_fact(fact_name, 'custom', fact_name, :custom) }
98 let(:cached_fact) { mock_resolved_fact(fact_name, 'cached_custom', fact_name, :custom) }
99
100 context 'when is found in custom_dir/fact_name.rb' do
101 before do
102 # mock custom_fact_by_filename to return resolved_fact
103 allow(fact_loader).to receive(:load_custom_fact).and_return(loaded_facts)
104 stub_query_parser([[user_query], loaded_facts], searched_facts)
105 stub_internal_manager(searched_facts, [resolved_fact])
106 stub_external_manager(searched_facts, [resolved_fact])
107 stub_cache_manager(searched_facts, [])
108
109 # mock core_or_external_fact to return nil
110 allow(fact_loader).to receive(:load_internal_facts).and_return([])
111 allow(fact_loader).to receive(:load_external_facts).and_return([])
112 stub_query_parser([[user_query], []], [])
113 stub_internal_manager([], [])
114 stub_external_manager([], [])
115 stub_cache_manager([], [])
116 end
117
118 it 'tries to load it from fact_name.rb' do
119 Facter::FactManager.instance.resolve_fact(user_query)
120
121 expect(logger).to have_received(:debug)
122 .with("Searching fact: #{user_query} in file: #{user_query}.rb")
123 end
124
125 it 'loads core and external facts' do
126 Facter::FactManager.instance.resolve_fact(user_query)
127
128 expect(logger).to have_received(:debug)
129 .with("Searching fact: #{user_query} in core facts and external facts")
130 end
131
132 it 'does not load all custom facts' do
133 Facter::FactManager.instance.resolve_fact(user_query)
134
135 expect(logger).not_to have_received(:debug)
136 .with("Searching fact: #{user_query} in all custom facts")
137 end
138
139 it 'resolves fact' do
140 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
141
142 expect(resolved_facts).to eql([resolved_fact])
143 end
144 end
145
146 context 'when is not found in custom_dir/fact_name.rb' do
147 before do
148 # mock custom_fact_by_filename to return nil
149 allow(fact_loader).to receive(:load_custom_fact).and_return([])
150 stub_query_parser([[user_query], []], [])
151 stub_external_manager(searched_facts, [])
152 stub_cache_manager([], [])
153
154 # mock core_or_external_fact to return nil
155 allow(fact_loader).to receive(:load_internal_facts).and_return([])
156 allow(fact_loader).to receive(:load_external_facts).and_return([])
157 stub_query_parser([[user_query], []], [])
158 stub_internal_manager([], [])
159 stub_external_manager([], [])
160 stub_cache_manager([], [])
161
162 # mock all_custom_facts to return resolved_fact
163 allow(fact_loader).to receive(:load_custom_facts).and_return(loaded_facts)
164 stub_query_parser([[user_query], loaded_facts], searched_facts)
165 stub_external_manager(searched_facts, [resolved_fact])
166 stub_cache_manager(searched_facts, [])
167 end
168
169 it 'tries to load it from fact_name.rb' do
170 Facter::FactManager.instance.resolve_fact(user_query)
171
172 expect(logger).to have_received(:debug)
173 .with("Searching fact: #{user_query} in file: #{user_query}.rb")
174 end
175
176 it 'loads core and external facts' do
177 Facter::FactManager.instance.resolve_fact(user_query)
178
179 expect(logger).to have_received(:debug)
180 .with("Searching fact: #{user_query} in core facts and external facts")
181 end
182
183 it 'loads all custom facts' do
184 Facter::FactManager.instance.resolve_fact(user_query)
185
186 expect(logger).to have_received(:debug)
187 .with("Searching fact: #{user_query} in all custom facts")
188 end
189
190 it 'resolves fact' do
191 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
192
193 expect(resolved_facts).to eql([resolved_fact])
194 end
195 end
196
197 context 'when fact is cached' do
198 before do
199 # mock custom_fact_by_filename to return cached_fact
200 allow(fact_loader).to receive(:load_custom_fact).and_return(loaded_facts)
201 stub_query_parser([[user_query], loaded_facts], searched_facts)
202 stub_internal_manager(searched_facts, [])
203 stub_external_manager(searched_facts, [])
204 stub_cache_manager(searched_facts, cached_fact)
205
206 # mock core_or_external_fact to return nil
207 allow(fact_loader).to receive(:load_internal_facts).and_return([])
208 allow(fact_loader).to receive(:load_external_facts).and_return([])
209 stub_query_parser([[user_query], []], [])
210 stub_internal_manager([], [])
211 stub_external_manager([], [])
212 stub_cache_manager([], [])
213 end
214
215 it 'returns the cached fact' do
216 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
217
218 expect(resolved_facts).to eql([cached_fact])
219 end
220 end
221 end
222
223 context 'with core fact' do
224 let(:user_query) { 'os.name' }
225 let(:os_klass) { instance_double(Facts::Linux::Os::Name) }
226 let(:bool_klass) { instance_double(Facts::Linux::FipsEnabled) }
227 let(:nil_klass) { instance_double(Facts::Linux::Virtual) }
228 let(:core_fact) { instance_double(Facter::LoadedFact, name: 'os.name', klass: os_klass, type: :core) }
229 let(:bool_core_fact) { instance_double(Facter::LoadedFact, name: 'fips_enabled', klass: bool_klass, type: :core) }
230 let(:nil_core_fact) { instance_double(Facter::LoadedFact, name: 'virtual', klass: nil_klass, type: :core) }
231 let(:loaded_facts) { [core_fact, bool_core_fact, nil_core_fact] }
232
233 let(:searched_facts) do
234 [
235 instance_double(
236 Facter::SearchedFact,
237 name: 'os', fact_class: os_klass,
238 user_query: '', type: :core
239 ),
240 instance_double(
241 Facter::SearchedFact,
242 name: 'fips_enabled', fact_class: bool_klass,
243 user_query: '', type: :core
244 ),
245 instance_double(
246 Facter::SearchedFact,
247 name: 'virtual', fact_class: nil_klass,
248 user_query: '', type: :core
249 )
250 ]
251 end
252
253 let(:resolved_fact) { mock_resolved_fact('os.name', 'darwin', 'os.name', :core) }
254
255 before do
256 # mock custom_fact_by_filename to return nil
257 allow(fact_loader).to receive(:load_custom_fact).and_return([])
258 stub_query_parser([[user_query], []], [])
259 stub_external_manager(searched_facts, [])
260 stub_cache_manager([], [])
261
262 # mock core_or_external_fact to return the core resolved_fact
263 allow(fact_loader).to receive(:load_internal_facts).and_return(loaded_facts)
264 allow(fact_loader).to receive(:load_external_facts).and_return([])
265 stub_query_parser([[user_query], loaded_facts], searched_facts)
266 stub_internal_manager(searched_facts, [resolved_fact])
267 stub_external_manager([], [])
268 stub_cache_manager(searched_facts, [])
269 end
270
271 it 'tries to load it from fact_name.rb' do
272 Facter::FactManager.instance.resolve_fact(user_query)
273
274 expect(logger).to have_received(:debug)
275 .with("Searching fact: #{user_query} in file: #{user_query}.rb")
276 end
277
278 it 'loads core and external facts' do
279 Facter::FactManager.instance.resolve_fact(user_query)
280
281 expect(logger).to have_received(:debug)
282 .with("Searching fact: #{user_query} in core facts and external facts")
283 end
284
285 it 'does not load all custom facts' do
286 Facter::FactManager.instance.resolve_fact(user_query)
287
288 expect(logger).not_to have_received(:debug)
289 .with("Searching fact: #{user_query} in all custom facts")
290 end
291
292 it 'resolves fact' do
293 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
294
295 expect(resolved_facts).to eql([resolved_fact])
296 end
297
298 context 'when nil' do
299 let(:user_query) { 'virtual' }
300 let(:resolved_fact) { mock_resolved_fact('virtual', nil, 'virtual', :core) }
301
302 before do
303 # mock all custom facts to return []
304 allow(fact_loader).to receive(:load_custom_facts).and_return([])
305 end
306
307 it 'does not resolve fact' do
308 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
309 expect(resolved_facts).to be_empty
310 end
311 end
312
313 context 'when boolean false' do
314 let(:user_query) { 'fips_enabled' }
315 let(:resolved_fact) { mock_resolved_fact('fips_enabled', false, 'fips_enabled', :core) }
316
317 it 'resolves fact to false' do
318 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
319 expect(resolved_facts.first.value).to be(false)
320 end
321 end
322 end
323
324 context 'with non existent fact' do
325 let(:user_query) { 'non_existent' }
326 let(:fact_name) { 'non_existent' }
327 let(:custom_fact) { instance_double(Facter::LoadedFact, name: 'custom_fact', klass: nil, type: :custom) }
328 let(:loaded_facts) { [custom_fact] }
329
330 let(:resolved_fact) { mock_resolved_fact(fact_name, 'custom', fact_name, :custom) }
331
332 before do
333 # mock custom_fact_by_filename to return nil
334 allow(fact_loader).to receive(:load_custom_fact).and_return([])
335 stub_query_parser([[user_query], []], [])
336 stub_external_manager([], [])
337 stub_cache_manager([], [])
338
339 # mock core_or_external_fact to return nil
340 allow(fact_loader).to receive(:load_internal_facts).and_return([])
341 allow(fact_loader).to receive(:load_external_facts).and_return([])
342 stub_query_parser([[user_query], []], [])
343 stub_internal_manager([], [])
344 stub_external_manager([], [])
345 stub_cache_manager([], [])
346
347 # mock all_custom_facts to return nil
348 allow(fact_loader).to receive(:load_custom_facts).and_return([])
349 stub_query_parser([[user_query], []], [])
350 stub_external_manager([], [])
351 stub_cache_manager([], [])
352 end
353
354 it 'tries to load it from fact_name.rb' do
355 Facter::FactManager.instance.resolve_fact(user_query)
356
357 expect(logger).to have_received(:debug)
358 .with("Searching fact: #{user_query} in file: #{user_query}.rb")
359 end
360
361 it 'loads core and external facts' do
362 Facter::FactManager.instance.resolve_fact(user_query)
363
364 expect(logger).to have_received(:debug)
365 .with("Searching fact: #{user_query} in core facts and external facts")
366 end
367
368 it 'loads all custom facts' do
369 Facter::FactManager.instance.resolve_fact(user_query)
370
371 expect(logger).to have_received(:debug)
372 .with("Searching fact: #{user_query} in all custom facts")
373 end
374
375 it 'resolves fact' do
376 resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
377
378 expect(resolved_facts).to eql([])
379 end
380 end
381 end
382
383 describe '#resolve_core' do
384 let(:user_query) { [] }
385 let(:ubuntu_os_name) { class_double(Facts::Linux::Os::Name) }
386 let(:loaded_facts) do
387 instance_double(Facter::LoadedFact, name: 'os.name', klass: ubuntu_os_name, type: :core)
388 end
389 let(:searched_fact) do
390 instance_double(
391 Facter::SearchedFact,
392 name: 'os', fact_class: ubuntu_os_name,
393 user_query: '', type: :core
394 )
395 end
396 let(:resolved_fact) { mock_resolved_fact('os', 'Ubuntu', 'os') }
397
398 it 'resolves all core facts' do
399 allow(fact_loader).to receive(:load_internal_facts).and_return(loaded_facts)
400 allow(fact_loader).to receive(:internal_facts).and_return(loaded_facts)
401 allow(fact_loader).to receive(:load_external_facts).and_return([])
402
403 stub_query_parser([user_query, loaded_facts], [searched_fact])
404 stub_internal_manager([searched_fact], [resolved_fact])
405 stub_cache_manager([searched_fact], [])
406
407 resolved_facts = Facter::FactManager.instance.resolve_core(user_query)
408
409 expect(resolved_facts).to eq([resolved_fact])
410 end
411 end
412 end
0 # frozen_string_literal: true
1
2 describe Facter::OptionStore do
3 subject(:option_store) { Facter::OptionStore }
4
5 before do
6 option_store.reset
7 end
8
9 after do
10 option_store.reset
11 end
12
13 describe '#all' do
14 it 'returns default values' do
15 expect(option_store.all).to eq(
16 debug: false,
17 verbose: false,
18 log_level: :warn,
19 show_legacy: true,
20 custom_facts: true,
21 blocked_facts: [],
22 ruby: true,
23 external_facts: true,
24 config: nil,
25 strict: false,
26 json: false,
27 hocon: false,
28 cache: true,
29 yaml: false,
30 puppet: false,
31 ttls: [],
32 block: true,
33 cli: nil,
34 config_file_custom_dir: [],
35 config_file_external_dir: [],
36 default_external_dir: [],
37 fact_groups: {},
38 sequential: true,
39 block_list: {},
40 color: true,
41 trace: false,
42 timing: false,
43 external_dir: [],
44 custom_dir: [],
45 allow_external_loggers: true,
46 force_dot_resolution: false,
47 http_debug: false
48 )
49 end
50 end
51
52 describe '#no_ruby=' do
53 context 'when false' do
54 before do
55 option_store.no_ruby = false
56 end
57
58 it 'sets ruby to true' do
59 expect(option_store.ruby).to be true
60 end
61 end
62
63 context 'when true' do
64 before do
65 option_store.no_ruby = true
66 end
67
68 it 'sets ruby to false' do
69 expect(option_store.ruby).to be false
70 end
71
72 it 'sets custom facts to false' do
73 expect(option_store.custom_facts).to be false
74 end
75
76 it 'adds ruby to blocked facts' do
77 expect(option_store.blocked_facts).to include('ruby')
78 end
79 end
80 end
81
82 describe '#no_block' do
83 context 'when true' do
84 before do
85 option_store.no_block = true
86 end
87
88 it 'sets block to false' do
89 expect(option_store.block).to be false
90 end
91 end
92
93 context 'when false' do
94 before do
95 option_store.no_block = false
96 end
97
98 it 'sets block to true' do
99 expect(option_store.block).to be true
100 end
101 end
102 end
103
104 describe '#no_cache' do
105 context 'when true' do
106 before do
107 option_store.no_cache = true
108 end
109
110 it 'sets block to false' do
111 expect(option_store.cache).to be false
112 end
113 end
114
115 context 'when false' do
116 before do
117 option_store.no_cache = false
118 end
119
120 it 'sets cache to true' do
121 expect(option_store.cache).to be true
122 end
123 end
124 end
125
126 describe '#no_color' do
127 context 'when true' do
128 before do
129 option_store.no_color = true
130 end
131
132 it 'sets color to false' do
133 expect(option_store.color).to be false
134 end
135 end
136
137 context 'when false' do
138 before do
139 option_store.no_color = false
140 end
141
142 it 'sets color to true' do
143 expect(option_store.color).to be true
144 end
145 end
146 end
147
148 describe '#external_dir' do
149 context 'with external_dir with values' do
150 before do
151 option_store.external_dir = ['external_dir_path']
152 end
153
154 it 'returns external_dir' do
155 expect(option_store.external_dir).to eq(['external_dir_path'])
156 end
157 end
158
159 context 'with @no_external_facts true' do
160 before do
161 option_store.no_external_facts = true
162 end
163
164 it 'returns external dir' do
165 expect(option_store.external_dir).to be_empty
166 end
167 end
168
169 context 'with @no_external_facts false and external_dir empty' do
170 before do
171 option_store.no_external_facts = false
172 option_store.external_dir = []
173 end
174
175 context 'with @config_file_external_dir empty' do
176 before do
177 option_store.config_file_external_dir = []
178 option_store.default_external_dir = ['/default_path']
179 end
180
181 it 'returns default dir' do
182 expect(option_store.external_dir).to eq(['/default_path'])
183 end
184 end
185
186 context 'with @config_file_external_dir with values' do
187 before do
188 option_store.config_file_external_dir = ['/path_from_config_file']
189 end
190
191 it 'returns default dir' do
192 expect(option_store.external_dir).to eq(['/path_from_config_file'])
193 end
194 end
195 end
196 end
197
198 describe '#blocked_facts=' do
199 context 'with empty array' do
200 it 'does not override existing array' do
201 option_store.blocked_facts = ['os']
202
203 option_store.blocked_facts = []
204
205 expect(option_store.blocked_facts).to eq(['os'])
206 end
207 end
208
209 context 'with array' do
210 it 'appends dirs' do
211 option_store.blocked_facts = ['os']
212
213 option_store.blocked_facts = ['os2']
214
215 expect(option_store.blocked_facts).to eq(%w[os os2])
216 end
217 end
218 end
219
220 describe '#custom_dir' do
221 context 'when @custom_dir has values' do
222 before do
223 option_store.custom_dir = ['/custom_dir_path']
224 end
225
226 it 'returns custom_dir' do
227 expect(option_store.custom_dir).to eq(['/custom_dir_path'])
228 end
229 end
230
231 context 'when @custom_dir is empty' do
232 before do
233 option_store.custom_dir = []
234 option_store.config_file_custom_dir = ['/path_from_config_file']
235 end
236
237 it 'returns config_file_custom_dir' do
238 expect(option_store.custom_dir).to eq(['/path_from_config_file'])
239 end
240 end
241 end
242
243 describe '#custom_dir=' do
244 context 'with array' do
245 it 'overrides dirs' do
246 option_store.custom_dir = ['/customdir1']
247
248 option_store.custom_dir = ['/customdir2']
249
250 expect(option_store.custom_dir).to eq(['/customdir2'])
251 end
252
253 it 'sets ruby to true' do
254 option_store.instance_variable_set(:@ruby, false)
255
256 expect do
257 option_store.custom_dir = ['/customdir1']
258 end.to change(option_store, :ruby)
259 .from(false).to(true)
260 end
261 end
262 end
263
264 describe '#debug' do
265 before do
266 allow(Facter::Log).to receive(:level=).with(level)
267 end
268
269 context 'when true' do
270 let(:level) { :debug }
271
272 it 'sets debug to true and log_level to :debug' do
273 expect do
274 option_store.debug = true
275 end.to change(option_store, :log_level)
276 .from(:warn).to(:debug)
277 end
278 end
279
280 context 'when false' do
281 let(:level) { :warn }
282
283 it 'sets log_level to default (:warn)' do
284 option_store.instance_variable_set(:@log_level, :info)
285
286 expect do
287 option_store.debug = false
288 end.to change(option_store, :log_level)
289 .from(:info).to(:warn)
290 end
291 end
292 end
293
294 describe '#verbose=' do
295 before do
296 allow(Facter::Log).to receive(:level=).with(level)
297 end
298
299 context 'when true' do
300 let(:level) { :info }
301
302 it 'sets log_level to :info' do
303 expect do
304 option_store.verbose = true
305 end.to change(option_store, :log_level)
306 .from(:warn).to(:info)
307 end
308 end
309
310 context 'when false' do
311 let(:level) { :warn }
312
313 it 'sets log_level to default (:warn)' do
314 option_store.instance_variable_set(:@log_level, :debug)
315
316 expect do
317 option_store.debug = false
318 end.to change(option_store, :log_level)
319 .from(:debug).to(:warn)
320 end
321 end
322 end
323
324 describe '#no_custom_facts=' do
325 context 'when false' do
326 it 'sets ruby to true' do
327 option_store.instance_variable_set(:@ruby, false)
328
329 expect do
330 option_store.no_custom_facts = false
331 end.to change(option_store, :ruby)
332 .from(false).to(true)
333 end
334 end
335 end
336
337 describe '#log_level=' do
338 let(:facter_log) { class_spy('Facter::Log') }
339
340 before do
341 stub_const('Facter::Log', facter_log)
342 allow(Facter).to receive(:trace)
343 end
344
345 context 'when :trace' do
346 it 'sets log_level to :debug' do
347 expect do
348 option_store.log_level = :trace
349 end.to change(option_store, :log_level)
350 .from(:warn).to(:debug)
351 end
352 end
353
354 context 'when :debug' do
355 it 'sets logl_level :debug' do
356 expect do
357 option_store.log_level = :debug
358 end.to change(option_store, :log_level)
359 .from(:warn).to(:debug)
360 end
361 end
362
363 context 'when :info' do
364 it 'sets log_level to :info' do
365 expect do
366 option_store.log_level = :info
367 end.to change(option_store, :log_level)
368 .from(:warn).to(:info)
369 end
370 end
371 end
372
373 describe '#show_legacy=' do
374 context 'when true' do
375 it 'sets ruby to true' do
376 option_store.instance_variable_set(:@ruby, false)
377
378 expect do
379 option_store.show_legacy = true
380 end.to change(option_store, :ruby)
381 .from(false).to(true)
382 end
383 end
384
385 context 'when false' do
386 it 'sets show_legacy to false' do
387 option_store.show_legacy = false
388 expect(option_store.show_legacy).to be false
389 end
390 end
391 end
392
393 describe '#send' do
394 it 'sets values for attributes' do
395 option_store.instance_variable_set(:@strict, true)
396
397 expect do
398 option_store.set('strict', false)
399 end.to change(option_store, :strict)
400 .from(true).to(false)
401 end
402 end
403
404 describe '#reset' do
405 before do
406 allow(Facter::Log).to receive(:level=).with(:debug)
407 end
408
409 it 'resets to default' do
410 default = option_store.all
411
412 option_store.cli = true
413 option_store.log_level = :debug
414 option_store.show_legacy = false
415 option_store.external_dir = ['external_dir_path']
416
417 option_store.reset
418 expect(option_store.all).to eq(default)
419 end
420 end
421 end
0 # frozen_string_literal: true
1
2 describe Facter::OptionsValidator do
3 describe '#validate' do
4 let(:logger) { instance_spy(Facter::Log) }
5
6 before do
7 allow(Facter::Log).to receive(:new).and_return(logger)
8 end
9
10 context 'when options are invalid pairs' do
11 let(:options) { ['--puppet', '--no-ruby'] }
12 let(:error_code) { 1 }
13
14 it 'writes message and exit' do
15 allow(logger).to receive(:error).with('--puppet and --no-ruby options conflict:'\
16 ' please specify only one.', true)
17 allow(Facter::Cli).to receive(:start).with(['--help'])
18
19 expect { Facter::OptionsValidator.validate(options) }.to raise_error(
20 an_instance_of(SystemExit)
21 .and(having_attributes(status: error_code))
22 )
23 end
24 end
25
26 context 'when options are duplicated' do
27 let(:options) { ['--puppet', '-p'] }
28 let(:error_code) { 1 }
29
30 it 'writes message and exit' do
31 allow(logger).to receive(:error).with('option --puppet '\
32 'cannot be specified more than once.', true)
33 allow(Facter::Cli).to receive(:start).with(['--help'])
34
35 expect { Facter::OptionsValidator.validate(options) }.to raise_error(
36 an_instance_of(SystemExit)
37 .and(having_attributes(status: error_code))
38 )
39 end
40 end
41
42 context 'when options are valid' do
43 let(:options) { ['--puppet', '--no-external-facts'] }
44
45 it 'writes message and exit' do
46 expect { Facter::OptionsValidator.validate(options) }.not_to raise_error
47 end
48 end
49 end
50 end
0 # frozen_string_literal: true
1
2 describe Facter::Options do
3 subject(:options) { Facter::Options }
4
5 describe '#init_from_cli' do
6 let(:option_store) { class_spy('Facter::OptionStore') }
7 let(:config_file_options) { class_spy('Facter::ConfigFileOptions') }
8 let(:options_validator) { class_spy('Facter::OptionsValidator') }
9
10 before do
11 stub_const('Facter::ConfigFileOptions', config_file_options)
12 stub_const('Facter::OptionStore', option_store)
13 stub_const('Facter::OptionsValidator', options_validator)
14 allow(config_file_options).to receive(:get).and_return({})
15 end
16
17 it 'calls OptionStore with cli' do
18 Facter::Options.init_from_cli
19
20 expect(option_store).to have_received(:cli=).with(true)
21 end
22
23 it 'calls OptionStore with show_legacy' do
24 Facter::Options.init_from_cli
25
26 expect(option_store).to have_received(:show_legacy=).with(false)
27 end
28
29 context 'with config_file' do
30 let(:config_file_opts) { { 'debug' => true, 'ruby' => true } }
31
32 before do
33 allow(config_file_options).to receive(:get).and_return(config_file_opts)
34 end
35
36 it 'calls ConfigFileOptions.init with config_path' do
37 Facter::Options.init_from_cli(config: 'path/to/config')
38
39 expect(config_file_options).to have_received(:init).with('path/to/config')
40 end
41
42 it 'calls OptionStore.set.init with cli_options' do
43 Facter::Options.init_from_cli
44
45 config_file_opts.each do |key, value|
46 expect(option_store).to have_received(:set).with(key, value)
47 end
48 end
49 end
50
51 context 'with cli_options' do
52 let(:cli_options) { { 'debug' => true, 'ruby' => true } }
53
54 it 'calls OptionStore.set.init with cli_options' do
55 Facter::Options.init_from_cli(cli_options)
56
57 cli_options.each do |key, value|
58 expect(option_store).to have_received(:set).with(key, value)
59 end
60 end
61
62 context 'with log_level as option' do
63 it 'munges the value none to unknown' do
64 Facter::Options.init_from_cli({ 'log_level' => 'none' })
65
66 expect(option_store).to have_received(:set).with('log_level', 'unknown')
67 end
68
69 it 'munges the value log_level to empty string' do
70 Facter::Options.init_from_cli({ 'log_level' => 'log_level' })
71
72 expect(option_store).to have_received(:set).with('log_level', '')
73 end
74
75 it 'leaves known log level unmunged' do
76 Facter::Options.init_from_cli({ 'log_level' => 'debug' })
77
78 expect(option_store).to have_received(:set).with('log_level', 'debug')
79 end
80 end
81 end
82 end
83
84 describe '#init' do
85 let(:option_store) { class_spy('Facter::OptionStore') }
86 let(:config_file_options) { class_spy('Facter::ConfigFileOptions') }
87
88 before do
89 stub_const('Facter::ConfigFileOptions', config_file_options)
90 stub_const('Facter::OptionStore', option_store)
91 allow(config_file_options).to receive(:get).and_return({})
92 end
93
94 it 'calls OptionStore with cli' do
95 Facter::Options.init
96
97 expect(option_store).to have_received(:cli=).with(false)
98 end
99
100 context 'with config_file' do
101 let(:config_file_opts) { { 'debug' => true, 'ruby' => true } }
102
103 before do
104 allow(config_file_options).to receive(:get).and_return(config_file_opts)
105 end
106
107 it 'calls OptionStore.set.init with cli_options' do
108 Facter::Options.init
109
110 config_file_opts.each do |key, value|
111 expect(option_store).to have_received(:set).with(key, value)
112 end
113 end
114 end
115 end
116 end
0 # frozen_string_literal: true
1
2 describe Facter::SessionCache do
3 let(:uname_resolver) { spy(Facter::Resolvers::Uname) }
4
5 it 'registers resolver subscription' do
6 Facter::SessionCache.subscribe(uname_resolver)
7 Facter::SessionCache.invalidate_all_caches
8
9 expect(uname_resolver).to have_received(:invalidate_cache)
10 end
11 end
0 # frozen_string_literal: true
1
2 require 'rbconfig'
3
4 describe OsDetector do
5 let(:os_hierarchy) { instance_spy(Facter::OsHierarchy) }
6 let(:logger) { instance_spy(Facter::Log) }
7 let(:initial_os) { RbConfig::CONFIG['host_os'] }
8
9 before do
10 RbConfig::CONFIG['host_os'] = initial_os
11 Singleton.__init__(OsDetector)
12 allow(Facter::Log).to receive(:new).and_return(logger)
13 allow(Facter::OsHierarchy).to receive(:new).and_return(os_hierarchy)
14 end
15
16 after do
17 RbConfig::CONFIG['host_os'] = initial_os
18 end
19
20 describe 'initialize' do
21 context 'when os is macosx' do
22 before do
23 RbConfig::CONFIG['host_os'] = 'darwin'
24 allow(os_hierarchy).to receive(:construct_hierarchy).with(:macosx).and_return(['macosx'])
25 end
26
27 it 'detects os as macosx' do
28 expect(OsDetector.instance.identifier).to eq(:macosx)
29 end
30
31 it 'calls hierarchy construction with macosx identifier' do
32 OsDetector.instance
33
34 expect(os_hierarchy).to have_received(:construct_hierarchy).with(:macosx)
35 end
36
37 it 'construct hierarchy with darwin identifier' do
38 expect(OsDetector.instance.hierarchy).to eq(['macosx'])
39 end
40 end
41
42 context 'when os is windows' do
43 before do
44 RbConfig::CONFIG['host_os'] = 'mingw'
45 allow(os_hierarchy).to receive(:construct_hierarchy).with(:windows).and_return(['windows'])
46 end
47
48 it 'detects os as windows' do
49 expect(OsDetector.instance.identifier).to eq(:windows)
50 end
51
52 it 'calls hierarchy construction with windows identifier' do
53 OsDetector.instance
54
55 expect(os_hierarchy).to have_received(:construct_hierarchy).with(:windows)
56 end
57
58 it 'construct hierarchy with windows identifier' do
59 expect(OsDetector.instance.hierarchy).to eq(['windows'])
60 end
61 end
62
63 context 'when os is solaris' do
64 before do
65 RbConfig::CONFIG['host_os'] = 'solaris'
66 allow(os_hierarchy).to receive(:construct_hierarchy).with(:solaris).and_return(['solaris'])
67 end
68
69 it 'detects os as solaris' do
70 expect(OsDetector.instance.identifier).to eq(:solaris)
71 end
72
73 it 'calls hierarchy construction with solaris identifier' do
74 OsDetector.instance
75
76 expect(os_hierarchy).to have_received(:construct_hierarchy).with(:solaris)
77 end
78
79 it 'construct hierarchy with solaris identifier' do
80 expect(OsDetector.instance.hierarchy).to eq(['solaris'])
81 end
82 end
83
84 context 'when os is aix' do
85 before do
86 RbConfig::CONFIG['host_os'] = 'aix'
87 allow(os_hierarchy).to receive(:construct_hierarchy).with(:aix).and_return(['aix'])
88 end
89
90 it 'detects os as aix' do
91 expect(OsDetector.instance.identifier).to eq(:aix)
92 end
93
94 it 'calls hierarchy construction with aix identifier' do
95 OsDetector.instance
96
97 expect(os_hierarchy).to have_received(:construct_hierarchy).with(:aix)
98 end
99
100 it 'construct hierarchy with aix identifier' do
101 expect(OsDetector.instance.hierarchy).to eq(['aix'])
102 end
103 end
104
105 context 'when os cannot be detected' do
106 before do
107 RbConfig::CONFIG['host_os'] = 'my_custom_os'
108 end
109
110 it 'raises error if it could not detect os' do
111 expect { OsDetector.instance.identifier }.to raise_error(RuntimeError, 'unknown os: "my_custom_os"')
112 end
113 end
114
115 context 'when host_os is linux' do
116 before do
117 RbConfig::CONFIG['host_os'] = 'linux'
118
119 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id)
120 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:id).and_return(:redhat)
121 allow(Facter::Resolvers::SuseRelease).to receive(:resolve).with(:id)
122
123 allow(os_hierarchy).to receive(:construct_hierarchy).with(:redhat).and_return(%w[linux redhat])
124 end
125
126 it 'detects linux distro' do
127 expect(OsDetector.instance.identifier).to be(:redhat)
128 end
129
130 it 'calls Facter::OsHierarchy with construct_hierarchy' do
131 OsDetector.instance
132
133 expect(os_hierarchy).to have_received(:construct_hierarchy).with(:redhat)
134 end
135
136 it 'calls Facter::Resolvers::OsRelease with id' do
137 OsDetector.instance
138
139 expect(Facter::Resolvers::OsRelease).to have_received(:resolve).with(:id)
140 end
141
142 it 'calls Facter::Resolvers::RedHatRelease with id' do
143 OsDetector.instance
144
145 expect(Facter::Resolvers::RedHatRelease).to have_received(:resolve).with(:id)
146 end
147
148 context 'when distribution is not known' do
149 before do
150 allow(Facter::Resolvers::RedHatRelease).to receive(:resolve).with(:id).and_return('my_linux_distro')
151 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id_like).and_return(nil)
152
153 allow(os_hierarchy).to receive(:construct_hierarchy).and_return([])
154 allow(os_hierarchy).to receive(:construct_hierarchy).with(:linux).and_return(['linux'])
155 Singleton.__init__(OsDetector)
156 end
157
158 it 'falls back to linux' do
159 expect(OsDetector.instance.identifier).to eq(:my_linux_distro)
160 end
161
162 context 'when no hierarchy for os identifier' do
163 it 'logs debug message' do
164 OsDetector.instance
165
166 expect(logger)
167 .to have_received(:debug)
168 .with('Could not detect hierarchy using os identifier: my_linux_distro , trying with family')
169 end
170 end
171
172 context 'when no os family detected' do
173 it 'logs debug message' do
174 OsDetector.instance
175
176 expect(logger)
177 .to have_received(:debug)
178 .with('Could not detect hierarchy using family , falling back to Linux')
179 end
180 end
181
182 it 'constructs hierarchy with linux' do
183 expect(OsDetector.instance.hierarchy).to eq(['linux'])
184 end
185
186 context 'when family is known' do
187 before do
188 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id_like).and_return(:ubuntu)
189 allow(os_hierarchy).to receive(:construct_hierarchy).with('ubuntu').and_return(%w[Linux Debian Ubuntu])
190 Singleton.__init__(OsDetector)
191 end
192
193 it 'constructs hierarchy with linux' do
194 expect(OsDetector.instance.hierarchy).to eq(%w[Linux Debian Ubuntu])
195 end
196
197 it 'logs debug message' do
198 OsDetector.instance
199
200 expect(logger)
201 .to have_received(:debug)
202 .with('Could not detect hierarchy using os identifier: my_linux_distro , trying with family')
203 end
204 end
205
206 context 'when there are multiple families' do
207 before do
208 allow(Facter::Resolvers::OsRelease).to receive(:resolve).with(:id_like).and_return('Rhel centos fedora')
209 allow(os_hierarchy).to receive(:construct_hierarchy).with('Rhel').and_return(%w[])
210 allow(os_hierarchy).to receive(:construct_hierarchy).with('centos').and_return(%w[Linux El])
211 Singleton.__init__(OsDetector)
212 end
213
214 it 'constructs hierarchy with linux and el' do
215 expect(OsDetector.instance.hierarchy).to eq(%w[Linux El])
216 end
217
218 it 'does not call construct hierarchy with fedora' do
219 OsDetector.instance
220
221 expect(os_hierarchy).not_to have_received(:construct_hierarchy).with('fedora')
222 end
223 end
224 end
225 end
226 end
227 end
0 # frozen_string_literal: true
1
2 describe Facter::OsHierarchy do
3 subject(:os_hierarchy) { Facter::OsHierarchy.new }
4
5 before do
6 stub_const('Facter::Config::OS_HIERARCHY', JSON.parse(load_fixture('os_hierarchy').read))
7 allow(Facter::Log).to receive(:new).and_return(log)
8 end
9
10 let(:log) { instance_spy(Facter::Log) }
11
12 describe '#construct_hierarchy' do
13 context 'when searched_os is ubuntu' do
14 it 'constructs hierarchy' do
15 hierarchy = os_hierarchy.construct_hierarchy(:ubuntu)
16
17 expect(hierarchy).to eq(%w[Linux Debian Ubuntu])
18 end
19 end
20
21 context 'when searched_os is debian' do
22 it 'constructs hierarchy' do
23 hierarchy = os_hierarchy.construct_hierarchy(:debian)
24
25 expect(hierarchy).to eq(%w[Linux Debian])
26 end
27 end
28
29 context 'when searched_os is linux' do
30 it 'constructs hierarchy' do
31 hierarchy = os_hierarchy.construct_hierarchy(:linux)
32
33 expect(hierarchy).to eq(%w[Linux])
34 end
35 end
36
37 context 'when there is no os hierarchy' do
38 let(:my_os_name) { 'Myos' }
39
40 before do
41 stub_const('Facter::Config::OS_HIERARCHY', nil)
42 end
43
44 it 'returns the searched os as the hierarchy' do
45 hierarchy = os_hierarchy.construct_hierarchy(:myos)
46
47 expect(hierarchy).to eq([my_os_name])
48 end
49
50 it 'logs debug message' do
51 os_hierarchy.construct_hierarchy(:myos)
52
53 expect(log).to have_received(:debug).with("There is no os_hierarchy, will fall back to: #{my_os_name}")
54 end
55 end
56
57 context 'when searched_os is nil' do
58 it 'constructs hierarchy' do
59 hierarchy = os_hierarchy.construct_hierarchy(nil)
60
61 expect(hierarchy).to eq([])
62 end
63 end
64
65 context 'when searched_os is not in hierarchy' do
66 it 'constructs hierarchy' do
67 hierarchy = os_hierarchy.construct_hierarchy(:my_os)
68
69 expect(hierarchy).to eq([])
70 end
71 end
72 end
73 end
0 # frozen_string_literal: true
1
2 describe Facter::FormatterFactory do
3 it 'creates a json formatter' do
4 options = { json: true }
5 json_fact_formatter = Facter::FormatterFactory.build(options)
6
7 expect(json_fact_formatter).to be_a Facter::JsonFactFormatter
8 end
9
10 it 'creates a yaml formatter' do
11 options = { yaml: true }
12 json_fact_formatter = Facter::FormatterFactory.build(options)
13
14 expect(json_fact_formatter).to be_a Facter::YamlFactFormatter
15 end
16
17 it 'creates a legacy formatter' do
18 options = {}
19 json_fact_formatter = Facter::FormatterFactory.build(options)
20
21 expect(json_fact_formatter).to be_a Facter::LegacyFactFormatter
22 end
23 end
0 # frozen_string_literal: true
1
2 describe Facter::HoconFactFormatter do
3 it 'formats to hocon when no user query' do
4 resolved_fact1 =
5 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
6 user_query: '', type: :core)
7 resolved_fact2 =
8 double(Facter::ResolvedFact, name: 'os.family', value: 'Darwin',
9 user_query: '', type: :core)
10 resolved_fact3 =
11 double(Facter::ResolvedFact, name: 'os.architecture', value: 'x86_64',
12 user_query: '', type: :core)
13 resolved_fact_list = [resolved_fact1, resolved_fact2, resolved_fact3]
14
15 formatted_output = Facter::HoconFactFormatter.new.format(resolved_fact_list)
16
17 expected_output = "os={\n \s\sarchitecture=\"x86_64\"\n \s\sfamily=Darwin\n \s\sname=Darwin\n}\n"
18
19 expect(formatted_output).to eq(expected_output)
20 end
21
22 it 'formats to hocon for a single user query' do
23 resolved_fact =
24 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
25 user_query: 'os.name', type: :core)
26 resolved_fact_list = [resolved_fact]
27 formatted_output = Facter::HoconFactFormatter.new.format(resolved_fact_list)
28
29 expected_output = 'Darwin'
30
31 expect(formatted_output).to eq(expected_output)
32 end
33
34 it 'formats to hocon for multiple user queries' do
35 resolved_fact1 =
36 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
37 user_query: 'os.name', type: :core)
38 resolved_fact2 =
39 double(Facter::ResolvedFact, name: 'os.family', value: 'Darwin',
40 user_query: 'os.family', type: :core)
41 resolved_fact_list = [resolved_fact1, resolved_fact2]
42 formatted_output = Facter::HoconFactFormatter.new.format(resolved_fact_list)
43
44 expected_output = "\"os.family\"=Darwin\n\"os.name\"=Darwin\n"
45
46 expect(formatted_output).to eq(expected_output)
47 end
48
49 it 'formats to hocon for empty resolved fact array' do
50 formatted_output = Facter::HoconFactFormatter.new.format([])
51
52 expect(formatted_output).to eq(nil)
53 end
54
55 it 'returns empty string when the fact value is nil' do
56 resolved_fact1 =
57 double(Facter::ResolvedFact, name: 'my_external_fact', value: nil,
58 user_query: 'my_external_fact', type: :core)
59 resolved_fact_list = [resolved_fact1]
60
61 formatted_output = Facter::HoconFactFormatter.new.format(resolved_fact_list)
62 expect(formatted_output).to eq('')
63 end
64 end
0 # frozen_string_literal: true
1
2 describe Facter::JsonFactFormatter do
3 it 'formats to json when no user query' do
4 resolved_fact1 =
5 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
6 user_query: '', type: :core)
7 resolved_fact2 =
8 double(Facter::ResolvedFact, name: 'os.family', value: 'Darwin',
9 user_query: '', type: :core)
10 resolved_fact3 =
11 double(Facter::ResolvedFact, name: 'os.architecture', value: 'x86_64',
12 user_query: '', type: :core)
13 resolved_fact_list = [resolved_fact1, resolved_fact2, resolved_fact3]
14
15 double
16
17 formatted_output = Facter::JsonFactFormatter.new.format(resolved_fact_list)
18
19 expected_output =
20 "{\n \"os\": {\n \"architecture\": \"x86_64\",\n \"family\": \"Darwin\",\n \"name\": \"Darwin\"\n }\n}"
21
22 expect(formatted_output).to eq(expected_output)
23 end
24
25 it 'formats to json for a single user query' do
26 resolved_fact =
27 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
28 user_query: 'os.name', type: :core)
29 resolved_fact_list = [resolved_fact]
30 formatted_output = Facter::JsonFactFormatter.new.format(resolved_fact_list)
31
32 expected_output = "{\n \"os.name\": \"Darwin\"\n}"
33
34 expect(formatted_output).to eq(expected_output)
35 end
36
37 it 'formats to json for multiple user queries' do
38 resolved_fact1 =
39 double(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
40 user_query: 'os.name', type: :core)
41 resolved_fact2 =
42 double(Facter::ResolvedFact, name: 'os.family', value: 'Darwin',
43 user_query: 'os.family', type: :core)
44 resolved_fact_list = [resolved_fact1, resolved_fact2]
45 formatted_output = Facter::JsonFactFormatter.new.format(resolved_fact_list)
46
47 expected_output = "{\n \"os.family\": \"Darwin\",\n \"os.name\": \"Darwin\"\n}"
48
49 expect(formatted_output).to eq(expected_output)
50 end
51 end
0 # frozen_string_literal: true
1
2 describe Facter::LegacyFactFormatter do
3 subject(:legacy_formatter) { Facter::LegacyFactFormatter.new }
4
5 let(:resolved_fact1) do
6 Facter::ResolvedFact.new('resolved_fact1', 'resolved_fact1_value')
7 end
8
9 let(:resolved_fact2) do
10 Facter::ResolvedFact.new('resolved_fact2', 'resolved_fact2_value')
11 end
12
13 let(:nested_fact1) do
14 Facter::ResolvedFact.new('my.nested.fact1', [0, 1, 2, 3, 'my_nested_fact_value'])
15 end
16
17 let(:nested_fact2) do
18 Facter::ResolvedFact.new('my.nested.fact2', [0, 1, 2, 'my_nested_fact_value'])
19 end
20
21 let(:nested_fact3) do
22 Facter::ResolvedFact.new('my.nested.fact3', { 'my_nested_fact_value' => ['first', { 'another' => 'value' }] })
23 end
24
25 let(:nil_resolved_fact1) do
26 Facter::ResolvedFact.new('nil_resolved_fact1', nil)
27 end
28
29 let(:nil_resolved_fact2) do
30 Facter::ResolvedFact.new('nil_resolved_fact2', nil)
31 end
32
33 let(:nil_nested_fact1) do
34 Facter::ResolvedFact.new('my.nested.fact1', nil)
35 end
36
37 let(:nil_nested_fact2) do
38 Facter::ResolvedFact.new('my.nested.fact2', nil)
39 end
40
41 let(:win_path) do
42 Facter::ResolvedFact.new('path', 'C:\\Program Files\\Puppet Labs\\Puppet\\bin;C:\\cygwin64\\bin')
43 end
44
45 let(:wrong_query_fact) do
46 Facter::ResolvedFact.new('mountpoints', { '/tmp' => 'something' })
47 end
48
49 before do
50 resolved_fact1.user_query = 'resolved_fact1'
51
52 resolved_fact2.user_query = 'resolved_fact2'
53
54 nested_fact1.user_query = 'my.nested.fact1.4'
55
56 nested_fact2.user_query = 'my.nested.fact2.3'
57
58 nested_fact3.user_query = 'my.nested.fact3.my_nested_fact_value.1.another'
59
60 nil_resolved_fact1.user_query = 'nil_resolved_fact1'
61
62 nil_resolved_fact2.user_query = 'nil_resolved_fact2'
63
64 nil_nested_fact1.user_query = 'my'
65
66 nil_nested_fact2.user_query = 'my.nested.fact2'
67
68 win_path.user_query = ''
69
70 wrong_query_fact.user_query = 'mountpoints.asd'
71 end
72
73 context 'when no user query' do
74 let(:expected_output) { "resolved_fact1 => resolved_fact1_value\nresolved_fact2 => resolved_fact2_value" }
75
76 context 'when facts have value' do
77 it 'returns output' do
78 expect(legacy_formatter.format([resolved_fact1, resolved_fact2])).to eq(expected_output)
79 end
80 end
81
82 context 'when facts values are nil' do
83 before do
84 nil_resolved_fact1.user_query = ''
85 nil_resolved_fact2.user_query = ''
86 resolved_fact2.user_query = ''
87 nil_nested_fact1.user_query = ''
88 nil_nested_fact2.user_query = ''
89 end
90
91 context 'when is root level fact' do
92 it 'prints no values if all facts are nil' do
93 expect(legacy_formatter.format([nil_resolved_fact1, nil_resolved_fact2])).to eq('')
94 end
95
96 it 'prints only the fact that is not nil' do
97 expect(legacy_formatter.format([nil_resolved_fact1, resolved_fact2])).to eq('resolved_fact2 =>'\
98 ' resolved_fact2_value')
99 end
100 end
101
102 context 'when facts are nested' do
103 it 'prints no values if all facts are nil' do
104 expect(legacy_formatter.format([nil_nested_fact1, nil_nested_fact2])).to eq('')
105 end
106
107 it 'prints only the fact that is not nil' do
108 expect(legacy_formatter.format([nil_nested_fact1, nil_nested_fact2, resolved_fact2]))
109 .to eq('resolved_fact2 => resolved_fact2_value')
110 end
111 end
112 end
113 end
114
115 context 'when one user query' do
116 context 'when facts have values' do
117 it 'returns single value' do
118 expect(legacy_formatter.format([resolved_fact1])).to eq('resolved_fact1_value')
119 end
120
121 it 'returns a single value for a nested fact' do
122 expect(legacy_formatter.format([nested_fact1])).to eq('my_nested_fact_value')
123 end
124
125 context 'when there is a single user query that contains :' do
126 let(:resolved_fact) do
127 instance_spy(Facter::ResolvedFact, name: 'networking.ip6', value: 'fe80::7ca0:ab22:703a:b329',
128 user_query: 'networking.ip6', type: :core)
129 end
130
131 it 'returns single value without replacing : with =>' do
132 expect(legacy_formatter.format([resolved_fact])).to eq('fe80::7ca0:ab22:703a:b329')
133 end
134 end
135
136 context 'when Windows path' do
137 let(:expected_output) { 'path => C:\\Program Files\\Puppet Labs\\Puppet\\bin;C:\\cygwin64\\bin' }
138
139 it 'returns path with one escaped backslash' do
140 expect(legacy_formatter.format([win_path])).to eq(expected_output)
141 end
142 end
143 end
144
145 context 'when fact value is nil' do
146 context 'with root level fact' do
147 it 'prints no values if all facts are nil' do
148 expect(legacy_formatter.format([nil_resolved_fact1])).to eq('')
149 end
150 end
151
152 context 'with facts that are nested' do
153 it 'returns empty strings for first level query' do
154 expect(legacy_formatter.format([nil_nested_fact1])).to eq('')
155 end
156
157 it 'returns empty strings for leaf level query' do
158 nil_nested_fact1.user_query = 'my.nested.fact1'
159
160 expect(legacy_formatter.format([nil_nested_fact1])).to eq('')
161 end
162 end
163 end
164
165 context 'when user query is wrong' do
166 it 'prints no value' do
167 expect(legacy_formatter.format([wrong_query_fact])).to eq('')
168 end
169
170 it 'does not raise if fact leaf is string' do
171 wrong_query_fact.user_query = 'mountpoints./tmp.asd'
172
173 expect { legacy_formatter.format([wrong_query_fact]) }.not_to raise_error
174 end
175 end
176 end
177
178 context 'when multiple user queries' do
179 context 'with facts that have values' do
180 let(:expected_output) { "resolved_fact1 => resolved_fact1_value\nresolved_fact2 => resolved_fact2_value" }
181 let(:nested_expected_output) do
182 "my.nested.fact1.4 => my_nested_fact_value\nmy.nested.fact2.3 => my_nested_fact_value"
183 end
184
185 it 'returns output' do
186 expect(legacy_formatter.format([resolved_fact1, resolved_fact2])).to eq(expected_output)
187 end
188
189 it 'returns output for multiple user queries' do
190 expect(legacy_formatter.format([nested_fact1, nested_fact2])).to eq(nested_expected_output)
191 end
192
193 context 'when value is a hash' do
194 it "returns 'value'" do
195 expect(legacy_formatter.format([nested_fact3])).to eq('value')
196 end
197 end
198 end
199
200 context 'with fact value that is nil' do
201 context 'with a root level fact' do
202 it 'prints no values if all facts are nil' do
203 expect(legacy_formatter.format([nil_resolved_fact1, nil_resolved_fact2]))
204 .to eq("nil_resolved_fact1 => \nnil_resolved_fact2 => ")
205 end
206
207 it 'prints a value only for the fact that is not nil' do
208 expect(legacy_formatter.format([nil_resolved_fact1, resolved_fact2]))
209 .to eq("nil_resolved_fact1 => \nresolved_fact2 => resolved_fact2_value")
210 end
211 end
212
213 context 'with facts that are nested' do
214 it 'returns empty strings for first and leaf level query' do
215 expect(legacy_formatter.format([nil_resolved_fact1, nil_nested_fact2]))
216 .to eq("my.nested.fact2 => \nnil_resolved_fact1 => ")
217 end
218
219 it 'returns empty strings for leaf level query' do
220 nil_nested_fact1.user_query = 'my.nested.fact1'
221
222 expect(legacy_formatter.format([nil_resolved_fact1, resolved_fact2]))
223 .to eq("nil_resolved_fact1 => \nresolved_fact2 => resolved_fact2_value")
224 end
225 end
226 end
227 end
228
229 context 'when there is an empty resolved fact array' do
230 it 'returns nil' do
231 expect(legacy_formatter.format([])).to eq(nil)
232 end
233 end
234
235 context 'when fact starts with double ":"' do
236 let(:resolved_fact) do
237 instance_spy(Facter::ResolvedFact, name: 'networking', value: { ip6: '::1' },
238 user_query: 'networking', type: :core)
239 end
240
241 it 'formats the fact correctly' do
242 expect(legacy_formatter.format([resolved_fact]))
243 .to eq("{\n ip6 => \"::1\"\n}")
244 end
245 end
246
247 context 'when fact name contains a windows path' do
248 let(:resolved_fact) do
249 instance_spy(Facter::ResolvedFact, name: 'C:\\Program Files\\App', value: 'bin_dir',
250 user_query: '', type: :core)
251 end
252
253 it 'formats the fact correctly' do
254 expect(legacy_formatter.format([resolved_fact]))
255 .to eq('C:\\Program Files\\App => bin_dir')
256 end
257 end
258
259 context 'when fact value contains newline' do
260 let(:resolved_fact) do
261 instance_spy(Facter::ResolvedFact, name: 'custom_fact', value: 'value1 \n value2',
262 user_query: '', type: :core)
263 end
264
265 it 'formats the fact correctly' do
266 expect(legacy_formatter.format([resolved_fact]))
267 .to eq("custom_fact => value1 \n value2")
268 end
269 end
270 end
0 # frozen_string_literal: true
1
2 describe Facter::YamlFactFormatter do
3 subject(:yaml_formatter) { Facter::YamlFactFormatter.new }
4
5 let(:resolved_fact1) do
6 instance_spy(Facter::ResolvedFact, name: 'os.name', value: 'Darwin',
7 user_query: user_query1, type: :core)
8 end
9 let(:resolved_fact2) do
10 instance_spy(Facter::ResolvedFact, name: 'os.family', value: 'Darwin',
11 user_query: user_query2, type: :core)
12 end
13 let(:resolved_fact3) do
14 instance_spy(Facter::ResolvedFact, name: 'os.architecture', value: 'x86_64',
15 user_query: user_query3, type: :core)
16 end
17
18 let(:float_fact) do
19 instance_spy(Facter::ResolvedFact, name: 'memory', value: 1024.0,
20 user_query: '', type: :core)
21 end
22
23 let(:user_query1) { '' }
24 let(:user_query2) { '' }
25 let(:user_query3) { '' }
26
27 context 'when no user query' do
28 let(:resolved_fact_list) { [resolved_fact1, resolved_fact2, resolved_fact3] }
29 let(:expected_output) { "os:\n architecture: x86_64\n family: \"Darwin\"\n name: \"Darwin\"\n" }
30
31 it 'formats to yaml' do
32 expect(yaml_formatter.format(resolved_fact_list)).to eq(expected_output)
33 end
34 end
35
36 context 'when there is a single user query' do
37 let(:resolved_fact_list) { [resolved_fact1] }
38 let(:expected_output) { "os.name: \"Darwin\"\n" }
39 let(:user_query1) { 'os.name' }
40
41 it 'formats to yaml' do
42 expect(yaml_formatter.format(resolved_fact_list)).to eq(expected_output)
43 end
44 end
45
46 context 'when there are multiple user queries' do
47 let(:resolved_fact_list) { [resolved_fact1, resolved_fact2] }
48 let(:expected_output) { "os.family: \"Darwin\"\nos.name: \"Darwin\"\n" }
49 let(:user_query1) { 'os.name' }
50 let(:user_query2) { 'os.family' }
51
52 it 'formats to yaml' do
53 expect(yaml_formatter.format(resolved_fact_list)).to eq(expected_output)
54 end
55 end
56
57 context 'when on Windows' do
58 let(:win_path) do
59 instance_spy(Facter::ResolvedFact, name: 'path', value: value,
60 user_query: '', type: :core)
61 end
62 let(:value) { 'C:\\Program Files\\Puppet Labs\\Puppet\\bin;C:\\cygwin64\\bin' }
63 let(:expected_output) { "path: \"C:\\\\Program Files\\\\Puppet Labs\\\\Puppet\\\\bin;C:\\\\cygwin64\\\\bin\"\n" }
64 let(:resolved_fact_list) { [win_path] }
65
66 it 'formats quoted path with double escaped backslashes' do
67 expect(yaml_formatter.format(resolved_fact_list)).to eq(expected_output)
68 end
69 end
70
71 context 'when resolving float numbers' do
72 let(:resolved_fact_list) { [float_fact] }
73 let(:expected_output) { "memory: 1024.0\n" }
74
75 it 'does not use quotes' do
76 expect(yaml_formatter.format(resolved_fact_list)).to eq(expected_output)
77 end
78 end
79 end
0 # frozen_string_literal: true
1
2 describe Logger do
3 subject(:log) { Facter::Log.new(Class) }
4
5 let(:multi_logger_double) { instance_spy(Logger) }
6
7 before do
8 Facter::Log.class_variable_set(:@@logger, multi_logger_double)
9 Facter::Options[:color] = false
10 end
11
12 after do
13 Facter::Log.class_variable_set(:@@logger, Logger.new(STDOUT))
14 end
15
16 describe '#debug' do
17 before do
18 allow(Facter).to receive(:debugging?).and_return(true)
19 end
20
21 context 'when debugging is set to false' do
22 it 'no debug messages are sent' do
23 allow(Facter).to receive(:debugging?).and_return(false)
24
25 log.debug('info_message')
26
27 expect(multi_logger_double).not_to have_received(:debug)
28 end
29 end
30
31 shared_examples 'writes debug message with no color' do
32 it 'calls debug on multi_logger' do
33 log.debug('debug_message')
34
35 expect(multi_logger_double).to have_received(:debug).with('Class - debug_message')
36 end
37 end
38
39 shared_examples 'writes debug message with color' do
40 it 'calls debug on multi_logger' do
41 log.debug('debug_message')
42
43 expect(multi_logger_double).to have_received(:debug)
44 .with("Class - #{Facter::CYAN}debug_message#{Facter::RESET}")
45 end
46 end
47
48 it_behaves_like 'writes debug message with no color'
49
50 context 'when message callback is provided' do
51 after do
52 Facter::Log.class_variable_set(:@@message_callback, nil)
53 end
54
55 it 'provides on_message hook' do
56 logger_double = instance_spy(Logger)
57 Facter.on_message do |level, message|
58 logger_double.debug("on_message called with level: #{level}, message: #{message}")
59 end
60
61 log.debug('test')
62
63 expect(logger_double).to have_received(:debug).with('on_message called with level: debug, message: test')
64 end
65 end
66
67 context 'when call is made during os detection in os_detector.rb and facter.rb is not fully loaded' do
68 before do
69 allow(Facter).to receive(:respond_to?).with(:debugging?).and_return(false)
70 end
71
72 it_behaves_like 'writes debug message with no color'
73
74 it 'does not call Facter.debugging?' do
75 log.debug('debug_message')
76
77 expect(Facter).not_to have_received(:debugging?)
78 end
79 end
80
81 context 'when non Windows OS' do
82 before do
83 allow(OsDetector.instance).to receive(:identifier).and_return(:macosx)
84 end
85
86 context 'when --colorize option is enabled' do
87 before do
88 Facter::Options[:color] = true
89 end
90
91 it_behaves_like 'writes debug message with color'
92 end
93 end
94
95 context 'when Windows OS' do
96 before do
97 allow(OsDetector.instance).to receive(:identifier).and_return(:windows)
98 end
99
100 context 'when --colorize option is enabled' do
101 before do
102 Facter::Options[:color] = true
103 end
104
105 it_behaves_like 'writes debug message with color'
106 end
107 end
108 end
109
110 describe '#debugonce' do
111 before do
112 allow(Facter).to receive(:debugging?).and_return(true)
113 end
114
115 it 'writes the same debug message only once' do
116 message = 'Some error message'
117
118 log.debugonce(message)
119 log.debugonce(message)
120
121 expect(multi_logger_double).to have_received(:debug).once.with("Class - #{message}")
122 end
123 end
124
125 describe '#info' do
126 it 'writes info message' do
127 log.info('info_message')
128
129 expect(multi_logger_double).to have_received(:info).with('Class - info_message')
130 end
131
132 context 'when non Windows OS' do
133 before do
134 allow(OsDetector.instance).to receive(:identifier).and_return(:macosx)
135 end
136
137 context 'when --colorize option is enabled' do
138 before do
139 Facter::Options[:color] = true
140 end
141
142 it 'print Green (32) info message' do
143 log.info('info_message')
144
145 expect(multi_logger_double)
146 .to have_received(:info)
147 .with("Class - #{Facter::GREEN}info_message#{Facter::RESET}")
148 end
149 end
150 end
151
152 context 'when Windows OS' do
153 before do
154 allow(OsDetector.instance).to receive(:identifier).and_return(:windows)
155 end
156
157 context 'when --colorize option is enabled' do
158 before do
159 Facter::Options[:color] = true
160 end
161
162 it 'print info message' do
163 log.info('info_message')
164
165 expect(multi_logger_double).to have_received(:info)
166 .with("Class - #{Facter::GREEN}info_message#{Facter::RESET}")
167 end
168 end
169 end
170 end
171
172 describe '#warn' do
173 it 'writes warn message' do
174 log.warn('warn_message')
175
176 expect(multi_logger_double).to have_received(:warn).with('Class - warn_message')
177 end
178
179 context 'when non Windows OS' do
180 before do
181 allow(OsDetector.instance).to receive(:identifier).and_return(:macosx)
182 end
183
184 context 'when --colorize option is enabled' do
185 before do
186 Facter::Options[:color] = true
187 end
188
189 it 'print Yellow (33) info message' do
190 log.warn('warn_message')
191
192 expect(multi_logger_double)
193 .to have_received(:warn)
194 .with("Class - #{Facter::YELLOW}warn_message#{Facter::RESET}")
195 end
196 end
197 end
198
199 context 'when Windows OS' do
200 before do
201 allow(OsDetector.instance).to receive(:identifier).and_return(:windows)
202 end
203
204 context 'when --colorize option is enabled' do
205 before do
206 Facter::Options[:color] = true
207 end
208
209 it 'print warn message' do
210 log.warn('warn_message')
211
212 expect(multi_logger_double).to have_received(:warn)
213 .with("Class - #{Facter::YELLOW}warn_message#{Facter::RESET}")
214 end
215 end
216 end
217 end
218
219 describe '#warnonce' do
220 before do
221 allow(Facter).to receive(:debugging?).and_return(true)
222 end
223
224 it 'writes the same debug message only once' do
225 message = 'Some error message'
226
227 log.warnonce(message)
228 log.warnonce(message)
229
230 expect(multi_logger_double).to have_received(:warn).once.with("Class - #{message}")
231 end
232 end
233
234 describe '#error' do
235 it 'writes error message with color on macosx' do
236 allow(OsDetector.instance).to receive(:detect).and_return(:macosx)
237
238 log.error('error_message', true)
239
240 expect(multi_logger_double).to have_received(:error).with("Class - #{Facter::RED}error_message#{Facter::RESET}")
241 end
242
243 it 'writes error message colorized on Windows' do
244 allow(OsDetector.instance).to receive(:identifier).and_return(:windows)
245
246 log.error('error_message', true)
247
248 expect(multi_logger_double).to have_received(:error).with("Class - #{Facter::RED}error_message#{Facter::RESET}")
249 end
250
251 it 'writes error message' do
252 log.error('error_message')
253
254 expect(multi_logger_double).to have_received(:error).with('Class - error_message')
255 end
256 end
257
258 describe '#level=' do
259 it 'sets the log level' do
260 Facter::Log.level = :error
261
262 expect(multi_logger_double).to have_received(:level=).with(:error)
263 end
264 end
265
266 describe '#log_exception' do
267 let(:exception) { Exception.new('Test exception') }
268
269 it 'writes exception message without --trace option' do
270 log.log_exception(exception)
271
272 expect(multi_logger_double).to have_received(:error).with("Class - #{colorize('Test exception', Facter::RED)}")
273 end
274
275 it 'writes exception message and backtrace with --trace option' do
276 allow(Facter::Options).to receive(:[])
277 allow(Facter::Options).to receive(:[]).with(:trace).and_return(true)
278 allow(exception).to receive(:backtrace).and_return(['backtrace:1'])
279 log.log_exception(exception)
280
281 expect(multi_logger_double)
282 .to have_received(:error)
283 .with("Class - #{colorize("Test exception\nbacktrace:1", Facter::RED)}")
284 end
285 end
286
287 describe '.clear_messages' do
288 before do
289 allow(Facter).to receive(:debugging?).and_return(true)
290 end
291
292 it 'clears debugonce messages' do
293 message = 'Some error message'
294
295 log.debugonce(message)
296 Facter::Log.clear_messages
297 log.debugonce(message)
298
299 expect(multi_logger_double).to have_received(:debug).twice.with("Class - #{message}")
300 end
301
302 it 'clears warnonce messages' do
303 message = 'Some error message'
304
305 log.warnonce(message)
306 Facter::Log.clear_messages
307 log.warnonce(message)
308
309 expect(multi_logger_double).to have_received(:warn).twice.with("Class - #{message}")
310 end
311 end
312
313 describe '.show_time' do
314 before do
315 allow(Facter::Log).to receive(:timing?).and_return(true)
316 end
317
318 it 'prints the message to stderr' do
319 expect { Facter::Log.show_time('foo') }.to output("\e[32mfoo\e[0m\n").to_stderr
320 end
321 end
322
323 describe 'setting the timing mode' do
324 it 'is enabled when the given value is true' do
325 Facter::Log.timing(true)
326 expect(Facter::Log.timing?).to be true
327 end
328
329 it 'is disabled when the given value is false' do
330 Facter::Log.timing(false)
331 expect(Facter::Log.timing?).to be false
332 end
333
334 it 'is disabled when the given value is nil' do
335 Facter::Log.timing(nil)
336 expect(Facter::Log.timing?).to be nil
337 end
338 end
339 end
0 # frozen_string_literal: true
1
2 describe Facter::Utils do
3 let(:hash_to_change) { { this: { is: [{ 1 => 'test' }] } } }
4 let(:result) { { 'this' => { 'is' => [{ '1' => 'test' }] } } }
5
6 describe '.deep_stringify_keys' do
7 it 'stringifies keys in hash' do
8 expect(Facter::Utils.deep_stringify_keys(hash_to_change)).to eql(result)
9 end
10 end
11
12 describe '.try_to_bool' do
13 it 'converts to bool when truthy' do
14 expect(Facter::Utils.try_to_bool('true')).to be true
15 end
16
17 it 'converts to bool when falsey' do
18 expect(Facter::Utils.try_to_bool('false')).to be false
19 end
20
21 it 'leaves the string unchanged otherwise' do
22 expect(Facter::Utils.try_to_bool('something else')).to eql('something else')
23 end
24 end
25
26 describe '.try_to_int' do
27 context 'when possible, converts to int' do
28 it 'leaves int as it is' do
29 expect(Facter::Utils.try_to_int(7)).to be(7)
30 end
31
32 it 'converts string int' do
33 expect(Facter::Utils.try_to_int('7')).to be(7)
34 end
35
36 it 'converts positive string int' do
37 expect(Facter::Utils.try_to_int('+7')).to be(7)
38 end
39
40 it 'converts negative string int' do
41 expect(Facter::Utils.try_to_int('-7')).to be(-7)
42 end
43
44 it 'converts float' do
45 expect(Facter::Utils.try_to_int(7.10)).to be(7)
46 end
47 end
48
49 context 'when not possible, does not convert to int' do
50 it 'does not convert non-numerical string' do
51 expect(Facter::Utils.try_to_int('string')).to be('string')
52 end
53
54 it 'does not convert partial string int' do
55 expect(Facter::Utils.try_to_int('7string')).to be('7string')
56 end
57
58 it 'does not convert boolean true' do
59 expect(Facter::Utils.try_to_int(true)).to be(true)
60 end
61
62 it 'does not convert boolean false' do
63 expect(Facter::Utils.try_to_int(false)).to be(false)
64 end
65
66 it 'does not convert string float' do
67 expect(Facter::Utils.try_to_int('7.10')).to be('7.10')
68 end
69 end
70 end
71 end
0 # frozen_string_literal: true
1
2 class Augeas
3 def self.create(opts = {}, &block); end
4
5 def self.open(root = nil, loadpath = nil, flags = NONE, &block); end
6 end
0 # frozen_string_literal: true
1
2 module FFI
3 ERROR_MORE_DATA = 234
4 CURRENT_PROCESS = 0
5 MAX_PATH = 32_767
6
7 @error_number = nil
8 def self.typedef(arg1, arg2); end
9
10 def self.errno
11 @error_number
12 end
13
14 def self.define_errno(arg)
15 @error_number = arg
16 end
17
18 def self.type_size(arg); end
19
20 module Library
21 LIBC = 'libc'
22
23 def ffi_convention(arg); end
24
25 def ffi_lib(*); end
26
27 def attach_function(*); end
28
29 def enum(*); end
30
31 def GetAdaptersAddresses(*); end
32
33 def getkerninfo(*)
34 0
35 end
36
37 def getloadavg(*); end
38
39 def sysctl(*); end
40
41 def sysctlbyname(*); end
42
43 def WSAAddressToStringW(*); end
44
45 def GetNativeSystemInfo(*); end
46
47 def GetUserNameExW(*); end
48
49 def IsUserAnAdmin(*); end
50
51 def RtlGetVersion(*); end
52
53 def GetPerformanceInfo(*); end
54
55 def IsWow64Process(*); end
56
57 def GetCurrentProcess(*); end
58 end
59
60 class Pointer
61 NULL = nil
62
63 def write_uint32(); end
64
65 def read_uint32(); end
66
67 def get_bytes(*); end
68
69 def self.size; end
70 end
71
72 class MemoryPointer
73 def initialize(*); end
74
75 def read_array_of_double(*); end
76
77 def to_ptr; end
78
79 def [](*); end
80
81 def write_int(*); end
82
83 def read_int; end
84
85 def read_uint32(); end
86
87 def self.size; end
88 end
89
90 class Struct
91 def self.layout(*); end
92
93 def self.size; end
94
95 def initialize(*); end
96
97 def [](*); end
98
99 def []=(*); end
100 end
101
102 class Union
103 def self.layout(*); end
104
105 def self.size; end
106 end
107 end
0 # frozen_string_literal: true
1
2 class FlushFakeError < StandardError; end
0 # frozen_string_literal: true
1
2 LIBS_TO_SKIP = %w[win32ole ffi win32/registry sys/filesystem augeas].freeze
3 module Kernel
4 alias old_require require
5 def require(path)
6 old_require(path) unless LIBS_TO_SKIP.include?(path)
7 end
8
9 def at_exit(&block); end
10 end
0 # frozen_string_literal: true
1
2 class Ifaddr
3 def name; end
4
5 def addr; end
6
7 def netmask; end
8 end
9
10 class AddrInfo
11 def ip_address; end
12
13 def ip?; end
14
15 def ipv4?; end
16
17 def getnameinfo; end
18
19 def inspect_sockaddr; end
20 end
0 # frozen_string_literal: true
1
2 module Sys
3 class Filesystem
4 def self.mounts; end
5
6 class Mount
7 def self.mount_point; end
8
9 def self.mount_type; end
10
11 def self.options; end
12
13 def self.mount_time; end
14
15 def self.name; end
16
17 def self.dump_frequency; end
18
19 def self.pass_number; end
20 end
21
22 class Stat
23 def self.path; end
24
25 def self.base_type; end
26
27 def self.fragment_size; end
28
29 def self.block_size; end
30
31 def self.blocks; end
32
33 def self.blocks_available; end
34
35 def self.blocks_free; end
36
37 def self.bytes_total; end
38
39 def self.bytes_available; end
40
41 def self.bytes_free; end
42
43 def self.bytes_used; end
44 end
45 end
46 end
0 # frozen_string_literal: true
1
2 def mock_os(os_name)
3 allow(OsDetector.instance)
4 .to receive(:identifier)
5 .and_return(os_name)
6 end
7
8 def mock_fact_loader_with_legacy(os_name, loaded_facts_hash)
9 allow(Facter::InternalFactLoader)
10 .to receive(:load_with_legacy)
11 .with(os_name)
12 .and_return(loaded_facts_hash)
13 end
14
15 def mock_fact_loader(os_name, loaded_fact_hash)
16 allow(Facter::InternalFactLoader)
17 .to receive(:load)
18 .with(os_name)
19 .and_return(loaded_fact_hash)
20 end
21
22 def mock_query_parser(user_query, loaded_fact_hash)
23 query_parser_spy = instance_spy(Facter::QueryParser)
24 allow(query_parser_spy)
25 .to receive(:parse)
26 .with(user_query, loaded_fact_hash)
27 end
28
29 private_methods def allow_attr_change(resolved_fact_mock, fact_name, fact_value)
30 allow(resolved_fact_mock)
31 .to receive(:value=)
32 .with(fact_value)
33
34 allow(resolved_fact_mock)
35 .to receive(:name=)
36 .with(fact_name)
37
38 allow(resolved_fact_mock)
39 .to receive(:user_query=)
40 end
41
42 def mock_resolved_fact(fact_name, fact_value, user_query = nil, type = :core)
43 resolved_fact_mock = double(Facter::ResolvedFact, name: fact_name, value: fact_value,
44 user_query: user_query, type: type,
45 legacy?: type == :legacy, core?: type == :core, file: nil,
46 resolves?: fact_name == user_query)
47
48 allow_attr_change(resolved_fact_mock, fact_name, fact_value)
49 resolved_fact_mock
50 end
51
52 def mock_fact(fact_class_name, resolved_fact_to_return, fact_name = nil)
53 fact_mock = instance_spy(fact_class_name)
54
55 allow(fact_class_name)
56 .to receive(:new)
57 .and_return(fact_mock)
58
59 allow(fact_class_name)
60 .to receive(:call_the_resolver)
61 .and_return(resolved_fact_to_return)
62
63 stub_const(fact_class_name.to_s, fact_name) if fact_name.present?
64
65 fact_mock
66 end
67
68 def load_fixture(filename)
69 File.open(File.join('spec', 'fixtures', filename))
70 end
0 # frozen_string_literal: true
1
2 module Win32
3 class Registry
4 def keys; end
5
6 def close; end
7
8 class HKEY_LOCAL_MACHINE
9 def self.open(*); end
10
11 def close; end
12
13 def any?; end
14
15 def each; end
16
17 def [](*); end
18
19 def keys; end
20
21 def read(*); end
22 end
23 end
24 end
0 # frozen_string_literal: true
1
2 module Win32
3 class Registry
4 class Error < RuntimeError
5 end
6 end
7 end
0 # frozen_string_literal: true
1
2 class WIN32OLE
3 def initialize(*); end
4
5 def ConnectServer(*); end
6
7 def Security_; end
8
9 def ImpersonationLevel=; end
10
11 def Model; end
12
13 def Manufacturer; end
14
15 def OEMStringArray; end
16 end
0 # frozen_string_literal: true
1
2 require 'pathname'
3
4 ROOT_DIR = Pathname.new(File.expand_path('..', __dir__)) unless defined?(ROOT_DIR)
5
6 ENV['RACK_ENV'] = 'test'
7
8 require 'bundler/setup'
9
10 require 'simplecov'
11
12 # Configure SimpleCov
13 SimpleCov.start do
14 track_files 'lib/**/*.rb'
15 add_filter 'spec'
16 end
17
18 require 'open3'
19 require 'thor'
20 require 'fileutils'
21 require 'timecop'
22
23 require_relative '../lib/facter/resolvers/base_resolver'
24
25 Dir[ROOT_DIR.join('spec/mocks/*.rb')].sort.each { |file| require file }
26 require_relative 'custom_facts/puppetlabs_spec/files'
27
28 require 'facter'
29 require 'facter/framework/cli/cli'
30 require 'facter/framework/cli/cli_launcher'
31
32 Dir.glob(File.join('./lib/facter/util', '/**/*/', '*.rb'), &method(:require))
33 Dir.glob(File.join('./lib/facter/facts', '/**/*/', '*.rb'), &method(:require))
34 Dir.glob(File.join('./lib/facter/resolvers', '/**/*/', '*.rb'), &method(:require))
35
36 default_coverage = 90
37 SimpleCov.minimum_coverage ENV['COVERAGE'] || default_coverage
38
39 def colorize(str, color)
40 "#{color}#{str}#{Facter::RESET}"
41 end
42
43 # Configure webmock
44 require 'webmock/rspec'
45 WebMock.disable_net_connect!
46
47 # Configure RSpec
48 RSpec.configure do |config|
49 # Enable flags like --only-failures and --next-failure
50 config.example_status_persistence_file_path = '.rspec_status'
51
52 # Disable RSpec exposing methods globally on `Module` and `main`
53 config.disable_monkey_patching!
54 config.expose_dsl_globally = true
55
56 config.expect_with :rspec do |c|
57 c.syntax = :expect
58 end
59
60 config.mock_with :rspec do |mocks|
61 # This option should be set when all dependencies are being loaded
62 # before a spec run, as is the case in a typical spec helper. It will
63 # cause any verifying double instantiation for a class that does not
64 # exist to raise, protecting against incorrectly spelt names.
65 mocks.verify_doubled_constant_names = true
66
67 # This option forces the same argument and method existence checks that are
68 # performed for object_double are also performed on partial doubles.
69 # You should set this unless you have a good reason not to.
70 # It defaults to off only for backwards compatibility.
71
72 mocks.verify_partial_doubles = true
73 end
74
75 config.before do
76 LegacyFacter.clear
77 Facter.clear_messages
78 end
79
80 config.after do
81 Facter::OptionStore.reset
82 end
83
84 # This will cleanup any files that were created with tmpdir or tmpfile
85 config.extend PuppetlabsSpec::Files
86 config.after do
87 PuppetlabsSpec::Files.cleanup
88 end
89 end
0 # frozen_string_literal: true
1
2 require 'json'
3 require_relative 'spec_helper'
4
5 describe 'Facter' do
6 context 'when legacy facts is blocked' do
7 let(:facter_conf_content) do
8 'facts : {
9 blocklist : [ "legacy" ],
10 }'
11 end
12
13 let(:core_facts) do
14 %w[dmi facterversion identity is_virtual kernel kernelmajversion kernelrelease
15 kernelversion memory networking os path processors ruby system_uptime timezone virtual]
16 end
17
18 let(:legacy_facts) do
19 %w[architecture augeasversion dhcp_servers fqdn gid hardwareisa hardwaremodel hostname id interfaces
20 ipaddress ipaddress6 ipaddress6_.* ipaddress_.* macaddress macaddress_.* memoryfree
21 memoryfree_mb memorysize memorysize_mb mtu_.* netmask netmask6 netmask6_.*
22 netmask_.* network network6 network6_.* network_.* operatingsystem operatingsystemmajrelease
23 operatingsystemrelease osfamily physicalprocessorcount processor[0-9]+.*
24 processorcount productname rubyplatform rubysitedir rubyversion scope6 scope6_.* sp_boot_mode
25 sp_boot_volume sp_cpu_type sp_current_processor_speed sp_kernel_version sp_l2_cache_core
26 sp_l3_cache sp_local_host_name sp_machine_model sp_machine_name sp_number_processors sp_os_version
27 sp_packages sp_physical_memory sp_platform_uuid sp_secure_vm sp_serial_number sp_uptime
28 sp_user_name swapencrypted swapfree swapfree_mb swapsize swapsize_mb uptime uptime_days
29 uptime_hours uptime_seconds]
30 end
31
32 let(:facter_output) do
33 facter_conf_path = IntegrationHelper.create_file('./temp/facter.conf', facter_conf_content)
34 out, = IntegrationHelper.exec_facter('-c', facter_conf_path, '--show-legacy', '-j')
35 JSON.parse(out)
36 end
37
38 after do
39 FileUtils.rm_rf('./temp')
40 end
41
42 it 'checks legacy facts are blocked' do
43 expect(facter_output.keys).not_to match_array(legacy_facts)
44 end
45
46 it 'checks core fact are not blocked' do
47 expect(facter_output.keys).to include(*core_facts)
48 end
49 end
50 end
0 # frozen_string_literal: true
1
2 require_relative 'spec_helper'
3
4 describe Facter do
5 context 'when calling the ruby API resolve' do
6 it 'returns a hash that includes legacy values' do
7 result = Facter.resolve('--show-legacy')
8
9 expect(result['uptime_hours']).not_to be_nil
10 end
11
12 it "returns a hash that doesn't include legacy values" do
13 result = Facter.resolve('')
14
15 expect(result['uptime_hours']).to be_nil
16 end
17
18 context 'when calling for specfic legacy fact' do
19 it 'returns a hash that includes legacy values' do
20 result = Facter.resolve('uptime_hours')
21
22 expect(result['uptime_hours']).not_to be_nil
23 end
24 end
25 end
26 end
0 # frozen_string_literal: true
1
2 require_relative 'spec_helper'
3
4 describe 'Facter' do
5 include PuppetlabsSpec::Files
6
7 let(:ext_facts_dir) { tmpdir('external_facts') }
8 let(:custom_facts_dir) { tmpdir('custom_facts') }
9
10 def write_to_file(file_name, to_write, dir = ext_facts_dir)
11 file = File.join(dir, file_name)
12 File.open(file, 'w') { |f| f.print to_write }
13 end
14
15 def tmp_filename(tmp_filename)
16 "#{(0...8).map { rand(65..90).chr }.join}_#{tmp_filename}"
17 end
18
19 describe '.value' do
20 context 'with core facts' do
21 context 'when facts are structured' do
22 it 'does not return wrong values when the query is wrong' do
23 expect(Facter.value('os.name.something')).to be(nil)
24 end
25 end
26
27 context 'when facts have hash values' do
28 it 'does not return wrong values when the query is wrong' do
29 expect(Facter.value('mountpoints./.available.something')).to be(nil)
30 end
31 end
32
33 context 'with array as value' do
34 it 'can access value by index' do
35 expect(Facter.value('processors.models.0')).not_to be_nil
36 end
37
38 it 'cannot access non existent index' do
39 expect(Facter.value('processors.models.1000')).to be_nil
40 end
41
42 it 'does not use non numeric string as index' do
43 expect(Facter.value('processors.models.abc')).to be_nil
44 end
45
46 it 'does not use non negative index' do
47 expect(Facter.value('processors.models.-1')).to be_nil
48 end
49
50 it 'respects the filter tokens' do
51 expect(Facter.value('processors')['models'][0]).to eql(Facter.value('processors.models.0'))
52 end
53 end
54 end
55
56 context 'with custom facts' do
57 context 'with array as value' do
58 before do
59 Facter.add('arr_fact') do
60 setcode { %w[x y z] }
61 end
62 end
63
64 it 'can access value by index' do
65 expect(Facter.value('arr_fact.0')).to eql('x')
66 end
67
68 it 'cannot access non existent index' do
69 expect(Facter.value('arr_fact.3')).to be_nil
70 end
71
72 it 'does not use non numeric string as index' do
73 expect(Facter.value('arr_fact.abc')).to be_nil
74 end
75
76 it 'does not use non negative index' do
77 expect(Facter.value('arr_fact.-1')).to be_nil
78 end
79 end
80
81 context 'when having name collision with a core legacy fact' do
82 # core legacy fact -> network_.*
83 before do
84 Facter.search(custom_facts_dir)
85 data = <<-RUBY
86 Facter.add(:network_nexthop_ip) do
87 setcode { 'network_nexthop_ip' }
88 end
89 RUBY
90
91 write_to_file(tmp_filename('custom_fact.rb'), data, custom_facts_dir)
92 end
93
94 it 'resolves the custom fact' do
95 expect(Facter.value(:network_nexthop_ip)).to eql('network_nexthop_ip')
96 end
97 end
98 end
99
100 context 'with external facts' do
101 context 'with array as value' do
102 before do
103 Facter.search_external([ext_facts_dir])
104 data = { 'arr_ext_fact' => %w[ex1 ex2] }
105 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
106 end
107
108 it 'can access value by index' do
109 expect(Facter.value('arr_ext_fact.0')).to eql('ex1')
110 end
111
112 it 'cannot access non existent index' do
113 expect(Facter.value('arr_ext_fact.3')).to be_nil
114 end
115
116 it 'does not use non numeric string as index' do
117 expect(Facter.value('arr_ext_fact.abc')).to be_nil
118 end
119
120 it 'does not use non negative index' do
121 expect(Facter.value('arr_ext_fact.-1')).to be_nil
122 end
123 end
124 end
125
126 context 'when structured facts are disabled' do
127 before do
128 Facter::Options[:force_dot_resolution] = false
129 end
130
131 context 'with custom fact' do
132 context 'with nested Facter.value calls' do
133 before do
134 Facter.search(custom_facts_dir)
135 data = <<-RUBY
136 Facter.add(:a) do
137 setcode { 'a' }
138 end
139
140 Facter.value(:kernel)
141
142 Facter.add(:b) do
143 setcode { 'b' }
144 end
145 RUBY
146 write_to_file(tmp_filename('custom_fact.rb'), data, custom_facts_dir)
147 end
148
149 it 'does not override original user query' do
150 expect(Facter.value('b')).to eql('b')
151 end
152 end
153
154 context 'when has the same name as a structured core fact' do
155 before do
156 Facter.add('os.name', weight: 999) do
157 setcode { 'custom_fact' }
158 end
159 end
160
161 it 'overrides part of the core fact' do
162 expect(Facter.value('os.name')).to eql('custom_fact')
163 end
164
165 it 'does not override in the root fact' do
166 expect(Facter.value('os')['name']).not_to eql('custom_fact')
167 end
168
169 it 'does not override the whole root fact' do
170 expect(Facter.value('os')['family']).not_to be(nil)
171 end
172 end
173
174 context 'when has the same name as a root core fact' do
175 before do
176 Facter.add('os', weight: 999) do
177 setcode { 'custom_fact_root' }
178 end
179 end
180
181 it 'overrides the core fact' do
182 expect(Facter.value('os')).to eql('custom_fact_root')
183 end
184 end
185
186 context 'when standalone fact' do
187 before do
188 Facter.add('a.b.c') do
189 setcode { 'custom' }
190 end
191 end
192
193 it 'works with fact name' do
194 expect(Facter.value('a.b.c')).to eql('custom')
195 end
196
197 it 'does not work with extra token in fact name' do
198 expect(Facter.value('a.b.c.d')).to be(nil)
199 end
200
201 it 'does not work with partial fact name' do
202 expect(Facter.value('a.b')).to be(nil)
203 end
204
205 it 'does not work with first fact segment' do
206 expect(Facter.value('a')).to be(nil)
207 end
208 end
209 end
210
211 context 'with external fact' do
212 context 'when has the same name as a structured core fact' do
213 before do
214 Facter.search_external([ext_facts_dir])
215 data = { 'os.name' => 'external_fact' }
216 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
217 end
218
219 it 'overrides part of the core fact' do
220 expect(Facter.value('os.name')).to eql('external_fact')
221 end
222
223 it 'does not override in the root fact' do
224 expect(Facter.value('os')['name']).not_to eql('external_fact')
225 end
226
227 it 'does not override the whole root fact' do
228 expect(Facter.value('os')['family']).not_to be(nil)
229 end
230 end
231
232 context 'when has the same name as a root core fact' do
233 before do
234 Facter.search_external([ext_facts_dir])
235 data = { 'os' => 'external_fact_root' }
236 write_to_file(tmp_filename('os_root_fact.yaml'), YAML.dump(data))
237 end
238
239 it 'overrides the core fact' do
240 expect(Facter.value('os')).to eql('external_fact_root')
241 end
242 end
243
244 context 'with standalone fact' do
245 before do
246 Facter.search_external([ext_facts_dir])
247 data = { 'a.b.c' => 'external' }
248 write_to_file(tmp_filename('data.yaml'), YAML.dump(data))
249 end
250
251 it 'works with full fact name' do
252 expect(Facter.value('a.b.c')).to eql('external')
253 end
254
255 it 'does not work with extra token in fact name' do
256 expect(Facter.value('a.b.c.d')).to be(nil)
257 end
258
259 it 'does not work with partial fact name' do
260 expect(Facter.value('a.b')).to be(nil)
261 end
262
263 it 'does not work with first fact segment' do
264 expect(Facter.value('a')).to be(nil)
265 end
266 end
267 end
268 end
269
270 context 'when structured facts are enabled' do
271 before do
272 Facter::Options[:force_dot_resolution] = true
273 end
274
275 after do
276 Facter::Options[:force_dot_resolution] = false
277 end
278
279 context 'with custom fact' do
280 context 'when has the same name as a structured core fact' do
281 before do
282 Facter.add('os.name', weight: 999) do
283 setcode { 'custom_fact' }
284 end
285 end
286
287 it 'overrides part of the core fact' do
288 expect(Facter.value('os.name')).to eql('custom_fact')
289 end
290
291 it 'overrides in the root fact' do
292 expect(Facter.value('os')['name']).to eql('custom_fact')
293 end
294
295 it 'does not override the whole root fact' do
296 expect(Facter.value('os')['family']).not_to be(nil)
297 end
298 end
299
300 context 'when has the same name as a root core fact' do
301 before do
302 Facter.add('os', weight: 999) do
303 setcode { 'custom_fact_root' }
304 end
305 end
306
307 it 'overrides the core fact' do
308 expect(Facter.value('os')).to eql('custom_fact_root')
309 end
310 end
311
312 context 'when standalone fact' do
313 let(:fact_name) { 'a.b.c' }
314
315 before do
316 Facter.add(fact_name) do
317 setcode { 'custom' }
318 end
319 end
320
321 it 'works with fact name' do
322 expect(Facter.value('a.b.c')).to eql('custom')
323 end
324
325 it 'does not work with extra token in fact name' do
326 expect(Facter.value('a.b.c.d')).to be(nil)
327 end
328
329 it 'works with partial fact name' do
330 expect(Facter.value('a.b')).to eql({ 'c' => 'custom' })
331 end
332
333 it 'works with first fact segment' do
334 expect(Facter.value('a')).to eql({ 'b' => { 'c' => 'custom' } })
335 end
336 end
337 end
338
339 context 'with external fact' do
340 context 'when has the same name as a structured core fact' do
341 before do
342 Facter.search_external([ext_facts_dir])
343 data = { 'os.name' => 'external_fact' }
344 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
345 end
346
347 it 'overrides part of the core fact' do
348 expect(Facter.value('os.name')).to eql('external_fact')
349 end
350
351 it 'overrides in the root fact' do
352 expect(Facter.value('os')['name']).to eql('external_fact')
353 end
354
355 it 'does not override the whole root fact' do
356 expect(Facter.value('os')['family']).not_to be(nil)
357 end
358 end
359
360 context 'when has the same name as a root core fact' do
361 before do
362 Facter.search_external([ext_facts_dir])
363 data = { 'os' => 'external_fact_root' }
364 write_to_file(tmp_filename('os_root_fact.yaml'), YAML.dump(data))
365 end
366
367 it 'overrides the core fact' do
368 expect(Facter.value('os')).to eql('external_fact_root')
369 end
370 end
371
372 context 'when standalone fact' do
373 before do
374 Facter.search_external([ext_facts_dir])
375 data = { 'a.b.c' => 'external' }
376 write_to_file(tmp_filename('data.yaml'), YAML.dump(data))
377 end
378
379 it 'works with full fact name' do
380 expect(Facter.value('a.b.c')).to eql('external')
381 end
382
383 it 'does not work with extra token in fact name' do
384 expect(Facter.value('a.b.c.d')).to be(nil)
385 end
386
387 it 'works with partial fact name' do
388 expect(Facter.value('a.b')).to eql({ 'c' => 'external' })
389 end
390
391 it 'works with first fact segment' do
392 expect(Facter.value('a')).to eql({ 'b' => { 'c' => 'external' } })
393 end
394 end
395 end
396 end
397 end
398
399 describe '.to_user_output' do
400 context 'with custom facts' do
401 context 'with array as value' do
402 before do
403 Facter.add('arr_fact') do
404 setcode { %w[x y z] }
405 end
406 end
407
408 it 'can access value by index' do
409 expect(Facter.to_user_output({}, 'arr_fact.0')).to eql(['x', 0])
410 end
411
412 it 'cannot access non existent index' do
413 expect(Facter.to_user_output({}, 'arr_fact.3')).to eql(['', 0])
414 end
415
416 it 'does not use non numeric string as index' do
417 expect(Facter.to_user_output({}, 'arr_fact.abc')).to eql(['', 0])
418 end
419
420 it 'does not use non negative index' do
421 expect(Facter.to_user_output({}, 'arr_fact.-1')).to eql(['', 0])
422 end
423 end
424 end
425
426 context 'with external facts' do
427 context 'with array as value' do
428 before do
429 Facter.search_external([ext_facts_dir])
430 data = { 'arr_ext_fact' => %w[ex1 ex2] }
431 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
432 end
433
434 it 'can access value by index' do
435 expect(Facter.to_user_output({}, 'arr_ext_fact.0')).to eql(['ex1', 0])
436 end
437
438 it 'cannot access non existent index' do
439 expect(Facter.to_user_output({}, 'arr_ext_fact.3')).to eql(['', 0])
440 end
441
442 it 'does not use non numeric string as index' do
443 expect(Facter.to_user_output({}, 'arr_ext_fact.abc')).to eql(['', 0])
444 end
445
446 it 'does not use non negative index' do
447 expect(Facter.to_user_output({}, 'arr_ext_fact.-1')).to eql(['', 0])
448 end
449 end
450 end
451
452 context 'when structured facts are disabled' do
453 before do
454 Facter::Options[:force_dot_resolution] = false
455 end
456
457 context 'with custom fact' do
458 context 'when has the same name as a structured core fact' do
459 before do
460 Facter.add('os.name', weight: 999) do
461 setcode { 'custom_fact' }
462 end
463 end
464
465 it 'overrides part of the core fact' do
466 expect(Facter.to_user_output({}, 'os.name')).to eql(['custom_fact', 0])
467 end
468
469 it 'does not override in the root fact' do
470 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
471
472 expect(result['os']['name']).not_to eql('custom_fact')
473 end
474
475 it 'does not override the whole fact' do
476 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
477
478 expect(result['os']['family']).not_to be(nil)
479 end
480 end
481
482 context 'when has the same name as a root core fact' do
483 before do
484 Facter.add('os', weight: 999) do
485 setcode { 'custom_fact_root' }
486 end
487 end
488
489 it 'overrides the core fact' do
490 expect(Facter.to_user_output({}, 'os')).to eql(['custom_fact_root', 0])
491 end
492 end
493
494 context 'when standalone fact' do
495 let(:fact_name) { 'a.b.c' }
496
497 before do
498 Facter.add(fact_name) do
499 setcode { 'custom' }
500 end
501 end
502
503 it 'works with fact name' do
504 expect(Facter.to_user_output({}, 'a.b.c')).to eql(['custom', 0])
505 end
506
507 it 'does not work with extra token in fact name' do
508 expect(Facter.to_user_output({}, 'a.b.c.d')).to eql(['', 0])
509 end
510
511 it 'does not work with partial fact name' do
512 expect(Facter.to_user_output({}, 'a.b')).to eql(['', 0])
513 end
514
515 it 'does not work with first fact segment' do
516 expect(Facter.to_user_output({}, 'a')).to eql(['', 0])
517 end
518 end
519 end
520
521 context 'with external fact' do
522 context 'when has the same name as a structured core fact' do
523 before do
524 Facter.search_external([ext_facts_dir])
525 data = { 'os.name' => 'external_fact' }
526 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
527 end
528
529 it 'overrides part of the core fact' do
530 expect(Facter.to_user_output({}, 'os.name')).to eql(['external_fact', 0])
531 end
532
533 it 'does not override in the root fact' do
534 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
535
536 expect(result['os']['name']).not_to eql('external_fact')
537 end
538
539 it 'does not override the whole fact' do
540 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
541
542 expect(result['os']['family']).not_to be(nil)
543 end
544 end
545
546 context 'when has the same name as a root core fact' do
547 before do
548 Facter.search_external([ext_facts_dir])
549 data = { 'os' => 'external_fact_root' }
550 write_to_file(tmp_filename('os_root_fact.yaml'), YAML.dump(data))
551 end
552
553 it 'overrides the core fact' do
554 expect(Facter.to_user_output({}, 'os')).to eql(['external_fact_root', 0])
555 end
556 end
557
558 context 'when standalone fact' do
559 before do
560 Facter.search_external([ext_facts_dir])
561 data = { 'a.b.c' => 'external' }
562 write_to_file(tmp_filename('data.yaml'), YAML.dump(data))
563 end
564
565 it 'works with full fact name' do
566 expect(Facter.to_user_output({}, 'a.b.c')).to eql(['external', 0])
567 end
568
569 it 'does not work with extra token in fact name' do
570 expect(Facter.to_user_output({}, 'a.b.c.d')).to eql(['', 0])
571 end
572
573 it 'does not work with partial fact name' do
574 expect(Facter.to_user_output({}, 'a.b')).to eql(['', 0])
575 end
576
577 it 'does not work with first fact segment' do
578 expect(Facter.to_user_output({}, 'a')).to eql(['', 0])
579 end
580 end
581 end
582 end
583
584 context 'when structured facts are enabled' do
585 before do
586 Facter::Options[:force_dot_resolution] = true
587 end
588
589 after do
590 Facter::Options[:force_dot_resolution] = false
591 end
592
593 context 'with custom fact' do
594 context 'when has the same name as a structured core fact' do
595 before do
596 Facter.add('os.name', weight: 999) do
597 setcode { 'custom_fact' }
598 end
599 end
600
601 it 'overrides part of the core fact' do
602 expect(Facter.to_user_output({}, 'os.name')).to eql(['custom_fact', 0])
603 end
604
605 it 'overrides in the root fact' do
606 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
607
608 expect(result['os']['name']).to eql('custom_fact')
609 end
610
611 it 'does not override the whole fact' do
612 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
613
614 expect(result['os']['family']).not_to be(nil)
615 end
616 end
617
618 context 'when has the same name as a root core fact' do
619 before do
620 Facter.add('os', weight: 999) do
621 setcode { 'custom_fact_root' }
622 end
623 end
624
625 it 'overrides the core fact' do
626 expect(Facter.to_user_output({}, 'os')).to eql(['custom_fact_root', 0])
627 end
628 end
629
630 context 'when standalone fact' do
631 let(:fact_name) { 'a.b.c' }
632
633 before do
634 Facter.add(fact_name) do
635 setcode { 'custom' }
636 end
637 end
638
639 it 'works with fact name' do
640 expect(Facter.to_user_output({}, 'a.b.c')).to eql(['custom', 0])
641 end
642
643 it 'does not work with extra token in fact name' do
644 expect(Facter.to_user_output({}, 'a.b.c.d')).to eql(['', 0])
645 end
646
647 it 'works with partial fact name' do
648 expect(Facter.to_user_output({}, 'a.b')).to eql(["{\n c => \"custom\"\n}", 0])
649 end
650
651 it 'works with first fact segment' do
652 expect(Facter.to_user_output({}, 'a')).to eql(["{\n b => {\n c => \"custom\"\n }\n}", 0])
653 end
654 end
655 end
656
657 context 'with external fact' do
658 context 'when has the same name as a structured core fact' do
659 before do
660 Facter.search_external([ext_facts_dir])
661 data = { 'os.name' => 'external_fact' }
662 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
663 end
664
665 it 'overrides part of the core fact' do
666 expect(Facter.to_user_output({}, 'os.name')).to eql(['external_fact', 0])
667 end
668
669 it 'overrides in the root fact' do
670 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
671
672 expect(result['os']['name']).to eql('external_fact')
673 end
674
675 it 'does not override the whole fact' do
676 result = JSON.parse(Facter.to_user_output({ json: true }, 'os')[0])
677
678 expect(result['os']['family']).not_to be(nil)
679 end
680 end
681
682 context 'when has the same name as a root core fact' do
683 before do
684 Facter.search_external([ext_facts_dir])
685 data = { 'os' => 'external_fact_root' }
686 write_to_file(tmp_filename('os_root_fact.yaml'), YAML.dump(data))
687 end
688
689 it 'overrides the core fact' do
690 expect(Facter.to_user_output({}, 'os')).to eql(['external_fact_root', 0])
691 end
692 end
693
694 context 'when standalone fact' do
695 before do
696 Facter.search_external([ext_facts_dir])
697 data = { 'a.b.c' => 'external' }
698 write_to_file(tmp_filename('data.yaml'), YAML.dump(data))
699 end
700
701 it 'works with full fact name' do
702 expect(Facter.to_user_output({}, 'a.b.c')).to eql(['external', 0])
703 end
704
705 it 'does not work with extra token in fact name' do
706 expect(Facter.to_user_output({}, 'a.b.c.d')).to eql(['', 0])
707 end
708
709 it 'works with partial fact name' do
710 expect(Facter.to_user_output({}, 'a.b')).to eql(["{\n c => \"external\"\n}", 0])
711 end
712
713 it 'works with first fact segment' do
714 expect(Facter.to_user_output({}, 'a')).to eql(["{\n b => {\n c => \"external\"\n }\n}", 0])
715 end
716 end
717 end
718 end
719 end
720
721 describe '.fact' do
722 context 'with core facts' do
723 context 'when facts are structured' do
724 it 'does not return ResolvedFact when the query is wrong' do
725 expect(Facter.fact('os.name.something')).to be_nil
726 end
727 end
728
729 context 'when facts have hash values' do
730 it 'does not return ResolvedFact when the query is wrong' do
731 expect(Facter.fact('mountpoints./.available.something')).to be_nil
732 end
733 end
734
735 context 'with array as value' do
736 it 'returns a ResolvedFact with value' do
737 expect(Facter.fact('processors.models.0')).to be_instance_of(Facter::ResolvedFact)
738 end
739
740 it 'cannot access non existent index' do
741 expect(Facter.fact('processors.models.1000')).to be_nil
742 end
743
744 it 'does not use non numeric string as index' do
745 expect(Facter.fact('processors.models.abc')).to be_nil
746 end
747
748 it 'does not use non negative index' do
749 expect(Facter.fact('processors.models.-1')).to be_nil
750 end
751 end
752 end
753
754 context 'with custom facts' do
755 context 'when fact has value' do
756 before do
757 Facter.add('my_fact') do
758 setcode { 'my_value' }
759 end
760 end
761
762 it 'returns a ResolvedFact with value' do
763 expect(Facter.fact('my_fact')).to be_instance_of(Facter::ResolvedFact).and have_attributes(value: 'my_value')
764 end
765 end
766
767 context 'when fact value is nil' do
768 before do
769 Facter.add('custom1', weight: 999) do
770 setcode { nil }
771 end
772 end
773
774 it 'returns a ResolvedFact with value: nil' do
775 expect(Facter.fact('custom1')).to be_instance_of(Facter::ResolvedFact).and have_attributes(value: nil)
776 end
777 end
778
779 context 'with array as value' do
780 before do
781 Facter.add('arr_fact') do
782 setcode { %w[x y z] }
783 end
784 end
785
786 it 'returns a ResolvedFact with value for an existent index' do
787 expect(Facter.fact('arr_fact.0')).to be_instance_of(Facter::ResolvedFact).and have_attributes(value: 'x')
788 end
789
790 it 'cannot access non existent index' do
791 expect(Facter.fact('arr_fact.3')).to be_nil
792 end
793
794 it 'does not use non numeric string as index' do
795 expect(Facter.fact('arr_fact.abc')).to be_nil
796 end
797
798 it 'does not use non negative index' do
799 expect(Facter.fact('arr_fact.-1')).to be_nil
800 end
801 end
802 end
803
804 context 'with external facts' do
805 context 'with array as value' do
806 before do
807 Facter.search_external([ext_facts_dir])
808 data = { 'arr_ext_fact' => %w[ex1 ex2] }
809 write_to_file(tmp_filename('os_fact.yaml'), YAML.dump(data))
810 end
811
812 it 'can access value by index' do
813 expect(Facter.fact('arr_ext_fact.0'))
814 .to be_instance_of(Facter::ResolvedFact)
815 .and have_attributes(value: 'ex1')
816 end
817
818 it 'cannot access non existent index' do
819 expect(Facter.fact('arr_ext_fact.3')).to be_nil
820 end
821
822 it 'does not use non numeric string as index' do
823 expect(Facter.fact('arr_ext_fact.abc')).to be_nil
824 end
825
826 it 'does not use non negative index' do
827 expect(Facter.fact('arr_ext_fact.-1')).to be_nil
828 end
829 end
830 end
831
832 context 'when searching for a fact that does not exists' do
833 it 'returns nil' do
834 expect(Facter.fact('non_existent')).to be_nil
835 end
836 end
837 end
838 end
0 # frozen_string_literal: true
1
2 require_relative 'spec_helper'
3
4 describe Facter do
5 context 'when calling facter cli' do
6 context 'with no user query' do
7 it 'returns no stderr' do
8 _, err, = IntegrationHelper.exec_facter
9
10 expect(err).not_to match(/ERROR Facter/)
11 end
12
13 it 'returns 0 exit code' do
14 _, _, status = IntegrationHelper.exec_facter
15
16 expect(status.exitstatus).to eq(0)
17 end
18
19 it 'returns valid output' do
20 out, = IntegrationHelper.exec_facter
21
22 root_facts = ['memory => {', 'networking => {',
23 'os => {', 'path =>', 'processors => {']
24
25 expect(out).to include(*root_facts)
26 end
27
28 it 'returns valid json output' do
29 out, = IntegrationHelper.exec_facter('-j')
30
31 expect do
32 JSON.parse(out)
33 end.not_to raise_exception
34 end
35 end
36
37 context 'when concatenating short options' do
38 context 'when using valid flags' do
39 it 'returns no error' do
40 _, err = IntegrationHelper.exec_facter('-pjdt')
41
42 expect(err).not_to match(/ERROR Facter::OptionsValidator - unrecognised option/)
43 end
44 end
45
46 context 'when using flags and subcommands' do
47 it 'returns validation error' do
48 _, err = IntegrationHelper.exec_facter('-pjdtz')
49
50 expect(err).to match(/ERROR Facter::OptionsValidator - .*unrecognised option '-z'/)
51 end
52 end
53
54 context 'when concatenating JSON and DEBUG flags' do
55 out, err = IntegrationHelper.exec_facter('-jd')
56
57 it 'outputs in valid JSON format' do
58 expect do
59 JSON.parse(out)
60 end.not_to raise_exception
61 end
62
63 it 'outputs DEBUG logs' do
64 expect(err).to match(/DEBUG/)
65 end
66 end
67
68 context 'when concatenating timing and DEBUG flags' do
69 out, err = IntegrationHelper.exec_facter('-td')
70
71 it 'outputs timings of facts' do
72 expect(out).to match(/fact .*, took: .* seconds/)
73 end
74
75 it 'outputs DEBUG logs' do
76 expect(err).to match(/DEBUG/)
77 end
78 end
79 end
80
81 context 'with user query' do
82 it 'returns fqdn' do
83 out, = IntegrationHelper.exec_facter('fqdn')
84
85 expect(out).not_to be_empty
86 end
87
88 it 'returns ip' do
89 out, = IntegrationHelper.exec_facter('ipaddress')
90
91 expect(out).to match(/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)
92 end
93
94 it 'returns ipaddress6', :skip_outside_ci do
95 out, = IntegrationHelper.exec_facter('ipaddress6')
96
97 expect(out).to match(/([a-z0-9]{1,4}:{1,2})+[a-z0-9]{1,4}/)
98 end
99
100 it 'returns hostname' do
101 out, = IntegrationHelper.exec_facter('hostname')
102
103 expect(out).not_to be_empty
104 end
105
106 it 'returns domain', if: IntegrationHelper.jruby? do
107 out, = IntegrationHelper.exec_facter('domain')
108
109 expect(out).not_to be_empty
110 end
111 end
112 end
113
114 context 'when calling the ruby API to_hash' do
115 it 'returns a hash with values' do
116 fact_hash = Facter.to_hash
117
118 expect(fact_hash).to be_instance_of(Hash)
119 end
120
121 it 'contains fqdn' do
122 fact_hash = Facter.to_hash
123
124 expect(fact_hash['fqdn']).not_to be_empty
125 end
126
127 it 'contains ipaddress' do
128 fact_hash = Facter.to_hash
129
130 expect(fact_hash['ipaddress']).to match(/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)
131 end
132
133 it 'contains ipaddress6', :skip_outside_ci do
134 fact_hash = Facter.to_hash
135
136 expect(fact_hash['ipaddress6']).to match(/([a-z0-9]{1,4}:{1,2})+[a-z0-9]{1,4}/)
137 end
138
139 it 'contains hostname' do
140 fact_hash = Facter.to_hash
141
142 expect(fact_hash['hostname']).not_to be_empty
143 end
144
145 it 'contains domain', if: IntegrationHelper.jruby? do
146 fact_hash = Facter.to_hash
147
148 expect(fact_hash['domain']).not_to be_empty
149 end
150 end
151 end
0 # frozen_string_literal: true
1
2 require 'open3'
3
4 class IntegrationHelper
5 class << self
6 def exec_facter(*args)
7 cmd = %w[bundle exec facter].concat(args)
8 Open3.capture3(*cmd)
9 end
10
11 def jruby?
12 RUBY_PLATFORM == 'java'
13 end
14
15 def create_file(path, content)
16 dir = File.dirname(path)
17 FileUtils.mkdir_p(dir)
18 File.write(path, content)
19
20 File.expand_path(path)
21 end
22 end
23 end
0 # frozen_string_literal: true
1
2 require 'facter'
3 require_relative 'integration_helper'
4 require_relative '../spec/custom_facts/puppetlabs_spec/files'
5
6 # prevent facter from loading its spec files as facts
7 $LOAD_PATH.delete_if { |entry| entry =~ %r{facter/spec} }
8
9 # Configure RSpec
10 RSpec.configure do |config|
11 # Enable flags like --only-failures and --next-failure
12 config.example_status_persistence_file_path = '.rspec_status'
13
14 # exclude `skip_outside_ci` tests if not running on CI
15 config.filter_run_excluding :skip_outside_ci unless ENV['CI']
16
17 # Disable RSpec exposing methods globally on `Module` and `main`
18 config.disable_monkey_patching!
19 config.expose_dsl_globally = true
20
21 config.expect_with :rspec do |c|
22 c.syntax = :expect
23 end
24
25 config.mock_with :rspec do |mocks|
26 # This option should be set when all dependencies are being loaded
27 # before a spec run, as is the case in a typical spec helper. It will
28 # cause any verifying double instantiation for a class that does not
29 # exist to raise, protecting against incorrectly spelt names.
30 mocks.verify_doubled_constant_names = true
31
32 # This option forces the same argument and method existence checks that are
33 # performed for object_double are also performed on partial doubles.
34 # You should set this unless you have a good reason not to.
35 # It defaults to off only for backwards compatibility.
36
37 mocks.verify_partial_doubles = true
38 end
39
40 config.after do
41 Facter.reset
42 Facter.clear
43 Facter::OptionStore.reset
44 LegacyFacter.clear
45 end
46
47 # This will cleanup any files that were created with tmpdir or tmpfile
48 config.extend PuppetlabsSpec::Files
49 config.after(:all) do
50 PuppetlabsSpec::Files.cleanup
51 end
52 end
0 # frozen_string_literal: true
1
2 desc 'Check changes before committing'
3 task(:check) do
4 puts
5 puts '<------------- Running unit tests ------------->'
6 Rake::Task['spec_random'].invoke
7
8 puts
9 puts '<------------- Running integration tests ------------->'
10 Rake::Task['spec_integration'].invoke
11
12 puts
13 puts '<------------- Running rubocop ------------->'
14 Rake::Task['rubocop'].invoke
15 end
0 # frozen_string_literal: true
1
2 desc 'verify that commit messages match CONTRIBUTING.md requirements'
3 task(:commits) do
4 # This rake task looks at the summary from every commit from this branch not
5 # in the branch targeted for a PR. This is accomplished by using the
6 # TRAVIS_COMMIT_RANGE environment variable, which is present in travis CI and
7 # populated with the range of commits the PR contains. If not available, this
8 # falls back to `main..HEAD` as a next best bet as `main` is unlikely to
9 # ever be absent.
10 commit_range = 'HEAD^..HEAD'
11 puts "Checking commits #{commit_range}"
12 `git log --no-merges --pretty=%s #{commit_range}`.each_line do |commit_summary|
13 # This regex tests for the currently supported commit summary tokens: maint, packaging, doc, gem, or fact-<number>.
14 # The exception tries to explain it in more full.
15 if /^\((maint|packaging|doc|docs|gem|fact-\d+)\)|revert|merge/i.match(commit_summary).nil?
16 raise "\n\n\n\tThis commit summary didn't match CONTRIBUTING.md guidelines:\n" \
17 "\n\t\t#{commit_summary}\n" \
18 "\tThe commit summary (i.e. the first line of the commit message) should start with one of:\n" \
19 "\t\t(FACT-<digits>) # this is most common and should be a ticket at tickets.puppet.com\n" \
20 "\t\t(docs)\n" \
21 "\t\t(docs)(DOCUMENT-<digits>)\n" \
22 "\t\t(maint)\n" \
23 "\t\t(gem)\n" \
24 "\t\t(packaging)\n" \
25 "\n\tThis test for the commit summary is case-insensitive.\n\n\n"
26 else
27 puts commit_summary.to_s
28 end
29 puts '...passed'
30 end
31 end
0 # frozen_string_literal: true
1
2 desc 'Create a fact list for the specified os'
3 task :fact_list_generator, [:os_name] do |_, args|
4 ROOT_DIR = Pathname.new(File.expand_path('..', __dir__)) unless defined?(ROOT_DIR)
5
6 require 'facter/framework/core/file_loader'
7 load_dir(['facts', '**'])
8
9 os_hierarchy = Facter::OsHierarchy.new
10 hierarchy = os_hierarchy.construct_hierarchy(args[:os_name])
11
12 internal_fact_loader = Facter::InternalFactLoader.new(hierarchy)
13 facts = internal_fact_loader.facts
14
15 fact_mapping = []
16 facts.each do |loaded_fact|
17 fact_hash = {}
18 fact_hash[:name] = loaded_fact.name
19 fact_hash[:klass] = loaded_fact.klass
20 fact_hash[:type] = loaded_fact.type
21 fact_mapping << fact_hash
22 end
23
24 puts JSON.pretty_generate(fact_mapping)
25 end
0 # frozen_string_literal: true
1
2 desc 'Build Facter manpages'
3 task :gen_manpages do
4 require 'fileutils'
5
6 ronn_args = '--manual="Facter manual" --organization="Puppet, Inc." --roff'
7
8 # Locate ronn
9 begin
10 require 'ronn'
11 rescue LoadError
12 abort('Run `bundle install --with documentation` to install the `ronn` gem.')
13 end
14
15 ronn = `which ronn`.chomp
16
17 abort('Ronn does not appear to be installed') unless File.executable?(ronn)
18
19 FileUtils.mkdir_p('./man/man8')
20
21 `RUBYLIB=./lib:$RUBYLIB bin/facter --man > ./man/man8/facter.8.ronn`
22 `#{ronn} #{ronn_args} ./man/man8/facter.8.ronn`
23 FileUtils.rm('./man/man8/facter.8.ronn')
24 end
0 # frozen_string_literal: true
1
2 require 'rubocop/rake_task'
3
4 RuboCop::RakeTask.new(:rubocop) do |t|
5 t.options = ['--display-cop-names', '--parallel']
6 end
0 # frozen_string_literal: true
1
2 begin
3 require 'rspec/core/rake_task'
4
5 desc 'Run rspec test in sequential order'
6 RSpec::Core::RakeTask.new(:spec)
7
8 desc 'Run rspec test in random order'
9 RSpec::Core::RakeTask.new(:spec_random) do |t|
10 t.rspec_opts = '--order random'
11 end
12
13 desc 'Run rspec integration test in random order'
14 RSpec::Core::RakeTask.new(:spec_integration) do |t|
15 t.rspec_opts = '--pattern spec_integration/**/*_spec.rb '\
16 '--default-path spec_integration --order random'
17 end
18 rescue LoadError
19 puts 'Could not load rspec'
20 end