Codebase list facter / 41de1b5
Imported Upstream version 2.0.1 Stig Sandbeck Mathisen 10 years ago
190 changed file(s) with 6237 addition(s) and 5028 deletion(s). Raw diff Collapse all Expand all
0 Committing changes to Facter
1 ====
2
3 We would like to make it easier for community members to contribute to facter
4 using pull requests, even if it makes the task of reviewing and committing
5 these changes a little harder. Pull requests are only ever based on a single
6 branch, however, we maintain more than one active branch. As a result
7 contributors should target their changes at the facter-2 branch. This makes the
8 process of contributing a little easier for the contributor since they don't
9 need to concern themselves with the question, "What branch do I base my changes
10 on?" This is already called out in the [CONTRIBUTING.md](http://goo.gl/XRH2J).
11
12 Therefore, it is the responsibility of the committer to re-base the change set
13 on the appropriate branch which should receive the contribution.
14
15 The rest of this document addresses the concerns of the committer. This
16 document will help guide the committer decide which branch to base, or re-base
17 a contribution on top of. This document also describes our branch management
18 strategy, which is closely related to the decision of what branch to commit
19 changes into.
20
21 Terminology
22 ====
23
24 Many of these terms have more than one meaning. For the purposes of this
25 document, the following terms refer to specific things.
26
27 **contributor** - A person who makes a change to facter and submits a change
28 set in the form of a pull request.
29
30 **change set** - A set of discrete patches which combined together form a
31 contribution. A change set takes the form of Git commits and is submitted to
32 facter in the form of a pull request.
33
34 **committer** - A person responsible for reviewing a pull request and then
35 making the decision what base branch to merge the change set into.
36
37 **base branch** - A branch in Git that contains an active history of changes
38 and will eventually be released using semantic version guidelines. The branch
39 named master will always exist as a base branch.
40
41 Committer Guide
42 ====
43
44 This section provides a guide to follow while committing change sets to facter
45 base branches.
46
47 How to decide what release(s) should be patched
48 ---
49
50 This section provides a guide to help a committer decide the specific base
51 branch that a change set should be merged into.
52
53 The latest minor release of a major release is the only base branch that should
54 be patched. Older minor releases in a major release do not get patched. Before
55 the switch to [semantic versions](http://semver.org/) committers did not have
56 to think about the difference between minor and major releases. Committing to
57 the latest minor release of a major release is a policy intended to limit the
58 number of active base branches that must be managed.
59
60 Security patches are handled as a special case. Security patches may be
61 applied to earlier minor releases of a major release.
62
63 How to commit a change set to multiple base branches
64 ---
65
66 A change set may apply to multiple releases. In this situation the change set
67 needs to be committed to multiple base branches. This section provides a guide
68 for how to merge patches across releases, e.g. 2.7 is patched, how should the
69 changes be applied to 3.0?
70
71 First, merge the change set into the lowest numbered base branch, e.g. 2.7.
72 Next, merge the changed base branch up through all later base branches by using
73 the `--no-ff --log` git merge options. We commonly refer to this as our "merge
74 up process" because we merge in once, then merge up multiple times.
75
76 When a new minor release branch is created (e.g. 3.1.x) then the previous one
77 is deleted (e.g. 3.0.x). Any security or urgent fixes that might have to be
78 applied to the older code line is done by creating an ad-hoc branch from the
79 tag of the last patch release of the old minor line.
80
81 Code review checklist
82 ---
83
84 This section aims to provide a checklist of things to look for when reviewing a
85 pull request and determining if the change set should be merged into a base
86 branch:
87
88 * All tests pass
89 * Are there any platform gotchas? (Does a change make an assumption about
90 platform specific behavior that is incompatible with other platforms? e.g.
91 Windows paths vs. POSIX paths.)
92 * Is the change backwards compatible? (It should be)
93 * Are there YARD docs for API changes?
94 * Does the change set also require documentation changes? If so is the
95 documentation being kept up to date?
96 * Does the change set include clean code? (software code that is formatted
97 correctly and in an organized manner so that another coder can easily read
98 or modify it.) HINT: `git diff --check`
99 * Does the change set conform to the contributing guide?
100
101
102 Commit citizen guidelines:
103 ---
104
105 This section aims to provide guidelines for being a good commit citizen by
106 paying attention to our automated build tools.
107
108 * Don’t push on a broken build. (A broken build is defined as a failing job
109 in the [Facter](https://jenkins.puppetlabs.com/view/Facter/)
110 page.)
111 * Watch the build until your changes have gone through green
112 * Update the ticket status and target version. The target version field in
113 our issue tracker should be updated to be the next release of facter. For
114 example, if the most recent release of facter is 2.0.1 and you merge a
115 backwards compatible change set into facter-2, then the target version should
116 be 2.1.0 in the issue tracker.)
117 * Ensure the pull request is closed (Hint: amend your merge commit to contain
118 the string `closes #123` where 123 is the pull request number.
119
120 Example Procedure
121 ====
122
123 This section helps a committer rebase a contribution onto an earlier base
124 branch, then merge into the base branch and up through all active base
125 branches.
126
127 Suppose a contributor submits a pull request based on facter-2. The change set
128 fixes a bug reported against facter 2.0.1 which is the most recently released
129 version of facter.
130
131 In this example the committer should rebase the change set onto the stable
132 branch since this is a bug rather than new functionality.
133
134 First, the committer pulls down the branch using the `hub` gem. This tool
135 automates the process of adding the remote repository and creating a local
136 branch to track the remote branch.
137
138 $ hub checkout https://github.com/puppetlabs/facter/pull/1234
139 Branch jeffmccune-fix_foo_error set up to track remote branch fix_foo_error from jeffmccune.
140 Switched to a new branch 'jeffmccune-fix_foo_error'
141
142 At this point the topic branch is a descendant of facter-2, but we want it to
143 descend from stable. The committer creates a new branch then re-bases the
144 change set:
145
146 $ git branch bug/stable/fix_foo_error
147 $ git rebase --onto stable master bug/stable/fix_foo_error
148 First, rewinding head to replay your work on top of it...
149 Applying: (#23456) Fix FooError that always bites users in 2.0.1
150
151 The `git rebase` command may be interpreted as, "First, check out the branch
152 named `bug/stable/fix_foo_error`, then take the changes that were previously
153 based on `facter-2` and re-base them onto `stable`.
154
155 Now that we have a topic branch containing the change set based on the correct
156 release branch, the committer merges in:
157
158 $ git checkout stable
159 Switched to branch 'stable'
160 $ git merge --no-ff --log bug/stable/fix_foo_error
161 Merge made by the 'recursive' strategy.
162 foo | 0
163 1 file changed, 0 insertions(+), 0 deletions(-)
164 create mode 100644 foo
165
166 Once merged into the first base branch, the committer merges up to facter-2:
167
168 $ git checkout facter-2
169 Switched to branch 'facter-2'
170 $ git merge --no-ff --log stable
171 Merge made by the 'recursive' strategy.
172 foo | 0
173 1 file changed, 0 insertions(+), 0 deletions(-)
174 create mode 100644 foo
175
176 And then merges up to master:
177
178 $ git checkout master
179 Switched to branch 'master'
180 $ git merge --no-ff --log stable
181 Merge made by the 'recursive' strategy.
182 foo | 0
183 1 file changed, 0 insertions(+), 0 deletions(-)
184 create mode 100644 foo
185
186 Once the change set has been merged "in and up." the committer pushes. (Note,
187 the checklist should be complete at this point.) Note that the stable,
188 facter-2 and master branches are being pushed at the same time.
189
190 $ git push puppetlabs master:master facter-2:facter-2 stable:stable
191
192 That's it! The committer then updates the pull request, updates the issue in
193 our issue tracker, and keeps an eye on the build status.
0 Checklist/Outline (The short version)
1 =================================================
0 # How to contribute
21
3 * Getting Started:
4 - Make sure you have a [Redmine account](http://projects.puppetlabs.com)
5 - Submit a ticket for your issue, assuming one does not already exist.
6 - Decide what to base your work off of
7 * `1.6.x`: bug fixes only
8 * `2.x`: new features that are not breaking changes
9 * `master`: new features that are breaking changes
10
11 * Making Changes:
12 - Make sure you have a [GitHub account](https://github.com/signup/free)
13 - Fork the repository on GitHub
14 - Make commits of logical units.
15 - Check for unnecessary whitespace with "git diff --check" before committing.
16 - Make sure your commit messages are in the proper format
17 - Make sure you have added the necessary tests for your changes
18 - Run _all_ the tests to assure nothing else was accidentally broken
2 Third-party patches are essential for keeping facter great. We simply can't
3 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 get things working in your environment. There are a few guidelines that we
6 need contributors to follow so that we can have a chance of keeping on
7 top of things.
198
20 * Submitting Changes:
21 - Sign the [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign)
22 - Push your changes to a topic branch in your fork of the repository.
23 - Submit a pull request to the repository in the puppetlabs organization.
24 - Update your Redmine ticket
25
26 The long version
27 ================
9 ## Getting Started
2810
29 0. Create a Redmine ticket for the change you'd like to make.
30
31 It's very important that there be a Redmine ticket for the change
32 you are making. Considering the number of contributions which are
33 submitted, it is crucial that we know we can find the ticket on Redmine.
34
35 Before making a ticket however, be sure that one does not already exist.
36 You can do this by searching Redmine or by trying a Google search which
37 includes `sites:projects.puppetlabs.com` in addition to some of the keywords
38 related to your issue.
39
40 If you do not find a ticket that that accurately describes the work
41 you're going to be doing, go ahead and create one. But be sure to
42 look for related tickets and add them to the 'related tickets' section.
11 * Make sure you have a [Jira account](http://tickets.puppetlabs.com)
12 * Make sure you have a [GitHub account](https://github.com/signup/free)
13 * Submit a ticket for your issue, assuming one does not already exist.
14 * Clearly describe the issue including steps to reproduce when it is a bug.
15 * Make sure you fill in the earliest version that you know has the issue.
16 * Fork the repository on GitHub
4317
44 1. Decide what to base your work on.
18 ## Making Changes
4519
46 In general, you should always base your work on the oldest
47 branch that your change is relevant to, and it will be
48 eventually merged up. Currently, branches will be merged up as
49 follows:
50 1.6.x => 2.x => master
51
52 Currently, this is how you should decide where to target your changes:
53
54 A bug fix should be based off the the earliest place where it is
55 relevant. If it first appears in `1.6.x`, then it should be
56 targeted here and eventually merged up to `2.x` and master.
57
58 New features which are _backwards compatible_ should be targeted
59 at the next release, which currently is `2.x`.
60
61 New features that are _breaking changes_ should be targeted at
62 `master`.
20 * Create a topic branch from where you want to base your work.
21 * This is usually the facter-2 branch.
22 * Only target release branches if you are certain your fix must be on that
23 branch.
24 * To quickly create a topic branch based on facter-2; `git branch
25 fix/facter-2/my_contribution facter-2` then checkout the new branch with `git
26 checkout fix/facter-2/my_contribution`. Please avoid working directly on the
27 `facter-2` branch.
28 * Make commits of logical units.
29 * Check for unnecessary whitespace with `git diff --check` before committing.
30 * Make sure your commit messages are in the proper format.
6331
64 Part of deciding what to what your work should be based off of includes naming
65 your topic branch to reflect this. Your branch name should have the following
66 format:
67 `ticket/target_branch/ticket_number_short_description_of_issuee`
68
69 For example, if you are fixing a bug relating to a hostname problem on aix,
70 which has Redmine ticket number 12345, then your branch should be named:
71 `ticket/2.x/12345_fix_hostname_on_aix`
72
73 There is a good chance that if you submit a pull request _from_ master _to_ master,
74 Puppet Labs developers will suspect that you're not sure about the process. This is
75 why clear naming of branches and basing your work off the right place will be
76 extremely helpful in ensuring that your submission is reviewed and merged. Often times
77 if your change is targeted at the wrong place, we will bounce it back to you and wait
78 to review it until it has been retargeted.
32 ````
33 (#99999) Make the example in CONTRIBUTING imperative and concrete
7934
80 2. Make separate commits for logically separate changes.
35 Without this patch applied the example commit message in the CONTRIBUTING
36 document is not a concrete example. This is a problem because the
37 contributor is left to imagine what the commit message should look like
38 based on a description rather than an example. This patch fixes the
39 problem by making the example concrete and imperative.
8140
82 Please break your commits down into logically consistent units
83 which include new or changed tests relevent to the rest of the
84 change. The goal of doing this is to make the diff easier to
85 read for whoever is reviewing your code. In general, the easier
86 your diff is to read, the more likely someone will be happy to
87 review it and get it into the code base.
41 The first line is a real life imperative statement with a ticket number
42 from our issue tracker. The body describes the behavior without the patch,
43 why this is a problem, and how the patch fixes the problem when applied.
44 ````
8845
89 If you're going to refactor a piece of code, please do so as a
90 separate commit from your feature or bug fix changes.
46 * Make sure you have added the necessary tests for your changes.
47 * Run _all_ the tests to assure nothing else was accidentally broken.
9148
92 It's crucial that your changes include tests to make
93 sure the bug isn't re-introduced, and that the feature isn't
94 accidentally broken.
49 ## Submitting Changes
9550
96 Describe the technical detail of the change(s). If your
97 description starts to get too long, that's a good sign that you
98 probably need to split up your commit into more finely grained
99 pieces.
51 * Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla).
52 * Push your changes to a topic branch in your fork of the repository.
53 * Submit a pull request to the repository in the puppetlabs organization.
54 * Update your ticket to mark that you have submitted code and are ready for it to be reviewed.
55 * Include a link to the pull request in the ticket
10056
101 Commits which plainly describe the the things which help
102 reviewers check the patch and future developers understand the
103 code are much more likely to be merged in with a minimum of
104 bike-shedding or requested changes. Ideally, the commit message
105 would include information, and be in a form suitable for
106 inclusion in the release notes for the version of Facter that
107 includes them.
57 # Additional Resources
10858
109 Please also check that you are not introducing any trailing
110 whitespaces or other "whitespace errors". You can do this by
111 running "git diff --check" on your changes before you commit.
112
113 When writing commit messages, please be sure they meet
114 [these standards](https://github.com/erlang/otp/wiki/Writing-good-commit-messages), and please include the ticket number in your
115 short summary. It should look something like this: `(#12345) Fix this issue in Facter`
116
117 3. Sign the Contributor License Agreement
118
119 Before we can accept your changes, we do need a signed Puppet
120 Labs Contributor License Agreement (CLA).
121
122 You can access the CLA via the
123 [Contributor License Agreement link](https://projects.puppetlabs.com/contributor_licenses/sign)
124 in the top menu bar of our Redmine instance. Once you've signed
125 the CLA, a badge will show up next to your name on the
126 [Puppet Project Overview Page](http://projects.puppetlabs.com/projects/puppet?jump=welcome),
127 and your name will be listed under "Contributor License Signers"
128 section.
129
130 If you have any questions about the CLA, please feel free to
131 contact Puppet Labs via email at cla-submissions@puppetlabs.com.
132
133 4. Sending your patches
134
135 To submit your changes via a GitHub pull request, you must
136 have them on a topic branch, instead of directly on "master"
137 or one of the release, or RC branches. It makes things much easier
138 to keep track of, especially if you decide to work on another thing
139 before your first change is merged in.
140
141 GitHub has some pretty good
142 [general documentation](http://help.github.com/) on using
143 their site. They also have documentation on
144 [creating pull requests](http://help.github.com/send-pull-requests/).
145
146 In general, after pushing your topic branch up to your
147 repository on GitHub, you'll switch to the branch in the
148 GitHub UI and click "Pull Request" towards the top of the page
149 in order to open a pull request.
150
151 You'll want to make sure that you have the appropriate
152 destination branch in the repository under the puppetlabs
153 organization. This should be the same branch that you based
154 your changes off of.
155
156 5. Update the related Redmine ticket.
157
158 You should update the Redmine ticket associated
159 with the change you submitted to include the location of your branch
160 on the `branch` field of the ticket, and change the status to
161 "In Topic Branch Pending Review", along with any other commentary
162 you may wish to make.
163
164 How to track the status of your change after it's been submitted
165 ================================================================
166
167 Shortly after opening a pull request, there should be an automatic
168 email sent via GitHub. This notification is used to let the Puppet
169 development community know about your requested change to give them a
170 chance to review, test, and comment on the change(s).
171
172 We do our best to comment on or merge submitted changes within a about week.
173 However, if there hasn't been any commentary on the pull request or
174 mailed patches, and it hasn't been merged in after a week, then feel
175 free to ask for an update by replying on the mailing list to the
176 automatic notification or mailed patches. It probably wasn't
177 intentional, and probably just slipped through the cracks.
178
179 Additional Resources
180 ====================
181
182 * [Getting additional help](http://projects.puppetlabs.com/projects/puppet/wiki/Getting_Help)
183
184 * [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests)
185
186 * [Bug tracker (Redmine)](http://projects.puppetlabs.com)
187
188 * [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign)
189
59 * [More information on contributing](http://links.puppetlabs.com/contribute-to-puppet)
60 * [Bug tracker (Jira)](http://tickets.puppetlabs.com)
61 * [Contributor License Agreement](http://links.puppetlabs.com/cla)
19062 * [General GitHub documentation](http://help.github.com/)
191
19263 * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
193
194 If you have commit access to the repository
195 ===========================================
196
197 Even if you have commit access to the repository, you'll still need to
198 go through the process above, and have someone else review and merge
199 in your changes. The rule is that all changes must be reviewed by a
200 developer on the project (that didn't write the code) to ensure that
201 all changes go through a code review process.
202
203 Having someone other than the author of the topic branch recorded as
204 performing the merge is the record that they performed the code
205 review.
206
207 * Merging topic branches
208
209 When merging code from a topic branch into the integration branch
210 (Ex: master, 2.7.x, 1.6.x, etc.), there should always be a merge
211 commit. You can accomplish this by always providing the `--no-ff`
212 flag to `git merge`.
213
214 git merge --no-ff --log tickets/master/1234-fix-something-broken
215
216 The reason for always forcing this merge commit is that it
217 provides a consistent way to look up what changes & commits were
218 in a topic branch, whether that topic branch had one, or 500
219 commits. For example, if the merge commit had an abbreviated
220 SHA-1 of `coffeebad`, then you could use the following `git log`
221 invocation to show you which commits it brought in:
222
223 git log coffeebad^1..coffeebad^2
224
225 The following would show you which changes were made on the topic
226 branch:
227
228 git diff coffeebad^1...coffeebad^2
229
230 Because we _always_ merge the topic branch into the integration
231 branch the first parent (`^1`) of a merge commit will be the most
232 recent commit on the integration branch from just before we merged
233 in the topic, and the second parent (`^2`) will always be the most
234 recent commit that was made in the topic branch. This also serves
235 as the record of who performed the code review, as mentioned
236 above.
64 * #puppet-dev IRC channel on freenode.org
0 if gem_source = ENV['GEM_SOURCE']
1 source gem_source
2 else
3 source "https://rubygems.org"
4 end
0 source ENV['GEM_SOURCE'] || "https://rubygems.org"
51
62 # C Ruby (MRI) or Rubinius, but NOT Windows
73 platforms :ruby do
84 gem 'watchr', :group => :development
95 gem 'pry', :group => :development
106 gem 'yard', :group => :development
11 gem 'redcarpet', :group => :development
7 gem 'redcarpet', '<= 2.3.0', :group => :development
128 end
139
1410 group :development, :test do
15 gem 'rake'
16 gem 'facter', ">= 1.0.0", :path => File.expand_path("..", __FILE__)
11 gem 'rake', "~> 10.1.0"
1712 gem 'rspec', "~> 2.11.0"
1813 gem 'mocha', "~> 0.10.5"
19 gem 'json', "~> 1.7"
14 gem 'json', "~> 1.7", :platforms => :ruby
2015 gem 'puppetlabs_spec_helper'
2116 end
2217
23 platform :mswin, :mingw do
24 gem "win32-api", "~> 1.4.8"
25 gem "win32-dir", "~> 0.3.7"
26 gem "windows-api", "~> 0.4.1"
27 gem "windows-pr", "~> 1.2.1"
28 gem "win32console", "~> 1.3.2"
18 require 'yaml'
19 data = YAML.load_file(File.join(File.dirname(__FILE__), 'ext', 'project_data.yaml'))
20 bundle_platforms = data['bundle_platforms']
21 data['gem_platform_dependencies'].each_pair do |gem_platform, info|
22 if bundle_deps = info['gem_runtime_dependencies']
23 bundle_platform = bundle_platforms[gem_platform] or raise "Missing bundle_platform"
24 platform(bundle_platform.intern) do
25 bundle_deps.each_pair do |name, version|
26 gem(name, version, :require => false)
27 end
28 end
29 end
2930 end
31
32 gem 'facter', ">= 1.0.0", :path => File.expand_path("..", __FILE__)
3033
3134 if File.exists? "#{__FILE__}.local"
3235 eval(File.read("#{__FILE__}.local"), binding)
22
33 This package is largely meant to be a library for collecting facts about your
44 system. These facts are mostly strings (i.e., not numbers), and are things
5 like the output of `uname`, public ssh and cfengine keys, the number of
6 processors, etc.
5 like the output of `uname`, public ssh keys, the number of processors, etc.
76
87 See `bin/facter` for an example of the interface.
98
66 $LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks')
77
88 require 'rake'
9 Dir['tasks/**/*.rake'].each { |t| load t }
910
1011 begin
1112 load File.join(File.dirname(__FILE__), 'ext', 'packaging', 'packaging.rake')
2223 end
2324 end
2425
25 Dir['tasks/**/*.rake'].each { |t| load t }
2626
2727 build_defs_file = 'ext/build_defaults.yaml'
2828 if File.exist?(build_defs_file)
0 source ENV['GEM_SOURCE'] || "https://rubygems.org"
1
2 gem "beaker", "~> 1.3.1"
3
4 # json-schema does not support windows, so use the 'ruby' platform to exclude it on windows
5 platforms :ruby do
6 # json-schema uses multi_json, but chokes with multi_json 1.7.9, so prefer 1.7.7
7 gem "multi_json", "1.7.7", :require => false
8 gem "json-schema", "2.1.1", :require => false
9 end
10
11 if File.exists? "#{__FILE__}.local"
12 eval(File.read("#{__FILE__}.local"), binding)
13 end
0 HOSTS:
1 centos-55-64-1:
2 roles:
3 - master
4 - database
5 - agent
6 platform: el-5-x86_64
7 template: centos-5-x86_64
8 hypervisor: vcloud
9 centos-55-386-1:
10 roles:
11 - agent
12 platform: el-5-i386
13 template: centos-5-i386
14 hypervisor: vcloud
15 CONFIG:
16 nfs_server: none
17 consoleport: 443
18 datastore: instance0
19 folder: Delivery/Quality Assurance/FOSS/Dynamic
20 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
21 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 HOSTS:
1 master:
2 roles:
3 - master
4 - agent
5 platform: fedora-19-x86_64
6 hypervisor: vcloud
7 template: Delivery/Quality Assurance/Templates/vCloud/fedora-19-x86_64
8 agent:
9 roles:
10 - agent
11 platform: fedora-19-i386
12 hypervisor: vcloud
13 template: Delivery/Quality Assurance/Templates/vCloud/fedora-19-i386
14 CONFIG:
15 filecount: 12
16 datastore: instance0
17 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
18 folder: Delivery/Quality Assurance/FOSS/Dynamic
19 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 HOSTS:
1 rhel-6-latest-64-1:
2 roles:
3 - master
4 - database
5 - agent
6 platform: el-6-x86_64
7 template: redhat-6-x86_64
8 hypervisor: vcloud
9 rhel-6-latest-32-1:
10 roles:
11 - agent
12 platform: el-6-i386
13 template: redhat-6-i386
14 hypervisor: vcloud
15 CONFIG:
16 nfs_server: none
17 consoleport: 443
18 datastore: instance0
19 folder: Delivery/Quality Assurance/FOSS/Dynamic
20 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
21 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 HOSTS:
1 centos-6-x86_64:
2 roles:
3 - master
4 - database
5 - agent
6 - dashboard
7 platform: el-6-x86_64
8 hypervisor: vcloud
9 template: centos-6-x86_64
10 solaris-11-x86_64:
11 roles:
12 - agent
13 platform: solaris-11-x86_64
14 hypervisor: vcloud
15 template: solaris-11-x86_64
16 CONFIG:
17 nfs_server: NONE
18 consoleport: 443
19 datastore: instance0
20 folder: Delivery/Quality Assurance/FOSS/Dynamic
21 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
22 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 HOSTS:
1 ubuntu-1004-64-1:
2 roles:
3 - master
4 - database
5 - agent
6 platform: ubuntu-10.04-amd64
7 template: ubuntu-1004-x86_64
8 hypervisor: vcloud
9 ubuntu-1004-32-1:
10 roles:
11 - agent
12 platform: ubuntu-10.04-i386
13 template: ubuntu-1004-i386
14 hypervisor: vcloud
15 CONFIG:
16 nfs_server: none
17 consoleport: 443
18 datastore: instance0
19 folder: Delivery/Quality Assurance/FOSS/Dynamic
20 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
21 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 HOSTS:
1 centos-6-x86_64:
2 roles:
3 - master
4 - database
5 - agent
6 - dashboard
7 platform: el-6-x86_64
8 hypervisor: vcloud
9 template: centos-6-x86_64
10 win2003-32:
11 roles:
12 - agent
13 platform: windows-2003-32
14 hypervisor: vcloud
15 template: win-2003-i386
16 CONFIG:
17 nfs_server: NONE
18 consoleport: 443
19 datastore: instance0
20 folder: Delivery/Quality Assurance/FOSS/Dynamic
21 resourcepool: delivery/Quality Assurance/FOSS/Dynamic
22 pooling_api: http://vcloud.delivery.puppetlabs.net/
0 require 'open-uri'
1
2 module Puppet
3 module Acceptance
4 module InstallUtils
5 PLATFORM_PATTERNS = {
6 :redhat => /fedora|el|centos/,
7 :debian => /debian|ubuntu/,
8 :solaris => /solaris/,
9 :windows => /windows/,
10 }.freeze
11
12 # Installs packages on the hosts.
13 #
14 # @param hosts [Array<Host>] Array of hosts to install packages to.
15 # @param package_hash [Hash{Symbol=>Array<String,Array<String,String>>}]
16 # Keys should be a symbol for a platform in PLATFORM_PATTERNS. Values
17 # should be an array of package names to install, or of two element
18 # arrays where a[0] is the command we expect to find on the platform
19 # and a[1] is the package name (when they are different).
20 # @param options [Hash{Symbol=>Boolean}]
21 # @option options [Boolean] :check_if_exists First check to see if
22 # command is present before installing package. (Default false)
23 # @return true
24 def install_packages_on(hosts, package_hash, options = {})
25 check_if_exists = options[:check_if_exists]
26 hosts = [hosts] unless hosts.kind_of?(Array)
27 hosts.each do |host|
28 package_hash.each do |platform_key,package_list|
29 if pattern = PLATFORM_PATTERNS[platform_key]
30 if pattern.match(host['platform'])
31 package_list.each do |cmd_pkg|
32 if cmd_pkg.kind_of?(Array)
33 command, package = cmd_pkg
34 else
35 command = package = cmd_pkg
36 end
37 if !check_if_exists || !host.check_for_package(command)
38 host.logger.notify("Installing #{package}")
39 additional_switches = '--allow-unauthenticated' if platform_key == :debian
40 host.install_package(package, additional_switches)
41 end
42 end
43 end
44 else
45 raise("Unknown platform '#{platform_key}' in package_hash")
46 end
47 end
48 end
49 return true
50 end
51 end
52 end
53 end
0 test_name "Setup environment"
1
2 step "Ensure Git and Ruby"
3
4 require 'puppet/acceptance/install_utils'
5 extend Puppet::Acceptance::InstallUtils
6 require 'beaker/dsl/install_utils'
7 extend Beaker::DSL::InstallUtils
8
9 PACKAGES = {
10 :redhat => [
11 'git',
12 'ruby',
13 ],
14 :debian => [
15 ['git', 'git-core'],
16 'ruby',
17 ],
18 :solaris => [
19 ['git', 'developer/versioning/git'],
20 ['ruby', 'runtime/ruby-18'],
21 ],
22 :windows => [
23 'git',
24 ],
25 }
26
27 install_packages_on(hosts, PACKAGES, :check_if_exists => true)
28
29 hosts.each do |host|
30 if host['platform'] =~ /windows/
31 step "#{host} Install ruby from git"
32 install_from_git(host, "/opt/puppet-git-repos", :name => 'puppet-win32-ruby', :path => 'git://github.com/puppetlabs/puppet-win32-ruby')
33 on host, 'cd /opt/puppet-git-repos/puppet-win32-ruby; cp -r ruby/* /'
34 on host, 'cd /lib; icacls ruby /grant "Everyone:(OI)(CI)(RX)"'
35 on host, 'cd /lib; icacls ruby /reset /T'
36 on host, 'ruby --version'
37 on host, 'cmd /c gem list'
38 end
39 end
0 test_name "Install packages and repositories on target machines..." do
1 extend Beaker::DSL::InstallUtils
2
3 SourcePath = Beaker::DSL::InstallUtils::SourcePath
4 GitURI = Beaker::DSL::InstallUtils::GitURI
5 GitHubSig = Beaker::DSL::InstallUtils::GitHubSig
6
7 tmp_repositories = []
8 options[:install].each do |uri|
9 raise(ArgumentError, "#{uri} is not recognized.") unless(uri =~ GitURI)
10 tmp_repositories << extract_repo_info_from(uri)
11 end
12
13 repositories = order_packages(tmp_repositories)
14
15 versions = {}
16 hosts.each_with_index do |host, index|
17 on host, "echo #{GitHubSig} >> $HOME/.ssh/known_hosts"
18
19 repositories.each do |repository|
20 step "Install #{repository[:name]}"
21 install_from_git host, SourcePath, repository
22
23 if index == 1
24 versions[repository[:name]] = find_git_repo_versions(host,
25 SourcePath,
26 repository)
27 end
28 end
29 end
30 end
0 require 'json'
1 require 'json-schema'
2
3 test_name "Running facter --json should validate against the schema"
4
5 agents.each do |agent|
6 step "Agent #{agent}: Install json gem (needed on older platforms)"
7 on(agent, "gem install json") unless agent['platform'] =~ /windows/
8
9 step "Agent #{agent}: run 'facter --json' and validate"
10 on(agent, facter('--json')) do
11 schema = JSON.parse(File.read("../schema/facter.json"))
12 fail_test "facter --json was invalid" unless JSON::Validator.validate!(schema, stdout)
13 end
14 end
0 test_name "#22944: Facter executes external executable facts many times"
1
2 unix_content = <<EOM
3 #!/bin/sh
4 echo "SCRIPT CALLED" >&2
5 echo "test=value"
6 EOM
7
8 win_content = <<EOM
9 echo "SCRIPT CALLED" >&2
10 echo "test=value"
11 EOM
12
13 agents.each do |agent|
14 step "Agent #{agent}: create external executable fact"
15
16 # assume we're running as root
17 if agent['platform'] =~ /windows/
18 if on(agent, facter('kernelmajversion')).stdout.chomp.to_f < 6.0
19 factsd = 'C:/Documents and Settings/All Users/Application Data/PuppetLabs/facter/facts.d'
20 else
21 factsd = 'C:/ProgramData/PuppetLabs/facter/facts.d'
22 end
23 ext = '.bat'
24 content = win_content
25 else
26 factsd = '/etc/facter/facts.d'
27 ext = '.sh'
28 content = unix_content
29 end
30
31
32 step "Agent #{agent}: create facts.d directory"
33 on(agent, "mkdir -p '#{factsd}'")
34
35
36 step "Agent #{agent}: create external fact"
37 ext_fact = "#{factsd}/external_fact#{ext}"
38
39 teardown do
40 on(agent, "rm -f '#{ext_fact}'")
41 end
42
43 create_remote_file(agent, ext_fact, content)
44
45 step "Agent #{agent}: make it executable"
46 on(agent, "chmod +x '#{ext_fact}'")
47
48 step "Agent #{agent}: ensure it only executes once"
49 on(agent, facter) do
50 lines = stderr.split('\n')
51 times = lines.count { |line| line =~ /SCRIPT CALLED/ }
52 if times == 1
53 step "External executable fact executed once"
54 else
55 fail_test "External fact executed #{times} times, expected once: #{stderr}"
56 end
57 end
58 end
1313 end
1414 }
1515
16 agent1=agents.first
17 step "Agent: Create fact file with multiple facts"
18 create_remote_file(agent1, '/tmp/test_facts.rb', fact_file )
16 agents.each do |agent|
17 step "Agent: Create fact file with multiple facts"
18 dir = agent.tmpdir('facter7039')
19 create_remote_file(agent, "#{dir}/test_facts.rb", fact_file)
20 env = { 'FACTERLIB' => dir }
1921
20 step "Agent: Verify test_fact1 from /tmp/test_facts.rb"
21 on(agent1, "export FACTERLIB=/tmp && facter --puppet test_fact1") do
22 fail_test "Fact 1 not returned by facter --puppet test_fact1" unless
23 stdout.include? 'test fact 1'
22 step "Agent: Verify test_fact1 from #{dir}/test_facts.rb"
23 on(agent, facter('--puppet', 'test_fact1', :environment => env)) do
24 fail_test "Fact 1 not returned by facter --puppet test_fact1" unless
25 stdout.include? 'test fact 1'
26 end
27
28 step "Agent: Verify test_fact2 from #{dir}/test_facts.rb"
29 on(agent, facter('--puppet', 'test_fact2', :environment => env)) do
30 fail_test "Fact 1 not returned by facter --puppet test_fact2" unless
31 stdout.include? 'test fact 2'
32 end
2433 end
25
26 step "Agent: Verify test_fact2 from /tmp/test_facts.rb"
27 on(agent1, "export FACTERLIB=/tmp && facter --puppet test_fact2") do
28 fail_test "Fact 1 not returned by facter --puppet test_fact2" unless
29 stdout.include? 'test fact 2'
30 end
00 #!/usr/bin/env ruby
1 #
2 # = Synopsis
3 #
4 # Collect and display facts about the system.
5 #
6 # = Usage
7 #
8 # facter [-d|--debug] [-h|--help] [-p|--puppet] [-v|--version] [-y|--yaml] [-j|--json] [--external-dir DIR] [--no-external-dir] [fact] [fact] [...]
9 #
10 # = Description
11 #
12 # Collect and display facts about the current system. The library behind
13 # Facter is easy to expand, making Facter an easy way to collect information
14 # about a system from within the shell or within Ruby.
15 #
16 # If no facts are specifically asked for, then all facts will be returned.
17 #
18 # = Options
19 #
20 # yaml::
21 # Emit facts in YAML format.
22 #
23 # json::
24 # Emit facts in JSON format.
25 #
26 # puppet::
27 # Load the Puppet libraries, thus allowing Facter to load Puppet-specific facts.
28 #
29 # version::
30 # Print the version and exit.
31 #
32 # help::
33 # Print this help message.
34 #
35 # debug::
36 # Enable debugging.
37 #
38 # trace::
39 # Enable backtraces.
40 #
41 # timing::
42 # Enable timing.
43 #
44 # facts.d::
45 # The directory to use for external facts.
46 #
47 # = Example
48 #
49 # facter kernel
50 #
51 # = Author
52 #
53 # Luke Kanies
54 #
55 # = Copyright
56 #
57 # Copyright (c) 2011 Puppet Labs, Inc
58 # Licensed under the Apache 2.0 license
591
602 # Bundler and rubygems maintain a set of directories from which to
613 # load gems. If Bundler is loaded, let it determine what can be
11 packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=master'
22 packaging_repo: 'packaging'
33 default_cow: 'base-squeeze-i386.cow'
4 cows: 'base-lucid-i386.cow base-lucid-amd64.cow base-precise-i386.cow base-precise-amd64.cow base-quantal-i386.cow base-quantal-amd64.cow base-raring-i386.cow base-raring-amd64.cow base-sid-i386.cow base-sid-amd64.cow base-squeeze-i386.cow base-squeeze-amd64.cow base-stable-i386.cow base-stable-amd64.cow base-testing-i386.cow base-testing-amd64.cow base-unstable-i386.cow base-unstable-amd64.cow base-wheezy-i386.cow base-wheezy-amd64.cow'
4 cows: 'base-lucid-i386.cow base-lucid-amd64.cow base-precise-i386.cow base-precise-amd64.cow base-quantal-i386.cow base-quantal-amd64.cow base-raring-i386.cow base-raring-amd64.cow base-saucy-i386.cow base-saucy-amd64.cow base-sid-i386.cow base-sid-amd64.cow base-squeeze-i386.cow base-squeeze-amd64.cow base-stable-i386.cow base-stable-amd64.cow base-testing-i386.cow base-testing-amd64.cow base-trusty-i386.cow base-trusty-amd64.cow base-unstable-i386.cow base-unstable-amd64.cow base-wheezy-i386.cow base-wheezy-amd64.cow'
55 pbuild_conf: '/etc/pbuilderrc'
66 packager: 'puppetlabs'
77 gpg_name: 'info@puppetlabs.com'
88 gpg_key: '4BD6EC30'
99 sign_tar: FALSE
1010 # a space separated list of mock configs
11 final_mocks: 'pl-el-5-i386 pl-el-5-x86_64 pl-el-6-i386 pl-el-6-x86_64 pl-fedora-17-i386 pl-fedora-17-x86_64 pl-fedora-18-i386 pl-fedora-18-x86_64 pl-fedora-19-i386 pl-fedora-19-x86_64'
11 final_mocks: 'pl-el-5-i386 pl-el-5-x86_64 pl-el-6-i386 pl-el-6-x86_64 pl-el-7-x86_64 pl-fedora-19-i386 pl-fedora-19-x86_64 pl-fedora-20-i386 pl-fedora-20-x86_64'
1212 yum_host: 'yum.puppetlabs.com'
1313 yum_repo_path: '/opt/repository/yum/'
1414 build_gem: TRUE
0 facter (1.7.3-1puppetlabs1) lucid unstable sid wheezy lucid squeeze precise quantal raring; urgency=low
0 facter (2.0.1-1puppetlabs1) lucid unstable sid wheezy lucid squeeze precise quantal raring; urgency=low
11
22 * Update to version
33
4 -- Puppet Labs Release <info@puppetlabs.com> Mon, 09 Sep 2013 16:24:04 -0700
4 -- Puppet Labs Release <info@puppetlabs.com> Tue, 01 Apr 2014 10:23:07 -0700
55
66 facter (1.7.2-1puppetlabs2) lucid unstable sid wheezy lucid squeeze precise; urgency=low
77
66 BINDIR=$(shell /usr/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["bindir"]')
77
88 binary-install/facter::
9 /usr/bin/ruby install.rb --sitelibdir=$(LIBDIR) --bindir=$(BINDIR) --ruby=/usr/bin/ruby --destdir=$(CURDIR)/debian/$(cdbs_curpkg) --quick
9 /usr/bin/ruby install.rb --sitelibdir=$(LIBDIR) --bindir=$(BINDIR) --ruby=/usr/bin/ruby --destdir=$(CURDIR)/debian/$(cdbs_curpkg) --quick --man
0 set name=pkg.fmri value=pkg://puppetlabs.com/application/facter@1.7.3,11.4.2-0
0 set name=pkg.fmri value=pkg://puppetlabs.com/application/@2.0.1,13.1.0-0
11 set name=pkg.summary value="Facter, a system inventory tool"
2 set name=pkg.human-version value="1.7.3"
2 set name=pkg.human-version value="2.0.1"
33 set name=pkg.description value="You can prove anything with facts!"
44 set name=info.classification value="org.opensolaris.category.2008:Applications/System Utilities"
55 set name=org.opensolaris.consolidation value="puppet"
00 directories:
11 lib:
2 path: 'usr/lib/ruby/site_ruby/1.8'
2 path: 'Library/Ruby/Site'
33 owner: 'root'
44 group: 'wheel'
55 perms: '0644'
77 # ${3} is the destination volume so that this works correctly
88 # when being installed to volumes other than the current OS.
99
10 <% begin %>
11 <% require 'rubygems' %>
12 <% rescue LoadError %>
13 <% end %>
14 <% require 'rake' %>
10 <%- ["@apple_libdir", "@apple_sbindir", "@apple_bindir", "@apple_docdir", "@package_name"].each do |i| -%>
11 <%- val = instance_variable_get(i) -%>
12 <%- raise "Critical variable #{i} is unset!" if val.nil? or val.empty? -%>
13 <%- end -%>
1514
16 # remove libdir
17 <% Dir.chdir("lib") %>
18 <% FileList["**/*"].select {|i| File.file?(i)}.each do |file| %>
19 /bin/rm -Rf "${3}<%= @apple_libdir %>/<%=file%>"
20 <% end %>
15 # remove ruby library files
16 <%- Dir.chdir("lib") do -%>
17 <%- [@apple_old_libdir, @apple_libdir].compact.each do |libdir| -%>
18 <%- Dir.glob("*").each do |file| -%>
19 /bin/rm -Rf "${3}<%= libdir %>/<%= file %>"
20 <%- end -%>
21 <%- end -%>
22 <%- end -%>
23
2124 # remove bin files
22 <% Dir.chdir("../bin") %>
23 <% FileList["**/*"].select {|i| File.file?(i)}.each do |file| %>
24 /bin/rm -Rf "${3}<%= @apple_bindir %>/<%=file%>"
25 <% end %>
26 <% Dir.chdir("..") %>
25 <%- Dir.chdir("bin") do -%>
26 <%- Dir.glob("*").each do |file| -%>
27 /bin/rm -Rf "${3}<%= @apple_bindir %>/<%= file %>"
28 <%- end -%>
29 <%- end -%>
2730
2831 # remove old doc files
29 /bin/rm -Rf "${3}<%= @apple_docdir %>/<%=@package_name%>"
32 /bin/rm -Rf "${3}<%= @apple_docdir %>/<%= @package_name %>"
1212 gem_test_files: 'spec/**/*'
1313 gem_executables: 'facter'
1414 gem_default_executables: 'facter'
15 gem_platform_dependencies:
16 universal-darwin:
17 gem_runtime_dependencies:
18 CFPropertyList: '~> 2.2.6'
19 x86-mingw32:
20 gem_runtime_dependencies:
21 ffi: '1.9.0'
22 sys-admin: '1.5.6'
23 win32-api: '1.4.8'
24 win32-dir: '~> 0.4.3'
25 windows-api: '~> 0.4.2'
26 windows-pr: '~> 1.2.2'
27 win32console: '~> 1.3.2'
28 bundle_platforms:
29 universal-darwin: ruby
30 x86-mingw32: mingw
31 pre_tasks:
32 'package:apple': 'cfpropertylist'
0 # Fedora 17 ships with ruby 1.9, which uses vendorlibdir instead
0 # Fedora 17 ships with ruby 1.9, RHEL 7 with ruby 2.0, which use vendorlibdir instead
11 # of sitelibdir
2 %if 0%{?fedora} >= 17
2 %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7
33 %global facter_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendorlibdir"]')
44 %else
55 %global facter_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["sitelibdir"]')
66 %endif
77
88 # VERSION is subbed out during rake srpm process
9 %global realversion 1.7.3
10 %global rpmversion 1.7.3
9 %global realversion 2.0.1
10 %global rpmversion 2.0.1
1111
1212 Summary: Ruby module for collecting simple facts about a host operating system
1313 Name: facter
2323
2424 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
2525
26 Requires: ruby >= 1.8.5
26 Requires: ruby >= 1.8.7
2727 Requires: which
2828 # dmidecode and pciutils are not available on all arches
2929 %ifarch %ix86 x86_64 ia64
3131 Requires: pciutils
3232 %endif
3333 Requires: virt-what
34 Requires: ruby >= 1.8.5
35 BuildRequires: ruby >= 1.8.5
36
37 # In Fedora 17 ruby-rdoc is called rubygem-rdoc
38 %if 0%{?fedora} >= 17
34 Requires: ruby >= 1.8.7
35 BuildRequires: ruby >= 1.8.7
36
37 # In Fedora 17+ or RHEL 7+ ruby-rdoc is called rubygem-rdoc
38 %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7
3939 BuildRequires: rubygem-rdoc
4040 %else
4141 BuildRequires: ruby-rdoc
6969
7070
7171 %changelog
72 * Mon Sep 09 2013 Puppet Labs Release <info@puppetlabs.com> - 1:1.7.3-1
73 - Build for 1.7.3
72 * Tue Apr 01 2014 Puppet Labs Release <info@puppetlabs.com> - 1:2.0.1-1
73 - Build for 2.0.1
7474
7575 * Mon Apr 01 2013 Matthaus Owens <matthaus@puppetlabs.com> - 1:1.7.0-0.1rc1
7676 - Add dependency on virt-what to facter for better virutalization detection
159159 opts.on('--[no-]man', 'Presents the creation of man pages.', 'Default on.') do |onman|
160160 InstallOptions.man = onman
161161 end
162 opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default off.') do |ontest|
163 InstallOptions.tests = ontest
164 warn "The tests flag has never worked in Facter, is deprecated as of Nov 29, 2012, and will be removed in a future release of Facter."
165 end
166162 opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir|
167163 InstallOptions.destdir = destdir
168164 end
232228 mandir = RbConfig::CONFIG['mandir']
233229 end
234230
235 # To be deprecated once people move over to using --destdir option
236 if (destdir = ENV['DESTDIR'])
237 warn "DESTDIR is deprecated. Use --destdir instead."
238 bindir = join(destdir, bindir)
239 mandir = join(destdir, mandir)
240 sitelibdir = join(destdir, sitelibdir)
241
242 FileUtils.makedirs(bindir)
243 FileUtils.makedirs(mandir)
244 FileUtils.makedirs(sitelibdir)
245 # This is the new way forward
246 elsif (destdir = InstallOptions.destdir)
231 if (destdir = InstallOptions.destdir)
247232 bindir = join(destdir, bindir)
248233 mandir = join(destdir, mandir)
249234 sitelibdir = join(destdir, sitelibdir)
0 require 'optparse'
1 require 'facter'
2 require 'facter/util/formatter'
3
04 module Facter
15 module Application
26
610 begin
711 Facter::Util::Config.ext_fact_loader = Facter::Util::DirectoryLoader.loader_for(dir)
812 rescue Facter::Util::DirectoryLoader::NoSuchDirectoryError => error
9 $stderr.puts "Specified external facts directory #{dir} does not exist."
13 Facter.log_exception(error, "Specified external facts directory #{dir} does not exist.")
1014 exit(1)
1115 end
1216 end
1620 end
1721
1822 def self.run(argv)
19 require 'optparse'
20 require 'facter'
21
2223 options = parse(argv)
2324
2425 # Accept fact names to return from the command line
3435 begin
3536 facts[name] = Facter.value(name)
3637 rescue => error
37 $stderr.puts "Could not retrieve #{name}: #{error}"
38 exit 10
38 Facter.log_exception(error, "Could not retrieve #{name}: #{error.message}")
39 exit(10)
3940 end
4041 end
4142 end
4344 # Print everything if they didn't ask for specific facts.
4445 facts ||= Facter.to_hash
4546
46 # Print the facts as YAML and exit
47 output = nil
48
4749 if options[:yaml]
48 require 'yaml'
49 puts YAML.dump(facts)
50 exit(0)
51 end
52
53 # Print the facts as JSON and exit
54 if options[:json]
55 begin
56 require 'json'
57 puts JSON.dump(facts)
58 exit(0)
59 rescue LoadError
60 $stderr.puts "You do not have JSON support in your version of Ruby. JSON output disabled"
61 exit(1)
62 end
63 end
64
65 # Print the value of a single fact, otherwise print a list sorted by fact
66 # name and separated by "=>"
67 if facts.length == 1
68 if value = facts.values.first
69 puts value
70 end
50 output = Facter::Util::Formatter.format_yaml(facts)
51 elsif options[:json]
52 output = Facter::Util::Formatter.format_json(facts)
53 elsif options[:plaintext]
54 output = Facter::Util::Formatter.format_plaintext(facts)
7155 else
72 facts.sort_by{ |fact| fact.first }.each do |name,value|
73 puts "#{name} => #{value}"
74 end
75 end
56 output = Facter::Util::Formatter.format_plaintext(facts)
57 end
58
59 puts output
60 exit(0)
7661
7762 rescue => e
78 if options && options[:trace]
79 raise e
80 else
81 $stderr.puts "Error: #{e}"
82 exit(12)
83 end
63 Facter.log_exception(e)
64 exit(12)
8465 end
8566
8667 private
9273 # @return [Hash] options hash
9374 def self.parse(argv)
9475 options = {}
95 OptionParser.new do |opts|
96 opts.on("-y", "--yaml") { |v| options[:yaml] = v }
97 opts.on("-j", "--json") { |v| options[:json] = v }
98 opts.on( "--trace") { |v| options[:trace] = v }
99 opts.on( "--external-dir DIR") { |v| create_directory_loader(v) }
100 opts.on( "--no-external-dir") { |v| create_nothing_loader }
101 opts.on("-d", "--debug") { |v| Facter.debugging(1) }
102 opts.on("-t", "--timing") { |v| Facter.timing(1) }
103 opts.on("-p", "--puppet") { |v| load_puppet }
104
105 opts.on_tail("-v", "--version") do
76 parser = OptionParser.new do |opts|
77 opts.banner = <<-BANNER
78 facter(8) -- Gather system information
79 ======
80
81 SYNOPSIS
82 --------
83
84 Collect and display facts about the system.
85
86 USAGE
87 -----
88
89 facter [-h|--help] [-t|--timing] [-d|--debug] [-p|--puppet] [-v|--version]
90 [-y|--yaml] [-j|--json] [--plaintext] [--external-dir DIR] [--no-external-dir]
91 [fact] [fact] [...]
92
93 DESCRIPTION
94 -----------
95
96 Collect and display facts about the current system. The library behind
97 Facter is easy to expand, making Facter an easy way to collect information
98 about a system from within the shell or within Ruby.
99
100 If no facts are specifically asked for, then all facts will be returned.
101
102 EXAMPLE
103 -------
104
105 Display all facts:
106
107 $ facter
108 architecture => amd64
109 blockdevices => sda,sr0
110 domain => example.com
111 fqdn => puppet.example.com
112 hardwaremodel => x86_64
113 [...]
114
115 Display a single fact:
116
117 $ facter kernel
118 Linux
119
120 Format facts as JSON:
121
122 $ facter --json architecture kernel hardwaremodel
123 {
124 "architecture": "amd64",
125 "kernel": "Linux",
126 "hardwaremodel": "x86_64"
127 }
128
129 AUTHOR
130 ------
131 Luke Kanies
132
133 COPYRIGHT
134 ---------
135 Copyright (c) 2011-2014 Puppet Labs, Inc Licensed under the Apache 2.0 license
136
137 OPTIONS
138 -------
139 BANNER
140 opts.on("-y",
141 "--yaml",
142 "Emit facts in YAML format.") { |v| options[:yaml] = v }
143 opts.on("-j",
144 "--json",
145 "Emit facts in JSON format.") { |v| options[:json] = v }
146 opts.on("--plaintext",
147 "Emit facts in plaintext format.") { |v| options[:plaintext] = v }
148 opts.on("--trace",
149 "Enable backtraces.") { |v| Facter.trace(true) }
150 opts.on("--external-dir DIR",
151 "The directory to use for external facts.") { |v| create_directory_loader(v) }
152 opts.on("--no-external-dir",
153 "Turn off external facts.") { |v| create_nothing_loader }
154 opts.on("-d",
155 "--debug",
156 "Enable debugging.") { |v| Facter.debugging(1) }
157 opts.on("-t",
158 "--timing",
159 "Enable timing.") { |v| Facter.timing(1) }
160 opts.on("-p",
161 "--puppet",
162 "Load the Puppet libraries, thus allowing Facter to load Puppet-specific facts.") { |v| load_puppet }
163
164 opts.on_tail("-v",
165 "--version",
166 "Print the version and exit.") do
106167 puts Facter.version
107168 exit(0)
108169 end
109170
110 opts.on_tail("-h", "--help") do
111 begin
112 require 'rdoc/ri/ri_paths'
113 require 'rdoc/usage'
114 RDoc.usage # print usage and exit
115 rescue LoadError
116 $stderr.puts "No help available unless your RDoc has RDoc.usage"
117 exit(1)
118 rescue => e
119 $stderr.puts "fatal: #{e}"
120 exit(1)
121 end
171 opts.on_tail("-h",
172 "--help",
173 "Print this help message.") do
174 puts parser
175 exit(0)
122176 end
123 end.parse!(argv)
177 end
178
179 parser.parse!(argv)
124180
125181 options
126182 rescue OptionParser::InvalidOption => e
33 # Return the CPU hardware architecture.
44 #
55 # Resolution:
6 # On OpenBSD, Linux and Debian's kfreebsd, use the hardwaremodel fact.
6 # On non-AIX IBM, OpenBSD, Linux and Debian's kfreebsd, use the hardwaremodel fact.
7 # On AIX get the arch value from lsattr -El proc0 -a type
78 # Gentoo and Debian call "x86_86" "amd64".
89 # Gentoo also calls "i386" "x86".
910 #
1011 # Caveats:
1112 #
1213
14 require 'facter/util/architecture'
15
1316 Facter.add(:architecture) do
1417 setcode do
1518 model = Facter.value(:hardwaremodel)
1619 case model
1720 # most linuxen use "x86_64"
21 when /IBM*/
22 case Facter.value(:operatingsystem)
23 when "AIX"
24 arch = Facter::Util::Architecture.lsattr
25 if (match = arch.match /type\s(\S+)\s/)
26 match[1]
27 end
28 else
29 model
30 end
1831 when "x86_64"
1932 case Facter.value(:operatingsystem)
2033 when "Debian", "Gentoo", "GNU/kFreeBSD", "Ubuntu"
0 require 'facter'
1 require 'facter/core/directed_graph'
2 require 'facter/core/suitable'
3 require 'facter/core/resolvable'
4 require 'facter/util/values'
5
6 # Aggregates provide a mechanism for facts to be resolved in multiple steps.
7 #
8 # Aggregates are evaluated in two parts: generating individual chunks and then
9 # aggregating all chunks together. Each chunk is a block of code that generates
10 # a value, and may depend on other chunks when it runs. After all chunks have
11 # been evaluated they are passed to the aggregate block as Hash<name, result>.
12 # The aggregate block converts the individual chunks into a single value that is
13 # returned as the final value of the aggregate.
14 #
15 # @api public
16 # @since 2.0.0
17 class Facter::Core::Aggregate
18
19 include Facter::Core::Suitable
20 include Facter::Core::Resolvable
21
22 # @!attribute [r] name
23 # @return [Symbol] The name of the aggregate resolution
24 attr_reader :name
25
26 # @!attribute [r] deps
27 # @api private
28 # @return [Facter::Core::DirectedGraph]
29 attr_reader :deps
30
31 # @!attribute [r] confines
32 # @return [Array<Facter::Core::Confine>] An array of confines restricting
33 # this to a specific platform
34 # @see Facter::Core::Suitable
35 attr_reader :confines
36
37 # @!attribute [r] fact
38 # @return [Facter::Util::Fact]
39 # @api private
40 attr_reader :fact
41
42 def initialize(name, fact)
43 @name = name
44 @fact = fact
45
46 @confines = []
47 @chunks = {}
48
49 @aggregate = nil
50 @deps = Facter::Core::DirectedGraph.new
51 end
52
53 def set_options(options)
54 if options[:name]
55 @name = options.delete(:name)
56 end
57
58 if options.has_key?(:timeout)
59 @timeout = options.delete(:timeout)
60 end
61
62 if options.has_key?(:weight)
63 @weight = options.delete(:weight)
64 end
65
66 if not options.keys.empty?
67 raise ArgumentError, "Invalid aggregate options #{options.keys.inspect}"
68 end
69 end
70
71 def evaluate(&block)
72 instance_eval(&block)
73 end
74
75 # Define a new chunk for the given aggregate
76 #
77 # @api public
78 #
79 # @example Defining a chunk with no dependencies
80 # aggregate.chunk(:mountpoints) do
81 # # generate mountpoint information
82 # end
83 #
84 # @example Defining an chunk to add mount options
85 # aggregate.chunk(:mount_options, :require => [:mountpoints]) do |mountpoints|
86 # # `mountpoints` is the result of the previous chunk
87 # # generate mount option information based on the mountpoints
88 # end
89 #
90 # @param name [Symbol] A name unique to this aggregate describing the chunk
91 # @param opts [Hash]
92 # @options opts [Array<Symbol>, Symbol] :require One or more chunks
93 # to evaluate and pass to this block.
94 # @yield [*Object] Zero or more chunk results
95 #
96 # @return [void]
97 def chunk(name, opts = {}, &block)
98 if not block_given?
99 raise ArgumentError, "#{self.class.name}#chunk requires a block"
100 end
101
102 deps = Array(opts.delete(:require))
103
104 if not opts.empty?
105 raise ArgumentError, "Unexpected options passed to #{self.class.name}#chunk: #{opts.keys.inspect}"
106 end
107
108 @deps[name] = deps
109 @chunks[name] = block
110 end
111
112 # Define how all chunks should be combined
113 #
114 # @api public
115 #
116 # @example Merge all chunks
117 # aggregate.aggregate do |chunks|
118 # final_result = {}
119 # chunks.each_value do |chunk|
120 # final_result.deep_merge(chunk)
121 # end
122 # final_result
123 # end
124 #
125 # @example Sum all chunks
126 # aggregate.aggregate do |chunks|
127 # total = 0
128 # chunks.each_value do |chunk|
129 # total += chunk
130 # end
131 # total
132 # end
133 #
134 # @yield [Hash<Symbol, Object>] A hash containing chunk names and
135 # chunk values
136 #
137 # @return [void]
138 def aggregate(&block)
139 if block_given?
140 @aggregate = block
141 else
142 raise ArgumentError, "#{self.class.name}#aggregate requires a block"
143 end
144 end
145
146 def resolution_type
147 :aggregate
148 end
149
150 private
151
152 # Evaluate the results of this aggregate.
153 #
154 # @see Facter::Core::Resolvable#value
155 # @return [Object]
156 def resolve_value
157 chunk_results = run_chunks()
158 aggregate_results(chunk_results)
159 end
160
161 # Order all chunks based on their dependencies and evaluate each one, passing
162 # dependent chunks as needed.
163 #
164 # @return [Hash<Symbol, Object>] A hash containing the chunk that
165 # generated value and the related value.
166 def run_chunks
167 results = {}
168 order_chunks.each do |(name, block)|
169 input = @deps[name].map { |dep_name| results[dep_name] }
170
171 output = block.call(*input)
172 results[name] = Facter::Util::Values.deep_freeze(output)
173 end
174
175 results
176 end
177
178 # Process the results of all chunks with the aggregate block and return the
179 # results. If no aggregate block has been specified, fall back to deep
180 # merging the given data structure
181 #
182 # @param results [Hash<Symbol, Object>] A hash of chunk names and the output
183 # of that chunk.
184 # @return [Object]
185 def aggregate_results(results)
186 if @aggregate
187 @aggregate.call(results)
188 else
189 default_aggregate(results)
190 end
191 end
192
193 def default_aggregate(results)
194 results.values.inject do |result, current|
195 Facter::Util::Values.deep_merge(result, current)
196 end
197 rescue Facter::Util::Values::DeepMergeError => e
198 raise ArgumentError, "Could not deep merge all chunks (Original error: " +
199 "#{e.message}), ensure that chunks return either an Array or Hash or " +
200 "override the aggregate block", e.backtrace
201 end
202
203 # Order chunks based on their dependencies
204 #
205 # @return [Array<Symbol, Proc>] A list of chunk names and blocks in evaluation order.
206 def order_chunks
207 if not @deps.acyclic?
208 raise DependencyError, "Could not order chunks; found the following dependency cycles: #{@deps.cycles.inspect}"
209 end
210
211 sorted_names = @deps.tsort
212
213 sorted_names.map do |name|
214 [name, @chunks[name]]
215 end
216 end
217
218 class DependencyError < StandardError; end
219 end
0 require 'set'
1 require 'tsort'
2
3 module Facter
4 module Core
5 class DirectedGraph < Hash
6 include TSort
7
8 def acyclic?
9 cycles.empty?
10 end
11
12 def cycles
13 cycles = []
14 each_strongly_connected_component do |component|
15 cycles << component if component.size > 1
16 end
17 cycles
18 end
19
20 alias tsort_each_node each_key
21
22 def tsort_each_child(node)
23 fetch(node, []).each do |child|
24 yield child
25 end
26 end
27
28 def tsort
29 missing = Set.new(self.values.flatten) - Set.new(self.keys)
30
31 if not missing.empty?
32 raise MissingVertex, "Cannot sort elements; cannot depend on missing elements #{missing.to_a}"
33 end
34
35 super
36
37 rescue TSort::Cyclic
38 raise CycleError, "Cannot sort elements; found the following cycles: #{cycles.inspect}"
39 end
40
41 class CycleError < StandardError; end
42 class MissingVertex < StandardError; end
43 end
44 end
45 end
0 class Facter::Core::Execution::Base
1
2 def with_env(values)
3 old = {}
4 values.each do |var, value|
5 # save the old value if it exists
6 if old_val = ENV[var]
7 old[var] = old_val
8 end
9 # set the new (temporary) value for the environment variable
10 ENV[var] = value
11 end
12 # execute the caller's block, capture the return value
13 rv = yield
14 # use an ensure block to make absolutely sure we restore the variables
15 ensure
16 # restore the old values
17 values.each do |var, value|
18 if old.include?(var)
19 ENV[var] = old[var]
20 else
21 # if there was no old value, delete the key from the current environment variables hash
22 ENV.delete(var)
23 end
24 end
25 # return the captured return value
26 rv
27 end
28
29 def execute(command, options = {})
30
31 on_fail = options.fetch(:on_fail, :raise)
32
33 ## Set LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
34 ## output of the command can expect it to be in a consistent / predictable format / locale
35 with_env "LANG" => "C" do
36
37 expanded_command = expand_command(command)
38
39 if expanded_command.nil?
40 if on_fail == :raise
41 raise Facter::Core::Execution::ExecutionFailure.new, "Could not execute '#{command}': command not found"
42 else
43 return on_fail
44 end
45 end
46
47 out = ''
48
49 begin
50 wait_for_child = true
51 out = %x{#{expanded_command}}.chomp
52 wait_for_child = false
53 rescue => detail
54 if on_fail == :raise
55 raise Facter::Core::Execution::ExecutionFailure.new, "Failed while executing '#{expanded_command}': #{detail.message}"
56 else
57 return on_fail
58 end
59 ensure
60 if wait_for_child
61 # We need to ensure that if this command exits early then any spawned
62 # children will be reaped. Process execution is frequently
63 # terminated using Timeout.timeout but since the timeout isn't in
64 # this scope we can't rescue the raised exception. The best that
65 # we can do is determine if the child has exited, and if it hasn't
66 # then we need to spawn a thread to wait for the child.
67 #
68 # Due to the limitations of Ruby 1.8 there aren't good ways to
69 # asynchronously run a command and grab the PID of that command
70 # using the standard library. The best we can do is blindly wait
71 # on all processes and hope for the best. This issue is described
72 # at https://tickets.puppetlabs.com/browse/FACT-150
73 Thread.new { Process.waitall }
74 end
75 end
76
77 out
78 end
79 end
80 end
0 class Facter::Core::Execution::Posix < Facter::Core::Execution::Base
1
2 DEFAULT_SEARCH_PATHS = ['/sbin', '/usr/sbin']
3
4 def search_paths
5 # Make sure facter is usable even for non-root users. Most commands
6 # in /sbin (like ifconfig) can be run as non privileged users as
7 # long as they do not modify anything - which we do not do with facter
8 ENV['PATH'].split(File::PATH_SEPARATOR) + DEFAULT_SEARCH_PATHS
9 end
10
11 def which(bin)
12 if absolute_path?(bin)
13 return bin if File.executable?(bin)
14 else
15 search_paths.each do |dir|
16 dest = File.join(dir, bin)
17 return dest if File.executable?(dest)
18 end
19 end
20 nil
21 end
22
23 ABSOLUTE_PATH_REGEX = %r{^/}
24
25 def absolute_path?(path)
26 !! (path =~ ABSOLUTE_PATH_REGEX)
27 end
28
29 DOUBLE_QUOTED_COMMAND = /^"(.+?)"(?:\s+(.*))?/
30 SINGLE_QUOTED_COMMAND = /^'(.+?)'(?:\s+(.*))?/
31
32 def expand_command(command)
33 exe = nil
34 args = nil
35
36 if (match = (command.match(DOUBLE_QUOTED_COMMAND) || command.match(SINGLE_QUOTED_COMMAND)))
37 exe, args = match.captures
38 else
39 exe, args = command.split(/ /,2)
40 end
41
42 if exe and (expanded = which(exe))
43 expanded = "'#{expanded}'" if expanded.match(/\s/)
44 expanded << " #{args}" if args
45
46 return expanded
47 end
48 end
49 end
0 class Facter::Core::Execution::Windows < Facter::Core::Execution::Base
1
2 def search_paths
3 ENV['PATH'].split(File::PATH_SEPARATOR)
4 end
5
6 DEFAULT_COMMAND_EXTENSIONS = %w[.COM .EXE .BAT .CMD]
7
8 def which(bin)
9 if absolute_path?(bin)
10 return bin if File.executable?(bin)
11 else
12 search_paths.each do |dir|
13 dest = File.join(dir, bin)
14 dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
15 if File.extname(dest).empty?
16 exts = ENV['PATHEXT']
17 exts = exts ? exts.split(File::PATH_SEPARATOR) : DEFAULT_COMMAND_EXTENSIONS
18 exts.each do |ext|
19 destext = dest + ext
20 return destext if File.executable?(destext)
21 end
22 end
23 return dest if File.executable?(dest)
24 end
25 end
26 nil
27 end
28
29 slash = '[\\\\/]'
30 name = '[^\\\\/]+'
31 ABSOLUTE_PATH_REGEX = %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i
32
33 def absolute_path?(path)
34 !! (path =~ ABSOLUTE_PATH_REGEX)
35 end
36
37 DOUBLE_QUOTED_COMMAND = /^"(.+?)"(?:\s+(.*))?/
38
39 def expand_command(command)
40 exe = nil
41 args = nil
42
43 if (match = command.match(DOUBLE_QUOTED_COMMAND))
44 exe, args = match.captures
45 else
46 exe, args = command.split(/ /,2)
47 end
48
49 if exe and (expanded = which(exe))
50 expanded = "\"#{expanded}\"" if expanded.match(/\s+/)
51 expanded << " #{args}" if args
52
53 return expanded
54 end
55 end
56 end
0 require 'facter/util/config'
1
2 module Facter
3 module Core
4 module Execution
5
6 require 'facter/core/execution/base'
7 require 'facter/core/execution/windows'
8 require 'facter/core/execution/posix'
9
10 @@impl = if Facter::Util::Config.is_windows?
11 Facter::Core::Execution::Windows.new
12 else
13 Facter::Core::Execution::Posix.new
14 end
15
16 def self.impl
17 @@impl
18 end
19
20 module_function
21
22 # Returns the locations to be searched when looking for a binary. This
23 # is currently determined by the +PATH+ environment variable plus
24 # `/sbin` and `/usr/sbin` when run on unix
25 #
26 # @return [Array<String>] the paths to be searched for binaries
27 # @api private
28 def search_paths
29 @@impl.search_paths
30 end
31
32 # Determines the full path to a binary. If the supplied filename does not
33 # already describe an absolute path then different locations (determined
34 # by {search_paths}) will be searched for a match.
35 #
36 # Returns nil if no matching executable can be found otherwise returns
37 # the expanded pathname.
38 #
39 # @param bin [String] the executable to locate
40 # @return [String,nil] the full path to the executable or nil if not
41 # found
42 #
43 # @api public
44 def which(bin)
45 @@impl.which(bin)
46 end
47
48 # Determine in a platform-specific way whether a path is absolute. This
49 # defaults to the local platform if none is specified.
50 #
51 # @param path [String] the path to check
52 # @param platform [:posix,:windows,nil] the platform logic to use
53 def absolute_path?(path, platform = nil)
54 @@impl.absolute_path?(path, platform)
55 end
56
57 # Given a command line, this returns the command line with the
58 # executable written as an absolute path. If the executable contains
59 # spaces, it has be put in double quotes to be properly recognized.
60 #
61 # @param command [String] the command line
62 #
63 # @return [String, nil] the command line with the executable's path
64 # expanded, or nil if the executable cannot be found.
65 def expand_command(command)
66 @@impl.expand_command(command)
67 end
68
69 # Overrides environment variables within a block of code. The
70 # specified values will be set for the duration of the block, after
71 # which the original values (if any) will be restored.
72 #
73 # @overload with_env(values, { || ... })
74 #
75 # @param values [Hash<String=>String>] A hash of the environment
76 # variables to override
77 #
78 # @return [void]
79 #
80 # @api public
81 def with_env(values, &block)
82 @@impl.with_env(values, &block)
83 end
84
85 # Try to execute a command and return the output.
86 #
87 # @param code [String] the program to run
88 #
89 # @return [String] the output of the program, or nil if the command does
90 # not exist or could not be executed.
91 #
92 # @deprecated Use #{execute} instead
93 # @api public
94 def exec(command)
95 @@impl.execute(command, :on_fail => nil)
96 end
97
98 # Execute a command and return the output of that program.
99 #
100 # @param code [String] the program to run
101 # @param options [Hash]
102 #
103 # @option options [Object] :on_fail How to behave when the command could
104 # not be run. Specifying :raise will raise an error, anything else will
105 # return that object on failure. Default is :raise.
106 #
107 # @raise [Facter::Core::Execution::ExecutionFailure] If the command does
108 # not exist or could not be executed.
109 #
110 # @return [String] the output of the program, or the value of :on_fail if
111 # command execution failed and :on_fail was specified.
112 #
113 # @api public
114 # @since 2.0.1
115 def execute(command, options = {})
116 @@impl.execute(command, options)
117 end
118
119 class ExecutionFailure < StandardError; end
120 end
121 end
122 end
0 require 'facter'
1
2 module Facter::Core::Logging
3
4 extend self
5
6 # @api private
7 GREEN = ""
8 # @api private
9 RESET = ""
10
11 # @api private
12 @@debug = false
13 # @api private
14 @@timing = false
15 # @api private
16 @@trace = false
17
18 # @api private
19 @@warn_messages = {}
20 # @api private
21 @@debug_messages = {}
22
23 # Prints a debug message if debugging is turned on
24 #
25 # @param msg [String] the debug message
26 # @return [void]
27 def debug(msg)
28 if self.debugging?
29 if msg.nil? or msg.empty?
30 invoker = caller[0].slice(/.*:\d+/)
31 self.warn "#{self.class}#debug invoked with invalid message #{msg.inspect}:#{msg.class} at #{invoker}"
32 else
33 puts GREEN + msg + RESET
34 end
35 end
36 end
37
38 # Prints a debug message only once.
39 #
40 # @note Uniqueness is based on the string, not the specific location
41 # of the method call.
42 #
43 # @param msg [String] the debug message
44 # @return [void]
45 def debugonce(msg)
46 if msg and not msg.empty? and @@debug_messages[msg].nil?
47 @@debug_messages[msg] = true
48 debug(msg)
49 end
50 end
51
52 # Prints a warning message. The message is only printed if debugging
53 # is enabled.
54 #
55 # @param msg [String] the warning message to be printed
56 #
57 # @return [void]
58 def warn(msg)
59 if msg.nil? or msg.empty?
60 invoker = caller[0].slice(/.*:\d+/)
61 Kernel.warn "#{self.class}#debug invoked with invalid message #{msg.inspect}:#{msg.class} at #{invoker}"
62 else
63 Kernel.warn msg
64 end
65 end
66
67 # Prints a warning message only once per process. Each unique string
68 # is printed once.
69 #
70 # @note Unlike {warn} the message will be printed even if debugging is
71 # not turned on. This behavior is likely to change and should not be
72 # relied on.
73 #
74 # @param msg [String] the warning message to be printed
75 #
76 # @return [void]
77 def warnonce(msg)
78 if @@warn_messages[msg].nil?
79 self.warn(msg)
80 @@warn_messages[msg] = true
81 end
82 end
83
84 def log_exception(exception, message = :default)
85 self.warn(format_exception(exception, message, @@trace))
86 end
87
88 def format_exception(exception, message, trace)
89 arr = []
90
91 if message == :default
92 arr << exception.message
93 elsif message
94 arr << message
95 end
96
97 if trace
98 arr.concat(exception.backtrace)
99 end
100
101 arr.flatten.join("\n")
102 end
103
104 # Print an exception message, and optionally a backtrace if trace is set
105
106 # Print timing information
107 #
108 # @param string [String] the time to print
109 # @return [void]
110 #
111 # @api private
112 def show_time(string)
113 $stderr.puts "#{GREEN}#{string}#{RESET}" if string and self.timing?
114 end
115
116 # Enable or disable logging of debug messages
117 #
118 # @param bool [true, false]
119 # @return [void]
120 #
121 # @api private
122 def debugging(bool)
123 @@debug = bool
124 end
125
126 # Is debugging enabled?
127 #
128 # @return [true, false]
129 #
130 # @api private
131 def debugging?
132 @@debug
133 end
134
135 # Enable or disable logging of timing information
136 #
137 # @param bool [true, false]
138 # @return [void]
139 #
140 # @api private
141 def timing(bool)
142 @@timing = bool
143 end
144
145 # Returns whether timing output is turned on
146 #
147 # @api private
148 def timing?
149 @@timing
150 end
151
152 def trace(bool)
153 @@trace = bool
154 end
155
156 def trace?
157 @@trace
158 end
159
160 # Clears the seen state of warning messages. See {warnonce}.
161 #
162 # @return [void]
163 #
164 # @api private
165 def clear_messages
166 @@warn_messages.clear
167 end
168 end
0 require 'timeout'
1
2 # The resolvable mixin defines behavior for evaluating and returning fact
3 # resolutions.
4 #
5 # Classes including this mixin should implement at #name method describing
6 # the value being resolved and a #resolve_value that actually executes the code
7 # to resolve the value.
8 module Facter::Core::Resolvable
9
10 # The timeout, in seconds, for evaluating this resolution.
11 # @return [Integer]
12 # @api public
13 attr_accessor :timeout
14
15 # Return the timeout period for resolving a value.
16 # (see #timeout)
17 # @return [Numeric]
18 # @comment requiring 'timeout' stdlib class causes Object#timeout to be
19 # defined which delegates to Timeout.timeout. This method may potentially
20 # overwrite the #timeout attr_reader on this class, so we define #limit to
21 # avoid conflicts.
22 def limit
23 @timeout || 0
24 end
25
26 ##
27 # on_flush accepts a block and executes the block when the resolution's value
28 # is flushed. This makes it possible to model a single, expensive system
29 # call inside of a Ruby object and then define multiple dynamic facts which
30 # resolve by sending messages to the model instance. If one of the dynamic
31 # facts is flushed then it can, in turn, flush the data stored in the model
32 # instance to keep all of the dynamic facts in sync without making multiple,
33 # expensive, system calls.
34 #
35 # Please see the Solaris zones fact for an example of how this feature may be
36 # used.
37 #
38 # @see Facter::Util::Fact#flush
39 # @see Facter::Util::Resolution#flush
40 #
41 # @api public
42 def on_flush(&block)
43 @on_flush_block = block
44 end
45
46 ##
47 # flush executes the block, if any, stored by the {on_flush} method
48 #
49 # @see Facter::Util::Fact#flush
50 # @see Facter::Util::Resolution#on_flush
51 #
52 # @api private
53 def flush
54 @on_flush_block.call if @on_flush_block
55 end
56
57 def value
58 result = nil
59
60 with_timing do
61 Timeout.timeout(limit) do
62 result = resolve_value
63 end
64 end
65
66 Facter::Util::Normalization.normalize(result)
67 rescue Timeout::Error => detail
68 Facter.log_exception(detail, "Timed out after #{limit} seconds while resolving #{qualified_name}")
69 return nil
70 rescue Facter::Util::Normalization::NormalizationError => detail
71 Facter.log_exception(detail, "Fact resolution #{qualified_name} resolved to an invalid value: #{detail.message}")
72 return nil
73 rescue => detail
74 Facter.log_exception(detail, "Could not retrieve #{qualified_name}: #{detail.message}")
75 return nil
76 end
77
78 private
79
80 def with_timing
81 starttime = Time.now.to_f
82
83 yield
84
85 finishtime = Time.now.to_f
86 ms = (finishtime - starttime) * 1000
87 Facter.show_time "#{qualified_name}: #{"%.2f" % ms}ms"
88 end
89
90 def qualified_name
91 "fact='#{@fact.name.to_s}', resolution='#{@name || '<anonymous>'}'"
92 end
93 end
0 require 'facter'
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 Facter::Core::Suitable
8
9 attr_writer :weight
10
11 # Sets the weight of this resolution. If multiple suitable resolutions
12 # are found, the one with the highest weight will be used. If weight
13 # is not given, the number of confines set on a resolution will be
14 # used as its weight (so that the most specific resolution is used).
15 #
16 # @param weight [Integer] the weight of this resolution
17 #
18 # @return [void]
19 #
20 # @api public
21 def has_weight(weight)
22 @weight = weight
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.exist? '/bin/foo' }
73 def confine(confines = nil, &block)
74 case confines
75 when Hash
76 confines.each do |fact, values|
77 @confines.push Facter::Util::Confine.new(fact, *values)
78 end
79 else
80 if block
81 if confines
82 @confines.push Facter::Util::Confine.new(confines, &block)
83 else
84 @confines.push Facter::Util::Confine.new(&block)
85 end
86 else
87 end
88 end
89 end
90
91 # Returns the importance of this resolution. If the weight was not
92 # given, the number of confines is used instead (so that a more
93 # specific resolution wins over a less specific one).
94 #
95 # @return [Integer] the weight of this resolution
96 #
97 # @api private
98 def weight
99 if @weight
100 @weight
101 else
102 @confines.length
103 end
104 end
105
106 # Is this resolution mechanism suitable on the system in question?
107 #
108 # @api private
109 def suitable?
110 unless defined? @suitable
111 @suitable = ! @confines.detect { |confine| ! confine.true? }
112 end
113
114 return @suitable
115 end
116 end
4040 basic_hostname
4141 end
4242
43 if name = Facter::Util::Resolution.exec(hostname_command) \
43 if name = Facter::Core::Execution.exec(hostname_command) \
4444 and name =~ /.*?\.(.+$)/
4545
4646 return_value = $1
47 elsif Facter.value(:kernel) != "windows" and domain = Facter::Util::Resolution.exec('dnsdomainname 2> /dev/null') \
47 elsif Facter.value(:kernel) != "windows" and domain = Facter::Core::Execution.exec('dnsdomainname 2> /dev/null') \
4848 and domain =~ /.+/
4949
5050 return_value = domain
6363 return_value ||= domain
6464 return_value ||= search
6565 end
66 return_value = '' if return_value.nil?
67 return_value.gsub(/\.$/, '')
66
67 if return_value
68 return_value.gsub(/\.$/, '')
69 end
6870 end
6971 end
7072
7274 confine :kernel => :windows
7375 setcode do
7476 require 'facter/util/registry'
75 domain = ""
77
78 domain = nil
7679 regvalue = Facter::Util::Registry.hklm_read('SYSTEM\CurrentControlSet\Services\Tcpip\Parameters', 'Domain')
77 domain = regvalue if regvalue
78 if domain == ""
80
81 if regvalue and not regvalue.empty?
82 domain = regvalue
83 else
7984 require 'facter/util/wmi'
8085 Facter::Util::WMI.execquery("select DNSDomain from Win32_NetworkAdapterConfiguration where IPEnabled = True").each { |nic|
8186 if nic.DNSDomain && nic.DNSDomain.length > 0
8590 }
8691 end
8792
88 domain ||= ''
8993
90 domain.gsub(/\.$/, '')
94 if domain
95 domain.gsub(/\.$/, '')
96 end
9197 end
9298 end
1717 # This is due to some problems with IO#read in Ruby and reading content of
1818 # the "proc" file system that was reported more than once in the past ...
1919 file_systems = []
20 Facter::Util::Resolution.exec('cat /proc/filesystems 2> /dev/null').each_line do |line|
20 Facter::Core::Execution.exec('cat /proc/filesystems 2> /dev/null').each_line do |line|
2121 # Remove bloat ...
2222 line.strip!
2323
1717 Facter.add(:hardwaremodel) do
1818 confine :operatingsystem => :aix
1919 setcode do
20 model = Facter::Util::Resolution.exec('lsattr -El sys0 -a modelname')
20 model = Facter::Core::Execution.exec('lsattr -El sys0 -a modelname')
2121 if model =~ /modelname\s(\S+)\s/
2222 $1
2323 end
1010 # Caveats:
1111 #
1212
13 Facter.add(:hostname, :ldapname => "cn") do
13 Facter.add(:hostname) do
1414 setcode do
1515 hostname = nil
16 if name = Facter::Util::Resolution.exec('hostname')
16 if name = Facter::Core::Execution.execute('hostname')
1717 if name =~ /(.*?)\./
1818 hostname = $1
1919 else
2626
2727 Facter.add(:hostname) do
2828 confine :kernel => :darwin, :kernelrelease => "R7"
29 setcode do
30 Facter::Util::Resolution.exec('/usr/sbin/scutil --get LocalHostName')
31 end
29 setcode '/usr/sbin/scutil --get LocalHostName'
3230 end
1414 #
1515
1616 require 'facter/util/ip'
17 require 'facter/util/macaddress'
1718
1819 # Note that most of this only works on a fixed list of platforms; notably, Darwin
1920 # is missing.
3334 %w{ipaddress ipaddress6 macaddress netmask mtu}.each do |label|
3435 Facter.add(label + "_" + Facter::Util::IP.alphafy(interface)) do
3536 setcode do
36 Facter::Util::IP.get_interface_value(interface, label)
37 value = Facter::Util::IP.get_interface_value(interface, label)
38 if label == "macaddress"
39 value = Facter::Util::Macaddress.standardize(value)
40 end
41 value
3742 end
3843 end
3944 end
120120 end
121121 end
122122
123 Facter.add(:ipaddress, :ldapname => "iphostnumber", :timeout => 2) do
123 Facter.add(:ipaddress, :timeout => 2) do
124124 setcode do
125125 if Facter.value(:kernel) == 'windows'
126126 require 'win32/resolv'
154154 if hostname = Facter.value(:hostname)
155155 # we need Hostname to exist for this to work
156156 host = nil
157 if host = Facter::Util::Resolution.exec("host #{hostname}")
157 if host = Facter::Core::Execution.execute("host #{hostname}")
158158 list = host.chomp.split(/\s/)
159159 if defined? list[-1] and
160160 list[-1] =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/
1515 if Facter::Util::Config.is_windows?
1616 'windows'
1717 else
18 Facter::Util::Resolution.exec("uname -s")
18 Facter::Core::Execution.exec("uname -s")
1919 end
2020 end
2121 end
2222 Facter.add(:kernelrelease) do
2323 confine :kernel => "hp-ux"
2424 setcode do
25 version = Facter::Util::Resolution.exec('uname -r')
25 version = Facter::Core::Execution.execute('uname -r')
2626 version[2..-1]
2727 end
2828 end
0 if Facter.value(:kernel) == 'SunOS'
1 virtinfo = Facter::Util::Resolution.exec('virtinfo -ap')
0 if Facter.value(:kernel) == 'SunOS' and Facter::Core::Execution.which('virtinfo')
1 virtinfo = Facter::Core::Execution.exec('virtinfo -ap')
22
33 # Convert virtinfo parseable output format to array of arrays.
44 # DOMAINROLE|impl=LDoms|control=true|io=true|service=true|root=true
1111
1212 Facter.add(:lsbdistcodename) do
1313 confine :kernel => [ :linux, :"gnu/kfreebsd" ]
14 setcode do
15 Facter::Util::Resolution.exec('lsb_release -c -s 2>/dev/null')
16 end
14 setcode 'lsb_release -c -s 2>/dev/null'
1715 end
1111
1212 Facter.add(:lsbdistdescription) do
1313 confine :kernel => [ :linux, :"gnu/kfreebsd" ]
14 confine do
15 Facter::Core::Execution.which("lsb_release")
16 end
17
1418 setcode do
15 if output = Facter::Util::Resolution.exec('lsb_release -d -s 2>/dev/null')
19 if output = Facter::Core::Execution.exec('lsb_release -d -s 2>/dev/null')
1620 # the output may be quoted (at least it is on gentoo)
1721 output.sub(/^"(.*)"$/,'\1')
1822 end
1111
1212 Facter.add(:lsbdistid) do
1313 confine :kernel => [ :linux, :"gnu/kfreebsd" ]
14 setcode do
15 Facter::Util::Resolution.exec('lsb_release -i -s 2>/dev/null')
16 end
14 setcode 'lsb_release -i -s 2>/dev/null'
1715 end
1111
1212 Facter.add(:lsbdistrelease) do
1313 confine :kernel => [ :linux, :"gnu/kfreebsd" ]
14 setcode do
15 Facter::Util::Resolution.exec('lsb_release -r -s 2>/dev/null')
16 end
14 setcode 'lsb_release -r -s 2>/dev/null'
1715 end
1111
1212 Facter.add(:lsbrelease) do
1313 confine :kernel => [ :linux, :"gnu/kfreebsd" ]
14 setcode do
15 Facter::Util::Resolution.exec('lsb_release -v -s 2>/dev/null')
16 end
14 setcode 'lsb_release -v -s 2>/dev/null'
1715 end
88
99 require 'facter/util/macaddress'
1010 require 'facter/util/ip'
11
12 Facter.add(:macaddress) do
13 confine :kernel => 'Linux'
14 has_weight 10 # about an order of magnitude faster
15 setcode do
16 begin
17 Dir.glob('/sys/class/net/*').reject {|x| x[-3..-1] == '/lo' }.first
18 path and File.read(path + '/address')
19 rescue Exception
20 nil
21 end
22 end
23 end
2411
2512 Facter.add(:macaddress) do
2613 confine :kernel => 'Linux'
5138 confine :osfamily => "Solaris"
5239 setcode do
5340 ether = []
54 output = Facter::Util::Resolution.exec("/usr/bin/netstat -np")
41 output = Facter::Core::Execution.exec("/usr/bin/netstat -np")
5542 output.each_line do |s|
5643 ether.push($1) if s =~ /(?:SPLA)\s+(\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})/
5744 end
2323 # at this point in time.
2424 # In particular, Installed Software might be an interesting addition.
2525
26 require 'facter/util/macosx'
26 if Facter.value(:kernel) == "Darwin"
27 require 'facter/util/macosx'
2728
28 if Facter.value(:kernel) == "Darwin"
2929 Facter::Util::Macosx.hardware_overview.each do |fact, value|
3030 Facter.add("sp_#{fact}") do
3131 confine :kernel => :darwin
8585 Facter.add("SwapEncrypted") do
8686 confine :kernel => :Darwin
8787 setcode do
88 swap = Facter::Util::Resolution.exec('sysctl vm.swapusage')
88 swap = Facter::Core::Execution.exec('sysctl vm.swapusage')
8989 encrypted = false
9090 if swap =~ /\(encrypted\)/ then encrypted = true; end
9191 encrypted
9898 Facter.add("memorysize_mb") do
9999 confine :kernel => :sunos
100100 # Total memory size available from prtconf
101 pconf = Facter::Util::Resolution.exec('/usr/sbin/prtconf 2>/dev/null')
101 pconf = Facter::Core::Execution.exec('/usr/sbin/prtconf 2>/dev/null')
102102 phymem = ""
103103 pconf.each_line do |line|
104104 if line =~ /^Memory size:\s+(\d+) Megabytes/
142142 Facter.add("swapsize_mb") do
143143 confine :kernel => :dragonfly
144144 setcode do
145 page_size = Facter::Util::Resolution.exec("/sbin/sysctl -n hw.pagesize").to_f
146 swaptotal = Facter::Util::Resolution.exec("/sbin/sysctl -n vm.swap_size").to_f * page_size
145 page_size = Facter::Core::Execution.exec("/sbin/sysctl -n hw.pagesize").to_f
146 swaptotal = Facter::Core::Execution.exec("/sbin/sysctl -n vm.swap_size").to_f * page_size
147147 "%.2f" % [(swaptotal.to_f / 1024.0) / 1024.0]
148148 end
149149 end
151151 Facter.add("swapfree_mb") do
152152 confine :kernel => :dragonfly
153153 setcode do
154 page_size = Facter::Util::Resolution.exec("/sbin/sysctl -n hw.pagesize").to_f
155 swaptotal = Facter::Util::Resolution.exec("/sbin/sysctl -n vm.swap_size").to_f * page_size
156 swap_anon_use = Facter::Util::Resolution.exec("/sbin/sysctl -n vm.swap_anon_use").to_f * page_size
157 swap_cache_use = Facter::Util::Resolution.exec("/sbin/sysctl -n vm.swap_cache_use").to_f * page_size
154 page_size = Facter::Core::Execution.exec("/sbin/sysctl -n hw.pagesize").to_f
155 swaptotal = Facter::Core::Execution.exec("/sbin/sysctl -n vm.swap_size").to_f * page_size
156 swap_anon_use = Facter::Core::Execution.exec("/sbin/sysctl -n vm.swap_anon_use").to_f * page_size
157 swap_cache_use = Facter::Core::Execution.exec("/sbin/sysctl -n vm.swap_cache_use").to_f * page_size
158158 swapfree = swaptotal - swap_anon_use - swap_cache_use
159159 "%.2f" % [(swapfree.to_f / 1024.0) / 1024.0]
160160 end
161161 end
162
163 # http://projects.puppetlabs.com/issues/11436
164 #
165 # Unifying naming for the amount of physical memory in a given host.
166 # This fact is DEPRECATED and will be removed in Facter 2.0 per
167 # http://projects.puppetlabs.com/issues/11466
168 Facter.add("MemoryTotal") do
169 setcode do
170 Facter.value("memorysize")
171 end
172 end
1515 setcode do
1616 # Use uname -v because /etc/release can change in zones under SmartOS.
1717 # It's apparently not trustworthy enough to rely on for this fact.
18 output = Facter::Util::Resolution.exec('uname -v')
18 output = Facter::Core::Execution.exec('uname -v')
1919 if output =~ /^joyent_/
2020 "SmartOS"
2121 elsif output =~ /^oi_/
11 #
22 # Purpose: Returns the major release of the operating system.
33 #
4 # Resolution: splits down the operatingsystemrelease fact at decimal point for
5 # osfamily RedHat derivatives and Debian.
4 # Resolution:
5 # Splits down the operatingsystemrelease fact at decimal point for
6 # osfamily RedHat derivatives and Debian.
7 # Uses operatingsystemrelease to the first non decimal character for
8 # operatingsystem Solaris
69 #
710 # This should be the same as lsbmajdistrelease, but on minimal systems there
811 # are too many dependencies to use LSB
1215 #"Debian" "Fedora" "Gentoo" "Mandrake" "Mandriva" "MeeGo" "OEL" "OpenSuSE"
1316 #"OracleLinux" "OVS" "PSBM" "RedHat" "Scientific" "Slackware" "Slamd64" "SLC"
1417 #"SLED" "SLES" "SuSE" "Ubuntu" "VMWareESX"
18
1519 Facter.add(:operatingsystemmajrelease) do
1620 confine :operatingsystem => [
1721 :Amazon,
3034 Facter.value('operatingsystemrelease').split('.').first
3135 end
3236 end
37
38 Facter.add(:operatingsystemmajrelease) do
39 confine :operatingsystem => :solaris
40 setcode do
41 if match = Facter.value(:operatingsystemrelease).match(/^(\d+)/)
42 match.captures[0]
43 end
44 end
45 end
44 # Resolution:
55 # On RedHat derivatives, returns their '/etc/<variant>-release' file.
66 # On Debian, returns '/etc/debian_version'.
7 # On Ubuntu, parses '/etc/issue' for the release version.
7 # On Ubuntu, parses '/etc/lsb-release' for the release version.
88 # On Suse, derivatives, parses '/etc/SuSE-release' for a selection of version
99 # information.
1010 # On Slackware, parses '/etc/slackware-version'.
6060 Facter.add(:operatingsystemrelease) do
6161 confine :operatingsystem => %w{Ubuntu}
6262 setcode do
63 if release = Facter::Util::FileRead.read('/etc/issue')
64 if match = release.match(/Ubuntu ((\d+.\d+)(\.(\d+))?)/)
63 if release = Facter::Util::FileRead.read('/etc/lsb-release')
64 if match = release.match(/DISTRIB_RELEASE=((\d+.\d+)(\.(\d+))?)/)
6565 # Return only the major and minor version numbers. This behavior must
6666 # be preserved for compatibility reasons.
6767 match[2]
140140 Facter.add(:operatingsystemrelease) do
141141 confine :operatingsystem => %w{VMwareESX}
142142 setcode do
143 release = Facter::Util::Resolution.exec('vmware -v')
143 release = Facter::Core::Execution.exec('vmware -v')
144144 if match = /VMware ESX .*?(\d.*)/.match(release)
145145 match[1]
146146 end
180180 setcode do
181181 if release = Facter::Util::FileRead.read('/etc/release')
182182 line = release.split("\n").first.chomp
183 # Solaris 10: Solaris 10 10/09 s10x_u8wos_08a X86
184 # Solaris 11 (old naming scheme): Oracle Solaris 11 11/11 X86
185 # Solaris 11 (new naming scheme): Oracle Solaris 11.1 SPARC
183186 if match = /\s+s(\d+)[sx]?(_u\d+)?.*(?:SPARC|X86)/.match(line)
184187 match.captures.join('')
185 end
186 end
188 elsif match = /Solaris ([0-9\.]+(?:\s*[0-9\.\/]+))\s*(?:SPARC|X86)/.match(line)
189 match.captures[0]
190 end
191 end
192 end
193 end
194
195 Facter.add(:operatingsystemrelease) do
196 confine :operatingsystem => :windows
197 setcode do
198 require 'facter/util/wmi'
199 result = nil
200 Facter::Util::WMI.execquery("SELECT version, producttype FROM Win32_OperatingSystem").each do |os|
201 result =
202 case os.version
203 when /^6\.2/
204 os.producttype == 1 ? "8" : "2012"
205 when /^6\.1/
206 os.producttype == 1 ? "7" : "2008 R2"
207 when /^6\.0/
208 os.producttype == 1 ? "Vista" : "2008"
209 when /^5\.2/
210 if os.producttype == 1
211 "XP"
212 else
213 begin
214 os.othertypedescription == "R2" ? "2003 R2" : "2003"
215 rescue NoMethodError
216 "2003"
217 end
218 end
219 else
220 Facter[:kernelrelease].value
221 end
222 break
223 end
224 result
187225 end
188226 end
189227
3838 lookup_pattern = "#{sysfs_cpu_directory}" +
3939 "/cpu*/topology/physical_package_id"
4040
41 Dir.glob(lookup_pattern).collect { |f| Facter::Util::Resolution.exec("cat #{f}")}.uniq.size
41 Dir.glob(lookup_pattern).collect { |f| Facter::Core::Execution.exec("cat #{f}")}.uniq.size.to_s
4242
4343 else
4444 #
4747 # We assume that /proc/cpuinfo has what we need and is so then we need
4848 # to make sure that we only count unique entries ...
4949 #
50 str = Facter::Util::Resolution.exec("grep 'physical.\\+:' /proc/cpuinfo")
50 str = Facter::Core::Execution.exec("grep 'physical.\\+:' /proc/cpuinfo")
5151
52 if str then str.scan(/\d+/).uniq.size; end
52 if str then str.scan(/\d+/).uniq.size.to_s; end
5353 end
5454 end
5555 end
5858 confine :kernel => :windows
5959 setcode do
6060 require 'facter/util/wmi'
61 Facter::Util::WMI.execquery("select Name from Win32_Processor").Count
61 Facter::Util::WMI.execquery("select Name from Win32_Processor").Count.to_s
6262 end
6363 end
6464
6565 Facter.add('physicalprocessorcount') do
6666 confine :kernel => :sunos
6767
68 # (#16526) The -p flag was not added until Solaris 8. Our intent is to
69 # split the kernel release fact value on the dot, take the second element,
70 # and disable the -p flag for values < 8 when.
6871 setcode do
69 # (#16526) The -p flag was not added until Solaris 8. Our intent is to
70 # split the kernel release fact value on the dot, take the second element,
71 # and disable the -p flag for values < 8 when.
7272 kernelrelease = Facter.value(:kernelrelease)
7373 (major_version, minor_version) = kernelrelease.split(".").map { |str| str.to_i }
74 cmd = "/usr/sbin/psrinfo"
75 result = nil
7674 if (major_version > 5) or (major_version == 5 and minor_version >= 8) then
77 result = Facter::Util::Resolution.exec("#{cmd} -p")
75 Facter::Core::Execution.exec("/usr/sbin/psrinfo -p")
7876 else
79 output = Facter::Util::Resolution.exec(cmd)
80 result = output.split("\n").length.to_s
77 output = Facter::Core::Execution.exec("/usr/sbin/psrinfo")
78 output.split("\n").length.to_s
8179 end
8280 end
8381 end
2121 require 'thread'
2222 require 'facter/util/processor'
2323
24 ## We have to enumerate these outside a Facter.add block to get the processorN descriptions iteratively
25 ## (but we need them inside the Facter.add block above for tests on processorcount to work)
24 # We have to enumerate these outside a Facter.add block to get the processorN
25 # descriptions iteratively (but we need them inside the Facter.add block above
26 # for tests on processorcount to work)
2627 processor_list = case Facter::Util::Processor.kernel_fact_value
2728 when "AIX"
2829 Facter::Util::Processor.aix_processor_list
8889
8990 Facter.add("Processor") do
9091 confine :kernel => :openbsd
91 setcode do
92 Facter::Util::Resolution.exec("uname -p")
93 end
94 end
95
96 Facter.add("ProcessorCount") do
97 confine :kernel => :openbsd
98 setcode do
99 Facter::Util::Resolution.exec("sysctl -n hw.ncpu")
100 end
92 setcode "uname -p"
10193 end
10294
10395 Facter.add("ProcessorCount") do
10496 confine :kernel => :Darwin
105 setcode do
106 Facter::Util::Resolution.exec("sysctl -n hw.ncpu")
107 end
97 setcode "sysctl -n hw.ncpu"
10898 end
10999
110100 if Facter.value(:kernel) == "windows"
147137
148138 Facter.add("Processor") do
149139 confine :kernel => [:dragonfly,:freebsd]
150 setcode do
151 Facter::Util::Resolution.exec("sysctl -n hw.model")
152 end
140 setcode "sysctl -n hw.model"
153141 end
154142
155143 Facter.add("ProcessorCount") do
156 confine :kernel => [:dragonfly,:freebsd]
157 setcode do
158 Facter::Util::Resolution.exec("sysctl -n hw.ncpu")
159 end
144 confine :kernel => [:dragonfly,:freebsd,:openbsd]
145 setcode "sysctl -n hw.ncpu"
160146 end
161147
162148 Facter.add("ProcessorCount") do
167153 result = nil
168154
169155 if (major_version > 5) or (major_version == 5 and minor_version >= 8) then
170 if kstat = Facter::Util::Resolution.exec("/usr/bin/kstat cpu_info")
156 if kstat = Facter::Core::Execution.exec("/usr/bin/kstat cpu_info")
171157 result = kstat.scan(/\bcore_id\b\s+\d+/).uniq.length
172158 end
173159 else
174 if output = Facter::Util::Resolution.exec("/usr/sbin/psrinfo") then
160 if output = Facter::Core::Execution.exec("/usr/sbin/psrinfo") then
175161 result = output.split("\n").length
176162 end
177163 end
178164
179 result
165 result.to_s
180166 end
181167 end
2121 # a hang. Reading from other parts of /proc does not seem to cause this problem.
2222 # The work around is to read the file in another process.
2323 # -- andy Fri Aug 31 2012
24 selinux_line = Facter::Util::Resolution.exec('cat /proc/self/mounts').lines.find { |line| line =~ /selinuxfs/ }
24 selinux_line = Facter::Core::Execution.exec('cat /proc/self/mounts').each_line.find { |line| line =~ /selinuxfs/ }
2525 if selinux_line
2626 path = selinux_line.split[1]
2727 end
7171 confine :selinux => :true
7272 setcode do
7373 result = 'unknown'
74 mode = Facter::Util::Resolution.exec(sestatus_cmd)
74 mode = Facter::Core::Execution.exec(sestatus_cmd)
7575 mode.each_line { |l| result = $1 if l =~ /^Current mode\:\s+(\w+)$/i }
7676 result.chomp
7777 end
8181 confine :selinux => :true
8282 setcode do
8383 result = 'unknown'
84 mode = Facter::Util::Resolution.exec(sestatus_cmd)
84 mode = Facter::Core::Execution.exec(sestatus_cmd)
8585 mode.each_line { |l| result = $1 if l =~ /^Mode from config file\:\s+(\w+)$/i }
8686 result.chomp
8787 end
9191 confine :selinux => :true
9292 setcode do
9393 result = 'unknown'
94 mode = Facter::Util::Resolution.exec(sestatus_cmd)
94 mode = Facter::Core::Execution.exec(sestatus_cmd)
9595 mode.each_line { |l| result = $1 if l =~ /^Policy from config file\:\s+(\w+)$/i }
9696 result.chomp
9797 end
9898 end
99
100 # This is a legacy fact which returns the old selinux_mode fact value to prevent
101 # breakages of existing manifests. It should be removed at the next major release.
102 # See ticket #6677.
103
104 Facter.add("selinux_mode") do
105 confine :selinux => :true
106 setcode do
107 Facter.value(:selinux_config_policy)
108 end
109 end
1010 ## Facts related to SSH
1111 ##
1212
13 {"SSHDSAKey" => { :file => "ssh_host_dsa_key.pub", :sshfprrtype => 2 } , "SSHRSAKey" => { :file => "ssh_host_rsa_key.pub", :sshfprrtype => 1 }, "SSHECDSAKey" => { :file => "ssh_host_ecdsa_key.pub", :sshfprrtype => 3 } }.each do |name,key|
14
13 {"SSHDSAKey" => { :file => "ssh_host_dsa_key.pub", :sshfprrtype => 2 },
14 "SSHRSAKey" => { :file => "ssh_host_rsa_key.pub", :sshfprrtype => 1 },
15 "SSHECDSAKey" => { :file => "ssh_host_ecdsa_key.pub", :sshfprrtype => 3 },
16 "SSHED25519Key" => { :file => "ssh_host_ed25519_key.pub", :sshfprrtype => 4 } }.each do |name,key|
17
1518 Facter.add(name) do
1619 setcode do
1720 value = nil
0 # A module to help test architecture facts on non-AIX test hardware
1
2 module Facter::Util::Architecture
3 ##
4 # lsattr is intended to directly delegate to Facter::Core::Execution.exec in
5 # an effort to make the processorX facts easier to test. See also the
6 # {lsdev} method.
7 def self.lsattr(command="lsattr -El proc0 -a type")
8 Facter::Core::Execution.exec(command)
9 end
10
11 ##
12 # kernel_fact_value is intended to directly delegate to Facter.value(:kernel)
13 # to make it easier to stub the kernel fact without affecting the entire
14 # system.
15 def self.kernel_fact_value
16 Facter.value(:kernel)
17 end
18 end
+0
-19
lib/facter/util/cfpropertylist/LICENSE less more
0 Copyright (c) 2010 Christian Kruse, <cjk@wwwtech.de>
1
2 Permission is hereby granted, free of charge, to any person obtaining a
3 copy of this software and associated documentation files (the
4 "Software"), to deal in the Software without restriction, including
5 without limitation the rights to use, copy, modify, merge, publish,
6 distribute, sublicense, and/or sell copies of the Software, and to
7 permit persons to whom the Software is furnished to do so, subject to
8 the following conditions:
9 The above copyright notice and this permission notice shall be included
10 in all copies or substantial portions of the Software.
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
12 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
15 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
16 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
+0
-44
lib/facter/util/cfpropertylist/README less more
0 CFPropertyList implementation
1 class to read, manipulate and write both XML and binary property list
2 files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
3 for more documentation.
4
5 == Installation
6
7 You could either use ruby gems and install it via
8
9 gem install CFPropertyList
10
11 or you could clone this repository and place it somewhere in your load path.
12
13 == Example
14 require 'cfpropertylist'
15
16 # create a arbitrary data structure of basic data types
17 data = {
18 'name' => 'John Doe',
19 'missing' => true,
20 'last_seen' => Time.now,
21 'friends' => ['Jane Doe','Julian Doe'],
22 'likes' => {
23 'me' => false
24 }
25 }
26
27 # create CFPropertyList::List object
28 plist = CFPropertyList::List.new
29
30 # call CFPropertyList.guess() to create corresponding CFType values
31 plist.value = CFPropertyList.guess(data)
32
33 # write plist to file
34 plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
35
36 # … later, read it again
37 plist = CFPropertyList::List.new(:file => "example.plist")
38 data = CFPropertyList.native_types(plist.value)
39
40 Author:: Christian Kruse (mailto:cjk@wwwtech.de)
41 Copyright:: Copyright (c) 2010
42 License:: MIT License
43
+0
-44
lib/facter/util/cfpropertylist/Rakefile less more
0 require 'rubygems'
1
2 require 'rubygems/package_task'
3 require 'rdoc/task'
4 require 'rake/testtask'
5
6 spec = Gem::Specification.new do |s|
7 s.name = "CFPropertyList"
8 s.version = "2.1"
9 s.author = "Christian Kruse"
10 s.email = "cjk@wwwtech.de"
11 s.homepage = "http://github.com/ckruse/CFPropertyList"
12 s.platform = Gem::Platform::RUBY
13 s.summary = "Read, write and manipulate both binary and XML property lists as defined by apple"
14 s.description = "This is a module to read, write and manipulate both binary and XML property lists as defined by apple."
15 s.files = FileList["lib/*"].to_a
16 s.require_path = "lib"
17 #s.autorequire = "name"
18 #s.test_files = FileList["{test}/**/*test.rb"].to_a
19 s.has_rdoc = true
20 s.extra_rdoc_files = ["README"]
21 s.add_development_dependency("rake",">=0.7.0")
22 end
23
24 desc 'Generate RDoc documentation for the CFPropertyList module.'
25 Rake::RDocTask.new do |rdoc|
26 files = ['README', 'LICENSE', 'lib/*.rb']
27 rdoc.rdoc_files.add(files)
28 rdoc.main = 'README'
29 rdoc.title = 'CFPropertyList RDoc'
30 rdoc.rdoc_dir = 'doc'
31 rdoc.options << '--line-numbers' << '--inline-source' << '-c utf8'
32 end
33
34 Gem::PackageTask.new(spec) do |pkg|
35 pkg.need_tar = true
36 end
37
38 Rake::TestTask.new do |test|
39 test.libs << 'test'
40 test.test_files = Dir.glob('test/test*.rb')
41 end
42
43 # eof
+0
-7
lib/facter/util/cfpropertylist/THANKS less more
0 Special thanks to:
1
2 Steve Madsen for providing a lot of performance patches and bugfixes!
3 Have a look at his Github account: <http://github.com/sjmadsen>
4
5
6
+0
-6
lib/facter/util/cfpropertylist/lib/cfpropertylist.rb less more
0 # -*- coding: utf-8 -*-
1
2 require File.dirname(__FILE__) + '/rbCFPropertyList.rb'
3
4
5 # eof
+0
-562
lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb less more
0 # -*- coding: utf-8 -*-
1
2 module Facter::Util::CFPropertyList
3 # Binary PList parser class
4 class Binary
5 # Read a binary plist file
6 def load(opts)
7 @unique_table = {}
8 @count_objects = 0
9 @object_refs = 0
10
11 @written_object_count = 0
12 @object_table = []
13 @object_ref_size = 0
14
15 @offsets = []
16
17 fd = nil
18 if(opts.has_key?(:file))
19 fd = File.open(opts[:file],"rb")
20 file = opts[:file]
21 else
22 fd = StringIO.new(opts[:data],"rb")
23 file = "<string>"
24 end
25
26 # first, we read the trailer: 32 byte from the end
27 fd.seek(-32,IO::SEEK_END)
28 buff = fd.read(32)
29
30 offset_size, object_ref_size, number_of_objects, top_object, table_offset = buff.unpack "x6CCx4Nx4Nx4N"
31
32 # after that, get the offset table
33 fd.seek(table_offset, IO::SEEK_SET)
34 coded_offset_table = fd.read(number_of_objects * offset_size)
35 raise CFFormatError.new("#{file}: Format error!") unless coded_offset_table.bytesize == number_of_objects * offset_size
36
37 @count_objects = number_of_objects
38
39 # decode offset table
40 formats = ["","C*","n*","(H6)*","N*"]
41 @offsets = coded_offset_table.unpack(formats[offset_size])
42 if(offset_size == 3)
43 0.upto(@offsets.size-1) { |i| @offsets[i] = @offsets[i].to_i(16) }
44 end
45
46 @object_ref_size = object_ref_size
47 val = read_binary_object_at(file,fd,top_object)
48
49 fd.close
50 val
51 end
52
53
54 # Convert Facter::Util::CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray
55 def to_str(opts={})
56 @unique_table = {}
57 @count_objects = 0
58 @object_refs = 0
59
60 @written_object_count = 0
61 @object_table = []
62
63 @offsets = []
64
65 binary_str = "bplist00"
66
67 @object_refs = count_object_refs(opts[:root])
68
69 opts[:root].to_binary(self)
70
71 next_offset = 8
72 offsets = @object_table.map do |object|
73 offset = next_offset
74 next_offset += object.bytesize
75 offset
76 end
77 binary_str << @object_table.join
78
79 table_offset = next_offset
80 offset_size = Binary.bytes_needed(table_offset)
81
82 if offset_size < 8
83 # Fast path: encode the entire offset array at once.
84 binary_str << offsets.pack((%w(C n N N)[offset_size - 1]) + '*')
85 else
86 # Slow path: host may be little or big endian, must pack each offset
87 # separately.
88 offsets.each do |offset|
89 binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
90 end
91 end
92
93 binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
94 binary_str << [@object_table.size].pack("x4N")
95 binary_str << [0].pack("x4N")
96 binary_str << [table_offset].pack("x4N")
97
98 binary_str
99 end
100
101 def object_ref_size object_refs
102 Binary.bytes_needed(object_refs)
103 end
104
105 # read a „null” type (i.e. null byte, marker byte, bool value)
106 def read_binary_null_type(length)
107 case length
108 when 0 then 0 # null byte
109 when 8 then CFBoolean.new(false)
110 when 9 then CFBoolean.new(true)
111 when 15 then 15 # fill type
112 else
113 raise CFFormatError.new("unknown null type: #{length}")
114 end
115 end
116 protected :read_binary_null_type
117
118 # read a binary int value
119 def read_binary_int(fname,fd,length)
120 if length > 3
121 raise CFFormatError.new("Integer greater than 8 bytes: #{length}")
122 end
123
124 nbytes = 1 << length
125
126 buff = fd.read(nbytes)
127
128 CFInteger.new(
129 case length
130 when 0 then buff.unpack("C")[0]
131 when 1 then buff.unpack("n")[0]
132 when 2 then buff.unpack("N")[0]
133 when 3
134 hiword,loword = buff.unpack("NN")
135 if (hiword & 0x80000000) != 0
136 # 8 byte integers are always signed, and are negative when bit 63 is
137 # set. Decoding into either a Fixnum or Bignum is tricky, however,
138 # because the size of a Fixnum varies among systems, and Ruby
139 # doesn't consider the number to be negative, and won't sign extend.
140 -(2**63 - ((hiword & 0x7fffffff) << 32 | loword))
141 else
142 hiword << 32 | loword
143 end
144 end
145 )
146 end
147 protected :read_binary_int
148
149 # read a binary real value
150 def read_binary_real(fname,fd,length)
151 raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
152
153 nbytes = 1 << length
154 buff = fd.read(nbytes)
155
156 CFReal.new(
157 case length
158 when 0 # 1 byte float? must be an error
159 raise CFFormatError.new("got #{length+1} byte float, must be an error!")
160 when 1 # 2 byte float? must be an error
161 raise CFFormatError.new("got #{length+1} byte float, must be an error!")
162 when 2 then
163 buff.reverse.unpack("f")[0]
164 when 3 then
165 buff.reverse.unpack("d")[0]
166 else
167 fail "unexpected length: #{length}"
168 end
169 )
170 end
171 protected :read_binary_real
172
173 # read a binary date value
174 def read_binary_date(fname,fd,length)
175 raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
176
177 nbytes = 1 << length
178 buff = fd.read(nbytes)
179
180 CFDate.new(
181 case length
182 when 0 then # 1 byte CFDate is an error
183 raise CFFormatError.new("#{length+1} byte CFDate, error")
184 when 1 then # 2 byte CFDate is an error
185 raise CFFormatError.new("#{length+1} byte CFDate, error")
186 when 2 then
187 buff.reverse.unpack("f")[0]
188 when 3 then
189 buff.reverse.unpack("d")[0]
190 end,
191 CFDate::TIMESTAMP_APPLE
192 )
193 end
194 protected :read_binary_date
195
196 # Read a binary data value
197 def read_binary_data(fname,fd,length)
198 CFData.new(read_fd(fd, length), CFData::DATA_RAW)
199 end
200 protected :read_binary_data
201
202 def read_fd fd, length
203 length > 0 ? fd.read(length) : ""
204 end
205
206 # Read a binary string value
207 def read_binary_string(fname,fd,length)
208 buff = read_fd fd, length
209 @unique_table[buff] = true unless @unique_table.has_key?(buff)
210 CFString.new(buff)
211 end
212 protected :read_binary_string
213
214 # Convert the given string from one charset to another
215 def Binary.charset_convert(str,from,to="UTF-8")
216 return str.clone.force_encoding(from).encode(to) if str.respond_to?("encode")
217 Iconv.conv(to,from,str)
218 end
219
220 # Count characters considering character set
221 def Binary.charset_strlen(str,charset="UTF-8")
222 if str.respond_to?(:encode)
223 size = str.length
224 else
225 utf8_str = Iconv.conv("UTF-8",charset,str)
226 size = utf8_str.scan(/./mu).size
227 end
228
229 # UTF-16 code units in the range D800-DBFF are the beginning of
230 # a surrogate pair, and count as one additional character for
231 # length calculation.
232 if charset =~ /^UTF-16/
233 if str.respond_to?(:encode)
234 str.bytes.to_a.each_slice(2) { |pair| size += 1 if (0xd8..0xdb).include?(pair[0]) }
235 else
236 str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
237 end
238 end
239
240 size
241 end
242
243 # Read a unicode string value, coded as UTF-16BE
244 def read_binary_unicode_string(fname,fd,length)
245 # The problem is: we get the length of the string IN CHARACTERS;
246 # since a char in UTF-16 can be 16 or 32 bit long, we don't really know
247 # how long the string is in bytes
248 buff = fd.read(2*length)
249
250 @unique_table[buff] = true unless @unique_table.has_key?(buff)
251 CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
252 end
253 protected :read_binary_unicode_string
254
255 # Read an binary array value, including contained objects
256 def read_binary_array(fname,fd,length)
257 ary = []
258
259 # first: read object refs
260 if(length != 0)
261 buff = fd.read(length * @object_ref_size)
262 objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
263
264 # now: read objects
265 0.upto(length-1) do |i|
266 object = read_binary_object_at(fname,fd,objects[i])
267 ary.push object
268 end
269 end
270
271 CFArray.new(ary)
272 end
273 protected :read_binary_array
274
275 # Read a dictionary value, including contained objects
276 def read_binary_dict(fname,fd,length)
277 dict = {}
278
279 # first: read keys
280 if(length != 0) then
281 buff = fd.read(length * @object_ref_size)
282 keys = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
283
284 # second: read object refs
285 buff = fd.read(length * @object_ref_size)
286 objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
287
288 # read real keys and objects
289 0.upto(length-1) do |i|
290 key = read_binary_object_at(fname,fd,keys[i])
291 object = read_binary_object_at(fname,fd,objects[i])
292 dict[key.value] = object
293 end
294 end
295
296 CFDictionary.new(dict)
297 end
298 protected :read_binary_dict
299
300 # Read an object type byte, decode it and delegate to the correct reader function
301 def read_binary_object(fname,fd)
302 # first: read the marker byte
303 buff = fd.read(1)
304
305 object_length = buff.unpack("C*")
306 object_length = object_length[0] & 0xF
307
308 buff = buff.unpack("H*")
309 object_type = buff[0][0].chr
310
311 if(object_type != "0" && object_length == 15) then
312 object_length = read_binary_object(fname,fd)
313 object_length = object_length.value
314 end
315
316 case object_type
317 when '0' # null, false, true, fillbyte
318 read_binary_null_type(object_length)
319 when '1' # integer
320 read_binary_int(fname,fd,object_length)
321 when '2' # real
322 read_binary_real(fname,fd,object_length)
323 when '3' # date
324 read_binary_date(fname,fd,object_length)
325 when '4' # data
326 read_binary_data(fname,fd,object_length)
327 when '5' # byte string, usually utf8 encoded
328 read_binary_string(fname,fd,object_length)
329 when '6' # unicode string (utf16be)
330 read_binary_unicode_string(fname,fd,object_length)
331 when 'a' # array
332 read_binary_array(fname,fd,object_length)
333 when 'd' # dictionary
334 read_binary_dict(fname,fd,object_length)
335 end
336 end
337 protected :read_binary_object
338
339 # Read an object type byte at position $pos, decode it and delegate to the correct reader function
340 def read_binary_object_at(fname,fd,pos)
341 position = @offsets[pos]
342 fd.seek(position,IO::SEEK_SET)
343 read_binary_object(fname,fd)
344 end
345 protected :read_binary_object_at
346
347 # pack an +int+ of +nbytes+ with size
348 def Binary.pack_it_with_size(nbytes,int)
349 case nbytes
350 when 1 then [int].pack('c')
351 when 2 then [int].pack('n')
352 when 4 then [int].pack('N')
353 when 8
354 [int >> 32, int & 0xFFFFFFFF].pack('NN')
355 else
356 raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
357 end
358 end
359
360 def Binary.pack_int_array_with_size(nbytes, array)
361 case nbytes
362 when 1 then array.pack('C*')
363 when 2 then array.pack('n*')
364 when 4 then array.pack('N*')
365 when 8
366 array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
367 else
368 raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
369 end
370 end
371
372 # calculate how many bytes are needed to save +count+
373 def Binary.bytes_needed(count)
374 case
375 when count < 2**8 then 1
376 when count < 2**16 then 2
377 when count < 2**32 then 4
378 when count < 2**64 then 8
379 else
380 raise CFFormatError.new("Data size too large: #{count}")
381 end
382 end
383
384 # Create a type byte for binary format as defined by apple
385 def Binary.type_bytes(type, length)
386 if length < 15
387 [(type << 4) | length].pack('C')
388 else
389 bytes = [(type << 4) | 0xF]
390 if length <= 0xFF
391 bytes.push(0x10, length).pack('CCC') # 1 byte length
392 elsif length <= 0xFFFF
393 bytes.push(0x11, length).pack('CCn') # 2 byte length
394 elsif length <= 0xFFFFFFFF
395 bytes.push(0x12, length).pack('CCN') # 4 byte length
396 elsif length <= 0x7FFFFFFFFFFFFFFF
397 bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
398 else
399 raise CFFormatError.new("Integer too large: #{int}")
400 end
401 end
402 end
403
404 def count_object_refs(object)
405 case object
406 when CFArray
407 contained_refs = 0
408 object.value.each do |element|
409 if CFArray === element || CFDictionary === element
410 contained_refs += count_object_refs(element)
411 end
412 end
413 return object.value.size + contained_refs
414 when CFDictionary
415 contained_refs = 0
416 object.value.each_value do |value|
417 if CFArray === value || CFDictionary === value
418 contained_refs += count_object_refs(value)
419 end
420 end
421 return object.value.keys.size * 2 + contained_refs
422 else
423 return 0
424 end
425 end
426
427 def Binary.ascii_string?(str)
428 if str.respond_to?(:ascii_only?)
429 str.ascii_only?
430 else
431 str !~ /[\x80-\xFF]/mn
432 end
433 end
434
435 # Uniques and transforms a string value to binary format and adds it to the object table
436 def string_to_binary(val)
437 val = val.to_s
438
439 @unique_table[val] ||= begin
440 if !Binary.ascii_string?(val)
441 utf8_strlen = Binary.charset_strlen(val, "UTF-8")
442 val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
443 bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
444
445 val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
446 @object_table[@written_object_count] = bdata << val
447 else
448 utf8_strlen = val.bytesize
449 bdata = Binary.type_bytes(0b0101,val.bytesize)
450 @object_table[@written_object_count] = bdata << val
451 end
452 @written_object_count += 1
453 @written_object_count - 1
454 end
455 end
456
457 # Codes an integer to binary format
458 def int_to_binary(value)
459 nbytes = 0
460 nbytes = 1 if value > 0xFF # 1 byte integer
461 nbytes += 1 if value > 0xFFFF # 4 byte integer
462 nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
463 nbytes = 3 if value < 0 # 8 byte integer, since signed
464
465 Binary.type_bytes(0b0001, nbytes) <<
466 if nbytes < 3
467 [value].pack(
468 if nbytes == 0 then "C"
469 elsif nbytes == 1 then "n"
470 else "N"
471 end
472 )
473 else
474 # 64 bit signed integer; we need the higher and the lower 32 bit of the value
475 high_word = value >> 32
476 low_word = value & 0xFFFFFFFF
477 [high_word,low_word].pack("NN")
478 end
479 end
480
481 # Codes a real value to binary format
482 def real_to_binary(val)
483 Binary.type_bytes(0b0010,3) << [val].pack("d").reverse
484 end
485
486 # Converts a numeric value to binary and adds it to the object table
487 def num_to_binary(value)
488 @object_table[@written_object_count] =
489 if value.is_a?(CFInteger)
490 int_to_binary(value.value)
491 else
492 real_to_binary(value.value)
493 end
494
495 @written_object_count += 1
496 @written_object_count - 1
497 end
498
499 # Convert date value (apple format) to binary and adds it to the object table
500 def date_to_binary(val)
501 val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
502
503 @object_table[@written_object_count] =
504 (Binary.type_bytes(0b0011, 3) << [val].pack("d").reverse)
505
506 @written_object_count += 1
507 @written_object_count - 1
508 end
509
510 # Convert a bool value to binary and add it to the object table
511 def bool_to_binary(val)
512
513 @object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
514 @written_object_count += 1
515 @written_object_count - 1
516 end
517
518 # Convert data value to binary format and add it to the object table
519 def data_to_binary(val)
520 @object_table[@written_object_count] =
521 (Binary.type_bytes(0b0100, val.bytesize) << val)
522
523 @written_object_count += 1
524 @written_object_count - 1
525 end
526
527 # Convert array to binary format and add it to the object table
528 def array_to_binary(val)
529 saved_object_count = @written_object_count
530 @written_object_count += 1
531 #@object_refs += val.value.size
532
533 values = val.value.map { |v| v.to_binary(self) }
534 bdata = Binary.type_bytes(0b1010, val.value.size) <<
535 Binary.pack_int_array_with_size(object_ref_size(@object_refs),
536 values)
537
538 @object_table[saved_object_count] = bdata
539 saved_object_count
540 end
541
542 # Convert dictionary to binary format and add it to the object table
543 def dict_to_binary(val)
544 saved_object_count = @written_object_count
545 @written_object_count += 1
546
547 #@object_refs += val.value.keys.size * 2
548
549 keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
550 keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
551
552 bdata = Binary.type_bytes(0b1101,val.value.size) <<
553 Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
554
555 @object_table[saved_object_count] = bdata
556 return saved_object_count
557 end
558 end
559 end
560
561 # eof
+0
-26
lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb less more
0 # -*- coding: utf-8 -*-
1 #
2 # Exceptions used:
3 # CFPlistError:: General base exception
4 # CFFormatError:: Format error
5 # CFTypeError:: Type error
6 #
7 # Easy and simple :-)
8 #
9 # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
10 # Copyright:: Copyright (c) 2010
11 # License:: MIT License
12
13 # general plist error. All exceptions thrown are derived from this class.
14 class CFPlistError < Exception
15 end
16
17 # Exception thrown when format errors occur
18 class CFFormatError < CFPlistError
19 end
20
21 # Exception thrown when type errors occur
22 class CFTypeError < CFPlistError
23 end
24
25 # eof
+0
-402
lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb less more
0 # -*- coding: utf-8 -*-
1
2 require 'kconv'
3 require 'date'
4 require 'time'
5
6 #
7 # Facter::Util::CFPropertyList implementation
8 #
9 # class to read, manipulate and write both XML and binary property list
10 # files (plist(5)) as defined by Apple. Have a look at Facter::Util::CFPropertyList::List
11 # for more documentation.
12 #
13 # == Example
14 # require 'cfpropertylist'
15 #
16 # # create a arbitrary data structure of basic data types
17 # data = {
18 # 'name' => 'John Doe',
19 # 'missing' => true,
20 # 'last_seen' => Time.now,
21 # 'friends' => ['Jane Doe','Julian Doe'],
22 # 'likes' => {
23 # 'me' => false
24 # }
25 # }
26 #
27 # # create Facter::Util::CFPropertyList::List object
28 # plist = Facter::Util::CFPropertyList::List.new
29 #
30 # # call Facter::Util::CFPropertyList.guess() to create corresponding CFType values
31 # # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
32 # plist.value = Facter::Util::CFPropertyList.guess(data)
33 #
34 # # write plist to file
35 # plist.save("example.plist", Facter::Util::CFPropertyList::List::FORMAT_BINARY)
36 #
37 # # … later, read it again
38 # plist = Facter::Util::CFPropertyList::List.new(:file => "example.plist")
39 # data = Facter::Util::CFPropertyList.native_types(plist.value)
40 #
41 # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
42 # Copyright:: Copyright (c) 2010
43 # License:: MIT License
44 module Facter::Util::CFPropertyList
45 # interface class for PList parsers
46 class ParserInterface
47 # load a plist
48 def load(opts={})
49 return ""
50 end
51
52 # convert a plist to string
53 def to_str(opts={})
54 return true
55 end
56 end
57
58 class XMLParserInterface < ParserInterface
59 def new_node(name)
60 end
61
62 def new_text(val)
63 end
64
65 def append_node(parent, child)
66 end
67 end
68 end
69
70 class String
71 unless("".respond_to?(:blob) && "".respond_to?(:blob=)) then
72 # The blob status of this string (to set to true if a binary string)
73 attr_accessor :blob
74 end
75
76 unless("".respond_to?(:blob?)) then
77 # Returns whether or not +str+ is a blob.
78 # @return [true,false] If true, this string contains binary data. If false, its a regular string
79 def blob?
80 blob
81 end
82 end
83
84 unless("".respond_to?(:bytesize)) then
85 def bytesize
86 self.length
87 end
88 end
89 end
90
91 dirname = File.dirname(__FILE__)
92 require dirname + '/rbCFPlistError.rb'
93 require dirname + '/rbCFTypes.rb'
94 require dirname + '/rbBinaryCFPropertyList.rb'
95
96 require 'iconv' unless "".respond_to?("encode")
97
98 begin
99 Enumerable::Enumerator.new([])
100 rescue NameError => e
101 module Enumerable
102 class Enumerator
103 end
104 end
105 end
106
107 begin
108 require dirname + '/rbLibXMLParser.rb'
109 try_nokogiri = false
110 rescue LoadError => e
111 try_nokogiri = true
112 end
113
114 if try_nokogiri then
115 begin
116 require dirname + '/rbNokogiriParser.rb'
117 rescue LoadError => e
118 require dirname + '/rbREXMLParser.rb'
119 end
120 end
121
122
123 module Facter::Util::CFPropertyList
124 # Create CFType hierarchy by guessing the correct CFType, e.g.
125 #
126 # x = {
127 # 'a' => ['b','c','d']
128 # }
129 # cftypes = Facter::Util::CFPropertyList.guess(x)
130 #
131 # pass optional options hash. Only possible value actually:
132 # +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
133 # +converter_method+:: Convert unknown objects to known objects calling +method_name+
134 #
135 # cftypes = Facter::Util::CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash, :converter_with_opts => true)
136 def guess(object, options = {})
137 case object
138 when Fixnum, Integer then CFInteger.new(object)
139 when Float then CFReal.new(object)
140 when TrueClass, FalseClass then CFBoolean.new(object)
141
142 when String
143 object.blob? ? CFData.new(object, CFData::DATA_RAW) : CFString.new(object)
144
145 when Time, DateTime, Date then CFDate.new(object)
146
147 when Array, Enumerator, Enumerable::Enumerator
148 ary = Array.new
149 object.each do |o|
150 ary.push Facter::Util::CFPropertyList.guess(o, options)
151 end
152 CFArray.new(ary)
153
154 when Hash
155 hsh = Hash.new
156 object.each_pair do |k,v|
157 k = k.to_s if k.is_a?(Symbol)
158 hsh[k] = Facter::Util::CFPropertyList.guess(v, options)
159 end
160 CFDictionary.new(hsh)
161 else
162 case
163 when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
164 CFReal.new(object)
165 when object.respond_to?(:read)
166 CFData.new(object.read(), CFData::DATA_RAW)
167 when options[:converter_method] && object.respond_to?(options[:converter_method])
168 if options[:converter_with_opts]
169 Facter::Util::CFPropertyList.guess(object.send(options[:converter_method],options),options)
170 else
171 Facter::Util::CFPropertyList.guess(object.send(options[:converter_method]),options)
172 end
173 when options[:convert_unknown_to_string]
174 CFString.new(object.to_s)
175 else
176 raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
177 end
178 end
179 end
180
181 # Converts a CFType hiercharchy to native Ruby types
182 def native_types(object,keys_as_symbols=false)
183 return if object.nil?
184
185 if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) then
186 return object.value
187 elsif(object.is_a?(CFData)) then
188 return object.decoded_value
189 elsif(object.is_a?(CFArray)) then
190 ary = []
191 object.value.each do
192 |v|
193 ary.push Facter::Util::CFPropertyList.native_types(v)
194 end
195
196 return ary
197 elsif(object.is_a?(CFDictionary)) then
198 hsh = {}
199 object.value.each_pair do
200 |k,v|
201 k = k.to_sym if keys_as_symbols
202 hsh[k] = Facter::Util::CFPropertyList.native_types(v)
203 end
204
205 return hsh
206 end
207 end
208
209 module_function :guess, :native_types
210
211 # Class representing a Facter::Util::CFPropertyList. Instanciate with #new
212 class List
213 # Format constant for binary format
214 FORMAT_BINARY = 1
215
216 # Format constant for XML format
217 FORMAT_XML = 2
218
219 # Format constant for automatic format recognizing
220 FORMAT_AUTO = 0
221
222 @@parsers = [Binary,XML]
223
224 # Path of PropertyList
225 attr_accessor :filename
226 # Path of PropertyList
227 attr_accessor :format
228 # the root value in the plist file
229 attr_accessor :value
230
231 # initialize a new Facter::Util::CFPropertyList, arguments are:
232 #
233 # :file:: Parse a file
234 # :format:: Format is one of FORMAT_BINARY or FORMAT_XML. Defaults to FORMAT_AUTO
235 # :data:: Parse a string
236 #
237 # All arguments are optional
238 def initialize(opts={})
239 @filename = opts[:file]
240 @format = opts[:format] || FORMAT_AUTO
241 @data = opts[:data]
242
243 load(@filename) unless @filename.nil?
244 load_str(@data) unless @data.nil?
245 end
246
247 # Load an XML PropertyList
248 # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
249 def load_xml(filename=nil)
250 load(filename,List::FORMAT_XML)
251 end
252
253 # read a binary plist file
254 # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
255 def load_binary(filename=nil)
256 load(filename,List::FORMAT_BINARY)
257 end
258
259 # load a plist from a XML string
260 # str:: The string containing the plist
261 def load_xml_str(str=nil)
262 load_str(str,List::FORMAT_XML)
263 end
264
265 # load a plist from a binary string
266 # str:: The string containing the plist
267 def load_binary_str(str=nil)
268 load_str(str,List::FORMAT_BINARY)
269 end
270
271 # load a plist from a string
272 # str = nil:: The string containing the plist
273 # format = nil:: The format of the plist
274 def load_str(str=nil,format=nil)
275 str = @data if str.nil?
276 format = @format if format.nil?
277
278 @value = {}
279 case format
280 when List::FORMAT_BINARY, List::FORMAT_XML then
281 prsr = @@parsers[format-1].new
282 @value = prsr.load({:data => str})
283
284 when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
285 filetype = str[0..5]
286 version = str[6..7]
287
288 prsr = nil
289 if filetype == "bplist" then
290 raise CFFormatError.new("Wong file version #{version}") unless version == "00"
291 prsr = Binary.new
292 else
293 prsr = XML.new
294 end
295
296 @value = prsr.load({:data => str})
297 end
298 end
299
300 # Read a plist file
301 # file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
302 # format = nil:: The format of the plist file. Auto-detect if nil
303 def load(file=nil,format=nil)
304 file = @filename if file.nil?
305 format = @format if format.nil?
306 @value = {}
307
308 raise IOError.new("File #{file} not readable!") unless File.readable? file
309
310 case format
311 when List::FORMAT_BINARY, List::FORMAT_XML then
312 prsr = @@parsers[format-1].new
313 @value = prsr.load({:file => file})
314
315 when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
316 magic_number = IO.read(file,8)
317 filetype = magic_number[0..5]
318 version = magic_number[6..7]
319
320 prsr = nil
321 if filetype == "bplist" then
322 raise CFFormatError.new("Wong file version #{version}") unless version == "00"
323 prsr = Binary.new
324 else
325 prsr = XML.new
326 end
327
328 @value = prsr.load({:file => file})
329 end
330 end
331
332 # Serialize Facter::Util::CFPropertyList object to specified format and write it to file
333 # file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
334 # format = nil:: The format to save in. Uses +format+ instance variable if nil
335 def save(file=nil,format=nil,opts={})
336 format = @format if format.nil?
337 file = @filename if file.nil?
338
339 raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML") if format != FORMAT_BINARY && format != FORMAT_XML
340
341 if(!File.exists?(file)) then
342 raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
343 elsif(!File.writable?(file)) then
344 raise IOError.new("File #{file} not writable!")
345 end
346
347 opts[:root] = @value
348 prsr = @@parsers[format-1].new
349 content = prsr.to_str(opts)
350
351 File.open(file, 'wb') {
352 |fd|
353 fd.write content
354 }
355 end
356
357 # convert plist to string
358 # format = List::FORMAT_BINARY:: The format to save the plist
359 # opts={}:: Pass parser options
360 def to_str(format=List::FORMAT_BINARY,opts={})
361 prsr = @@parsers[format-1].new
362 opts[:root] = @value
363 return prsr.to_str(opts)
364 end
365 end
366 end
367
368 class Array
369 # convert an array to plist format
370 def to_plist(options={})
371 options[:plist_format] ||= Facter::Util::CFPropertyList::List::FORMAT_BINARY
372
373 plist = Facter::Util::CFPropertyList::List.new
374 plist.value = Facter::Util::CFPropertyList.guess(self, options)
375 plist.to_str(options[:plist_format])
376 end
377 end
378
379 class Enumerator
380 # convert an array to plist format
381 def to_plist(options={})
382 options[:plist_format] ||= Facter::Util::CFPropertyList::List::FORMAT_BINARY
383
384 plist = Facter::Util::CFPropertyList::List.new
385 plist.value = Facter::Util::CFPropertyList.guess(self, options)
386 plist.to_str(options[:plist_format])
387 end
388 end
389
390 class Hash
391 # convert a hash to plist format
392 def to_plist(options={})
393 options[:plist_format] ||= Facter::Util::CFPropertyList::List::FORMAT_BINARY
394
395 plist = Facter::Util::CFPropertyList::List.new
396 plist.value = Facter::Util::CFPropertyList.guess(self, options)
397 plist.to_str(options[:plist_format])
398 end
399 end
400
401 # eof
+0
-244
lib/facter/util/cfpropertylist/lib/rbCFTypes.rb less more
0 # -*- coding: utf-8 -*-
1 #
2 # CFTypes, e.g. CFString, CFInteger
3 # needed to create unambiguous plists
4 #
5 # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
6 # Copyright:: Copyright (c) 2009
7 # License:: MIT License
8
9 require 'base64'
10
11 module Facter::Util::CFPropertyList
12 # This class defines the base class for all CFType classes
13 #
14 class CFType
15 # value of the type
16 attr_accessor :value
17
18 def initialize(value=nil)
19 @value = value
20 end
21
22 def to_xml(parser)
23 end
24
25 def to_binary(bplist) end
26 end
27
28 # This class holds string values, both, UTF-8 and UTF-16BE
29 # It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained)
30 class CFString < CFType
31 # convert to XML
32 def to_xml(parser)
33 n = parser.new_node('string')
34 n = parser.append_node(n, parser.new_text(@value)) unless @value.nil?
35 n
36 end
37
38 # convert to binary
39 def to_binary(bplist)
40 bplist.string_to_binary(@value);
41 end
42 end
43
44 # This class holds integer/fixnum values
45 class CFInteger < CFType
46 # convert to XML
47 def to_xml(parser)
48 n = parser.new_node('integer')
49 n = parser.append_node(n, parser.new_text(@value.to_s))
50 n
51 end
52
53 # convert to binary
54 def to_binary(bplist)
55 bplist.num_to_binary(self)
56 end
57 end
58
59 # This class holds float values
60 class CFReal < CFType
61 # convert to XML
62 def to_xml(parser)
63 n = parser.new_node('real')
64 n = parser.append_node(n, parser.new_text(@value.to_s))
65 n
66 end
67
68 # convert to binary
69 def to_binary(bplist)
70 bplist.num_to_binary(self)
71 end
72 end
73
74 # This class holds Time values. While Apple uses seconds since 2001,
75 # the rest of the world uses seconds since 1970. So if you access value
76 # directly, you get the Time class. If you access via get_value you either
77 # geht the timestamp or the Apple timestamp
78 class CFDate < CFType
79 TIMESTAMP_APPLE = 0
80 TIMESTAMP_UNIX = 1;
81 DATE_DIFF_APPLE_UNIX = 978307200
82
83 # create a XML date strimg from a time object
84 def CFDate.date_string(val)
85 # 2009-05-13T20:23:43Z
86 val.getutc.strftime("%Y-%m-%dT%H:%M:%SZ")
87 end
88
89 # parse a XML date string
90 def CFDate.parse_date(val)
91 # 2009-05-13T20:23:43Z
92 val =~ %r{^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$}
93 year,month,day,hour,min,sec = $1, $2, $3, $4, $5, $6
94 return Time.utc(year,month,day,hour,min,sec).getlocal
95 end
96
97 # set value to defined state
98 def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
99 if(value.is_a?(Time) || value.nil?) then
100 @value = value.nil? ? Time.now : value
101 elsif value.instance_of? Date
102 @value = Time.utc(value.year, value.month, value.day, 0, 0, 0)
103 elsif value.instance_of? DateTime
104 @value = value.to_time.utc
105 else
106 set_value(value,format)
107 end
108 end
109
110 # set value with timestamp, either Apple or UNIX
111 def set_value(value,format=CFDate::TIMESTAMP_UNIX)
112 if(format == CFDate::TIMESTAMP_UNIX) then
113 @value = Time.at(value)
114 else
115 @value = Time.at(value + CFDate::DATE_DIFF_APPLE_UNIX)
116 end
117 end
118
119 # get timestamp, either UNIX or Apple timestamp
120 def get_value(format=CFDate::TIMESTAMP_UNIX)
121 if(format == CFDate::TIMESTAMP_UNIX) then
122 @value.to_i
123 else
124 @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
125 end
126 end
127
128 # convert to XML
129 def to_xml(parser)
130 n = parser.new_node('date')
131 n = parser.append_node(n, parser.new_text(CFDate::date_string(@value)))
132 n
133 end
134
135 # convert to binary
136 def to_binary(bplist)
137 bplist.date_to_binary(@value)
138 end
139 end
140
141 # This class contains a boolean value
142 class CFBoolean < CFType
143 # convert to XML
144 def to_xml(parser)
145 parser.new_node(@value ? 'true' : 'false')
146 end
147
148 # convert to binary
149 def to_binary(bplist)
150 bplist.bool_to_binary(@value);
151 end
152 end
153
154 # This class contains binary data values
155 class CFData < CFType
156 # Base64 encoded data
157 DATA_BASE64 = 0
158 # Raw data
159 DATA_RAW = 1
160
161 # set value to defined state, either base64 encoded or raw
162 def initialize(value=nil,format=DATA_BASE64)
163 if(format == DATA_RAW)
164 @raw_value = value
165 @raw_value.blob = true
166 else
167 @value = value
168 end
169 end
170
171 # get base64 encoded value
172 def encoded_value
173 @value ||= "\n#{Base64.encode64(@raw_value).gsub("\n", '').scan(/.{1,76}/).join("\n")}\n"
174 end
175
176 # get base64 decoded value
177 def decoded_value
178 @raw_value ||= String.new(Base64.decode64(@value))
179 @raw_value.blob = true
180 @raw_value
181 end
182
183 # convert to XML
184 def to_xml(parser)
185 n = parser.new_node('data')
186 n = parser.append_node(n, parser.new_text(encoded_value()))
187 n
188 end
189
190 # convert to binary
191 def to_binary(bplist)
192 bplist.data_to_binary(decoded_value())
193 end
194 end
195
196 # This class contains an array of values
197 class CFArray < CFType
198 # create a new array CFType
199 def initialize(val=[])
200 @value = val
201 end
202
203 # convert to XML
204 def to_xml(parser)
205 n = parser.new_node('array')
206 @value.each do |v|
207 n = parser.append_node(n, v.to_xml(parser))
208 end
209 n
210 end
211
212 # convert to binary
213 def to_binary(bplist)
214 bplist.array_to_binary(self)
215 end
216 end
217
218 # this class contains a hash of values
219 class CFDictionary < CFType
220 # Create new CFDictonary type.
221 def initialize(value={})
222 @value = value
223 end
224
225 # convert to XML
226 def to_xml(parser)
227 n = parser.new_node('dict')
228 @value.each_pair do |key, value|
229 k = parser.append_node(parser.new_node('key'), parser.new_text(key.to_s))
230 n = parser.append_node(n, k)
231 n = parser.append_node(n, value.to_xml(parser))
232 end
233 n
234 end
235
236 # convert to binary
237 def to_binary(bplist)
238 bplist.dict_to_binary(self)
239 end
240 end
241 end
242
243 # eof
+0
-135
lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb less more
0 # -*- coding: utf-8 -*-
1
2 require 'libxml'
3
4 module Facter::Util::CFPropertyList
5 # XML parser
6 class XML < XMLParserInterface
7 # read a XML file
8 # opts::
9 # * :file - The filename of the file to load
10 # * :data - The data to parse
11 def load(opts)
12 if(opts.has_key?(:file)) then
13 doc = LibXML::XML::Document.file(opts[:file],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
14 else
15 doc = LibXML::XML::Document.string(opts[:data],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
16 end
17
18 root = doc.root.first
19 return import_xml(root)
20 end
21
22 # serialize Facter::Util::CFPropertyList object to XML
23 # opts = {}:: Specify options: :formatted - Use indention and line breaks
24 def to_str(opts={})
25 doc = LibXML::XML::Document.new
26
27 doc.root = LibXML::XML::Node.new('plist')
28 doc.encoding = LibXML::XML::Encoding::UTF_8
29
30 doc.root['version'] = '1.0'
31 doc.root << opts[:root].to_xml(self)
32
33 # ugly hack, but there's no other possibility I know
34 str = doc.to_s(:indent => opts[:formatted])
35 str1 = String.new
36 first = false
37 str.each_line do |line|
38 str1 << line
39 unless(first) then
40 str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
41 end
42
43 first = true
44 end
45
46 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
47 return str1
48 end
49
50 def new_node(name)
51 LibXML::XML::Node.new(name)
52 end
53
54 def new_text(val)
55 LibXML::XML::Node.new_text(val)
56 end
57
58 def append_node(parent, child)
59 parent << child
60 end
61
62 protected
63
64 # get the value of a DOM node
65 def get_value(n)
66 content = if n.children?
67 n.first.content
68 else
69 n.content
70 end
71
72 content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
73 content
74 end
75
76 # import the XML values
77 def import_xml(node)
78 ret = nil
79
80 case node.name
81 when 'dict'
82 hsh = Hash.new
83 key = nil
84
85 if node.children? then
86 node.children.each do |n|
87 next if n.text? # avoid a bug of libxml
88 next if n.comment?
89
90 if n.name == "key" then
91 key = get_value(n)
92 else
93 raise CFFormatError.new("Format error!") if key.nil?
94 hsh[key] = import_xml(n)
95 key = nil
96 end
97 end
98 end
99
100 ret = CFDictionary.new(hsh)
101
102 when 'array'
103 ary = Array.new
104
105 if node.children? then
106 node.children.each do |n|
107 ary.push import_xml(n)
108 end
109 end
110
111 ret = CFArray.new(ary)
112
113 when 'true'
114 ret = CFBoolean.new(true)
115 when 'false'
116 ret = CFBoolean.new(false)
117 when 'real'
118 ret = CFReal.new(get_value(node).to_f)
119 when 'integer'
120 ret = CFInteger.new(get_value(node).to_i)
121 when 'string'
122 ret = CFString.new(get_value(node))
123 when 'data'
124 ret = CFData.new(get_value(node))
125 when 'date'
126 ret = CFDate.new(CFDate.parse_date(get_value(node)))
127 end
128
129 return ret
130 end
131 end
132 end
133
134 # eof
+0
-140
lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb less more
0 # -*- coding: utf-8 -*-
1
2 require 'nokogiri'
3
4 module Facter::Util::CFPropertyList
5 # XML parser
6 class XML < ParserInterface
7 # read a XML file
8 # opts::
9 # * :file - The filename of the file to load
10 # * :data - The data to parse
11 def load(opts)
12 if(opts.has_key?(:file)) then
13 File.open(opts[:file], "rb") { |fd| doc = Nokogiri::XML::Document.parse(fd, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NOENT) }
14 else
15 doc = Nokogiri::XML::Document.parse(opts[:data], nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NOENT)
16 end
17
18 root = doc.root.children.first
19 return import_xml(root)
20 end
21
22 # serialize Facter::Util::CFPropertyList object to XML
23 # opts = {}:: Specify options: :formatted - Use indention and line breaks
24 def to_str(opts={})
25 doc = Nokogiri::XML::Document.new
26 @doc = doc
27
28 doc.root = doc.create_element 'plist', :version => '1.0'
29 doc.encoding = 'UTF-8'
30
31 doc.root << opts[:root].to_xml(self)
32
33 # ugly hack, but there's no other possibility I know
34 s_opts = Nokogiri::XML::Node::SaveOptions::AS_XML
35 s_opts |= Nokogiri::XML::Node::SaveOptions::FORMAT if opts[:formatted]
36
37 str = doc.serialize(:save_with => s_opts)
38 str1 = String.new
39 first = false
40 str.each_line do |line|
41 str1 << line
42 unless(first) then
43 str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
44 end
45
46 first = true
47 end
48
49 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
50 return str1
51 end
52
53 def new_node(name)
54 @doc.create_element name
55 end
56
57 def new_text(val)
58 @doc.create_text_node val
59 end
60
61 def append_node(parent, child)
62 parent << child
63 end
64
65 protected
66
67 # get the value of a DOM node
68 def get_value(n)
69 content = if n.children.empty?
70 n.content
71 else
72 n.children.first.content
73 end
74
75 content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
76 content
77 end
78
79 # import the XML values
80 def import_xml(node)
81 ret = nil
82
83 case node.name
84 when 'dict'
85 hsh = Hash.new
86 key = nil
87 children = node.children
88
89 unless children.empty? then
90 children.each do |n|
91 next if n.text? # avoid a bug of libxml
92 next if n.comment?
93
94 if n.name == "key" then
95 key = get_value(n)
96 else
97 raise CFFormatError.new("Format error!") if key.nil?
98 hsh[key] = import_xml(n)
99 key = nil
100 end
101 end
102 end
103
104 ret = CFDictionary.new(hsh)
105
106 when 'array'
107 ary = Array.new
108 children = node.children
109
110 unless children.empty? then
111 children.each do |n|
112 ary.push import_xml(n)
113 end
114 end
115
116 ret = CFArray.new(ary)
117
118 when 'true'
119 ret = CFBoolean.new(true)
120 when 'false'
121 ret = CFBoolean.new(false)
122 when 'real'
123 ret = CFReal.new(get_value(node).to_f)
124 when 'integer'
125 ret = CFInteger.new(get_value(node).to_i)
126 when 'string'
127 ret = CFString.new(get_value(node))
128 when 'data'
129 ret = CFData.new(get_value(node))
130 when 'date'
131 ret = CFDate.new(CFDate.parse_date(get_value(node)))
132 end
133
134 return ret
135 end
136 end
137 end
138
139 # eof
+0
-136
lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb less more
0 # -*- coding: utf-8 -*-
1
2 require 'rexml/document'
3
4 module Facter::Util::CFPropertyList
5 # XML parser
6 class XML < ParserInterface
7 # read a XML file
8 # opts::
9 # * :file - The filename of the file to load
10 # * :data - The data to parse
11 def load(opts)
12 if(opts.has_key?(:file)) then
13 File.open(opts[:file], "rb") { |fd| doc = REXML::Document.new(fd) }
14 else
15 doc = REXML::Document.new(opts[:data])
16 end
17
18 root = doc.root.elements[1]
19 return import_xml(root)
20 end
21
22 # serialize Facter::Util::CFPropertyList object to XML
23 # opts = {}:: Specify options: :formatted - Use indention and line breaks
24 def to_str(opts={})
25 doc = REXML::Document.new
26 @doc = doc
27
28 doc.context[:attribute_quote] = :quote
29
30 doc.add_element 'plist', {'version' => '1.0'}
31 doc.root << opts[:root].to_xml(self)
32
33 formatter = if opts[:formatted] then
34 f = REXML::Formatters::Pretty.new(2)
35 f.compact = true
36 f
37 else
38 REXML::Formatters::Default.new
39 end
40
41 str = formatter.write(doc.root, "")
42 str1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + str + "\n"
43 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
44
45 return str1
46 end
47
48 def new_node(name)
49 #LibXML::XML::Node.new(name)
50 REXML::Element.new(name)
51 end
52
53 def new_text(val)
54 val
55 end
56
57 def append_node(parent, child)
58 if child.is_a?(String) then
59 parent.add_text child
60 else
61 parent.elements << child
62 end
63 parent
64 end
65
66 protected
67
68 # get the value of a DOM node
69 def get_value(n)
70 content = n.text
71
72 content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
73 content
74 end
75
76 # import the XML values
77 def import_xml(node)
78 ret = nil
79
80 case node.name
81 when 'dict'
82 hsh = Hash.new
83 key = nil
84
85 if node.has_elements? then
86 node.elements.each do |n|
87 #print n.name + "\n"
88 next if n.name == '#text' # avoid a bug of libxml
89 next if n.name == '#comment'
90
91 if n.name == "key" then
92 key = get_value(n)
93 else
94 raise CFFormatError.new("Format error!") if key.nil?
95 hsh[key] = import_xml(n)
96 key = nil
97 end
98 end
99 end
100
101 ret = CFDictionary.new(hsh)
102
103 when 'array'
104 ary = Array.new
105
106 if node.has_elements? then
107 node.elements.each do |n|
108 ary.push import_xml(n)
109 end
110 end
111
112 ret = CFArray.new(ary)
113
114 when 'true'
115 ret = CFBoolean.new(true)
116 when 'false'
117 ret = CFBoolean.new(false)
118 when 'real'
119 ret = CFReal.new(get_value(node).to_f)
120 when 'integer'
121 ret = CFInteger.new(get_value(node).to_i)
122 when 'string'
123 ret = CFString.new(get_value(node))
124 when 'data'
125 ret = CFData.new(get_value(node))
126 when 'date'
127 ret = CFDate.new(CFDate.parse_date(get_value(node)))
128 end
129
130 return ret
131 end
132 end
133 end
134
135 # eof
+0
-6
lib/facter/util/cfpropertylist.rb less more
0 # -*- coding: utf-8 -*-
1
2 require File.join(File.dirname(__FILE__), 'cfpropertylist', 'lib', 'rbCFPropertyList.rb')
3
4
5 # eof
33
44 # Manage which facts exist and how we access them. Largely just a wrapper
55 # around a hash of facts.
6 #
7 # @api private
68 class Facter::Util::Collection
79
810 def initialize(internal_loader, external_loader)
1113 @external_loader = external_loader
1214 end
1315
14 # Return a fact object by name. If you use this, you still have to call
15 # 'value' on it to retrieve the actual value.
16 # Return a fact object by name.
1617 def [](name)
1718 value(name)
1819 end
1920
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 if block_given?
31 fact.instance_eval(&block)
32 end
33
34 fact
35 rescue => e
36 Facter.log_exception(e, "Unable to add fact #{name}: #{e}")
37 end
38
2039 # Add a resolution mechanism for a named fact. This does not distinguish
2140 # between adding a new fact and adding a new way to resolve a fact.
41 #
42 # @param name [Symbol] The name of the fact to define
43 # @param options [Hash] A hash of options to set on the fact and resolution
44 #
45 # @return [Facter::Util::Fact] The fact that was defined
2246 def add(name, options = {}, &block)
23 name = canonize(name)
47 fact = create_or_return_fact(name, options)
2448
25 unless fact = @facts[name]
26 fact = Facter::Util::Fact.new(name)
27
28 @facts[name] = fact
29 end
30
31 # Set any fact-appropriate options.
32 options.each do |opt, value|
33 method = opt.to_s + "="
34 if fact.respond_to?(method)
35 fact.send(method, value)
36 options.delete(opt)
37 end
38 end
39
40 if block_given?
41 resolve = fact.add(&block)
42 else
43 resolve = fact.add
44 end
45
46 # Set any resolve-appropriate options
47 if resolve
48 # If the resolve was actually added, set any resolve-appropriate options
49 options.each do |opt, value|
50 method = opt.to_s + "="
51 if resolve.respond_to?(method)
52 resolve.send(method, value)
53 options.delete(opt)
54 end
55 end
56 end
57
58 unless options.empty?
59 raise ArgumentError, "Invalid facter option(s) %s" % options.keys.collect { |k| k.to_s }.join(",")
60 end
49 fact.add(options, &block)
6150
6251 return fact
6352 end
7766
7867 # Return a fact by name.
7968 def fact(name)
80 name = canonize(name)
69 name = canonicalize(name)
8170
8271 # Try to load the fact if necessary
8372 load(name) unless @facts[name]
9584 # Flush all cached values.
9685 def flush
9786 @facts.each { |name, fact| fact.flush }
87 @external_facts_loaded = nil
9888 end
9989
10090 # Return a list of all of the facts.
10595
10696 def load(name)
10797 internal_loader.load(name)
108 external_loader.load(self)
98 load_external_facts
10999 end
110100
111101 # Load all known facts.
112102 def load_all
113103 internal_loader.load_all
114 external_loader.load(self)
104 load_external_facts
115105 end
116106
117107 def internal_loader
142132
143133 private
144134
145 # Provide a consistent means of getting the exact same fact name
146 # every time.
147 def canonize(name)
135 def create_or_return_fact(name, options)
136 name = canonicalize(name)
137
138 fact = @facts[name]
139
140 if fact.nil?
141 fact = Facter::Util::Fact.new(name, options)
142 @facts[name] = fact
143 else
144 fact.extract_ldapname_option!(options)
145 end
146
147 fact
148 end
149
150 def canonicalize(name)
148151 name.to_s.downcase.to_sym
149152 end
153
154 def load_external_facts
155 if ! @external_facts_loaded
156 @external_facts_loaded = true
157 external_loader.load(self)
158 end
159 end
150160 end
2828 end
2929 end
3030
31 def self.external_facts_dirs=(dir)
32 @external_facts_dirs = dir
33 end
34
3135 def self.external_facts_dirs
32 windows_dir = windows_data_dir
33 if windows_dir.nil? then
34 ["/etc/facter/facts.d", "/etc/puppetlabs/facter/facts.d"]
36 @external_facts_dirs
37 end
38
39 def self.setup_default_ext_facts_dirs
40 if Facter::Util::Root.root?
41 windows_dir = windows_data_dir
42 if windows_dir.nil? then
43 @external_facts_dirs = ["/etc/facter/facts.d", "/etc/puppetlabs/facter/facts.d"]
44 else
45 @external_facts_dirs = [File.join(windows_dir, 'PuppetLabs', 'facter', 'facts.d')]
46 end
3547 else
36 [File.join(windows_dir, 'PuppetLabs', 'facter', 'facts.d')]
48 @external_facts_dirs = [File.expand_path(File.join("~", ".facter", "facts.d"))]
3749 end
3850 end
51
52 if Facter::Util::Config.is_windows?
53 require 'win32/dir'
54 require 'facter/util/windows_root'
55 else
56 require 'facter/util/unix_root'
57 end
58
59 setup_default_ext_facts_dirs
3960 end
40
41 if Facter::Util::Config.is_windows?
42 require 'rubygems'
43 require 'win32/dir'
44 end
99
1010 # Add the restriction. Requires the fact name, an operator, and the value
1111 # we're comparing to.
12 def initialize(fact, *values)
13 raise ArgumentError, "The fact name must be provided" unless fact
14 raise ArgumentError, "One or more values must be provided" if values.empty?
12 #
13 # @param fact [Symbol] Name of the fact
14 # @param values [Array] One or more values to match against.
15 # They can be any type that provides a === method.
16 # @param block [Proc] Alternatively a block can be supplied as a check. The fact
17 # value will be passed as the argument to the block. If the block returns
18 # true then the fact will be enabled, otherwise it will be disabled.
19 def initialize(fact = nil, *values, &block)
20 raise ArgumentError, "The fact name must be provided" unless fact or block_given?
21 if values.empty? and not block_given?
22 raise ArgumentError, "One or more values or a block must be provided"
23 end
1524 @fact = fact
1625 @values = values
26 @block = block
1727 end
1828
1929 def to_s
30 return @block.to_s if @block
2031 return "'%s' '%s'" % [@fact, @values.join(",")]
2132 end
2233
2334 # Evaluate the fact, returning true or false.
35 # if we have a block paramter then we only evaluate that instead
2436 def true?
37 if @block and not @fact then
38 begin
39 return !! @block.call
40 rescue StandardError => error
41 Facter.debug "Confine raised #{error.class} #{error}"
42 return false
43 end
44 end
45
2546 unless fact = Facter[@fact]
2647 Facter.debug "No fact for %s" % @fact
2748 return false
3051
3152 return false if value.nil?
3253
33 return @values.any? { |v| convert(v) === value }
54 if @block then
55 begin
56 return !! @block.call(value)
57 rescue StandardError => error
58 Facter.debug "Confine raised #{error.class} #{error}"
59 return false
60 end
61 end
62
63 return @values.any? do |v| convert(v) === value end
3464 end
3565 end
5151 "arp -an"
5252 end
5353
54 if arp_table = Facter::Util::Resolution.exec(arp_command)
54 if arp_table = Facter::Core::Execution.exec(arp_command)
5555 return true if arp_table.match(mac_address_re)
5656 end
5757 return false
00 require 'facter'
11 require 'facter/util/resolution'
2
2 require 'facter/core/aggregate'
3
4 # This class represents a fact. Each fact has a name and multiple
5 # {Facter::Util::Resolution resolutions}.
6 #
7 # Create facts using {Facter.add}
8 #
9 # @api public
310 class Facter::Util::Fact
4 TIMEOUT = 5
5
6 attr_accessor :name, :ldapname
7
8 # Create a new fact, with no resolution mechanisms.
11 # The name of the fact
12 # @return [String]
13 attr_accessor :name
14
15 # @return [String]
16 # @deprecated
17 attr_accessor :ldapname
18
19 # Creates a new fact, with no resolution mechanisms. See {Facter.add}
20 # for the public API for creating facts.
21 # @param name [String] the fact name
22 # @param options [Hash] optional parameters
23 # @option options [String] :ldapname set the ldapname property on the fact
24 #
25 # @api private
926 def initialize(name, options = {})
1027 @name = name.to_s.downcase.intern
1128
12 # LAK:NOTE: This is slow for many options, but generally we won't have any and at
13 # worst we'll have one. If we add more, this should be made more efficient.
14 options.each do |name, value|
15 case name
16 when :ldapname; self.ldapname = value
17 else
18 raise ArgumentError, "Invalid fact option '%s'" % name
19 end
20 end
29 extract_ldapname_option!(options)
2130
2231 @ldapname ||= @name.to_s
2332
2736 @value = nil
2837 end
2938
30 # Add a new resolution mechanism. This requires a block, which will then
31 # be evaluated in the context of the new mechanism.
32 def add(value = nil, &block)
33 begin
34 resolve = Facter::Util::Resolution.new(@name)
35
36 resolve.instance_eval(&block) if block
37 @resolves << resolve
38
39 resolve
40 rescue => e
41 Facter.warn "Unable to add resolve for #{@name}: #{e}"
42 nil
43 end
44 end
45
46 ##
47 # Flush any cached values. If the resolver has a callback block defined
48 # using the on_flush DSL method, then invoke that block by sending a message
49 # to Resolution#flush.
39 # Adds a new {Facter::Util::Resolution resolution}. This requires a
40 # block, which will then be evaluated in the context of the new
41 # resolution.
42 #
43 # @param options [Hash] A hash of options to set on the resolution
44 #
45 # @return [Facter::Util::Resolution]
46 #
47 # @api private
48 def add(options = {}, &block)
49 define_resolution(nil, options, &block)
50 end
51
52 # Define a new named resolution or return an existing resolution with
53 # the given name.
54 #
55 # @param resolution_name [String] The name of the resolve to define or look up
56 # @param options [Hash] A hash of options to set on the resolution
57 # @return [Facter::Util::Resolution]
58 #
59 # @api public
60 def define_resolution(resolution_name, options = {}, &block)
61
62 resolution_type = options.delete(:type) || :simple
63
64 resolve = create_or_return_resolution(resolution_name, resolution_type)
65
66 resolve.set_options(options) unless options.empty?
67 resolve.evaluate(&block) if block
68
69 resolve
70 rescue => e
71 Facter.log_exception(e, "Unable to add resolve #{resolution_name.inspect} for fact #{@name}: #{e.message}")
72 end
73
74 # Retrieve an existing resolution by name
75 #
76 # @param name [String]
77 #
78 # @return [Facter::Util::Resolution, nil] The resolution if exists, nil if
79 # it doesn't exist or name is nil
80 def resolution(name)
81 return nil if name.nil?
82
83 @resolves.find { |resolve| resolve.name == name }
84 end
85
86 # Flushes any cached values.
87 #
88 # @return [void]
89 #
90 # @api private
5091 def flush
5192 @resolves.each { |r| r.flush }
5293 @value = nil
5394 end
5495
55 # Return the value for a given fact. Searches through all of the mechanisms
56 # and returns either the first value or nil.
96 # Returns the value for this fact. This searches all resolutions by
97 # suitability and weight (see {Facter::Util::Resolution}). If no
98 # suitable resolution is found, it returns nil.
99 #
100 # @api public
57101 def value
58102 return @value if @value
59103
74118 end
75119 end
76120
121 # @api private
122 # @deprecated
123 def extract_ldapname_option!(options)
124 if options[:ldapname]
125 Facter.warnonce("ldapname is deprecated and will be removed in a future version")
126 self.ldapname = options.delete(:ldapname)
127 end
128 end
77129
78130 private
79131
105157
106158 def find_first_real_value(resolutions)
107159 resolutions.each do |resolve|
108 value = normalize_value(resolve.value)
160 value = resolve.value
109161 if not value.nil?
110162 return value
111163 end
125177 end
126178 end
127179
128 def normalize_value(value)
129 value == "" ? nil : value
180 def create_or_return_resolution(resolution_name, resolution_type)
181 resolve = self.resolution(resolution_name)
182
183 if resolve
184 if resolution_type != resolve.resolution_type
185 raise ArgumentError, "Cannot return resolution #{resolution_name} with type" +
186 " #{resolution_type}; already defined as #{resolve.resolution_type}"
187 end
188 else
189 case resolution_type
190 when :simple
191 resolve = Facter::Util::Resolution.new(resolution_name, self)
192 when :aggregate
193 resolve = Facter::Core::Aggregate.new(resolution_name, self)
194 else
195 raise ArgumentError, "Expected resolution type to be one of (:simple, :aggregate) but was #{resolution_type}"
196 end
197
198 @resolves << resolve
199 end
200
201 resolve
130202 end
131203 end
00 module Facter
11 module Util
2 ##
2
33 # {Facter::Util::FileRead} is a utility module intended to provide easily
44 # mockable methods that delegate to simple file read methods. The intent is to
55 # avoid the need to execute the `cat` system command or `File.read` directly in
66 # Ruby, as mocking these behaviors can have wide-ranging effects.
77 #
88 # All Facter facts are encouraged to use this method instead of File.read or
9 # Facter::Util::Resolution.exec('cat ...')
9 # Facter::Core::Execution.exec('cat ...')
1010 #
1111 # @api public
1212 module FileRead
13 ##
1413 # read returns the raw content of a file as a string. If the file does not
1514 # exist, or the process does not have permission to read the file then nil is
1615 # returned.
1716 #
1817 # @api public
1918 #
20 # @return [String] the raw contents of the file at {path} or {nil} if the
21 # file cannot be read because it does not exist or the process does not have
22 # permission to read the file.
19 # @param path [String] the path to be read
20 #
21 # @return [String, nil] the raw contents of the file or `nil` if the
22 # file cannot be read because it does not exist or the process does not have
23 # permission to read the file.
2324 def self.read(path)
2425 File.read(path)
2526 rescue Errno::ENOENT, Errno::EACCES => detail
0 require 'yaml'
1
2 module Facter
3 module Util
4 module Formatter
5
6 def self.format_json(hash)
7 if Facter.json?
8 JSON.pretty_generate(hash)
9 else
10 raise "Cannot format facts as JSON; 'json' library is not present"
11 end
12 end
13
14 def self.format_yaml(hash)
15 YAML.dump(hash)
16 end
17
18 def self.format_plaintext(hash)
19 output = ''
20
21 # Print the value of a single fact, otherwise print a list sorted by fact
22 # name and separated by "=>"
23 if hash.length == 1
24 if value = hash.values.first
25 output = value
26 end
27 else
28 hash.sort_by { |(name, value)| name }.each do |name,value|
29 output << "#{name} => #{value}\n"
30 end
31 end
32
33 output
34 end
35 end
36 end
37 end
4242 interface_names = []
4343
4444 network_adapter_configurations.map do |nic|
45 Facter::Util::WMI.execquery("SELECT * FROM Win32_NetworkAdapter WHERE Index = #{nic.Index}").each do |nic|
46 interface_names << nic.NetConnectionId
45 Facter::Util::WMI.execquery("SELECT * FROM Win32_NetworkAdapter WHERE Index = #{nic.Index} AND NetEnabled = TRUE").each do |nic|
46 interface_names << nic.NetConnectionId unless nic.NetConnectionId.nil? or nic.NetConnectionId.empty?
4747 end
4848 end
4949
9595 #
9696 # @return [String] the output of `ifconfig #{arguments} 2>/dev/null` or nil
9797 def self.exec_ifconfig(additional_arguments=[])
98 Facter::Util::Resolution.exec("#{self.get_ifconfig} #{additional_arguments.join(' ')}")
98 Facter::Core::Execution.exec("#{self.get_ifconfig} #{additional_arguments.join(' ')}")
9999 end
100100 ##
101101 # get_ifconfig looks up the ifconfig binary
109109 # hpux_netstat_in is a delegate method that allows us to stub netstat -in
110110 # without stubbing exec.
111111 def self.hpux_netstat_in
112 Facter::Util::Resolution.exec("/bin/netstat -in")
112 Facter::Core::Execution.exec("/bin/netstat -in")
113113 end
114114
115115 def self.get_infiniband_macaddress(interface)
159159 end
160160
161161 def self.hpux_lanscan
162 Facter::Util::Resolution.exec("/usr/sbin/lanscan")
162 Facter::Core::Execution.exec("/usr/sbin/lanscan")
163163 end
164164
165165 def self.get_output_for_interface_and_label(interface, label)
44 # Load facts on demand.
55 class Facter::Util::Loader
66
7 def initialize
7 def initialize(environment_vars = ENV)
88 @loaded = []
9 @valid_path = {}
9 @environment_vars = environment_vars
1010 end
1111
1212 # Load all resolutions for a single fact.
13 #
14 # @api public
15 # @param name [Symbol]
1316 def load(fact)
1417 # Now load from the search path
1518 shortname = fact.to_s.downcase
1619 load_env(shortname)
1720
1821 filename = shortname + ".rb"
19 search_path.each do |dir|
20 # Load individual files
21 file = File.join(dir, filename)
2222
23 load_file(file) if FileTest.exist?(file)
23 paths = search_path
24 unless paths.nil?
25 paths.each do |dir|
26 # Load individual files
27 file = File.join(dir, filename)
2428
25 # And load any directories matching the name
26 factdir = File.join(dir, shortname)
27 load_dir(factdir) if FileTest.directory?(factdir)
29 load_file(file) if File.file?(file)
30 end
2831 end
2932 end
3033
3134 # Load all facts from all directories.
35 #
36 # @api public
3237 def load_all
3338 return if defined?(@loaded_all)
3439
3540 load_env
3641
37 search_path.each do |dir|
38 next unless FileTest.directory?(dir)
39
40 Dir.entries(dir).sort.each do |file|
41 path = File.join(dir, file)
42 if File.directory?(path)
43 load_dir(path)
44 elsif file =~ /\.rb$/
45 load_file(File.join(dir, file))
42 paths = search_path
43 unless paths.nil?
44 paths.each do |dir|
45 # dir is already an absolute path
46 Dir.glob(File.join(dir, '*.rb')).each do |path|
47 # exclude dirs that end with .rb
48 load_file(path) if File.file?(path)
4649 end
4750 end
4851 end
5053 @loaded_all = true
5154 end
5255
53 # The list of directories we're going to search through for facts.
56 # List directories to search for fact files.
57 #
58 # Search paths are gathered from the following sources:
59 #
60 # 1. $LOAD_PATH entries are expanded to absolute paths
61 # 2. ENV['FACTERLIB'] is split and used verbatim
62 # 3. Entries from Facter.search_path are used verbatim
63 #
64 # A warning will be generated for paths in Facter.search_path that are not
65 # absolute directories.
66 #
67 # @api public
68 # @return [Array<String>]
5469 def search_path
55 result = []
56 result += $LOAD_PATH.collect { |d| File.join(d, "facter") }
57 if ENV.include?("FACTERLIB")
58 result += ENV["FACTERLIB"].split(File::PATH_SEPARATOR)
70 search_paths = []
71 search_paths += $LOAD_PATH.map { |path| File.expand_path('facter', path) }
72
73 if @environment_vars.include?("FACTERLIB")
74 search_paths += @environment_vars["FACTERLIB"].split(File::PATH_SEPARATOR)
5975 end
6076
61 # This allows others to register additional paths we should search.
62 result += Facter.search_path
77 search_paths.delete_if { |path| ! valid_search_path?(path) }
6378
64 result.select do |dir|
65 good = valid_search_path? dir
66 Facter.debugonce("Relative directory #{dir} removed from search path.") unless good
67 good
79 Facter.search_path.each do |path|
80 if valid_search_path?(path)
81 search_paths << path
82 else
83 Facter.warn "Excluding #{path} from search path. Fact file paths must be an absolute directory"
84 end
6885 end
86
87 search_paths.delete_if { |path| ! File.directory?(path) }
88
89 search_paths.uniq
6990 end
70
71 def valid_search_path?(path)
72 return @valid_path[path] unless @valid_path[path].nil?
73
74 return @valid_path[path] = Pathname.new(path).absolute?
75 end
76 private :valid_search_path?
7791
7892 private
7993
80 def load_dir(dir)
81 return if dir =~ /\/\.+$/ or dir =~ /\/util$/ or dir =~ /\/lib$/
82
83 Dir.entries(dir).find_all { |f| f =~ /\.rb$/ }.sort.each do |file|
84 load_file(File.join(dir, file))
85 end
94 # Validate that the given path is valid, ie it is an absolute path.
95 #
96 # @api private
97 # @param path [String]
98 # @return [Boolean]
99 def valid_search_path?(path)
100 Pathname.new(path).absolute?
86101 end
87102
103 # Load a file and record is paths to prevent duplicate loads.
104 #
105 # @api private
106 # @params file [String] The *absolute path* to the file to load
88107 def load_file(file)
89108 return if @loaded.include? file
109
90110 # We have to specify Kernel.load, because we have a load method.
91111 begin
92112 # Store the file path so we don't try to reload it
96116 # Don't store the path if the file can't be loaded
97117 # in case it's loadable later on.
98118 @loaded.delete(file)
99 warn "Error loading fact #{file} #{detail}"
119 Facter.log_exception(detail, "Error loading fact #{file}: #{detail.message}")
100120 end
101121 end
102122
104124 # all will be loaded.
105125 def load_env(fact = nil)
106126 # Load from the environment, if possible
107 ENV.each do |name, value|
127 @environment_vars.each do |name, value|
108128 # Skip anything that doesn't match our regex.
109129 next unless name =~ /^facter_?(\w+)$/i
110130 env_name = $1
55 ##
66
77 module Facter::Util::Macosx
8 require 'thread'
9 require 'facter/util/cfpropertylist'
8 require 'cfpropertylist'
109 require 'facter/util/resolution'
1110
1211 Plist_Xml_Doctype = '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
1514 # by looking at the _name key of the _items dict for each _dataType
1615
1716 def self.profiler_xml(data_field)
18 Facter::Util::Resolution.exec("/usr/sbin/system_profiler -xml #{data_field}")
17 Facter::Core::Execution.exec("/usr/sbin/system_profiler -xml #{data_field}")
1918 end
2019
2120 def self.intern_xml(xml)
2524 xml.gsub!( bad_xml_doctype, Plist_Xml_Doctype )
2625 Facter.debug("Had to fix plist with incorrect DOCTYPE declaration")
2726 end
28 plist = Facter::Util::CFPropertyList::List.new
27 plist = CFPropertyList::List.new
2928 begin
3029 plist.load_str(xml)
31 rescue => e
32 fail("A plist file could not be properly read by Facter::Util::CFPropertyList: #{e.inspect}")
30 rescue CFFormatError => e
31 raise RuntimeError, "A plist file could not be properly read by CFPropertyList: #{e.message}", e.backtrace
3332 end
34 Facter::Util::CFPropertyList.native_types(plist.value)
33 CFPropertyList.native_types(plist.value)
3534 end
3635
3736 # Return an xml result, modified as we need it.
5756 def self.sw_vers
5857 ver = Hash.new
5958 [ "productName", "productVersion", "buildVersion" ].each do |option|
60 ver["macosx_#{option}"] = Facter::Util::Resolution.exec("/usr/bin/sw_vers -#{option}").strip
59 ver["macosx_#{option}"] = Facter::Core::Execution.exec("/usr/bin/sw_vers -#{option}").strip
6160 end
6261 productversion = ver["macosx_productVersion"]
6362 if not productversion.nil?
5353 name.each do |sysctlkey,facterkey|
5454 Facter.add(facterkey) do
5555 confine :kernel => [:openbsd, :darwin]
56 setcode do
57 Facter::Util::Resolution.exec("sysctl -n #{sysctlkey} 2>/dev/null")
58 end
56 setcode "sysctl -n #{sysctlkey} 2>/dev/null"
5957 end
6058 end
6159 end
6260
6361 def self.prtdiag_sparc_find_system_info()
6462 # Parses prtdiag for a SPARC architecture string, won't work with Solaris x86
65 output = Facter::Util::Resolution.exec('/usr/sbin/prtdiag 2>/dev/null')
63 output = Facter::Core::Execution.exec('/usr/sbin/prtdiag 2>/dev/null')
6664
6765 # System Configuration: Sun Microsystems sun4u Sun SPARC Enterprise M3000 Server
6866 if output and output =~ /^System Configuration:\s+(.+?)\s+(sun\d+\S+)\s+(.+)/
7977 end
8078
8179 Facter.add('serialnumber') do
82 setcode do
83 Facter::Util::Resolution.exec("/usr/sbin/sneep")
84 end
80 setcode "/usr/sbin/sneep"
8581 end
8682 end
8783
3434 def self.vmstat_find_free_memory(args = [])
3535 cmd = 'vmstat'
3636 cmd += (' ' + args.join(' ')) unless args.empty?
37 row = Facter::Util::Resolution.exec(cmd).split("\n")[-1]
37 row = Facter::Core::Execution.exec(cmd).split("\n")[-1]
3838 if row =~ /^\s*\d+\s*\d+\s*\d+\s*\d+\s*(\d+)/
3939 memfree = $1
4040 end
5151 pagesize = 0
5252 memspecfree = 0
5353
54 vmstats = Facter::Util::Resolution.exec('vm_stat')
54 vmstats = Facter::Core::Execution.exec('vm_stat')
5555 vmstats.each_line do |vmline|
5656 case
5757 when vmline =~ /page\ssize\sof\s(\d+)\sbytes/
6666 freemem = ( memfree + memspecfree ) * pagesize
6767 end
6868
69 # on AIX use svmon to get the free memory:
70 # it's the third value on the line starting with memory
71 # svmon can be run by non root users
72 def self.svmon_aix_find_free_memory()
73 Facter::Core::Execution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+\d+\s+\d+\s+(\d+)\s+/
74 $1
75 end
76
6977 def self.mem_free(kernel = Facter.value(:kernel))
7078 output = mem_free_info(kernel)
7179 scale_mem_free_value output, kernel
7987 vmstat_find_free_memory(["-H"])
8088 when /Darwin/i
8189 vmstat_darwin_find_free_memory()
90 when /AIX/i
91 svmon_aix_find_free_memory()
8292 end
8393 end
8494
8595 def self.scale_mem_free_value (value, kernel)
8696 case kernel
87 when /OpenBSD/i, /FreeBSD/i, /SunOS/i, /Dragonfly/i
97 when /OpenBSD/i, /FreeBSD/i, /SunOS/i, /Dragonfly/i, /AIX/i
8898 value.to_f / 1024.0
8999 when /Darwin/i
90100 value.to_f / 1024.0 / 1024.0
101111 def self.mem_size_info(kernel = Facter.value(:kernel))
102112 case kernel
103113 when /OpenBSD/i
104 Facter::Util::Resolution.exec("sysctl hw.physmem | cut -d'=' -f2")
105 when /FreeBSD/i
106 Facter::Util::Resolution.exec("sysctl -n hw.physmem")
107 when /Darwin/i
108 Facter::Util::Resolution.exec("sysctl -n hw.memsize")
114 Facter::Core::Execution.exec("sysctl hw.physmem | cut -d'=' -f2")
115 when /FreeBSD/i
116 Facter::Core::Execution.exec("sysctl -n hw.physmem")
117 when /Darwin/i
118 Facter::Core::Execution.exec("sysctl -n hw.memsize")
109119 when /Dragonfly/i
110 Facter::Util::Resolution.exec("sysctl -n hw.physmem")
120 Facter::Core::Execution.exec("sysctl -n hw.physmem")
121 when /AIX/i
122 if Facter::Core::Execution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+(\d+)\s+/
123 $1
124 end
111125 end
112126 end
113127
115129 case kernel
116130 when /OpenBSD/i, /FreeBSD/i, /Darwin/i, /Dragonfly/i
117131 value.to_f / 1024.0 / 1024.0
132 when /AIX/i
133 value.to_f / 1024.0
118134 else
119135 value.to_f
120136 end
133149 def self.swap_info(kernel = Facter.value(:kernel))
134150 case kernel
135151 when /AIX/i
136 (Facter.value(:id) == "root") ? Facter::Util::Resolution.exec('swap -l') : nil
152 (Facter.value(:id) == "root") ? Facter::Core::Execution.exec('swap -l 2>/dev/null') : nil
137153 when /OpenBSD/i
138 Facter::Util::Resolution.exec('swapctl -s')
139 when /FreeBSD/i
140 Facter::Util::Resolution.exec('swapinfo -k')
141 when /Darwin/i
142 Facter::Util::Resolution.exec('sysctl vm.swapusage')
154 Facter::Core::Execution.exec('swapctl -s')
155 when /FreeBSD/i
156 Facter::Core::Execution.exec('swapinfo -k')
157 when /Darwin/i
158 Facter::Core::Execution.exec('sysctl vm.swapusage')
143159 when /SunOS/i
144 Facter::Util::Resolution.exec('/usr/sbin/swap -l')
160 Facter::Core::Execution.exec('/usr/sbin/swap -l 2>/dev/null')
145161 end
146162 end
147163
+0
-39
lib/facter/util/monkey_patches.rb less more
0 # This provides an alias for RbConfig to Config for versions of Ruby older then
1 # version 1.8.5. This allows us to use RbConfig in place of the older Config in
2 # our code and still be compatible with at least Ruby 1.8.1.
3 require 'rbconfig'
4 require 'enumerator'
5
6 unless defined? ::RbConfig
7 ::RbConfig = ::Config
8 end
9
10 module Facter
11 module Util
12 module MonkeyPatches
13 module Lines
14 def lines(separator = $/)
15 if block_given?
16 self.each_line(separator) {|line| yield line }
17 return self
18 else
19 return enum_for(:each_line, separator)
20 end
21 end
22 end
23 end
24 end
25 end
26
27 public
28 class String
29 unless method_defined? :lines
30 include Facter::Util::MonkeyPatches::Lines
31 end
32 end
33
34 class IO
35 unless method_defined? :lines
36 include Facter::Util::MonkeyPatches::Lines
37 end
38 end
77 when 'Linux'
88 ops = {
99 :ifconfig_opts => ['2>/dev/null'],
10 :regex => %r{#{Facter.ipaddress}.*?(?:Mask:|netmask)\s*(#{ipregex})}x,
10 :regex => %r{#{Facter.value(:ipaddress)}.*?(?:Mask:|netmask)\s*(#{ipregex})}x,
1111 :munge => nil,
1212 }
1313 when 'SunOS'
1414 ops = {
1515 :ifconfig_opts => ['-a'],
16 :regex => %r{\s+ inet \s #{Facter.ipaddress} \s netmask \s (\w{8})}x,
16 :regex => %r{\s+ inet \s #{Facter.value(:ipaddress)} \s netmask \s (\w{8})}x,
1717 :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') }
1818 }
1919 when 'FreeBSD','NetBSD','OpenBSD', 'Darwin', 'GNU/kFreeBSD', 'DragonFly'
2020 ops = {
2121 :ifconfig_opts => ['-a'],
22 :regex => %r{\s+ inet \s #{Facter.ipaddress} \s netmask \s 0x(\w{8})}x,
22 :regex => %r{\s+ inet \s #{Facter.value(:ipaddress)} \s netmask \s 0x(\w{8})}x,
2323 :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') }
2424 }
2525 end
0 module Facter
1 module Util
2 module Normalization
3 class NormalizationError < StandardError; end
4
5 VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, String, Array, Hash]
6
7 module_function
8
9 # Recursively normalize the given data structure
10 #
11 # @api public
12 # @raise [NormalizationError] If the data structure contained an invalid element.
13 # @return [void]
14 def normalize(value)
15 case value
16 when Integer, Float, TrueClass, FalseClass, NilClass
17 value
18 when String
19 normalize_string(value)
20 when Array
21 normalize_array(value)
22 when Hash
23 normalize_hash(value)
24 else
25 raise NormalizationError, "Expected #{value} to be one of #{VALID_TYPES.inspect}, but was #{value.class}"
26 end
27 end
28
29 # @!method normalize_string(value)
30 #
31 # Attempt to normalize and validate the given string.
32 #
33 # On Ruby 1.8 the string is checked by stripping out all non UTF-8
34 # characters and comparing the converted string to the original. If they
35 # do not match then the string is considered invalid.
36 #
37 # On Ruby 1.9+, the string is validate by checking that the string encoding
38 # is UTF-8 and that the string content matches the encoding. If the string
39 # is not an expected encoding then it is converted to UTF-8.
40 #
41 # @api public
42 # @raise [NormalizationError] If the string used an unsupported encoding or did not match its encoding
43 # @param value [String]
44 # @return [void]
45
46 if RUBY_VERSION =~ /^1\.8/
47 require 'iconv'
48
49 def normalize_string(value)
50 converted = Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
51 if converted != value
52 raise NormalizationError, "String #{value.inspect} is not valid UTF-8"
53 end
54 value
55 end
56 else
57 def normalize_string(value)
58 value = value.encode(Encoding::UTF_8)
59
60 unless value.valid_encoding?
61 raise NormalizationError, "String #{value.inspect} doesn't match the reported encoding #{value.encoding}"
62 end
63
64 value
65 rescue EncodingError
66 raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
67 end
68 end
69
70 # Validate all elements of the array.
71 #
72 # @api public
73 # @raise [NormalizationError] If one of the elements failed validation
74 # @param value [Array]
75 # @return [void]
76 def normalize_array(value)
77 value.collect do |elem|
78 normalize(elem)
79 end
80 end
81
82 # Validate all keys and values of the hash.
83 #
84 # @api public
85 # @raise [NormalizationError] If one of the keys or values failed normalization
86 # @param value [Hash]
87 # @return [void]
88 def normalize_hash(value)
89 Hash[value.collect { |k, v| [ normalize(k), normalize(v) ] } ]
90 end
91 end
92 end
93 end
0 # An external fact loader that doesn't load anything
1
2 # This makes it possible to disable loading
3 # of external facts
4
50 module Facter
61 module Util
72
3 # An external fact loader that doesn't load anything
4 # This makes it possible to disable loading
5 # of external facts
86 class NothingLoader
9
107 def load(collection)
118 end
129 end
5454 def results
5555 parse_results
5656 rescue Exception => detail
57 Facter.warn("Failed to handle #{filename} as #{self.class} facts")
58 Facter.warn("detail: #{detail.class}: #{detail.message}")
59 Facter.debug(detail.backtrace.join("\n\t"))
57 Facter.log_exception(detail, "Failed to handle #{filename} as #{self.class} facts: #{detail.message}")
6058 nil
6159 end
6260
6765
6866 module KeyValuePairOutputFormat
6967 def self.parse(output)
68 return {} if output.nil?
69
7070 result = {}
7171 re = /^(.+?)=(.+)$/
7272 output.each_line do |line|
9999 end
100100
101101 class JsonParser < Base
102 def results
102 def parse_results
103103 if Facter.json?
104104 JSON.load(content)
105105 else
115115 end
116116
117117 class ScriptParser < Base
118 def results
119 KeyValuePairOutputFormat.parse Facter::Util::Resolution.exec(filename)
118 def parse_results
119 KeyValuePairOutputFormat.parse Facter::Core::Execution.exec(quote(filename))
120 end
121
122 private
123
124 def quote(filename)
125 filename.index(' ') ? "\"#{filename}\"" : filename
120126 end
121127 end
122128
124130 if Facter::Util::Config.is_windows?
125131 extension_matches?(filename, %w{bat cmd com exe}) && File.file?(filename)
126132 else
127 File.executable?(filename) && File.file?(filename)
133 File.executable?(filename) && File.file?(filename) && ! extension_matches?(filename, %w{bat cmd com exe})
128134 end
129135 end
130136
131137 # Executes and parses the key value output of Powershell scripts
132138 class PowershellParser < Base
133139 # Returns a hash of facts from powershell output
134 def results
140 def parse_results
135141 shell_command = "powershell -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -File \"#{filename}\""
136 KeyValuePairOutputFormat.parse Facter::Util::Resolution.exec(shell_command)
142 KeyValuePairOutputFormat.parse Facter::Core::Execution.exec(shell_command)
137143 end
138144 end
139145
5050 # Writes the serialized object's plist to the specified filename.
5151 def self.save_plist(obj, filename)
5252 File.open(filename, 'wb') do |f|
53 f.write(obj.to_plist)
53 f.write(Plist::Emit.dump(obj))
5454 end
5555 end
5656
4646 end
4747
4848 ##
49 # lsdev is intended to directly delegate to Facter::Util::Resolution.exec in an
49 # lsdev is intended to directly delegate to Facter::Core::Execution.exec in an
5050 # effort to make the processorX facts easier to test by stubbing only the
5151 # behaviors we need to stub to get the output of the system command.
5252 def self.lsdev(command="lsdev -Cc processor")
53 Facter::Util::Resolution.exec(command)
54 end
55
56 ##
57 # lsattr is intended to directly delegate to Facter::Util::Resolution.exec in
53 Facter::Core::Execution.exec(command)
54 end
55
56 ##
57 # lsattr is intended to directly delegate to Facter::Core::Execution.exec in
5858 # an effort to make the processorX facts easier to test. See also the
5959 # {lsdev} method.
6060 def self.lsattr(command="lsattr -El proc0 -a type")
61 Facter::Util::Resolution.exec(command)
61 Facter::Core::Execution.exec(command)
6262 end
6363
6464 ##
8686 elsif line.match(/\d+\s+((?:PA-RISC|Intel).*processors.*)/) then
8787 cpu = $1.to_s
8888 cpu.sub!(/processors/, "processor")
89 elsif line.match(/\s+(Intel.*Processor.*)/) then
90 cpu = $1.to_s
8991 end
9092 end
9193 end
178180 private_class_method :read_unistd_h
179181
180182 ##
181 # machinfo delegates directly to Facter::Util::Resolution.exec, as with lsdev
183 # machinfo delegates directly to Facter::Core::Execution.exec, as with lsdev
182184 # above.
183185 def self.machinfo(command="/usr/contrib/bin/machinfo")
184 Facter::Util::Resolution.exec(command)
185 end
186
187 ##
188 # model delegates directly to Facter::Util::Resolution.exec.
186 Facter::Core::Execution.exec(command)
187 end
188
189 ##
190 # model delegates directly to Facter::Core::Execution.exec.
189191 def self.model(command="model")
190 Facter::Util::Resolution.exec(command)
191 end
192
193 ##
194 # ioscan delegates directly to Facter::Util::Resolution.exec.
192 Facter::Core::Execution.exec(command)
193 end
194
195 ##
196 # ioscan delegates directly to Facter::Core::Execution.exec.
195197 def self.ioscan(command="ioscan -fknCprocessor")
196 Facter::Util::Resolution.exec(command)
197 end
198
199 ##
200 # getconf_cpu_version delegates directly to Facter::Util::Resolution.exec.
198 Facter::Core::Execution.exec(command)
199 end
200
201 ##
202 # getconf_cpu_version delegates directly to Facter::Core::Execution.exec.
201203 def self.getconf_cpu_version(command="getconf CPU_VERSION")
202 Facter::Util::Resolution.exec(command)
203 end
204
205 ##
206 # getconf_cpu_chip_type delegates directly to Facter::Util::Resolution.exec.
204 Facter::Core::Execution.exec(command)
205 end
206
207 ##
208 # getconf_cpu_chip_type delegates directly to Facter::Core::Execution.exec.
207209 def self.getconf_cpu_chip_type(command="getconf CPU_CHIP_TYPE")
208 Facter::Util::Resolution.exec(command)
210 Facter::Core::Execution.exec(command)
209211 end
210212
211213 def self.enum_cpuinfo
269271 processor_num = -1
270272 processor_list = []
271273 Thread::exclusive do
272 kstat = Facter::Util::Resolution.exec('/usr/bin/kstat cpu_info')
274 kstat = Facter::Core::Execution.exec('/usr/bin/kstat cpu_info')
273275 if kstat
274276 kstat.each_line do |l|
275277 if l =~ /cpu_info(\d+)/
0 # An actual fact resolution mechanism. These are largely just chunks of
1 # code, with optional confinements restricting the mechanisms to only working on
2 # specific systems. Note that the confinements are always ANDed, so any
3 # confinements specified must all be true for the resolution to be
4 # suitable.
50 require 'facter/util/confine'
61 require 'facter/util/config'
2 require 'facter/util/normalization'
3 require 'facter/core/execution'
4 require 'facter/core/resolvable'
5 require 'facter/core/suitable'
76
8 require 'timeout'
7 # This represents a fact resolution. A resolution is a concrete
8 # implementation of a fact. A single fact can have many resolutions and
9 # the correct resolution will be chosen at runtime. Each time
10 # {Facter.add} is called, a new resolution is created and added to the
11 # set of resolutions for the fact named in the call. Each resolution
12 # has a {#has_weight weight}, which defines its priority over other
13 # resolutions, and a set of {#confine _confinements_}, which defines the
14 # conditions under which it will be chosen. All confinements must be
15 # satisfied for a fact to be considered _suitable_.
16 #
17 # @api public
18 class Facter::Util::Resolution
19 # @api private
20 attr_accessor :code
21 attr_writer :value
922
10 class Facter::Util::Resolution
11 attr_accessor :interpreter, :code, :name, :timeout
12 attr_writer :value, :weight
23 extend Facter::Core::Execution
1324
14 INTERPRETER = Facter::Util::Config.is_windows? ? "cmd.exe" : "/bin/sh"
15
16 # Returns the locations to be searched when looking for a binary. This
17 # is currently determined by the +PATH+ environment variable plus
18 # <tt>/sbin</tt> and <tt>/usr/sbin</tt> when run on unix
19 def self.search_paths
20 if Facter::Util::Config.is_windows?
21 ENV['PATH'].split(File::PATH_SEPARATOR)
22 else
23 # Make sure facter is usable even for non-root users. Most commands
24 # in /sbin (like ifconfig) can be run as non priviledged users as
25 # long as they do not modify anything - which we do not do with facter
26 ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/sbin', '/usr/sbin' ]
27 end
25 class << self
26 # Expose command execution methods that were extracted into
27 # Facter::Util::Execution from Facter::Util::Resolution in Facter 2.0.0 for
28 # compatibility.
29 #
30 # @deprecated
31 public :search_paths, :which, :absolute_path?, :expand_command, :with_env, :exec
2832 end
2933
30 # Determine the full path to a binary. If the supplied filename does not
31 # already describe an absolute path then different locations (determined
32 # by <tt>self.search_paths</tt>) will be searched for a match.
33 #
34 # Returns nil if no matching executable can be found otherwise returns
35 # the expanded pathname.
36 def self.which(bin)
37 if absolute_path?(bin)
38 return bin if File.executable?(bin)
39 if Facter::Util::Config.is_windows? and File.extname(bin).empty?
40 exts = ENV['PATHEXT']
41 exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
42 exts.each do |ext|
43 destext = bin + ext
44 if File.executable?(destext)
45 Facter.warnonce("Using Facter::Util::Resolution.which with an absolute path like #{bin} but no fileextension is deprecated. Please add the correct extension (#{ext})")
46 return destext
47 end
48 end
49 end
50 else
51 search_paths.each do |dir|
52 dest = File.join(dir, bin)
53 if Facter::Util::Config.is_windows?
54 dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
55 if File.extname(dest).empty?
56 exts = ENV['PATHEXT']
57 exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
58 exts.each do |ext|
59 destext = dest + ext
60 return destext if File.executable?(destext)
61 end
62 end
63 end
64 return dest if File.executable?(dest)
65 end
66 end
67 nil
68 end
34 include Facter::Core::Resolvable
35 include Facter::Core::Suitable
6936
70 # Determine in a platform-specific way whether a path is absolute. This
71 # defaults to the local platform if none is specified.
72 def self.absolute_path?(path, platform=nil)
73 # Escape once for the string literal, and once for the regex.
74 slash = '[\\\\/]'
75 name = '[^\\\\/]+'
76 regexes = {
77 :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i,
78 :posix => %r!^/!,
79 }
80 platform ||= Facter::Util::Config.is_windows? ? :windows : :posix
37 # @!attribute [rw] name
38 # The name of this resolution. The resolution name should be unique with
39 # respect to the given fact.
40 # @return [String]
41 # @api public
42 attr_accessor :name
8143
82 !! (path =~ regexes[platform])
83 end
84
85 # Expand the executable of a commandline to an absolute path. The executable
86 # is the first word of the commandline. If the executable contains spaces,
87 # it has be but in double quotes to be properly recognized.
88 #
89 # Returns the commandline with the expanded binary or nil if the binary
90 # can't be found. If the path to the binary contains quotes, the whole binary
91 # is put in quotes.
92 def self.expand_command(command)
93 if match = /^"(.+?)"(?:\s+(.*))?/.match(command)
94 exe, arguments = match.captures
95 exe = which(exe) and [ "\"#{exe}\"", arguments ].compact.join(" ")
96 elsif match = /^'(.+?)'(?:\s+(.*))?/.match(command) and not Facter::Util::Config.is_windows?
97 exe, arguments = match.captures
98 exe = which(exe) and [ "'#{exe}'", arguments ].compact.join(" ")
99 else
100 exe, arguments = command.split(/ /,2)
101 if exe = which(exe)
102 # the binary was not quoted which means it contains no spaces. But the
103 # full path to the binary may do so.
104 exe = "\"#{exe}\"" if exe =~ /\s/ and Facter::Util::Config.is_windows?
105 exe = "'#{exe}'" if exe =~ /\s/ and not Facter::Util::Config.is_windows?
106 [ exe, arguments ].compact.join(" ")
107 end
108 end
109 end
110
111 #
112 # Call this method with a block of code for which you would like to temporarily modify
113 # one or more environment variables; the specified values will be set for the duration
114 # of your block, after which the original values (if any) will be restored.
115 #
116 # [values] a Hash containing the key/value pairs of any environment variables that you
117 # would like to temporarily override
118 def self.with_env(values)
119 old = {}
120 values.each do |var, value|
121 # save the old value if it exists
122 if old_val = ENV[var]
123 old[var] = old_val
124 end
125 # set the new (temporary) value for the environment variable
126 ENV[var] = value
127 end
128 # execute the caller's block, capture the return value
129 rv = yield
130 # use an ensure block to make absolutely sure we restore the variables
131 ensure
132 # restore the old values
133 values.each do |var, value|
134 if old.include?(var)
135 ENV[var] = old[var]
136 else
137 # if there was no old value, delete the key from the current environment variables hash
138 ENV.delete(var)
139 end
140 end
141 # return the captured return value
142 rv
143 end
144
145 # Execute a program and return the output of that program.
146 #
147 # Returns nil if the program can't be found, or if there is a problem
148 # executing the code.
149 #
150 def self.exec(code, interpreter = nil)
151 Facter.warnonce "The interpreter parameter to 'exec' is deprecated and will be removed in a future version." if interpreter
152
153 ## Set LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
154 ## output of the command can expect it to be in a consistent / predictable format / locale
155 with_env "LANG" => "C" do
156
157 if expanded_code = expand_command(code)
158 # if we can find the binary, we'll run the command with the expanded path to the binary
159 code = expanded_code
160 else
161 # if we cannot find the binary return nil on posix. On windows we'll still try to run the
162 # command in case it is a shell-builtin. In case it is not, windows will raise Errno::ENOENT
163 return nil unless Facter::Util::Config.is_windows?
164 return nil if absolute_path?(code)
165 end
166
167 out = nil
168
169 begin
170 out = %x{#{code}}.chomp
171 Facter.warnonce "Using Facter::Util::Resolution.exec with a shell built-in is deprecated. Most built-ins can be replaced with native ruby commands. If you really have to run a built-in, pass \"cmd /c your_builtin\" as a command (command responsible for this message was \"#{code}\")" unless expanded_code
172 rescue Errno::ENOENT => detail
173 # command not found on Windows
174 return nil
175 rescue => detail
176 $stderr.puts detail
177 return nil
178 end
179
180 if out == ""
181 return nil
182 else
183 return out
184 end
185 end
186 end
187
188 # Add a new confine to the resolution mechanism.
189 def confine(confines)
190 confines.each do |fact, values|
191 @confines.push Facter::Util::Confine.new(fact, *values)
192 end
193 end
194
195 def has_weight(weight)
196 @weight = weight
197 end
44 # @!attribute [r] fact
45 # @return [Facter::Util::Fact]
46 # @api private
47 attr_reader :fact
19848
19949 # Create a new resolution mechanism.
200 def initialize(name)
50 #
51 # @param name [String] The name of the resolution.
52 # @return [void]
53 #
54 # @api private
55 def initialize(name, fact)
20156 @name = name
57 @fact = fact
20258 @confines = []
20359 @value = nil
20460 @timeout = 0
20561 @weight = nil
20662 end
20763
208 # Return the importance of this resolution.
209 def weight
210 if @weight
211 @weight
64 # Evaluate the given block in the context of this resolution. If a block has
65 # already been evaluated emit a warning to that effect.
66 #
67 # @return [void]
68 def evaluate(&block)
69 if @last_evaluated
70 msg = "Already evaluated #{@name}"
71 msg << " at #{@last_evaluated}" if msg.is_a? String
72 msg << ", reevaluating anyways"
73 Facter.warn msg
74 end
75
76 instance_eval(&block)
77
78 # Ruby 1.9+ provides the source location of procs which can provide useful
79 # debugging information if a resolution is being evaluated twice. Since 1.8
80 # doesn't support this we opportunistically provide this information.
81 if block.respond_to? :source_location
82 @last_evaluated = block.source_location.join(':')
21283 else
213 @confines.length
84 @last_evaluated = true
21485 end
21586 end
21687
217 # We need this as a getter for 'timeout', because some versions
218 # of ruby seem to already have a 'timeout' method and we can't
219 # seem to override the instance methods, somehow.
220 def limit
221 @timeout
222 end
88 def set_options(options)
89 if options[:name]
90 @name = options.delete(:name)
91 end
22392
224 # Set our code for returning a value.
225 def setcode(string = nil, interp = nil, &block)
226 Facter.warnonce "The interpreter parameter to 'setcode' is deprecated and will be removed in a future version." if interp
227 if string
228 @code = string
229 @interpreter = interp || INTERPRETER
230 else
231 unless block_given?
232 raise ArgumentError, "You must pass either code or a block"
233 end
234 @code = block
93 if options.has_key?(:value)
94 @value = options.delete(:value)
95 end
96
97 if options.has_key?(:timeout)
98 @timeout = options.delete(:timeout)
99 end
100
101 if options.has_key?(:weight)
102 @weight = options.delete(:weight)
103 end
104
105 if not options.keys.empty?
106 raise ArgumentError, "Invalid resolution options #{options.keys.inspect}"
235107 end
236108 end
237109
238 ##
239 # on_flush accepts a block and executes the block when the resolution's value
240 # is flushed. This makes it possible to model a single, expensive system
241 # call inside of a Ruby object and then define multiple dynamic facts which
242 # resolve by sending messages to the model instance. If one of the dynamic
243 # facts is flushed then it can, in turn, flush the data stored in the model
244 # instance to keep all of the dynamic facts in sync without making multiple,
245 # expensive, system calls.
110 # Sets the code block or external program that will be evaluated to
111 # get the value of the fact.
246112 #
247 # Please see the Solaris zones fact for an example of how this feature may be
248 # used.
113 # @return [void]
249114 #
250 # @see Facter::Util::Fact#flush
251 # @see Facter::Util::Resolution#flush
115 # @overload setcode(string)
116 # Sets an external program to call to get the value of the resolution
117 # @param [String] string the external program to run to get the
118 # value
119 #
120 # @overload setcode(&block)
121 # Sets the resolution's value by evaluating a block at runtime
122 # @param [Proc] block The block to determine the resolution's value.
123 # This block is run when the fact is evaluated. Errors raised from
124 # inside the block are rescued and printed to stderr.
252125 #
253126 # @api public
254 def on_flush(&block)
255 @on_flush_block = block
127 def setcode(string = nil, &block)
128 if string
129 @code = Proc.new do
130 output = Facter::Core::Execution.execute(string, :on_fail => nil)
131 if output.nil? or output.empty?
132 nil
133 else
134 output
135 end
136 end
137 elsif block_given?
138 @code = block
139 else
140 raise ArgumentError, "You must pass either code or a block"
141 end
256142 end
257143
258 ##
259 # flush executes the block, if any, stored by the {on_flush} method
260 #
261 # @see Facter::Util::Fact#flush
262 # @see Facter::Util::Resolution#on_flush
263 #
264 # @api private
265 def flush
266 @on_flush_block.call if @on_flush_block
267 end
144 private
268145
269 def interpreter
270 Facter.warnonce "The 'Facter::Util::Resolution.interpreter' method is deprecated and will be removed in a future version."
271 @interpreter
272 end
273
274 def interpreter=(interp)
275 Facter.warnonce "The 'Facter::Util::Resolution.interpreter=' method is deprecated and will be removed in a future version."
276 @interpreter = interp
277 end
278
279 # Is this resolution mechanism suitable on the system in question?
280 def suitable?
281 unless defined? @suitable
282 @suitable = ! @confines.detect { |confine| ! confine.true? }
146 def resolve_value
147 if @value
148 @value
149 elsif @code.nil?
150 nil
151 elsif @code
152 @code.call()
283153 end
284
285 return @suitable
286 end
287
288 def to_s
289 return self.value()
290 end
291
292 # How we get a value for our resolution mechanism.
293 def value
294 return @value if @value
295 result = nil
296 return result if @code == nil
297
298 starttime = Time.now.to_f
299
300 begin
301 Timeout.timeout(limit) do
302 if @code.is_a?(Proc)
303 result = @code.call()
304 else
305 result = Facter::Util::Resolution.exec(@code)
306 end
307 end
308 rescue Timeout::Error => detail
309 warn "Timed out seeking value for %s" % self.name
310
311 # This call avoids zombies -- basically, create a thread that will
312 # dezombify all of the child processes that we're ignoring because
313 # of the timeout.
314 Thread.new { Process.waitall }
315 return nil
316 rescue => details
317 warn "Could not retrieve %s: %s" % [self.name, details]
318 return nil
319 end
320
321 finishtime = Time.now.to_f
322 ms = (finishtime - starttime) * 1000
323 Facter.show_time "#{self.name}: #{"%.2f" % ms}ms"
324
325 return nil if result == ""
326 return result
327154 end
328155 end
9292 #
9393 # @return [Hash] the parsed output of the zoneadm command
9494 def refresh
95 @zoneadm_output = Facter::Util::Resolution.exec(zoneadm_cmd)
95 @zoneadm_output = Facter::Core::Execution.execute(zoneadm_cmd, {:on_fail => nil})
9696 parse!
9797 end
9898
0 module Facter::Util::Root
1 def self.root?
2 Process.uid == 0
3 end
4 end
1919 private
2020
2121 def self.uptime_proc_uptime
22 if output = Facter::Util::Resolution.exec("/bin/cat #{uptime_file} 2>/dev/null")
22 output = Facter::Core::Execution.execute("/bin/cat #{uptime_file} 2>/dev/null")
23
24 if not output.empty?
2325 output.chomp.split(" ").first.to_i
2426 end
2527 end
2628
2729 def self.uptime_sysctl
28 if output = Facter::Util::Resolution.exec("#{uptime_sysctl_cmd} 2>/dev/null")
30 output = Facter::Core::Execution.execute("#{uptime_sysctl_cmd} 2>/dev/null", :on_fail => nil)
31 if output
2932 compute_uptime(Time.at(output.match(/\d+/)[0].to_i))
3033 end
3134 end
3235
3336 def self.uptime_executable
34 if output = Facter::Util::Resolution.exec("#{uptime_executable_cmd} 2>/dev/null")
37 output = Facter::Core::Execution.execute("#{uptime_executable_cmd} 2>/dev/null", :on_fail => nil)
38
39 if output
3540 up=0
3641 if output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+):(\d+)/
3742 # Regexp handles Solaris, AIX, HP-UX, and Tru64.
0 # A util module for facter containing helper methods
10 module Facter
21 module Util
2 # A util module for facter containing helper methods
33 module Values
44 module_function
5
6 class DeepFreezeError < StandardError; end
7
8 # Duplicate and deeply freeze a given data structure
9 #
10 # @param value [Object] The structure to freeze
11 # @return [void]
12 def deep_freeze(value)
13 case value
14 when Numeric, Symbol, TrueClass, FalseClass, NilClass
15 # These are immutable values, we can safely ignore them
16 value
17 when String
18 value.dup.freeze
19 when Array
20 value.map do |entry|
21 deep_freeze(entry)
22 end.freeze
23 when Hash
24 value.inject({}) do |hash, (key, value)|
25 hash[deep_freeze(key)] = deep_freeze(value)
26 hash
27 end.freeze
28 else
29 raise DeepFreezeError, "Cannot deep freeze #{value}:#{value.class}"
30 end
31 end
32
33 class DeepMergeError < StandardError; end
34
35 # Perform a deep merge of two nested data structures.
36 #
37 # @param left [Object]
38 # @param right [Object]
39 # @param path [Array<String>] The traversal path followed when merging nested hashes
40 #
41 # @return [Object] The merged data structure.
42 def deep_merge(left, right, path = [], &block)
43 ret = nil
44
45 if left.is_a? Hash and right.is_a? Hash
46 ret = left.merge(right) do |key, left_val, right_val|
47 path.push(key)
48 merged = deep_merge(left_val, right_val, path)
49 path.pop
50 merged
51 end
52 elsif left.is_a? Array and right.is_a? Array
53 ret = left.dup.concat(right)
54 elsif right.nil?
55 ret = left
56 elsif left.nil?
57 ret = right
58 elsif left.nil? and right.nil?
59 ret = nil
60 else
61 msg = "Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}"
62 if not path.empty?
63 msg << " at root"
64 msg << path.map { |part| "[#{part.inspect}]" }.join
65 end
66 raise DeepMergeError, msg
67 end
68
69 ret
70 end
571
672 def convert(value)
773 value = value.to_s if value.is_a?(Symbol)
33 ##
44 # virt_what is a delegating helper method intended to make it easier to stub
55 # the system call without affecting other calls to
6 # Facter::Util::Resolution.exec
6 # Facter::Core::Execution.exec
77 #
88 # Per https://bugzilla.redhat.com/show_bug.cgi?id=719611 when run as a
99 # non-root user the virt-what command may emit an error message on stdout,
1111 # method ensures stderr is redirected and that error messages are stripped
1212 # from stdout.
1313 def self.virt_what(command = "virt-what")
14 command = Facter::Util::Resolution.which(command)
14 command = Facter::Core::Execution.which(command)
1515 return unless command
1616
1717 if Facter.value(:kernel) == 'windows'
1919 else
2020 redirected_cmd = "#{command} 2>/dev/null"
2121 end
22 output = Facter::Util::Resolution.exec redirected_cmd
22 output = Facter::Core::Execution.exec redirected_cmd
2323 output.gsub(/^virt-what: .*$/, '') if output
2424 end
2525
2626 ##
2727 # lspci is a delegating helper method intended to make it easier to stub the
28 # system call without affecting other calls to Facter::Util::Resolution.exec
28 # system call without affecting other calls to Facter::Core::Execution.exec
2929 def self.lspci(command = "lspci 2>/dev/null")
30 Facter::Util::Resolution.exec command
30 Facter::Core::Execution.exec command
3131 end
3232
3333 def self.openvz?
3939 return false unless self.openvz?
4040 return false unless FileTest.exists?( '/proc/self/status' )
4141
42 envid = Facter::Util::Resolution.exec( 'grep "envID" /proc/self/status' )
42 envid = Facter::Core::Execution.exec( 'grep "envID" /proc/self/status' )
4343 if envid =~ /^envID:\s+0$/i
4444 return 'openvzhn'
4545 elsif envid =~ /^envID:\s+(\d+)$/i
5555
5656 def self.zone?
5757 return true if FileTest.directory?("/.SUNWnative")
58 z = Facter::Util::Resolution.exec("/sbin/zonename")
58 z = Facter::Core::Execution.exec("/sbin/zonename")
5959 return false unless z
6060 return z.chomp != 'global'
6161 end
9191 txt = if FileTest.exists?("/proc/cpuinfo")
9292 File.read("/proc/cpuinfo")
9393 elsif ["FreeBSD", "OpenBSD"].include? Facter.value(:kernel)
94 Facter::Util::Resolution.exec("/sbin/sysctl -n hw.model")
94 Facter::Core::Execution.exec("/sbin/sysctl -n hw.model")
9595 end
9696 (txt =~ /QEMU Virtual CPU/) ? true : false
9797 end
122122 when "FreeBSD" then "/sbin"
123123 when "GNU/kFreeBSD" then "/bin"
124124 end
125 Facter::Util::Resolution.exec("#{path}/sysctl -n security.jail.jailed") == "1"
125 Facter::Core::Execution.exec("#{path}/sysctl -n security.jail.jailed") == "1"
126126 end
127127
128128 def self.hpvm?
129 Facter::Util::Resolution.exec("/usr/bin/getconf MACHINE_MODEL").chomp =~ /Virtual Machine/
129 Facter::Core::Execution.exec("/usr/bin/getconf MACHINE_MODEL").chomp =~ /Virtual Machine/
130130 end
131131
132132 def self.zlinux?
11 #
22 module Facter::Util::Vlans
33 def self.get_vlan_config
4 output = ""
5 if File.exists?('/proc/net/vlan/config') and File.readable?('/proc/net/vlan/config')
6 output = File.open('/proc/net/vlan/config').read
7 end
8 output
4 if File.exist?('/proc/net/vlan/config') and File.readable?('/proc/net/vlan/config')
5 File.read('/proc/net/vlan/config')
6 end
97 end
108
119 def self.get_vlans
12 vlans = Array.new
13 if self.get_vlan_config
14 self.get_vlan_config.each_line do |line|
15 if line =~ /^([0-9A-Za-z]+)\.([0-9]+) /
16 vlans.insert(-1, $~[2]) if $~[2]
10 if (config = self.get_vlan_config)
11 vlans = []
12 config.each_line do |line|
13 if (match = line.match(/^([0-9A-Za-z]+)\.([0-9]+) /))
14 vlans << match[2] if match[2]
1715 end
1816 end
17 vlans.join(',') unless vlans.empty?
1918 end
20
21 vlans.join(',')
2219 end
2320 end
0 require 'windows/system_info'
1 require 'windows/security'
2 require 'sys/admin'
3
4 module Facter::Util::Root
5 extend ::Windows::SystemInfo
6 extend ::Windows::Security
7
8 def self.root?
9 # if Vista or later, check for unrestricted process token
10 return Win32::Security.elevated_security? unless windows_version < 6.0
11
12 # otherwise 2003 or less
13 check_token_membership
14 end
15
16 def self.check_token_membership
17 sid = 0.chr * 80
18 size = [80].pack('L')
19 member = 0.chr * 4
20
21 unless CreateWellKnownSid(Windows::Security::WinBuiltinAdministratorsSid, nil, sid, size)
22 raise "Failed to create administrators SID"
23 end
24
25 unless IsValidSid(sid)
26 raise "Invalid SID"
27 end
28
29 unless CheckTokenMembership(nil, sid, member)
30 raise "Failed to check membership"
31 end
32
33 # Is administrators SID enabled in calling thread's access token?
34 member.unpack('L')[0] == 1
35 end
36 end
11 #
22 module Facter::Util::Xendomains
33 def self.get_domains
4 if xm_list = Facter::Util::Resolution.exec('/usr/sbin/xm list 2>/dev/null')
4 if xm_list = Facter::Core::Execution.exec('/usr/sbin/xm list 2>/dev/null')
55 domains = xm_list.split("\n").reject { |line| line =~ /^(Name|Domain-0)/ }
66 domains.map { |line| line.split(/\s/)[0] }.join(',')
77 end
00 module Facter
11 if not defined? FACTERVERSION then
2 FACTERVERSION = '1.7.3'
2 FACTERVERSION = '2.0.1'
33 end
44
5 ##
6 # version is a public API method intended to always provide a fast and
7 # lightweight way to determine the version of Facter.
5 # Returns the running version of Facter.
86 #
9 # The intent is that software external to Facter be able to determine the
10 # Facter version with no side-effects. The expected use is:
7 # @comment The intent is that software external to Facter be able to
8 # determine the Facter version with no side-effects. The expected
9 # use is:
1110 #
1211 # require 'facter/version'
1312 # version = Facter.version
1413 #
15 # This function has the following ordering precedence. This precedence list
16 # is designed to facilitate automated packaging tasks by simply writing to
17 # the VERSION file in the same directory as this source file.
14 # This function has the following ordering precedence. This precedence list
15 # is designed to facilitate automated packaging tasks by simply writing to
16 # the VERSION file in the same directory as this source file.
1817 #
19 # 1. If a version has been explicitly assigned using the Facter.version=
20 # method, return that version.
21 # 2. If there is a VERSION file, read the contents, trim any
22 # trailing whitespace, and return that version string.
23 # 3. Return the value of the Facter::FACTERVERSION constant hard-coded into
24 # the source code.
18 # 1. If a version has been explicitly assigned using the Facter.version=
19 # method, return that version.
20 # 2. If there is a VERSION file, read the contents, trim any
21 # trailing whitespace, and return that version string.
22 # 3. Return the value of the Facter::FACTERVERSION constant hard-coded into
23 # the source code.
2524 #
26 # If there is no VERSION file, the method must return the version string of
27 # the nearest parent version that is an officially released version. That is
28 # to say, if a branch named 3.1.x contains 25 patches on top of the most
29 # recent official release of 3.1.1, then the version method must return the
30 # string "3.1.1" if no "VERSION" file is present.
25 # If there is no VERSION file, the method must return the version string of
26 # the nearest parent version that is an officially released version. That is
27 # to say, if a branch named 3.1.x contains 25 patches on top of the most
28 # recent official release of 3.1.1, then the version method must return the
29 # string "3.1.1" if no "VERSION" file is present.
3130 #
32 # By design the version identifier is _not_ intended to vary during the life
33 # a process. There is no guarantee provided that writing to the VERSION file
34 # while a Puppet process is running will cause the version string to be
35 # updated. On the contrary, the contents of the VERSION are cached to reduce
36 # filesystem accesses.
31 # By design the version identifier is _not_ intended to vary during the life of
32 # a process. There is no guarantee provided that writing to the VERSION file
33 # while a Puppet process is running will cause the version string to be
34 # updated. On the contrary, the contents of the VERSION are cached to reduce
35 # filesystem accesses.
3736 #
38 # The VERSION file is intended to be used by package maintainers who may be
39 # applying patches or otherwise changing the software version in a manner
40 # that warrants a different software version identifier. The VERSION file is
41 # intended to be managed and owned by the release process and packaging
42 # related tasks, and as such should not reside in version control. The
43 # FACTERVERSION constant is intended to be version controlled in history.
37 # The VERSION file is intended to be used by package maintainers who may be
38 # applying patches or otherwise changing the software version in a manner
39 # that warrants a different software version identifier. The VERSION file is
40 # intended to be managed and owned by the release process and packaging
41 # related tasks, and as such should not reside in version control. The
42 # FACTERVERSION constant is intended to be version controlled in history.
4443 #
45 # Ideally, this behavior will allow package maintainers to precisely specify
46 # the version of the software they're packaging as in the following example:
44 # Ideally, this behavior will allow package maintainers to precisely specify
45 # the version of the software they're packaging as in the following example:
4746 #
48 # $ git describe > lib/facter/VERSION
49 # $ ruby -r facter -e 'puts Facter.version'
50 # 1.6.14-6-g66f2c99
47 # $ git describe > lib/facter/VERSION
48 # $ ruby -r facter -e 'puts Facter.version'
49 # 1.6.14-6-g66f2c99
5150 #
5251 # @api public
5352 #
6160 @facter_version ||= FACTERVERSION
6261 end
6362
63 # Sets the Facter version
64 #
65 # @return [void]
66 #
67 # @api private
6468 def self.version=(version)
6569 @facter_version = version
6670 end
6771
68 ##
69 # read_version_file reads the content of the "VERSION" file that lives in the
72 # This reads the content of the "VERSION" file that lives in the
7073 # same directory as this source code file.
7174 #
7275 # @api private
7376 #
74 # @return [String] for example: "1.6.14-6-gea42046" or nil if the VERSION
77 # @return [String] the version -- for example: "1.6.14-6-gea42046" or nil if the VERSION
7578 # file does not exist.
7679 def self.read_version_file(path)
7780 if File.exists?(path)
5454 Facter.add("virtual") do
5555 confine :kernel => 'SunOS'
5656 has_weight 10
57 self.timeout = 6
58
5759 setcode do
5860 next "zone" if Facter::Util::Virtual.zone?
5961
60 resolver = Facter::Util::Resolution.new('prtdiag')
61 resolver.timeout = 6
62 resolver.setcode('prtdiag')
63 output = resolver.value
62 output = Facter::Core::Execution.exec('prtdiag')
6463 if output
6564 lines = output.split("\n")
6665 next "parallels" if lines.any? {|l| l =~ /Parallels/ }
9190 confine :kernel => 'OpenBSD'
9291 has_weight 10
9392 setcode do
94 output = Facter::Util::Resolution.exec('sysctl -n hw.product 2>/dev/null')
93 output = Facter::Core::Execution.exec('sysctl -n hw.product 2>/dev/null')
9594 if output
9695 lines = output.split("\n")
9796 next "parallels" if lines.any? {|l| l =~ /Parallels/ }
132131 end
133132
134133 # Parse dmidecode
135 output = Facter::Util::Resolution.exec('dmidecode')
134 output = Facter::Core::Execution.exec('dmidecode 2> /dev/null')
136135 if output
137136 lines = output.split("\n")
138137 next "parallels" if lines.any? {|l| l =~ /Parallels/ }
144143 next "ovirt" if lines.any? {|l| l =~ /Product Name: oVirt Node/ }
145144 end
146145
146 # Default to 'physical'
147 next 'physical'
148 end
149 end
150
151 Facter.add("virtual") do
152 confine do
153 Facter::Core::Execution.which('vmware')
154 end
155
156 setcode do
147157 # Sample output of vmware -v `VMware Server 1.0.5 build-80187`
148 output = Facter::Util::Resolution.exec("vmware -v")
158 output = Facter::Core::Execution.exec("vmware -v")
149159 if output
150160 mdata = output.match /(\S+)\s+(\S+)/
151161 next "#{mdata[1]}_#{mdata[2]}".downcase if mdata
152162 end
153
154 # Default to 'physical'
155 next 'physical'
156163 end
157164 end
158165
162169 require 'facter/util/wmi'
163170 result = nil
164171 Facter::Util::WMI.execquery("SELECT manufacturer, model FROM Win32_ComputerSystem").each do |computersystem|
165 result =
166 case computersystem.model
167 when /VirtualBox/ then "virtualbox"
168 when /Virtual Machine/
169 computersystem.manufacturer =~ /Microsoft/ ? "hyperv" : nil
170 when /VMware/ then "vmware"
171 when /KVM/ then "kvm"
172 else "physical"
173 end
172 case computersystem.model
173 when /VirtualBox/
174 result = "virtualbox"
175 when /Virtual Machine/
176 result = "hyperv" if computersystem.manufacturer =~ /Microsoft/
177 when /VMware/
178 result = "vmware"
179 when /KVM/
180 result = "kvm"
181 when /Bochs/
182 result = "bochs"
183 end
184
185 if result.nil? and computersystem.manufacturer =~ /Xen/
186 result = "xen"
187 end
188
174189 break
175190 end
191 result ||= "physical"
192
176193 result
177194 end
178195 end
241258 confine :kernel => %w{Linux FreeBSD OpenBSD SunOS HP-UX Darwin GNU/kFreeBSD windows}
242259
243260 setcode do
244 physical_types = %w{physical xen0 vmware_server vmware_workstation openvzhn}
261 physical_types = %w{physical xen0 vmware_server vmware_workstation openvzhn vserver_host}
245262
246263 if physical_types.include? Facter.value(:virtual)
247264 "false"
11
22 Facter.add('zfs_version') do
33 setcode do
4 if Facter::Util::Resolution.which('zfs')
5 zfs_v = Facter::Util::Resolution.exec('zfs upgrade -v')
6 zfs_version = zfs_v.scan(/^\s+(\d+)\s+/m).flatten.last unless zfs_v.nil?
4 if Facter::Core::Execution.which('zfs')
5 zfs_v = Facter::Core::Execution.exec('zfs upgrade -v')
6 zfs_version = zfs_v.scan(/^\s+(\d+)\s+/m).flatten.last unless zfs_v.empty?
77 end
88 end
99 end
11
22 Facter.add('zpool_version') do
33 setcode do
4 if Facter::Util::Resolution.which('zpool')
5 zpool_v = Facter::Util::Resolution.exec('zpool upgrade -v')
6 zpool_version = zpool_v.match(/ZFS pool version (\d+)./).captures.first unless zpool_v.nil?
4 if Facter::Core::Execution.which('zpool')
5 zpool_v = Facter::Core::Execution.exec('zpool upgrade -v')
6 zpool_version = zpool_v.match(/ZFS pool version (\d+)./).captures.first unless zpool_v.empty?
77 end
88 end
99 end
1515
1616 require 'facter/version'
1717
18 # Functions as a hash of 'facts' about your system system, such as MAC
19 # address, IP address, architecture, etc.
20 #
21 # @example Retrieve a fact
22 # puts Facter['operatingsystem'].value
23 #
24 # @example Retrieve all facts
25 # Facter.to_hash
26 # => { "kernel"=>"Linux", "uptime_days"=>0, "ipaddress"=>"10.0.0.1" }
27 #
28 # @api public
1829 module Facter
19 # This is just so the other classes have the constant.
30 # Most core functionality of facter is implemented in `Facter::Util`.
31 # @api public
2032 module Util; end
2133
2234 require 'facter/util/fact'
2335 require 'facter/util/collection'
24 require 'facter/util/monkey_patches'
2536
2637 include Comparable
2738 include Enumerable
2839
29 # = Facter
30 # Functions as a hash of 'facts' you might care about about your
31 # system, such as mac address, IP address, Video card, etc.
32 # returns them dynamically
33
34 # == Synopsis
35 #
36 # Generally, treat <tt>Facter</tt> as a hash:
37 # == Example
38 # require 'facter'
39 # puts Facter['operatingsystem']
40 #
41
42 GREEN = ""
43 RESET = ""
44 @@debug = 0
45 @@timing = 0
46 @@messages = {}
47 @@debug_messages = {}
40 require 'facter/core/logging'
41 extend Facter::Core::Logging
4842
4943 # module methods
5044
45 # Accessor for the collection object which holds all the facts
46 # @return [Facter::Util::Collection] the collection of facts
47 #
48 # @api private
5149 def self.collection
5250 unless defined?(@collection) and @collection
5351 @collection = Facter::Util::Collection.new(
5755 @collection
5856 end
5957
60 # Add some debugging
61 def self.debug(string)
62 if string.nil?
63 return
64 end
65 if self.debugging?
66 puts GREEN + string + RESET
67 end
68 end
69
70 # Debug once.
71 def self.debugonce(msg)
72 if msg and not msg.empty? and @@debug_messages[msg].nil?
73 @@debug_messages[msg] = true
74 debug(msg)
75 end
76 end
77
78 def self.debugging?
79 @@debug != 0
80 end
81
82 # show the timing information
83 def self.show_time(string)
84 puts "#{GREEN}#{string}#{RESET}" if string and Facter.timing?
85 end
86
87 def self.timing?
88 @@timing != 0
89 end
90
91 # Facter.json? is meant to provide a lightweight way to check if the JSON
92 # "feature" is available.
58 # Returns whether the JSON "feature" is available.
59 #
60 # @api private
9361 def self.json?
9462 begin
9563 require 'json'
9967 end
10068 end
10169
102 # Return a fact object by name. If you use this, you still have to call
103 # 'value' on it to retrieve the actual value.
70 # Returns a fact object by name. If you use this, you still have to
71 # call {Facter::Util::Fact#value `value`} on it to retrieve the actual
72 # value.
73 #
74 # @param name [String] the name of the fact
75 #
76 # @return [Facter::Util::Fact, nil] The fact object, or nil if no fact
77 # is found.
78 #
79 # @api public
10480 def self.[](name)
10581 collection.fact(name)
10682 end
10783
108 class << self
109 [:fact, :flush, :list, :value].each do |method|
110 define_method(method) do |*args|
111 collection.send(method, *args)
112 end
113 end
114
115 [:list, :to_hash].each do |method|
116 define_method(method) do |*args|
117 collection.load_all
118 collection.send(method, *args)
119 end
120 end
121 end
122
123
124 # Add a resolution mechanism for a named fact. This does not distinguish
125 # between adding a new fact and adding a new way to resolve a fact.
84 # (see [])
85 def self.fact(name)
86 collection.fact(name)
87 end
88
89 # Flushes cached values for all facts. This does not cause code to be
90 # reloaded; it only clears the cached results.
91 #
92 # @return [void]
93 #
94 # @api public
95 def self.flush
96 collection.flush
97 end
98
99 # Lists all fact names
100 #
101 # @return [Array<String>] array of fact names
102 #
103 # @api public
104 def self.list
105 collection.list
106 end
107
108 # Gets the value for a fact. Returns `nil` if no such fact exists.
109 #
110 # @param name [String] the fact name
111 # @return [Object, nil] the value of the fact, or nil if no fact is
112 # found
113 #
114 # @api public
115 def self.value(name)
116 collection.value(name)
117 end
118
119 # Gets a hash mapping fact names to their values
120 #
121 # @return [Hash{String => Object}] the hash of fact names and values
122 #
123 # @api public
124 def self.to_hash
125 collection.load_all
126 collection.to_hash
127 end
128
129 # Define a new fact or extend an existing fact.
130 #
131 # @param name [Symbol] The name of the fact to define
132 # @param options [Hash] A hash of options to set on the fact
133 #
134 # @return [Facter::Util::Fact] The fact that was defined
135 #
136 # @api public
137 # @see {Facter::Util::Collection#define_fact}
138 def self.define_fact(name, options = {}, &block)
139 collection.define_fact(name, options, &block)
140 end
141
142 # Adds a {Facter::Util::Resolution resolution} mechanism for a named
143 # fact. This does not distinguish between adding a new fact and adding
144 # a new way to resolve a fact.
145 #
146 # @overload add(name, options = {}, { || ... })
147 # @param name [String] the fact name
148 # @param options [Hash] optional parameters for the fact - attributes
149 # of {Facter::Util::Fact} and {Facter::Util::Resolution} can be
150 # supplied here
151 # @option options [Integer] :timeout set the
152 # {Facter::Util::Resolution#timeout timeout} for this resolution
153 # @param block [Proc] a block defining a fact resolution
154 #
155 # @return [Facter::Util::Fact] the fact object, which includes any previously
156 # defined resolutions
157 #
158 # @api public
126159 def self.add(name, options = {}, &block)
127160 collection.add(name, options, &block)
128161 end
129162
163 # Iterates over fact names and values
164 #
165 # @yieldparam [String] name the fact name
166 # @yieldparam [String] value the current value of the fact
167 #
168 # @return [void]
169 #
170 # @api public
130171 def self.each
131172 # Make sure all facts are loaded.
132173 collection.load_all
136177 end
137178 end
138179
139 class << self
140 # Allow users to call fact names directly on the Facter class,
141 # either retrieving the value or comparing it to an existing value.
142 def method_missing(name, *args)
143 question = false
144 if name.to_s =~ /\?$/
145 question = true
146 name = name.to_s.sub(/\?$/,'')
147 end
148
149 if fact = collection.fact(name)
150 if question
151 value = fact.value.downcase
152 args.each do |arg|
153 if arg.to_s.downcase == value
154 return true
155 end
156 end
157
158 # If we got this far, there was no match.
159 return false
160 else
161 return fact.value
162 end
163 else
164 # Else, fail like a normal missing method.
165 raise NoMethodError, "Could not find fact '%s'" % name
166 end
167 end
168 end
169
170 # Clear all facts. Mostly used for testing.
180 # Clears all cached values and removes all facts from memory.
181 #
182 # @return [void]
183 #
184 # @api public
171185 def self.clear
172186 Facter.flush
173187 Facter.reset
174188 end
175189
176 # Clear all messages. Used only in testing. Can't add to self.clear
177 # because we don't want to warn multiple times for items that are warnonce'd
178 def self.clear_messages
179 @@messages.clear
180 end
181
182 # Set debugging on or off.
183 def self.debugging(bit)
184 if bit
185 case bit
186 when TrueClass; @@debug = 1
187 when FalseClass; @@debug = 0
188 when Fixnum
189 if bit > 0
190 @@debug = 1
191 else
192 @@debug = 0
193 end
194 when String;
195 if bit.downcase == 'off'
196 @@debug = 0
197 else
198 @@debug = 1
199 end
200 else
201 @@debug = 0
202 end
203 else
204 @@debug = 0
205 end
206 end
207
208 # Set timing on or off.
209 def self.timing(bit)
210 if bit
211 case bit
212 when TrueClass; @@timing = 1
213 when Fixnum
214 if bit > 0
215 @@timing = 1
216 else
217 @@timing = 0
218 end
219 end
220 else
221 @@timing = 0
222 end
223 end
224
225 def self.warn(msg)
226 if Facter.debugging? and msg and not msg.empty?
227 msg = [msg] unless msg.respond_to? :each
228 msg.each { |line| Kernel.warn line }
229 end
230 end
231
232 # Warn once.
233 def self.warnonce(msg)
234 if msg and not msg.empty? and @@messages[msg].nil?
235 @@messages[msg] = true
236 Kernel.warn(msg)
237 end
238 end
239
240 # Remove them all.
190 # Removes all facts from memory. Use this when the fact code has
191 # changed on disk and needs to be reloaded.
192 #
193 # @return [void]
194 #
195 # @api public
241196 def self.reset
242197 @collection = nil
243 end
244
245 # Load all of the default facts, and then everything from disk.
198 reset_search_path!
199 end
200
201 # Loads all facts.
202 #
203 # @return [void]
204 #
205 # @api public
246206 def self.loadfacts
247207 collection.load_all
248208 end
249209
250 @search_path = []
251
252 # Register a directory to search through.
210 # Register directories to be searched for facts. The registered directories
211 # must be absolute paths or they will be ignored.
212 #
213 # @param dirs [String] directories to search
214 #
215 # @return [void]
216 #
217 # @api public
253218 def self.search(*dirs)
254219 @search_path += dirs
255220 end
256221
257 # Return our registered search directories.
222 # Returns the registered search directories.
223 #
224 # @return [Array<String>] An array of the directories searched
225 #
226 # @api public
258227 def self.search_path
259228 @search_path.dup
260229 end
230
231 # Reset the Facter search directories.
232 #
233 # @api private
234 # @return [void]
235 def self.reset_search_path!
236 @search_path = []
237 end
238
239 reset_search_path!
240
241 # Registers directories to be searched for external facts.
242 #
243 # @param dirs [Array<String>] directories to search
244 #
245 # @return [void]
246 #
247 # @api public
248 def self.search_external(dirs)
249 Facter::Util::Config.external_facts_dirs += dirs
250 end
251
252 # Returns the registered search directories.
253 #
254 # @return [Array<String>] An array of the directories searched
255 #
256 # @api public
257 def self.search_external_path
258 Facter::Util::Config.external_facts_dirs.dup
259 end
261260 end
0 .TH "FACTER" "8" "September 2012" "Puppet Labs, Inc" "Facter manual"
1 .SH NAME
2 Facter - Collect and display facts about the system.
0 .\" generated with Ronn/v0.7.3
1 .\" http://github.com/rtomayko/ronn/tree/0.7.3
32 .
4 .SH SYNOPSIS
5 .sp
6 Collect and display facts about the system.
7 .SH USAGE
8 .INDENT 0.0
9 .INDENT 3.5
10 facter [\-d|\-\-debug] [\-h|\-\-help] [\-p|\-\-puppet] [\-v|\-\-version] [\-y|\-\-yaml] [\-j|\-\-json] [fact] [fact] [...]
11 .UNINDENT
12 .UNINDENT
13 .SH DESCRIPTION
14 .sp
15 Collect and display facts about the current system. The library behind
16 Facter is easy to expand, making Facter an easy way to collect
17 information about a system from within the shell or within Ruby.
18 .sp
19 If no facts are specifically asked for, then all facts will be returned.
20 .SH OPTIONS
21 .sp
22 yaml: Emit facts in YAML format.
23 .sp
24 json: Emit facts in JSON format.
25 .sp
26 puppet: Load the Puppet libraries, thus allowing Facter to load Puppet specific (custom) facts
27 .sp
28 version: Print the version and exit.
29 .sp
30 help: Print this help message.
31 .sp
32 debug: Enable debugging.
33 .sp
34 trace: Enable backtraces.
35 .sp
36 timing: Enable timing.
37 .SH EXAMPLE
38 .INDENT 0.0
39 .INDENT 3.5
40 facter kernel
41 .UNINDENT
42 .UNINDENT
43 .SH AUTHOR
44 .sp
3 .TH "FACTER" "8" "February 2014" "" ""
4 .
5 .SH "NAME"
6 \fBfacter\fR \- Gather system information
7 .
8 .SH "SYNOPSIS"
9 Collect and display facts about the system\.
10 .
11 .SH "USAGE"
12 .
13 .nf
14
15 facter [\-h|\-\-help] [\-t|\-\-timing] [\-d|\-\-debug] [\-p|\-\-puppet] [\-v|\-\-version]
16 [\-y|\-\-yaml] [\-j|\-\-json] [\-\-plaintext] [\-\-external\-dir DIR] [\-\-no\-external\-dir]
17 [fact] [fact] [\.\.\.]
18 .
19 .fi
20 .
21 .SH "DESCRIPTION"
22 Collect and display facts about the current system\. The library behind Facter is easy to expand, making Facter an easy way to collect information about a system from within the shell or within Ruby\.
23 .
24 .P
25 If no facts are specifically asked for, then all facts will be returned\.
26 .
27 .SH "EXAMPLE"
28 Display all facts:
29 .
30 .IP "" 4
31 .
32 .nf
33
34 $ facter
35 architecture => amd64
36 blockdevices => sda,sr0
37 domain => example\.com
38 fqdn => puppet\.example\.com
39 hardwaremodel => x86_64
40 [\.\.\.]
41 .
42 .fi
43 .
44 .IP "" 0
45 .
46 .P
47 Display a single fact:
48 .
49 .IP "" 4
50 .
51 .nf
52
53 $ facter kernel
54 Linux
55 .
56 .fi
57 .
58 .IP "" 0
59 .
60 .P
61 Format facts as JSON:
62 .
63 .IP "" 4
64 .
65 .nf
66
67 $ facter \-\-json architecture kernel hardwaremodel
68 {
69 "architecture": "amd64",
70 "kernel": "Linux",
71 "hardwaremodel": "x86_64"
72 }
73 .
74 .fi
75 .
76 .IP "" 0
77 .
78 .SH "AUTHOR"
4579 Luke Kanies
46 .SH COPYRIGHT
47 .sp
48 Copyright (c) 2012 Puppet Labs, Inc Licensed under the Apache 2.0
49 license
5080 .
81 .SH "COPYRIGHT"
82 Copyright (c) 2011\-2014 Puppet Labs, Inc Licensed under the Apache 2\.0 license
83 .
84 .SH "OPTIONS"
85 .
86 .nf
87
88 \-y, \-\-yaml Emit facts in YAML format\.
89 \-j, \-\-json Emit facts in JSON format\.
90 \-\-plaintext Emit facts in plaintext format\.
91 \-\-trace Enable backtraces\.
92 \-\-external\-dir DIR The directory to use for external facts\.
93 \-\-no\-external\-dir Turn off external facts\.
94 \-d, \-\-debug Enable debugging\.
95 \-t, \-\-timing Enable timing\.
96 \-p, \-\-puppet Load the Puppet libraries, thus allowing Facter to load Puppet\-specific facts\.
97 \-v, \-\-version Print the version and exit\.
98 \-h, \-\-help Print this help message\.
99 .
100 .fi
101
0 processor : 0
1 vendor_id : GenuineIntel
2 cpu family : 6
3 model : 44
4 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
5 stepping : 2
6 cpu MHz : 3068.000
7 cache size : 12288 KB
8 physical id : 0
9 siblings : 12
10 core id : 0
11 cpu cores : 6
12 apicid : 0
13 initial apicid : 0
14 fpu : yes
15 fpu_exception : yes
16 cpuid level : 11
17 wp : yes
18 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
19 bogomips : 6133.05
20 clflush size : 64
21 cache_alignment : 64
22 address sizes : 40 bits physical, 48 bits virtual
23 power management:
24
25 processor : 1
26 vendor_id : GenuineIntel
27 cpu family : 6
28 model : 44
29 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
30 stepping : 2
31 cpu MHz : 3068.000
32 cache size : 12288 KB
33 physical id : 0
34 siblings : 12
35 core id : 1
36 cpu cores : 6
37 apicid : 2
38 initial apicid : 2
39 fpu : yes
40 fpu_exception : yes
41 cpuid level : 11
42 wp : yes
43 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
44 bogomips : 6133.05
45 clflush size : 64
46 cache_alignment : 64
47 address sizes : 40 bits physical, 48 bits virtual
48 power management:
49
50 processor : 2
51 vendor_id : GenuineIntel
52 cpu family : 6
53 model : 44
54 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
55 stepping : 2
56 cpu MHz : 3068.000
57 cache size : 12288 KB
58 physical id : 0
59 siblings : 12
60 core id : 2
61 cpu cores : 6
62 apicid : 4
63 initial apicid : 4
64 fpu : yes
65 fpu_exception : yes
66 cpuid level : 11
67 wp : yes
68 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
69 bogomips : 6133.05
70 clflush size : 64
71 cache_alignment : 64
72 address sizes : 40 bits physical, 48 bits virtual
73 power management:
74
75 processor : 3
76 vendor_id : GenuineIntel
77 cpu family : 6
78 model : 44
79 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
80 stepping : 2
81 cpu MHz : 3068.000
82 cache size : 12288 KB
83 physical id : 0
84 siblings : 12
85 core id : 8
86 cpu cores : 6
87 apicid : 16
88 initial apicid : 16
89 fpu : yes
90 fpu_exception : yes
91 cpuid level : 11
92 wp : yes
93 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
94 bogomips : 6133.05
95 clflush size : 64
96 cache_alignment : 64
97 address sizes : 40 bits physical, 48 bits virtual
98 power management:
99
100 processor : 4
101 vendor_id : GenuineIntel
102 cpu family : 6
103 model : 44
104 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
105 stepping : 2
106 cpu MHz : 3068.000
107 cache size : 12288 KB
108 physical id : 0
109 siblings : 12
110 core id : 9
111 cpu cores : 6
112 apicid : 18
113 initial apicid : 18
114 fpu : yes
115 fpu_exception : yes
116 cpuid level : 11
117 wp : yes
118 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
119 bogomips : 6133.05
120 clflush size : 64
121 cache_alignment : 64
122 address sizes : 40 bits physical, 48 bits virtual
123 power management:
124
125 processor : 5
126 vendor_id : GenuineIntel
127 cpu family : 6
128 model : 44
129 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
130 stepping : 2
131 cpu MHz : 3068.000
132 cache size : 12288 KB
133 physical id : 0
134 siblings : 12
135 core id : 10
136 cpu cores : 6
137 apicid : 20
138 initial apicid : 20
139 fpu : yes
140 fpu_exception : yes
141 cpuid level : 11
142 wp : yes
143 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
144 bogomips : 6133.05
145 clflush size : 64
146 cache_alignment : 64
147 address sizes : 40 bits physical, 48 bits virtual
148 power management:
149
150 processor : 6
151 vendor_id : GenuineIntel
152 cpu family : 6
153 model : 44
154 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
155 stepping : 2
156 cpu MHz : 3068.000
157 cache size : 12288 KB
158 physical id : 1
159 siblings : 12
160 core id : 0
161 cpu cores : 6
162 apicid : 32
163 initial apicid : 32
164 fpu : yes
165 fpu_exception : yes
166 cpuid level : 11
167 wp : yes
168 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
169 bogomips : 6133.17
170 clflush size : 64
171 cache_alignment : 64
172 address sizes : 40 bits physical, 48 bits virtual
173 power management:
174
175 processor : 7
176 vendor_id : GenuineIntel
177 cpu family : 6
178 model : 44
179 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
180 stepping : 2
181 cpu MHz : 3068.000
182 cache size : 12288 KB
183 physical id : 1
184 siblings : 12
185 core id : 1
186 cpu cores : 6
187 apicid : 34
188 initial apicid : 34
189 fpu : yes
190 fpu_exception : yes
191 cpuid level : 11
192 wp : yes
193 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
194 bogomips : 6133.17
195 clflush size : 64
196 cache_alignment : 64
197 address sizes : 40 bits physical, 48 bits virtual
198 power management:
199
200 processor : 8
201 vendor_id : GenuineIntel
202 cpu family : 6
203 model : 44
204 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
205 stepping : 2
206 cpu MHz : 3068.000
207 cache size : 12288 KB
208 physical id : 1
209 siblings : 12
210 core id : 2
211 cpu cores : 6
212 apicid : 36
213 initial apicid : 36
214 fpu : yes
215 fpu_exception : yes
216 cpuid level : 11
217 wp : yes
218 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
219 bogomips : 6133.17
220 clflush size : 64
221 cache_alignment : 64
222 address sizes : 40 bits physical, 48 bits virtual
223 power management:
224
225 processor : 9
226 vendor_id : GenuineIntel
227 cpu family : 6
228 model : 44
229 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
230 stepping : 2
231 cpu MHz : 3068.000
232 cache size : 12288 KB
233 physical id : 1
234 siblings : 12
235 core id : 8
236 cpu cores : 6
237 apicid : 48
238 initial apicid : 48
239 fpu : yes
240 fpu_exception : yes
241 cpuid level : 11
242 wp : yes
243 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
244 bogomips : 6133.17
245 clflush size : 64
246 cache_alignment : 64
247 address sizes : 40 bits physical, 48 bits virtual
248 power management:
249
250 processor : 10
251 vendor_id : GenuineIntel
252 cpu family : 6
253 model : 44
254 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
255 stepping : 2
256 cpu MHz : 3068.000
257 cache size : 12288 KB
258 physical id : 1
259 siblings : 12
260 core id : 9
261 cpu cores : 6
262 apicid : 50
263 initial apicid : 50
264 fpu : yes
265 fpu_exception : yes
266 cpuid level : 11
267 wp : yes
268 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
269 bogomips : 6133.17
270 clflush size : 64
271 cache_alignment : 64
272 address sizes : 40 bits physical, 48 bits virtual
273 power management:
274
275 processor : 11
276 vendor_id : GenuineIntel
277 cpu family : 6
278 model : 44
279 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
280 stepping : 2
281 cpu MHz : 3068.000
282 cache size : 12288 KB
283 physical id : 1
284 siblings : 12
285 core id : 10
286 cpu cores : 6
287 apicid : 52
288 initial apicid : 52
289 fpu : yes
290 fpu_exception : yes
291 cpuid level : 11
292 wp : yes
293 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
294 bogomips : 6133.17
295 clflush size : 64
296 cache_alignment : 64
297 address sizes : 40 bits physical, 48 bits virtual
298 power management:
299
300 processor : 12
301 vendor_id : GenuineIntel
302 cpu family : 6
303 model : 44
304 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
305 stepping : 2
306 cpu MHz : 3068.000
307 cache size : 12288 KB
308 physical id : 0
309 siblings : 12
310 core id : 0
311 cpu cores : 6
312 apicid : 1
313 initial apicid : 1
314 fpu : yes
315 fpu_exception : yes
316 cpuid level : 11
317 wp : yes
318 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
319 bogomips : 6133.05
320 clflush size : 64
321 cache_alignment : 64
322 address sizes : 40 bits physical, 48 bits virtual
323 power management:
324
325 processor : 13
326 vendor_id : GenuineIntel
327 cpu family : 6
328 model : 44
329 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
330 stepping : 2
331 cpu MHz : 3068.000
332 cache size : 12288 KB
333 physical id : 0
334 siblings : 12
335 core id : 1
336 cpu cores : 6
337 apicid : 3
338 initial apicid : 3
339 fpu : yes
340 fpu_exception : yes
341 cpuid level : 11
342 wp : yes
343 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
344 bogomips : 6133.05
345 clflush size : 64
346 cache_alignment : 64
347 address sizes : 40 bits physical, 48 bits virtual
348 power management:
349
350 processor : 14
351 vendor_id : GenuineIntel
352 cpu family : 6
353 model : 44
354 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
355 stepping : 2
356 cpu MHz : 3068.000
357 cache size : 12288 KB
358 physical id : 0
359 siblings : 12
360 core id : 2
361 cpu cores : 6
362 apicid : 5
363 initial apicid : 5
364 fpu : yes
365 fpu_exception : yes
366 cpuid level : 11
367 wp : yes
368 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
369 bogomips : 6133.05
370 clflush size : 64
371 cache_alignment : 64
372 address sizes : 40 bits physical, 48 bits virtual
373 power management:
374
375 processor : 15
376 vendor_id : GenuineIntel
377 cpu family : 6
378 model : 44
379 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
380 stepping : 2
381 cpu MHz : 3068.000
382 cache size : 12288 KB
383 physical id : 0
384 siblings : 12
385 core id : 8
386 cpu cores : 6
387 apicid : 17
388 initial apicid : 17
389 fpu : yes
390 fpu_exception : yes
391 cpuid level : 11
392 wp : yes
393 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
394 bogomips : 6133.05
395 clflush size : 64
396 cache_alignment : 64
397 address sizes : 40 bits physical, 48 bits virtual
398 power management:
399
400 processor : 16
401 vendor_id : GenuineIntel
402 cpu family : 6
403 model : 44
404 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
405 stepping : 2
406 cpu MHz : 3068.000
407 cache size : 12288 KB
408 physical id : 0
409 siblings : 12
410 core id : 9
411 cpu cores : 6
412 apicid : 19
413 initial apicid : 19
414 fpu : yes
415 fpu_exception : yes
416 cpuid level : 11
417 wp : yes
418 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
419 bogomips : 6133.05
420 clflush size : 64
421 cache_alignment : 64
422 address sizes : 40 bits physical, 48 bits virtual
423 power management:
424
425 processor : 17
426 vendor_id : GenuineIntel
427 cpu family : 6
428 model : 44
429 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
430 stepping : 2
431 cpu MHz : 3068.000
432 cache size : 12288 KB
433 physical id : 0
434 siblings : 12
435 core id : 10
436 cpu cores : 6
437 apicid : 21
438 initial apicid : 21
439 fpu : yes
440 fpu_exception : yes
441 cpuid level : 11
442 wp : yes
443 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
444 bogomips : 6133.05
445 clflush size : 64
446 cache_alignment : 64
447 address sizes : 40 bits physical, 48 bits virtual
448 power management:
449
450 processor : 18
451 vendor_id : GenuineIntel
452 cpu family : 6
453 model : 44
454 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
455 stepping : 2
456 cpu MHz : 3068.000
457 cache size : 12288 KB
458 physical id : 1
459 siblings : 12
460 core id : 0
461 cpu cores : 6
462 apicid : 33
463 initial apicid : 33
464 fpu : yes
465 fpu_exception : yes
466 cpuid level : 11
467 wp : yes
468 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
469 bogomips : 6133.17
470 clflush size : 64
471 cache_alignment : 64
472 address sizes : 40 bits physical, 48 bits virtual
473 power management:
474
475 processor : 19
476 vendor_id : GenuineIntel
477 cpu family : 6
478 model : 44
479 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
480 stepping : 2
481 cpu MHz : 3068.000
482 cache size : 12288 KB
483 physical id : 1
484 siblings : 12
485 core id : 1
486 cpu cores : 6
487 apicid : 35
488 initial apicid : 35
489 fpu : yes
490 fpu_exception : yes
491 cpuid level : 11
492 wp : yes
493 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
494 bogomips : 6133.17
495 clflush size : 64
496 cache_alignment : 64
497 address sizes : 40 bits physical, 48 bits virtual
498 power management:
499
500 processor : 20
501 vendor_id : GenuineIntel
502 cpu family : 6
503 model : 44
504 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
505 stepping : 2
506 cpu MHz : 3068.000
507 cache size : 12288 KB
508 physical id : 1
509 siblings : 12
510 core id : 2
511 cpu cores : 6
512 apicid : 37
513 initial apicid : 37
514 fpu : yes
515 fpu_exception : yes
516 cpuid level : 11
517 wp : yes
518 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
519 bogomips : 6133.17
520 clflush size : 64
521 cache_alignment : 64
522 address sizes : 40 bits physical, 48 bits virtual
523 power management:
524
525 processor : 21
526 vendor_id : GenuineIntel
527 cpu family : 6
528 model : 44
529 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
530 stepping : 2
531 cpu MHz : 3068.000
532 cache size : 12288 KB
533 physical id : 1
534 siblings : 12
535 core id : 8
536 cpu cores : 6
537 apicid : 49
538 initial apicid : 49
539 fpu : yes
540 fpu_exception : yes
541 cpuid level : 11
542 wp : yes
543 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
544 bogomips : 6133.17
545 clflush size : 64
546 cache_alignment : 64
547 address sizes : 40 bits physical, 48 bits virtual
548 power management:
549
550 processor : 22
551 vendor_id : GenuineIntel
552 cpu family : 6
553 model : 44
554 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
555 stepping : 2
556 cpu MHz : 3068.000
557 cache size : 12288 KB
558 physical id : 1
559 siblings : 12
560 core id : 9
561 cpu cores : 6
562 apicid : 51
563 initial apicid : 51
564 fpu : yes
565 fpu_exception : yes
566 cpuid level : 11
567 wp : yes
568 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
569 bogomips : 6133.17
570 clflush size : 64
571 cache_alignment : 64
572 address sizes : 40 bits physical, 48 bits virtual
573 power management:
574
575 processor : 23
576 vendor_id : GenuineIntel
577 cpu family : 6
578 model : 44
579 model name : Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
580 stepping : 2
581 cpu MHz : 3068.000
582 cache size : 12288 KB
583 physical id : 1
584 siblings : 12
585 core id : 10
586 cpu cores : 6
587 apicid : 53
588 initial apicid : 53
589 fpu : yes
590 fpu_exception : yes
591 cpuid level : 11
592 wp : yes
593 flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid
594 bogomips : 6133.17
595 clflush size : 64
596 cache_alignment : 64
597 address sizes : 40 bits physical, 48 bits virtual
598 power management:
599
0 CPU info:
1 Number of CPUs = 8
2 Number of enabled CPUs = 2
3 Number of enabled sockets = 2
4 Cores per socket = 2
5 Clock speed = 1598 MHz
6 Bus speed = 533 MT/s
7 CPUID registers
8 vendor information = "GenuineIntel"
9 processor serial number = 0x0000000000000000
10 processor version info = 0x0000000020000704
11 architecture revision: 0
12 processor family: 32 Intel(R) Itanium 2 9000 series
13 processor model: 1 Intel(R) Itanium 2 9000 series
14 Bus features
15 implemented = 0xbdf0000020000000
16 selected = 0x0020000000000000
17 Exclusive Bus Cache Line Replacement Enabled
18 processor revision: 7 Stepping C2
19 largest CPUID reg: 4
20 processor capabilities = 0x0000000000000005
21 implements long branch: 1
22 implements 16-byte atomic operations: 1
23
24 Cache info (per core):
25 L1 Instruction: size = 16 KB, associativity = 4
26 L1 Data: size = 16 KB, associativity = 4
27 L2 Instruction: size = 1024 KB, associativity = 8
28 L2 Data: size = 256 KB, associativity = 8
29 L3 Unified: size = 12288 KB, associativity = 12
30
31 Memory = 32700 MB (31.933594 GB)
32
33 Firmware info:
34 Firmware revision = 9.66
35 FP SWA driver revision: 1.18
36 IPMI is supported on this system.
37 ERROR: Unable to obtain manageability firmware revision info.
38
39 Platform info:
40 model string = "ia64 hp superdome server SD32B"
41 machine id number = STRING_WITH_DASHES
42 machine serial number = STRING
43
44 OS info:
45 sysname = HP-UX
46 nodename = HOSTNAME
47 release = B.11.23
48 version = U (unlimited-user license)
49 machine = ia64
50 idnumber = NUMBER
51 vmunix _release_version:
52 @(#) $Revision: vmunix: B11.23_LR FLAVOR=perf Fri Aug 29 22:35:38 PDT 2003 $
0 CPU info:
1 Intel(R) Itanium(R) Processor 9340 (1.6 GHz, 15 MB)
2 4 cores, 8 logical processors per socket
3 4.79 GT/s QPI, CPU version E0
4 Active processor count:
5 2 sockets
6 6 cores (3 per socket)
7 12 logical processors (6 per socket)
8 LCPU attribute is enabled
9
10 Memory: 14332 MB (14 GB)
11
12 Firmware info:
13 Firmware revision: 004.044.000
14 FP SWA driver revision: 1.18
15 IPMI is supported on this system.
16 BMC firmware revision: 2.53
17
18 Platform info:
19 Model: "ia64 hp Superdome2 16s"
20 Machine ID number: STRING_WITH_DASHES
21 Machine serial number: STRING
22
23 OS info:
24 Nodename: HOSTNAME
25 Release: HP-UX B.11.31
26 Version: U (unlimited-user license)
27 Machine: ia64
28 ID Number: NUMBER
29 vmunix _release_version:
30 @(#) $Revision: vmunix: B.11.31_LR FLAVOR=perf
0 0 on-line since 10/01/2012 21:05:55
1 1 on-line since 10/01/2012 21:06:00
2 2 on-line since 10/01/2012 21:06:00
3 3 on-line since 10/01/2012 21:06:00
4 4 on-line since 10/01/2012 21:06:00
5 5 on-line since 10/01/2012 21:06:00
6 6 on-line since 10/01/2012 21:06:00
7 7 on-line since 10/01/2012 21:06:00
8 8 on-line since 10/01/2012 21:06:00
9 9 on-line since 10/01/2012 21:06:00
10 10 on-line since 10/01/2012 21:06:00
11 11 on-line since 10/01/2012 21:06:00
12 12 on-line since 10/01/2012 21:06:01
13 13 on-line since 10/01/2012 21:06:01
14 14 on-line since 10/01/2012 21:06:01
15 15 on-line since 10/01/2012 21:06:01
16 16 on-line since 10/01/2012 21:06:01
17 17 on-line since 10/01/2012 21:06:01
18 18 on-line since 10/01/2012 21:06:01
19 19 on-line since 10/01/2012 21:06:01
20 20 on-line since 10/01/2012 21:06:01
21 21 on-line since 10/01/2012 21:06:01
22 22 on-line since 10/01/2012 21:06:01
23 23 on-line since 10/01/2012 21:06:01
0 lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
1 options=3<RXCSUM,TXCSUM>
2 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
3 inet 127.0.0.1 netmask 0xff000000
4 inet6 ::1 prefixlen 128
5 gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
6 stf0: flags=0<> mtu 1280
7 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
8 ether 28:cf:e9:1a:8a:2d
9 inet6 fe80::2acf:e9ff:fe1a:8a2d%en0 prefixlen 64 scopeid 0x5
10 inet 10.16.141.17 netmask 0xfffffc00 broadcast 10.16.143.255
11 media: autoselect
12 status: active
13 p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
14 ether 0a:cf:e9:1a:8a:2d
15 media: autoselect
16 status: inactive
17 vmnet1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
18 ether 00:50:56:c0:00:01
19 inet 192.168.51.1 netmask 0xffffff00 broadcast 192.168.51.255
20 vmnet8: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
21 ether 00:50:56:c0:00:08
22 inet 172.16.138.1 netmask 0xffffff00 broadcast 172.16.138.255
23 en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
24 options=4<VLAN_MTU>
25 ether 70:11:24:8c:33:df
26 inet6 fe80::7211:24ff:fe8c:33df%en3 prefixlen 64 scopeid 0x7
27 inet 10.16.16.205 netmask 0xfffffc00 broadcast 10.16.19.255
28 media: autoselect (100baseTX <full-duplex>)
29 status: active
0 VLAN Dev name | VLAN ID
1 Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD
0 require 'spec_helper'
1
2 module FacterSpec::Cpuinfo
3 def cpuinfo_fixtures(filename)
4 fixtures('cpuinfo', filename)
5 end
6
7 def cpuinfo_fixture_read(filename)
8 File.read(cpuinfo_fixtures(filename))
9 end
10
11 def cpuinfo_fixture_readlines(filename)
12 cpuinfo_fixture_read(filename).split(/\n/)
13 end
14 end
77 Facter::Application.parse(['architecture', 'kernel']).should == {}
88 end
99
10 [:yaml, :json, :trace].each do |option_key|
10 [:yaml, :json].each do |option_key|
1111 it "sets options[:#{option_key}] when given --#{option_key}" do
1212 options = Facter::Application.parse(["--#{option_key}"])
1313 options[option_key].should be_true
2828 Facter.should be_debugging
2929 Facter.debugging(false)
3030 end
31 end
32
33 it "enables tracing when given --trace" do
34 Facter.trace(false)
35 Facter::Application.parse(['--trace'])
36 Facter.should be_trace
37 Facter.trace(false)
3138 end
3239
3340 ['-t', '--timing'].each do |option|
5158 Facter::Application.parse(argv)
5259 argv.should == ['uptime', 'virtual']
5360 end
61
62 after(:all) do
63 Facter.debugging(false)
64 Facter.timing(false)
65 Facter.trace(false)
66 end
67 end
68
69 describe "formatting facts" do
70 before do
71 Facter.stubs(:to_hash)
72 Facter.stubs(:value)
73 Facter::Application.stubs(:puts)
74 end
75
76 it "delegates YAML formatting" do
77 Facter::Util::Formatter.expects(:format_yaml)
78 Facter::Application.stubs(:exit).with(0)
79 Facter::Application.run(['--yaml'])
80 end
81
82 it "delegates JSON formatting", :if => Facter.json? do
83 Facter::Util::Formatter.expects(:format_json)
84 Facter::Application.stubs(:exit).with(0)
85 Facter::Application.run(['--json'])
86 end
87
88 it "delegates plaintext formatting" do
89 Facter::Util::Formatter.expects(:format_plaintext)
90 Facter::Application.stubs(:exit).with(0)
91 Facter::Application.run(['--plaintext'])
92 end
93
94 it "defaults to plaintext" do
95 Facter::Util::Formatter.expects(:format_plaintext)
96 Facter::Application.stubs(:exit).with(0)
97 Facter::Application.run([])
98 end
5499 end
55100 end
00 #! /usr/bin/env ruby
11
22 require 'spec_helper'
3 require 'facter/util/architecture'
34
45 describe "Architecture fact" do
56
5152 end
5253 end
5354
55 it "(#16081) should be PowerPC_POWER7 if os is AIX" do
56 Facter.fact(:kernel).stubs(:value).returns("AIX")
57 Facter::Util::Architecture.stubs(:lsattr).returns("type PowerPC_POWER7 Processor type False")
58 Facter.fact(:hardwaremodel).stubs(:value).returns("IBM,8233-E8B")
59 Facter.fact(:architecture).value.should == "PowerPC_POWER7"
60 end
61
5462 end
0 require 'spec_helper'
1 require 'facter/core/aggregate'
2
3 describe Facter::Core::Aggregate do
4
5 let(:fact) { stub('stub_fact', :name => 'stub_fact') }
6
7 subject { obj = described_class.new('aggregated', fact) }
8
9 it "can be resolved" do
10 expect(subject).to be_a_kind_of Facter::Core::Resolvable
11 end
12
13 it "can be confined and weighted" do
14 expect(subject).to be_a_kind_of Facter::Core::Suitable
15 end
16
17 describe "setting options" do
18
19 it "can set the timeout" do
20 subject.set_options(:timeout => 314)
21 expect(subject.limit).to eq 314
22 end
23
24 it "can set the weight" do
25 subject.set_options(:weight => 27)
26 expect(subject.weight).to eq 27
27 end
28
29 it "can set the name" do
30 subject.set_options(:name => 'something')
31 expect(subject.name).to eq 'something'
32 end
33
34 it "fails on unhandled options" do
35 expect do
36 subject.set_options(:foo => 'bar')
37 end.to raise_error(ArgumentError, /Invalid aggregate options .*foo/)
38 end
39 end
40
41 describe "declaring chunks" do
42 it "requires that an chunk is given a block" do
43 expect { subject.chunk(:fail) }.to raise_error(ArgumentError, /requires a block/)
44 end
45
46 it "allows an chunk to have a list of requirements" do
47 subject.chunk(:data, :require => [:other]) { }
48 expect(subject.deps[:data]).to eq [:other]
49 end
50
51 it "converts a single chunk requirement to an array" do
52 subject.chunk(:data, :require => :other) { }
53 expect(subject.deps[:data]).to eq [:other]
54 end
55
56 it "raises an error when an unhandled option is passed" do
57 expect {
58 subject.chunk(:data, :before => [:other]) { }
59 }.to raise_error(ArgumentError, /Unexpected options.*#chunk: .*before/)
60 end
61 end
62
63 describe "handling interactions between chunks" do
64 it "generates a warning when there is a dependency cycle in chunks" do
65 subject.chunk(:first, :require => [:second]) { }
66 subject.chunk(:second, :require => [:first]) { }
67
68 Facter.expects(:warn) do |msg|
69 expect(msg).to match /dependency cycles: .*[:first, :second]/
70 end
71
72 subject.value
73 end
74
75 it "passes all requested chunk results to the depending chunk" do
76 subject.chunk(:first) { ['foo'] }
77 subject.chunk(:second, :require => [:first]) do |first|
78 [first[0] + ' bar']
79 end
80
81 output = subject.value
82 expect(output).to include 'foo'
83 expect(output).to include 'foo bar'
84 end
85
86 it "clones and freezes chunk results passed to other chunks" do
87 subject.chunk(:first) { 'foo' }
88 subject.chunk(:second, :require => [:first]) do |first|
89 expect(first).to be_frozen
90 end
91
92 subject.aggregate do |chunks|
93 chunks.values.each do |chunk|
94 expect(chunk).to be_frozen
95 end
96 end
97 end
98 end
99
100 describe "aggregating chunks" do
101 it "passes all chunk results as a hash to the aggregate block" do
102 subject.chunk(:data) { 'data chunk' }
103 subject.chunk(:datum) { 'datum chunk' }
104
105 subject.aggregate do |chunks|
106 expect(chunks).to eq(:data => 'data chunk', :datum => 'datum chunk')
107 end
108
109 subject.value
110 end
111
112 it "uses the result of the aggregate block as the value" do
113 subject.aggregate { "who needs chunks anyways" }
114 expect(subject.value).to eq "who needs chunks anyways"
115 end
116 end
117
118 describe "evaluating" do
119 it "evaluates the block in the context of the aggregate" do
120 subject.expects(:has_weight).with(5)
121 subject.evaluate { has_weight(5) }
122 end
123 end
124 end
0 require 'spec_helper'
1 require 'facter/core/directed_graph'
2
3 describe Facter::Core::DirectedGraph do
4 subject(:graph) { described_class.new }
5
6 describe "detecting cycles" do
7 it "is acyclic if the graph is empty" do
8 expect(graph).to be_acyclic
9 end
10
11 it "is acyclic if the graph has no edges" do
12 graph[:one] = []
13 graph[:two] = []
14
15 expect(graph).to be_acyclic
16 end
17
18 it "is acyclic if a vertex has an edge to itself" do
19 graph[:one] = [:one]
20 expect(graph).to be_acyclic
21 end
22
23 it "is acyclic if there are no loops in the graph" do
24 graph[:one] = [:two, :three]
25 graph[:two] = [:four]
26 graph[:three] = [:four]
27 graph[:four] = []
28
29 expect(graph).to be_acyclic
30 end
31
32 it "is cyclic if there is a loop in the graph" do
33 graph[:one] = [:two]
34 graph[:two] = [:one]
35 expect(graph).to_not be_acyclic
36 end
37
38 it "can return the cycles in the graph" do
39 graph[:one] = [:two]
40 graph[:two] = [:one]
41
42 graph[:three] = [:four]
43 graph[:four] = [:three]
44
45 first_cycle = graph.cycles.find { |cycle| cycle.include? :one }
46 second_cycle = graph.cycles.find { |cycle| cycle.include? :three }
47
48 expect(first_cycle).to include :two
49 expect(second_cycle).to include :four
50 end
51 end
52
53 describe "sorting" do
54 it "returns the vertices in topologically sorted order" do
55 graph[:one] = [:two, :three]
56 graph[:two] = [:three]
57 graph[:three] = []
58 expect(graph.tsort).to eq [:three, :two, :one]
59 end
60
61 it "raises an error if there is a cycle in the graph" do
62 graph[:one] = [:two]
63 graph[:two] = [:one]
64
65 expect {
66 graph.tsort
67 }.to raise_error(Facter::Core::DirectedGraph::CycleError, /found the following cycles:/)
68 end
69
70 it "raises an error if there is an edge to a non-existent vertex" do
71 graph[:one] = [:two, :three]
72 graph[:two] = [:three]
73 expect {
74 graph.tsort
75 }.to raise_error(Facter::Core::DirectedGraph::MissingVertex, /missing elements.*three/)
76 end
77 end
78 end
0 require 'spec_helper'
1 require 'facter/core/execution'
2
3 describe Facter::Core::Execution::Base do
4
5 describe "#with_env" do
6 it "should execute the caller's block with the specified env vars" do
7 test_env = { "LANG" => "C", "FOO" => "BAR" }
8 subject.with_env test_env do
9 test_env.keys.each do |key|
10 ENV[key].should == test_env[key]
11 end
12 end
13 end
14
15 it "should restore pre-existing environment variables to their previous values" 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 subject.with_env new_env do
31 orig_env.keys.each do |key|
32 ENV[key].should == new_env[key]
33 end
34 end
35
36 # verify that, after the 'with_env', the old values are restored
37 orig_env.keys.each do |key|
38 ENV[key].should == orig_env[key]
39 end
40 end
41
42 it "should not be affected by a 'return' statement in the yield block" do
43 @sentinel_var = :resolution_test_foo.to_s
44
45 # the intent of this test case is to test a yield block that contains a return statement. However, it's illegal
46 # to use a return statement outside of a method, so we need to create one here to give scope to the 'return'
47 def handy_method()
48 ENV[@sentinel_var] = "foo"
49 new_env = { @sentinel_var => "bar" }
50
51 subject.with_env new_env do
52 ENV[@sentinel_var].should == "bar"
53 return
54 end
55 end
56
57 handy_method()
58
59 ENV[@sentinel_var].should == "foo"
60
61 end
62 end
63
64 describe "#execute" do
65
66 it "switches LANG to C when executing the command" do
67 subject.expects(:with_env).with('LANG' => 'C')
68 subject.execute('foo')
69 end
70
71 it "switches LC_ALL to C when executing the command"
72
73 it "expands the command before running it" do
74 subject.stubs(:`).returns ''
75 subject.expects(:expand_command).with('foo').returns '/bin/foo'
76 subject.execute('foo')
77 end
78
79 describe "and the command is not present" do
80 it "raises an error when the :on_fail behavior is :raise" do
81 subject.expects(:expand_command).with('foo').returns nil
82 expect { subject.execute('foo') }.to raise_error(Facter::Core::Execution::ExecutionFailure)
83 end
84
85 it "returns the given value when :on_fail is set to a value" do
86 subject.expects(:expand_command).with('foo').returns nil
87 expect(subject.execute('foo', :on_fail => nil)).to be_nil
88 end
89 end
90
91 describe "when command execution fails" do
92 before do
93 subject.expects(:`).with("/bin/foo").raises "kaboom!"
94 subject.expects(:expand_command).with('foo').returns '/bin/foo'
95 end
96
97 it "raises an error when the :on_fail behavior is :raise" do
98 expect { subject.execute('foo') }.to raise_error(Facter::Core::Execution::ExecutionFailure)
99 end
100
101 it "returns the given value when :on_fail is set to a value" do
102 expect(subject.execute('foo', :on_fail => nil)).to be_nil
103 end
104 end
105
106 it "launches a thread to wait on children if the command was interrupted" do
107 subject.expects(:`).with("/bin/foo").raises "kaboom!"
108 subject.expects(:expand_command).with('foo').returns '/bin/foo'
109
110 Facter.stubs(:warn)
111 Thread.expects(:new).yields
112 Process.expects(:waitall).once
113
114 subject.execute("foo", :on_fail => nil)
115 end
116
117 it "returns the output of the command" do
118 subject.expects(:`).with("/bin/foo").returns 'hi'
119 subject.expects(:expand_command).with('foo').returns '/bin/foo'
120
121 expect(subject.execute("foo")).to eq 'hi'
122 end
123
124 it "strips off trailing newlines" do
125 subject.expects(:`).with("/bin/foo").returns "hi\n"
126 subject.expects(:expand_command).with('foo').returns '/bin/foo'
127
128 expect(subject.execute("foo")).to eq 'hi'
129 end
130 end
131 end
0 require 'spec_helper'
1
2 describe Facter::Core::Execution::Posix, :as_plaform => :posix do
3
4 describe "#search_paths" do
5 it "should use the PATH environment variable plus /sbin and /usr/sbin on unix" do
6 ENV.expects(:[]).with('PATH').returns "/bin:/usr/bin"
7 subject.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
8 end
9 end
10
11 describe "#which" do
12 before :each do
13 subject.stubs(:search_paths).returns [ '/bin', '/sbin', '/usr/sbin']
14 end
15
16 context "and provided with an absolute path" do
17 it "should return the binary if executable" do
18 File.expects(:executable?).with('/opt/foo').returns true
19 subject.which('/opt/foo').should == '/opt/foo'
20 end
21
22 it "should return nil if the binary is not executable" do
23 File.expects(:executable?).with('/opt/foo').returns false
24 subject.which('/opt/foo').should be_nil
25 end
26 end
27
28 context "and not provided with an absolute path" do
29 it "should return the absolute path if found" do
30 File.expects(:executable?).with('/bin/foo').returns false
31 File.expects(:executable?).with('/sbin/foo').returns true
32 File.expects(:executable?).with('/usr/sbin/foo').never
33 subject.which('foo').should == '/sbin/foo'
34 end
35
36 it "should return nil if not found" do
37 File.expects(:executable?).with('/bin/foo').returns false
38 File.expects(:executable?).with('/sbin/foo').returns false
39 File.expects(:executable?).with('/usr/sbin/foo').returns false
40 subject.which('foo').should be_nil
41 end
42 end
43 end
44
45 describe "#expand_command" do
46 it "should expand binary" do
47 subject.expects(:which).with('foo').returns '/bin/foo'
48 subject.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
49 end
50
51 it "should expand double quoted binary" do
52 subject.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
53 subject.expand_command(%q{"/tmp/my foo" bar}).should == %q{'/tmp/my foo' bar}
54 end
55
56 it "should expand single quoted binary" do
57 subject.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
58 subject.expand_command(%q{'my foo' -a}).should == %q{'/home/bob/my path/my foo' -a}
59 end
60
61 it "should quote expanded binary if found in path with spaces" do
62 subject.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
63 subject.expand_command('foo.sh /a /b').should == %q{'/home/bob/my tools/foo.sh' /a /b}
64 end
65
66 it "should return nil if not found" do
67 subject.expects(:which).with('foo').returns nil
68 subject.expand_command('foo -a | stuff >> /dev/null').should be_nil
69 end
70 end
71
72 describe "#absolute_path?" do
73 %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path|
74 it "should return true for #{path}" do
75 subject.should be_absolute_path(path)
76 end
77 end
78
79 %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path|
80 it "should return false for #{path}" do
81 subject.should_not be_absolute_path(path)
82 end
83 end
84 end
85 end
0 require 'spec_helper'
1
2 describe Facter::Core::Execution::Windows, :as_platform => :windows do
3
4 describe "#search_paths" do
5 it "should use the PATH environment variable to determine locations" do
6 ENV.expects(:[]).with('PATH').returns 'C:\Windows;C:\Windows\System32'
7 subject.search_paths.should == %w{C:\Windows C:\Windows\System32}
8 end
9 end
10
11 describe "#which" do
12 before :each do
13 subject.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
14 ENV.stubs(:[]).with('PATHEXT').returns nil
15 end
16
17 context "and provided with an absolute path" do
18 it "should return the binary if executable" do
19 File.expects(:executable?).with('C:\Tools\foo.exe').returns true
20 File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns true
21 subject.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
22 subject.which('\\\\remote\dir\foo.exe').should == '\\\\remote\dir\foo.exe'
23 end
24
25 it "should return nil if the binary is not executable" do
26 File.expects(:executable?).with('C:\Tools\foo.exe').returns false
27 File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns false
28 subject.which('C:\Tools\foo.exe').should be_nil
29 subject.which('\\\\remote\dir\foo.exe').should be_nil
30 end
31 end
32
33 context "and not provided with an absolute path" do
34 it "should return the absolute path if found" do
35 File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
36 File.expects(:executable?).with('C:\Windows\foo.exe').returns true
37 File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').never
38 subject.which('foo.exe').should == 'C:\Windows\foo.exe'
39 end
40
41 it "should return the absolute path with file extension if found" do
42 ['.COM', '.EXE', '.BAT', '.CMD', '' ].each do |ext|
43 File.stubs(:executable?).with('C:\Windows\system32\foo'+ext).returns false
44 File.stubs(:executable?).with('C:\Windows\System32\Wbem\foo'+ext).returns false
45 end
46 ['.COM', '.BAT', '.CMD', '' ].each do |ext|
47 File.stubs(:executable?).with('C:\Windows\foo'+ext).returns false
48 end
49 File.stubs(:executable?).with('C:\Windows\foo.EXE').returns true
50
51 subject.which('foo').should == 'C:\Windows\foo.EXE'
52 end
53
54 it "should return nil if not found" do
55 File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
56 File.expects(:executable?).with('C:\Windows\foo.exe').returns false
57 File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').returns false
58 subject.which('foo.exe').should be_nil
59 end
60 end
61 end
62
63 describe "#expand_command" do
64 it "should expand binary" do
65 subject.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
66 subject.expand_command(
67 'cmd /c echo foo > C:\bar'
68 ).should == 'C:\Windows\System32\cmd /c echo foo > C:\bar'
69 end
70
71 it "should expand double quoted binary" do
72 subject.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
73 subject.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
74 end
75
76 it "should not expand single quoted binary" do
77 subject.expects(:which).with('\'C:\My').returns nil
78 subject.expand_command('\'C:\My Tools\foo.exe\' /a /b').should be_nil
79 end
80
81 it "should quote expanded binary if found in path with spaces" do
82 subject.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
83 subject.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
84 end
85
86 it "should return nil if not found" do
87 subject.expects(:which).with('foo').returns nil
88 subject.expand_command('foo /a | stuff >> NUL').should be_nil
89 end
90 end
91
92 describe "#absolute_path?" do
93 %w[C:/foo C:\foo \\\\Server\Foo\Bar \\\\?\C:\foo\bar //Server/Foo/Bar //?/C:/foo/bar /\?\C:/foo\bar \/Server\Foo/Bar c:/foo//bar//baz].each do |path|
94 it "should return true for #{path}" do
95 subject.should be_absolute_path(path)
96 end
97 end
98
99 %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|
100 it "should return false for #{path}" do
101 subject.should_not be_absolute_path(path)
102 end
103 end
104 end
105 end
0 require 'spec_helper'
1 require 'facter/core/execution'
2
3 describe Facter::Core::Execution do
4 subject { described_class}
5 let(:impl) { described_class.impl }
6
7 it "delegates #search_paths to the implementation" do
8 impl.expects(:search_paths)
9 subject.search_paths
10 end
11
12 it "delegates #which to the implementation" do
13 impl.expects(:which).with('waffles')
14 subject.which('waffles')
15 end
16
17 it "delegates #absolute_path? to the implementation" do
18 impl.expects(:absolute_path?).with('waffles', nil)
19 subject.absolute_path?('waffles')
20 end
21
22 it "delegates #absolute_path? with an optional platform to the implementation" do
23 impl.expects(:absolute_path?).with('waffles', :windows)
24 subject.absolute_path?('waffles', :windows)
25 end
26
27 it "delegates #expand_command to the implementation" do
28 impl.expects(:expand_command).with('waffles')
29 subject.expand_command('waffles')
30 end
31
32 it "delegates #exec to #execute" do
33 impl.expects(:execute).with('waffles', {:on_fail => nil})
34 subject.exec('waffles')
35 end
36
37 it "delegates #execute to the implementation" do
38 impl.expects(:execute).with('waffles', {})
39 subject.execute('waffles')
40 end
41 end
0 require 'spec_helper'
1 require 'facter/core/logging'
2
3 describe Facter::Core::Logging do
4
5 subject { described_class }
6
7 after(:all) do
8 subject.debugging(false)
9 subject.timing(false)
10 end
11
12 describe "emitting debug messages" do
13 it "doesn't log a message when debugging is disabled" do
14 subject.debugging(false)
15 subject.expects(:puts).never
16 subject.debug("foo")
17 end
18
19 describe "and debugging is enabled" do
20 before { subject.debugging(true) }
21 it "emits a warning when called with nil" do
22 subject.expects(:warn).with { |msg| expect(msg).to match /invalid message nil:NilClass/ }
23 subject.debug(nil)
24 end
25
26 it "emits a warning when called with an empty string" do
27 subject.expects(:warn).with { |msg| expect(msg).to match /invalid message "":String/ }
28 subject.debug("")
29 end
30
31 it "prints the message when logging is enabled" do
32 subject.expects(:puts).with { |msg| expect(msg).to match /foo/ }
33 subject.debug("foo")
34 end
35 end
36 end
37
38 describe "when warning" do
39 it "emits a warning when given a string" do
40 subject.debugging(true)
41 Kernel.expects(:warn).with('foo')
42 subject.warn('foo')
43 end
44
45 it "emits a warning regardless of log level" do
46 subject.debugging(false)
47 Kernel.expects(:warn).with "foo"
48 subject.warn "foo"
49 end
50
51 it "emits a warning if nil is passed" do
52 Kernel.expects(:warn).with { |msg| expect(msg).to match /invalid message nil:NilClass/ }
53 subject.warn(nil)
54 end
55
56 it "emits a warning if an empty string is passed" do
57 Kernel.expects(:warn).with { |msg| expect(msg).to match /invalid message "":String/ }
58 subject.warn('')
59 end
60 end
61
62 describe "when warning once" do
63 it "only logs a given warning string once" do
64 subject.expects(:warn).with('foo').once
65 subject.warnonce('foo')
66 subject.warnonce('foo')
67 end
68 end
69
70 describe "when setting the debugging mode" do
71 it "is enabled when the given value is true" do
72 subject.debugging(true)
73 expect(subject.debugging?).to be_true
74 end
75
76 it "is disabled when the given value is false" do
77 subject.debugging(false)
78 expect(subject.debugging?).to be_false
79 end
80
81 it "is disabled when the given value is nil" do
82 subject.debugging(nil)
83 expect(subject.debugging?).to be_false
84 end
85 end
86
87 describe "when setting the timing mode" do
88 it "is enabled when the given value is true" do
89 subject.timing(true)
90 expect(subject.timing?).to be_true
91 end
92
93 it "is disabled when the given value is false" do
94 subject.timing(false)
95 expect(subject.timing?).to be_false
96 end
97
98 it "is disabled when the given value is nil" do
99 subject.timing(nil)
100 expect(subject.timing?).to be_false
101 end
102 end
103 end
0 require 'spec_helper'
1 require 'facter/core/resolvable'
2
3 describe Facter::Core::Resolvable do
4
5 class ResolvableClass
6 def initialize(name)
7 @name = name
8 @fact = Facter::Util::Fact.new("stub fact")
9 end
10 attr_accessor :name, :resolve_value
11 attr_reader :fact
12 include Facter::Core::Resolvable
13 end
14
15 subject { ResolvableClass.new('resolvable') }
16
17 it "has a default timeout of 0 seconds" do
18 expect(subject.limit).to eq 0
19 end
20
21 it "can specify a custom timeout" do
22 subject.timeout = 10
23 expect(subject.limit).to eq 10
24 end
25
26 describe "generating a value" do
27 it "returns the results of #resolve_value" do
28 subject.resolve_value = 'stuff'
29 expect(subject.value).to eq 'stuff'
30 end
31
32 it "normalizes the resolved value" do
33 Facter::Util::Normalization.expects(:normalize).returns 'stuff'
34 subject.resolve_value = 'stuff'
35 expect(subject.value).to eq('stuff')
36 end
37
38 it "logs a warning if an exception was raised" do
39 subject.expects(:resolve_value).raises RuntimeError, "kaboom!"
40 Facter.expects(:warn).with(regexp_matches(/Could not retrieve .*: kaboom!/))
41 expect(subject.value).to eq nil
42 end
43 end
44
45 describe "timing out" do
46 it "uses #limit instead of #timeout to determine the timeout period" do
47 subject.expects(:timeout).never
48 subject.expects(:limit).returns 25
49
50 Timeout.expects(:timeout).with(25)
51 subject.value
52 end
53
54 it "returns nil if the timeout was reached" do
55 Facter.expects(:warn).with(regexp_matches(/Timed out after 0\.1 seconds while resolving/))
56 Timeout.expects(:timeout).raises Timeout::Error
57
58 subject.timeout = 0.1
59
60 expect(subject.value).to be_nil
61 end
62 end
63
64 describe 'callbacks when flushing facts' do
65 class FlushFakeError < StandardError; end
66
67 context '#on_flush' do
68 it 'accepts a block with on_flush' do
69 subject.on_flush() { raise NotImplementedError }
70 end
71 end
72
73 context '#flush' do
74 it 'calls the block passed to on_flush' do
75 subject.on_flush() { raise FlushFakeError }
76 expect { subject.flush }.to raise_error FlushFakeError
77 end
78 end
79 end
80 end
0 require 'spec_helper'
1 require 'facter/core/suitable'
2
3 describe Facter::Core::Suitable do
4
5 class SuitableClass
6 def initialize
7 @confines = []
8 end
9 attr_reader :confines
10 include Facter::Core::Suitable
11 end
12
13 subject { SuitableClass.new }
14
15 describe "confining on facts" do
16 it "can add confines with a fact and a single value" do
17 subject.confine :kernel => 'Linux'
18 end
19
20 it "creates a Facter::Util::Confine object for the confine call" do
21 subject.confine :kernel => 'Linux'
22 conf = subject.confines.first
23 expect(conf).to be_a_kind_of Facter::Util::Confine
24 expect(conf.fact).to eq :kernel
25 expect(conf.values).to eq ['Linux']
26 end
27 end
28
29 describe "confining on blocks" do
30 it "can add a single fact with a block parameter" do
31 subject.confine(:one) { true }
32 end
33
34 it "creates a Util::Confine instance for the provided fact with block parameter" do
35 block = lambda { true }
36 Facter::Util::Confine.expects(:new).with("one")
37
38 subject.confine("one", &block)
39 end
40
41 it "should accept a single block parameter" do
42 subject.confine() { true }
43 end
44
45 it "should create a Util::Confine instance for the provided block parameter" do
46 block = lambda { true }
47 Facter::Util::Confine.expects(:new)
48
49 subject.confine(&block)
50 end
51 end
52
53 describe "determining weight" do
54 it "is zero if no confines are set" do
55 expect(subject.weight).to eq 0
56 end
57
58 it "defaults to the number of confines" do
59 subject.confine :kernel => 'Linux'
60 expect(subject.weight).to eq 1
61 end
62
63 it "can be explicitly set" do
64 subject.has_weight 10
65 expect(subject.weight).to eq 10
66 end
67
68 it "prefers an explicit weight over the number of confines" do
69 subject.confine :kernel => 'Linux'
70 subject.has_weight 11
71 expect(subject.weight).to eq 11
72 end
73 end
74
75 describe "determining suitability" do
76 it "is true if all confines for the object evaluate to true" do
77 subject.confine :kernel => 'Linux'
78 subject.confine :operatingsystem => 'Redhat'
79
80 subject.confines.each { |confine| confine.stubs(:true?).returns true }
81
82 expect(subject).to be_suitable
83 end
84
85 it "is false if any confines for the object evaluate to false" do
86 subject.confine :kernel => 'Linux'
87 subject.confine :operatingsystem => 'Redhat'
88
89 subject.confines.first.stubs(:true?).returns true
90 subject.confines.first.stubs(:true?).returns false
91
92 expect(subject).to_not be_suitable
93 end
94 end
95 end
2323 let(:dnsdomain_command) { "dnsdomainname 2> /dev/null" }
2424
2525 def the_hostname_is(value)
26 Facter::Util::Resolution.stubs(:exec).with(hostname_command).returns(value)
26 Facter::Core::Execution.stubs(:exec).with(hostname_command).returns(value)
2727 end
2828
2929 def the_dnsdomainname_is(value)
30 Facter::Util::Resolution.stubs(:exec).with(dnsdomain_command).returns(value)
30 Facter::Core::Execution.stubs(:exec).with(dnsdomain_command).returns(value)
3131 end
3232
3333 before do
196196 it "should return nil" do
197197 expects_dnsdomains([nil])
198198
199 Facter::Util::Resolution.stubs(:exec).with(hostname_command).returns('sometest')
199 Facter::Core::Execution.stubs(:exec).with(hostname_command).returns('sometest')
200200 FileTest.stubs(:exists?).with("/etc/resolv.conf").returns(false)
201201
202202 Facter.fact(:domain).value.should be_nil
287287
288288 describe scenario[:scenario] do
289289 before(:each) do
290 Facter::Util::Resolution.stubs(:exec).with("hostname -f 2> /dev/null").returns(scenario[:hostname])
291 Facter::Util::Resolution.stubs(:exec).with("dnsdomainname 2> /dev/null").returns(scenario[:dnsdomainname])
290 Facter::Core::Execution.stubs(:exec).with("hostname -f 2> /dev/null").returns(scenario[:hostname])
291 Facter::Core::Execution.stubs(:exec).with("dnsdomainname 2> /dev/null").returns(scenario[:dnsdomainname])
292292 resolv_conf_contains(
293293 "search #{scenario[:resolve_search]}",
294294 "domain #{scenario[:resolve_domain]}"
135135 end
136136
137137 it "should return nil if open fails" do
138 Facter.stubs(:warn) # do not pollute test output
138139 Facter.expects(:warn).with('Could not retrieve ec2 metadata: host unreachable')
139 Facter::Util::Resolution.any_instance.stubs(:warn) # do not pollute test output
140140
141141 Object.any_instance.expects(:open).
142142 with("#{api_prefix}/2008-02-01/meta-data/").
22 require 'spec_helper'
33
44 describe Facter do
5 it "should have a method for returning its collection" do
6 Facter.should respond_to(:collection)
7 end
8
9 it "should cache the collection" do
5 it "caches the collection" do
106 Facter.collection.should equal(Facter.collection)
117 end
128
13 it "should delegate the :flush method to the collection" do
14 Facter.collection.expects(:flush)
15 Facter.flush
9 describe "methods on the collection" do
10 it "delegates the :flush method to the collection" do
11 Facter.collection.expects(:flush)
12 Facter.flush
13 end
14
15 it "delegates the :fact method to the collection" do
16 Facter.collection.expects(:fact).with("afact")
17 Facter.fact("afact")
18 end
19
20 it "delegates the :list method to the collection" do
21 Facter.collection.expects(:list)
22 Facter.list
23 end
24
25 it "loads all facts when listing" do
26 Facter.collection.expects(:load_all)
27 Facter.list
28 end
29
30 it "delegates the :to_hash method to the collection" do
31 Facter.collection.expects(:to_hash)
32 Facter.to_hash
33 end
34
35 it "loads all facts when calling :to_hash" do
36 Facter.collection.expects(:load_all)
37 Facter.collection.stubs(:to_hash)
38 Facter.to_hash
39 end
40
41 it "delegates the :value method to the collection" do
42 Facter.collection.expects(:value).with("myvaluefact")
43 Facter.value("myvaluefact")
44 end
45
46 it "delegates the :each method to the collection" do
47 Facter.collection.expects(:each)
48 Facter.each
49 end
50
51 it "delegates the :add method to the collection" do
52 Facter.collection.expects(:add).with("factname", {})
53 Facter.add("factname")
54 end
55
56 it "delegates the :define_fact method to the collection" do
57 Facter.collection.expects(:define_fact).with("factname", {})
58 Facter.define_fact("factname")
59 end
60
61 it "loads all facts when calling :each" do
62 Facter.collection.expects(:load_all)
63 Facter.collection.stubs(:each)
64 Facter.each
65 end
1666 end
1767
18 it "should delegate the :fact method to the collection" do
19 Facter.collection.expects(:fact)
20 Facter.fact
21 end
22
23 it "should delegate the :list method to the collection" do
24 Facter.collection.expects(:list)
25 Facter.list
26 end
27
28 it "should load all facts when listing" do
29 Facter.collection.expects(:load_all)
30 Facter.collection.stubs(:list)
31 Facter.list
32 end
33
34 it "should delegate the :to_hash method to the collection" do
35 Facter.collection.expects(:to_hash)
36 Facter.to_hash
37 end
38
39 it "should load all facts when calling :to_hash" do
40 Facter.collection.expects(:load_all)
41 Facter.collection.stubs(:to_hash)
42 Facter.to_hash
43 end
44
45 it "should delegate the :value method to the collection" do
46 Facter.collection.expects(:value)
47 Facter.value
48 end
49
50 it "should delegate the :each method to the collection" do
51 Facter.collection.expects(:each)
52 Facter.each
53 end
54
55 it "should load all facts when calling :each" do
56 Facter.collection.expects(:load_all)
57 Facter.collection.stubs(:each)
58 Facter.each
59 end
60
61 it "should yield to the block when using :each" do
68 it "yields to the block when using :each" do
6269 Facter.collection.stubs(:load_all)
6370 Facter.collection.stubs(:each).yields "foo"
6471 result = []
6673 result.should == %w{foo}
6774 end
6875
69 describe "when provided code as a string" do
70 it "should execute the code in the shell" do
71 test_command = Facter::Util::Config.is_windows? ? 'cmd.exe /c echo yup' : 'echo yup'
72 Facter.add("shell_testing") do
73 setcode test_command
74 end
76 describe "when registering directories to search" do
77 after { Facter.reset_search_path! }
7578
76 Facter["shell_testing"].value.should == "yup"
77 end
78 end
79
80 describe "when asked for a fact as an undefined Facter class method" do
81 describe "and the collection is already initialized" do
82 it "should return the fact's value" do
83 Facter.collection
84 Facter.ipaddress.should == Facter['ipaddress'].value
85 end
86 end
87
88 describe "and the collection has been just reset" do
89 it "should return the fact's value" do
90 Facter.reset
91 Facter.ipaddress.should == Facter['ipaddress'].value
92 end
93 end
94 end
95
96 describe "when passed code as a block" do
97 it "should execute the provided block" do
98 Facter.add("block_testing") { setcode { "foo" } }
99
100 Facter["block_testing"].value.should == "foo"
101 end
102 end
103
104 describe "Facter[:hostname]" do
105 it "should have its ldapname set to 'cn'" do
106 Facter[:hostname].ldapname.should == "cn"
107 end
108 end
109
110 describe "Facter[:ipaddress]" do
111 it "should have its ldapname set to 'iphostnumber'" do
112 Facter[:ipaddress].ldapname.should == "iphostnumber"
113 end
114 end
115
116 # #33 Make sure we only get one mac address
117 it "should only return one mac address" do
118 if macaddress = Facter.value(:macaddress)
119 macaddress.should_not be_include(" ")
120 end
121 end
122
123 it "should have a method for registering directories to search" do
124 Facter.should respond_to(:search)
125 end
126
127 it "should have a method for returning the registered search directories" do
128 Facter.should respond_to(:search_path)
129 end
130
131 it "should have a method to query debugging mode" do
132 Facter.should respond_to(:debugging?)
133 end
134
135 it "should have a method to query timing mode" do
136 Facter.should respond_to(:timing?)
137 end
138
139 it "should have a method to show timing information" do
140 Facter.should respond_to(:show_time)
141 end
142
143 it "should have a method to warn" do
144 Facter.should respond_to(:warn)
145 end
146
147 describe "when warning" do
148 it "should warn if debugging is enabled" do
149 Facter.debugging(true)
150 Kernel.stubs(:warn)
151 Kernel.expects(:warn).with('foo')
152 Facter.warn('foo')
153 end
154
155 it "should not warn if debugging is enabled but nil is passed" do
156 Facter.debugging(true)
157 Kernel.stubs(:warn)
158 Kernel.expects(:warn).never
159 Facter.warn(nil)
160 end
161
162 it "should not warn if debugging is enabled but an empyt string is passed" do
163 Facter.debugging(true)
164 Kernel.stubs(:warn)
165 Kernel.expects(:warn).never
166 Facter.warn('')
167 end
168
169 it "should not warn if debugging is disabled" do
170 Facter.debugging(false)
171 Kernel.stubs(:warn)
172 Kernel.expects(:warn).never
173 Facter.warn('foo')
174 end
175
176 it "should warn for any given element for an array if debugging is enabled" do
177 Facter.debugging(true)
178 Kernel.stubs(:warn)
179 Kernel.expects(:warn).with('foo')
180 Kernel.expects(:warn).with('bar')
181 Facter.warn( ['foo','bar'])
182 end
183 end
184
185 describe "when warning once" do
186 it "should only warn once" do
187 Kernel.stubs(:warnonce)
188 Kernel.expects(:warn).with('foo').once
189 Facter.warnonce('foo')
190 Facter.warnonce('foo')
191 end
192
193 it "should not warnonce if nil is passed" do
194 Kernel.stubs(:warn)
195 Kernel.expects(:warnonce).never
196 Facter.warnonce(nil)
197 end
198
199 it "should not warnonce if an empty string is passed" do
200 Kernel.stubs(:warn)
201 Kernel.expects(:warnonce).never
202 Facter.warnonce('')
203 end
204 end
205
206 describe "when setting debugging mode" do
207 it "should have debugging enabled using 1" do
208 Facter.debugging(1)
209 Facter.should be_debugging
210 end
211 it "should have debugging enabled using true" do
212 Facter.debugging(true)
213 Facter.should be_debugging
214 end
215 it "should have debugging enabled using any string except off" do
216 Facter.debugging('aaaaa')
217 Facter.should be_debugging
218 end
219 it "should have debugging disabled using 0" do
220 Facter.debugging(0)
221 Facter.should_not be_debugging
222 end
223 it "should have debugging disabled using false" do
224 Facter.debugging(false)
225 Facter.should_not be_debugging
226 end
227 it "should have debugging disabled using the string 'off'" do
228 Facter.debugging('off')
229 Facter.should_not be_debugging
230 end
231 end
232
233 describe "when setting timing mode" do
234 it "should have timing enabled using 1" do
235 Facter.timing(1)
236 Facter.should be_timing
237 end
238 it "should have timing enabled using true" do
239 Facter.timing(true)
240 Facter.should be_timing
241 end
242 it "should have timing disabled using 0" do
243 Facter.timing(0)
244 Facter.should_not be_timing
245 end
246 it "should have timing disabled using false" do
247 Facter.timing(false)
248 Facter.should_not be_timing
249 end
250 end
251
252 describe "when registering directories to search" do
253 after { Facter.instance_variable_set("@search_path", []) }
254
255 it "should allow registration of a directory" do
79 it "allows registration of a directory" do
25680 Facter.search "/my/dir"
25781 end
25882
259 it "should allow registration of multiple directories" do
83 it "allows registration of multiple directories" do
26084 Facter.search "/my/dir", "/other/dir"
26185 end
26286
263 it "should return all registered directories when asked" do
87 it "returns all registered directories when asked" do
26488 Facter.search "/my/dir", "/other/dir"
26589 Facter.search_path.should == %w{/my/dir /other/dir}
26690 end
26791 end
92
93 describe "when registering directories to search for external facts" do
94 it "allows registration of a directory" do
95 Facter.search_external ["/my/dir"]
96 end
97
98 it "allows registration of multiple directories" do
99 Facter.search_external ["/my/dir", "/other/dir"]
100 end
101
102 it "returns all registered directories when asked" do
103 Facter.search_external ["/my/dir", "/other/dir"]
104 Facter.search_external_path.should include("/my/dir", "/other/dir")
105 end
106 end
268107 end
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33
1313 before :each do
1414 Facter.fact(:kernel).stubs(:value).returns('Linux')
1515 fixture_data = my_fixture_read('linux')
16 Facter::Util::Resolution.expects(:exec) \
16 Facter::Core::Execution.expects(:exec) \
1717 .with('cat /proc/filesystems 2> /dev/null').returns(fixture_data)
1818 Facter.collection.internal_loader.load(:filesystems)
1919 end
55 describe "Hardwareisa fact" do
66 it "should match uname -p on Linux" do
77 Facter.fact(:kernel).stubs(:value).returns("Linux")
8 Facter::Util::Resolution.stubs(:exec).with("uname -p").returns("Inky")
8 Facter::Core::Execution.stubs(:execute).with("uname -p", anything).returns("Inky")
99
1010 Facter.fact(:hardwareisa).value.should == "Inky"
1111 end
1212
1313 it "should match uname -p on Darwin" do
1414 Facter.fact(:kernel).stubs(:value).returns("Darwin")
15 Facter::Util::Resolution.stubs(:exec).with("uname -p").returns("Blinky")
15 Facter::Core::Execution.stubs(:execute).with("uname -p", anything).returns("Blinky")
1616
1717 Facter.fact(:hardwareisa).value.should == "Blinky"
1818 end
1919
2020 it "should match uname -p on SunOS" do
2121 Facter.fact(:kernel).stubs(:value).returns("SunOS")
22 Facter::Util::Resolution.stubs(:exec).with("uname -p").returns("Pinky")
22 Facter::Core::Execution.stubs(:execute).with("uname -p", anything).returns("Pinky")
2323
2424 Facter.fact(:hardwareisa).value.should == "Pinky"
2525 end
2626
2727 it "should match uname -p on FreeBSD" do
2828 Facter.fact(:kernel).stubs(:value).returns("FreeBSD")
29 Facter::Util::Resolution.stubs(:exec).with("uname -p").returns("Clyde")
29 Facter::Core::Execution.stubs(:execute).with("uname -p", anything).returns("Clyde")
3030
3131 Facter.fact(:hardwareisa).value.should == "Clyde"
3232 end
3333
3434 it "should match uname -m on HP-UX" do
3535 Facter.fact(:kernel).stubs(:value).returns("HP-UX")
36 Facter::Util::Resolution.stubs(:exec).with("uname -m").returns("Pac-Man")
36 Facter::Core::Execution.stubs(:execute).with("uname -m", anything).returns("Pac-Man")
3737
3838 Facter.fact(:hardwareisa).value.should == "Pac-Man"
3939 end
55 describe "Hardwaremodel fact" do
66 it "should match uname -m by default" do
77 Facter.fact(:kernel).stubs(:value).returns("Darwin")
8 Facter::Util::Resolution.stubs(:exec).with("uname -m").returns("Inky")
8 Facter::Core::Execution.stubs(:execute).with("uname -m", anything).returns("Inky")
99
1010 Facter.fact(:hardwaremodel).value.should == "Inky"
1111 end
2222 Facter::Util::WMI.expects(:execquery).returns([cpu])
2323
2424 Facter.fact(:hardwaremodel).value.should == "i486"
25 end
25 end
2626
2727 it "should detect i686" do
2828 cpu = mock('cpu', :Architecture => 0, :Level => 6)
1010 end
1111
1212 it "should use the hostname command" do
13 Facter::Util::Resolution.expects(:exec).with('hostname').at_least_once
13 Facter::Core::Execution.expects(:execute).with('hostname').at_least_once
1414 Facter.fact(:hostname).value.should be_nil
1515 end
1616
1717 it "should use hostname as the fact if unqualified" do
18 Facter::Util::Resolution.stubs(:exec).with('hostname').returns('host1')
18 Facter::Core::Execution.stubs(:execute).with('hostname').returns('host1')
1919 Facter.fact(:hostname).value.should == "host1"
2020 end
2121
2222 it "should truncate the domain name if qualified" do
23 Facter::Util::Resolution.stubs(:exec).with('hostname').returns('host1.example.com')
23 Facter::Core::Execution.stubs(:execute).with('hostname').returns('host1.example.com')
2424 Facter.fact(:hostname).value.should == "host1"
2525 end
2626 end
3232 end
3333
3434 it "should use scutil to get the hostname" do
35 Facter::Util::Resolution.expects(:exec).with('/usr/sbin/scutil --get LocalHostName').returns("host1")
35 Facter::Core::Execution.expects(:execute).with('/usr/sbin/scutil --get LocalHostName', anything).returns("host1")
3636 Facter.fact(:hostname).value.should == "host1"
3737 end
3838 end
1111 it "should return the current user" do
1212 given_a_configuration_of(:is_windows => k == 'windows')
1313 Facter.fact(:kernel).stubs(:value).returns(k)
14 Facter::Util::Resolution.expects(:exec).once.with('whoami').returns 'bar'
14 Facter::Core::Execution.expects(:execute).once.with('whoami', anything).returns 'bar'
1515
1616 Facter.fact(:id).value.should == 'bar'
1717 end
2020
2121 it "should return the current user on Solaris" do
2222 given_a_configuration_of(:is_windows => false)
23 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns('SunOS')
24 Facter::Util::Resolution.expects(:exec).once.with('/usr/xpg4/bin/id -un').returns 'bar'
23 Facter.fact(:kernel).stubs(:value).returns 'SunOS'
24 Facter::Core::Execution.expects(:execute).once.with('/usr/xpg4/bin/id -un', anything).returns 'bar'
2525
2626 Facter.fact(:id).value.should == 'bar'
2727 end
3030 Facter::Util::IP.stubs(:get_interfaces).returns ["Local Area Connection", "Loopback \"Pseudo-Interface\" (#1)"]
3131 Facter.fact(:interfaces).value.should == %{Local_Area_Connection,Loopback__Pseudo_Interface____1_}
3232 end
33
34 it "should properly format a mac address" do
35 Facter::Util::IP.stubs(:get_interfaces).returns ["net0"]
36 Facter::Util::IP.stubs(:get_interface_value).returns "0:12:34:56:78:90"
37
38 Facter.collection.internal_loader.load(:interfaces)
39
40 fact = Facter.fact("macaddress_net0".intern)
41 fact.value.should eq("00:12:34:56:78:90")
42 end
3343 end
3444
3545 describe "Netmask handling on Linux" do
1414 end
1515
1616 it "should return ipaddress6 information for Darwin" do
17 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns('Darwin')
17 Facter::Core::Execution.stubs(:exec).with('uname -s').returns('Darwin')
1818 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig")
1919 Facter::Util::IP.stubs(:exec_ifconfig).with(["-a"]).
2020 returns(ifconfig_fixture('darwin_ifconfig_all_with_multiple_interfaces'))
2323 end
2424
2525 it "should return ipaddress6 information for Linux" do
26 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns('Linux')
26 Facter::Core::Execution.stubs(:exec).with('uname -s').returns('Linux')
2727 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig")
2828 Facter::Util::IP.stubs(:exec_ifconfig).with(["2>/dev/null"]).
2929 returns(ifconfig_fixture('linux_ifconfig_all_with_multiple_interfaces'))
3232 end
3333
3434 it "should return ipaddress6 information for Linux with recent net-tools" do
35 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns('Linux')
35 Facter::Core::Execution.stubs(:exec).with('uname -s').returns('Linux')
3636 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig")
3737 Facter::Util::IP.stubs(:exec_ifconfig).with(["2>/dev/null"]).
3838 returns(ifconfig_fixture('ifconfig_net_tools_1.60.txt'))
4141 end
4242
4343 it "should return ipaddress6 information for Solaris" do
44 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns('SunOS')
44 Facter::Core::Execution.stubs(:exec).with('uname -s').returns('SunOS')
4545 Facter::Util::IP.stubs(:get_ifconfig).returns("/usr/sbin/ifconfig")
4646 Facter::Util::IP.stubs(:exec_ifconfig).with(["-a"]).
4747 returns(ifconfig_fixture('sunos_ifconfig_all_with_multiple_interfaces'))
5959 context "when you have no active network adapter" do
6060 it "should return nil if there are no active (or any) network adapters" do
6161 Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY).returns([])
62 Facter::Util::Resolution.stubs(:exec)
62 Facter::Core::Execution.stubs(:execute)
6363
6464 Facter.value(:ipaddress).should == nil
6565 end
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33
1515 describe "on everything else" do
1616 it "should return the kernel using 'uname -s'" do
1717 given_a_configuration_of(:is_windows => false)
18 Facter::Util::Resolution.stubs(:exec).with('uname -s').returns("test_kernel")
18 Facter::Core::Execution.stubs(:exec).with('uname -s').returns("test_kernel")
1919
2020 Facter.fact(:kernel).value.should == 'test_kernel'
2121 end
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33
4 describe "Kernel release fact" do
4 describe "Kernel release fact" do
55
6 describe "on Windows" do
7 before do
6 describe "on Windows" do
7 before do
88 Facter.fact(:kernel).stubs(:value).returns("windows")
99 require 'facter/util/wmi'
1010 version = stubs 'version'
1111 version.stubs(:Version).returns("test_kernel")
1212 Facter::Util::WMI.stubs(:execquery).with("SELECT Version from Win32_OperatingSystem").returns([version])
1313 end
14
15 it "should return the kernel release" do
14
15 it "should return the kernel release" do
1616 Facter.fact(:kernelrelease).value.should == "test_kernel"
17 end
18 end
17 end
18 end
1919
20 describe "on AIX" do
21 before do
20 describe "on AIX" do
21 before do
2222 Facter.fact(:kernel).stubs(:value).returns("aix")
23 Facter::Util::Resolution.stubs(:exec).with('oslevel -s').returns("test_kernel")
24 end
25
26 it "should return the kernel release" do
23 Facter::Core::Execution.stubs(:execute).with('oslevel -s', anything).returns("test_kernel")
24 end
25
26 it "should return the kernel release" do
2727 Facter.fact(:kernelrelease).value.should == "test_kernel"
28 end
29 end
28 end
29 end
3030
3131 describe "on HP-UX" do
3232 before do
33 Facter.fact(:kernel).stubs(:value).returns("hp-ux")
34 Facter::Util::Resolution.stubs(:exec).with('uname -r').returns("B.11.31")
35 end
36
33 Facter.fact(:kernel).stubs(:value).returns("hp-ux")
34 Facter::Core::Execution.stubs(:execute).with('uname -r').returns("B.11.31")
35 end
36
3737 it "should remove preceding letters" do
3838 Facter.fact(:kernelrelease).value.should == "11.31"
39 end
40 end
39 end
40 end
4141
42 describe "on everything else" do
42 describe "on everything else" do
4343 before do
4444 Facter.fact(:kernel).stubs(:value).returns("linux")
45 Facter::Util::Resolution.stubs(:exec).with('uname -r').returns("test_kernel")
46 end
47
45 Facter::Core::Execution.stubs(:execute).with('uname -r', anything).returns("test_kernel")
46 end
47
4848 it "should return the kernel release" do
4949 Facter.fact(:kernelrelease).value.should == "test_kernel"
50 end
51 end
52 end
50 end
51 end
52 end
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33
44 describe "Kernel version fact" do
5
5
66 describe "on Solaris/Sun OS" do
77 before do
88 Facter.fact(:kernel).stubs(:value).returns('sunos')
9 Facter::Util::Resolution.stubs(:exec).with('uname -v').returns("1.234.5")
10 end
11
12 it "should return the kernel version using 'uname -v'" do
9 Facter::Core::Execution.stubs(:execute).with('uname -v', anything).returns("1.234.5")
10 end
11
12 it "should return the kernel version using 'uname -v'" do
1313 Facter.fact(:kernelversion).value.should == "1.234.5"
14 end
14 end
1515 end
16
16
1717 describe "on everything else" do
18 before do
18 before do
1919 Facter.fact(:kernel).stubs(:value).returns('linux')
2020 Facter.fact(:kernelrelease).stubs(:value).returns('1.23.4-56')
21 end
22
23 it "should return the kernel version using kernel release" do
21 end
22
23 it "should return the kernel version using kernel release" do
2424 Facter.fact(:kernelversion).value.should == "1.23.4"
25 end
25 end
2626 end
27 end
27 end
2828
2929
3030
1414 before :each do
1515 # For virtinfo documentation:
1616 # http://docs.oracle.com/cd/E23824_01/html/821-1462/virtinfo-1m.html
17 Facter::Util::Resolution.stubs(:exec).with("virtinfo -ap").
17 Facter::Core::Execution.stubs(:which).with("virtinfo").returns 'virtinfo'
18 Facter::Core::Execution.stubs(:exec).with("virtinfo -ap").
1819 returns(ldom_fixtures('ldom_v1'))
1920 Facter.collection.internal_loader.load(:ldom)
2021 end
6263
6364 describe "when running on non ldom hardware" do
6465 before :each do
65 Facter::Util::Resolution.stubs(:exec).with("virtinfo -ap").returns(nil)
66 Facter::Core::Execution.stubs(:which).with("virtinfo").returns(nil)
6667 Facter.collection.internal_loader.load(:ldom)
6768 end
6869
99 Facter.fact(:kernel).stubs(:value).returns kernel
1010 end
1111
12 it "should return the codename through lsb_release -c -s 2>/dev/null" do
13 Facter::Util::Resolution.stubs(:exec).with('lsb_release -c -s 2>/dev/null').returns 'n/a'
14 Facter.fact(:lsbdistcodename).value.should == 'n/a'
12 it "returns the codename through lsb_release -c -s 2>/dev/null" do
13 Facter::Core::Execution.impl.stubs(:execute).with('lsb_release -c -s 2>/dev/null', anything).returns 'n/a'
14 expect(Facter.fact(:lsbdistcodename).value).to eq 'n/a'
1515 end
1616
17 it "should return nil if lsb_release is not installed" do
18 Facter::Util::Resolution.stubs(:exec).with('lsb_release -c -s 2>/dev/null').returns nil
19 Facter.fact(:lsbdistcodename).value.should be_nil
17 it "returns nil if lsb_release is not installed" do
18 Facter::Core::Execution.impl.stubs(:expand_command).with('lsb_release -c -s 2>/dev/null').returns nil
19 expect(Facter.fact(:lsbdistcodename).value).to be_nil
2020 end
2121 end
2222 end
99 Facter.fact(:kernel).stubs(:value).returns kernel
1010 end
1111
12 it "should return the description through lsb_release -d -s 2>/dev/null" do
13 Facter::Util::Resolution.stubs(:exec).with('lsb_release -d -s 2>/dev/null').returns '"Gentoo Base System release 2.1"'
14 Facter.fact(:lsbdistdescription).value.should == 'Gentoo Base System release 2.1'
12 it "returns the description through lsb_release -d -s 2>/dev/null" do
13 Facter::Core::Execution.stubs(:which).with('lsb_release').returns '/usr/bin/lsb_release'
14 Facter::Core::Execution.stubs(:exec).with('lsb_release -d -s 2>/dev/null', anything).returns '"Gentoo Base System release 2.1"'
15 expect(Facter.fact(:lsbdistdescription).value).to eq 'Gentoo Base System release 2.1'
1516 end
1617
17 it "should return nil if lsb_release is not installed" do
18 Facter::Util::Resolution.stubs(:exec).with('lsb_release -d -s 2>/dev/null').returns nil
19 Facter.fact(:lsbdistdescription).value.should be_nil
18 it "returns nil if lsb_release is not installed" do
19 Facter::Core::Execution.stubs(:which).with('lsb_release').returns nil
20 expect(Facter.fact(:lsbdistdescription).value).to be_nil
2021 end
2122 end
2223 end
23
2424 end
99 Facter.fact(:kernel).stubs(:value).returns kernel
1010 end
1111
12 it "should return the id through lsb_release -i -s 2>/dev/null" do
13 Facter::Util::Resolution.stubs(:exec).with('lsb_release -i -s 2>/dev/null').returns 'Gentoo'
14 Facter.fact(:lsbdistid).value.should == 'Gentoo'
12 it "returns the id through lsb_release -i -s 2>/dev/null" do
13 Facter::Core::Execution.impl.stubs(:execute).with('lsb_release -i -s 2>/dev/null', anything).returns 'Gentoo'
14 expect(Facter.fact(:lsbdistid).value).to eq 'Gentoo'
1515 end
1616
17 it "should return nil if lsb_release is not installed 2>/dev/null" do
18 Facter::Util::Resolution.stubs(:exec).with('lsb_release -i -s 2>/dev/null').returns nil
19 Facter.fact(:lsbdistid).value.should be_nil
17 it "returns nil if lsb_release is not installed" do
18 Facter::Core::Execution.impl.stubs(:expand_command).with('lsb_release -i -s 2>/dev/null').returns nil
19 expect(Facter.fact(:lsbdistid).value).to be_nil
2020 end
2121 end
2222 end
1010 end
1111
1212 it "should return the release through lsb_release -r -s 2>/dev/null" do
13 Facter::Util::Resolution.stubs(:exec).with('lsb_release -r -s 2>/dev/null').returns '2.1'
13 Facter::Core::Execution.stubs(:execute).with('lsb_release -r -s 2>/dev/null', anything).returns '2.1'
1414 Facter.fact(:lsbdistrelease).value.should == '2.1'
1515 end
1616
1717 it "should return nil if lsb_release is not installed" do
18 Facter::Util::Resolution.stubs(:exec).with('lsb_release -r -s 2>/dev/null').returns nil
18 Facter::Core::Execution.stubs(:execute).with('lsb_release -r -s 2>/dev/null', anything).returns nil
1919 Facter.fact(:lsbdistrelease).value.should be_nil
2020 end
2121 end
99 Facter.fact(:kernel).stubs(:value).returns kernel
1010 end
1111
12 it "should return the release through lsb_release -v -s 2>/dev/null" do
13 Facter::Util::Resolution.stubs(:exec).with('lsb_release -v -s 2>/dev/null').returns 'n/a'
14 Facter.fact(:lsbrelease).value.should == 'n/a'
12 it "returns the release through lsb_release -v -s 2>/dev/null" do
13 Facter::Core::Execution.impl.stubs(:execute).with('lsb_release -v -s 2>/dev/null', anything).returns 'n/a'
14 expect(Facter.fact(:lsbrelease).value).to eq 'n/a'
1515 end
1616
17 it "should return nil if lsb_release is not installed" do
18 Facter::Util::Resolution.stubs(:exec).with('lsb_release -v -s 2>/dev/null').returns nil
19 Facter.fact(:lsbrelease).value.should be_nil
17 it "returns nil if lsb_release is not installed" do
18 Facter::Core::Execution.impl.stubs(:expand_command).with('lsb_release -v -s 2>/dev/null').returns nil
19 expect(Facter.fact(:lsbrelease).value).to be_nil
2020 end
2121 end
2222 end
4747 before(:each) do
4848 Facter.clear
4949 Facter.fact(:kernel).stubs(:value).returns("Darwin")
50 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.memsize').returns('8589934592')
50 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.memsize').returns('8589934592')
5151 sample_vm_stat = <<VMSTAT
5252 Mach Virtual Memory Statistics: (page size of 4096 bytes)
5353 Pages free: 28430.
6363 Pageouts: 1384383.
6464 Object cache: 14 hits of 2619925 lookups (0% hit rate)
6565 VMSTAT
66 Facter::Util::Resolution.stubs(:exec).with('vm_stat').returns(sample_vm_stat)
67 Facter::Util::Resolution.stubs(:exec).with('sysctl vm.swapusage').returns("vm.swapusage: total = 64.00M used = 1.00M free = 63.00M (encrypted)")
66 Facter::Core::Execution.stubs(:exec).with('vm_stat').returns(sample_vm_stat)
67 Facter::Core::Execution.stubs(:exec).with('sysctl vm.swapusage').returns("vm.swapusage: total = 64.00M used = 1.00M free = 63.00M (encrypted)")
6868
6969 Facter.collection.internal_loader.load(:memory)
7070 end
153153 /dev/hd6 10, 2 512MB 508MB
154154 SWAP
155155
156 Facter::Util::Resolution.stubs(:exec).with('swap -l').returns(swapusage)
156 Facter::Core::Execution.stubs(:exec).with('swap -l 2>/dev/null').returns(swapusage)
157
158 svmon = <<SVMON
159 Unit: KB
160 --------------------------------------------------------------------------------------
161 size inuse free pin virtual available mmode
162 memory 32768000 9948408 22819592 2432080 4448928 27231828 Ded
163 pg space 34078720 15000
164
165 work pers clnt other
166 pin 1478228 0 0 953852
167 in use 4448928 0 5499480
168 SVMON
169
170 Facter::Core::Execution.stubs(:exec).with('/usr/bin/svmon -O unit=KB').returns(svmon)
157171
158172 Facter.collection.internal_loader.load(:memory)
159173 end
189203 Facter.fact(:swapfree_mb).value.should == "508.00"
190204 end
191205 end
206
207 it "should return the current memory free in MB" do
208 Facter.fact(:memoryfree_mb).value.should == "22284.76"
209 end
210
211 it "should return the current memory size in MB" do
212 Facter.fact(:memorysize_mb).value.should == "32000.00"
213 end
214
192215 end
193216
194217
198221 Facter.fact(:kernel).stubs(:value).returns("OpenBSD")
199222
200223 swapusage = "total: 148342k bytes allocated = 0k used, 148342k available"
201 Facter::Util::Resolution.stubs(:exec).with('swapctl -s').returns(swapusage)
224 Facter::Core::Execution.stubs(:exec).with('swapctl -s').returns(swapusage)
202225
203226 vmstat = <<EOS
204227 procs memory page disks traps cpu
205228 r b w avm fre flt re pi po fr sr cd0 sd0 int sys cs us sy id
206229 0 0 0 11048 181028 39 0 0 0 0 0 0 1 3 90 17 0 0 100
207230 EOS
208 Facter::Util::Resolution.stubs(:exec).with('vmstat').returns(vmstat)
209
210 Facter::Util::Resolution.stubs(:exec).with("sysctl hw.physmem | cut -d'=' -f2").returns('267321344')
231 Facter::Core::Execution.stubs(:exec).with('vmstat').returns(vmstat)
232
233 Facter::Core::Execution.stubs(:exec).with("sysctl hw.physmem | cut -d'=' -f2").returns('267321344')
211234
212235 Facter.collection.internal_loader.load(:memory)
213236 end
243266 System Peripherals (Software Nodes):
244267
245268 PRTCONF
246 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/prtconf 2>/dev/null').returns sample_prtconf
269 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/prtconf 2>/dev/null').returns sample_prtconf
247270
248271 vmstat_lines = <<VMSTAT
249272 kthr memory page disk faults cpu
250273 r b w swap free re mf pi po fr de sr s0 s3 -- -- in sy cs us sy id
251274 0 0 0 1154552 476224 8 19 0 0 0 0 0 0 0 0 0 460 294 236 1 2 97
252275 VMSTAT
253 Facter::Util::Resolution.stubs(:exec).with('vmstat').returns(vmstat_lines)
276 Facter::Core::Execution.stubs(:exec).with('vmstat').returns(vmstat_lines)
254277 end
255278
256279 after(:each) do
263286 swapfile dev swaplo blocks free
264287 /dev/swap 4294967295,4294967295 16 2097136 2097136
265288 SWAP
266 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/swap -l').returns sample_swap_line
289 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/swap -l 2>/dev/null').returns sample_swap_line
267290
268291 Facter.collection.internal_loader.load(:memory)
269292 end
292315 /dev/swap 4294967295,4294967295 16 2097136 2097136
293316 /dev/swap2 4294967295,4294967295 16 2097136 2097136
294317 SWAP
295 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/swap -l').returns sample_swap_line
318 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/swap -l 2>/dev/null').returns sample_swap_line
296319 Facter.collection.internal_loader.load(:memory)
297320 end
298321
315338
316339 describe "when no swap exists" do
317340 before(:each) do
318 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/swap -l').returns ""
341 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/swap -l 2>/dev/null').returns ""
319342
320343 Facter.collection.internal_loader.load(:memory)
321344 end
344367 Facter.fact(:kernel).stubs(:value).returns("dragonfly")
345368
346369 swapusage = "total: 148342k bytes allocated = 0k used, 148342k available"
347 Facter::Util::Resolution.stubs(:exec).with('/sbin/sysctl -n hw.pagesize').returns("4096")
348 Facter::Util::Resolution.stubs(:exec).with('/sbin/sysctl -n vm.swap_size').returns("128461")
349 Facter::Util::Resolution.stubs(:exec).with('/sbin/sysctl -n vm.swap_anon_use').returns("2635")
350 Facter::Util::Resolution.stubs(:exec).with('/sbin/sysctl -n vm.swap_cache_use').returns("0")
370 Facter::Core::Execution.stubs(:exec).with('/sbin/sysctl -n hw.pagesize').returns("4096")
371 Facter::Core::Execution.stubs(:exec).with('/sbin/sysctl -n vm.swap_size').returns("128461")
372 Facter::Core::Execution.stubs(:exec).with('/sbin/sysctl -n vm.swap_anon_use').returns("2635")
373 Facter::Core::Execution.stubs(:exec).with('/sbin/sysctl -n vm.swap_cache_use').returns("0")
351374
352375 vmstat = <<EOS
353376 procs memory page disks faults cpu
354377 r b w avm fre flt re pi po fr sr da0 sg1 in sy cs us sy id
355378 0 0 0 33152 13940 1902120 2198 53119 11642 6544597 5460994 0 0 6148243 7087927 3484264 0 1 9
356379 EOS
357 Facter::Util::Resolution.stubs(:exec).with('vmstat').returns(vmstat)
358
359 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.physmem").returns('248512512')
380 Facter::Core::Execution.stubs(:exec).with('vmstat').returns(vmstat)
381
382 Facter::Core::Execution.stubs(:exec).with("sysctl -n hw.physmem").returns('248512512')
360383
361384 Facter.collection.internal_loader.load(:memory)
362385 end
392415 r b w avm fre flt re pi po fr sr da0 cd0 in sy cs us sy id
393416 1 0 0 207600 656640 10 0 0 0 13 0 0 0 51 164 257 0 1 99
394417 VM_STAT
395 Facter::Util::Resolution.stubs(:exec).with('vmstat -H').returns sample_vmstat
418 Facter::Core::Execution.stubs(:exec).with('vmstat -H').returns sample_vmstat
396419 sample_physmem = <<PHYSMEM
397420 1056276480
398421 PHYSMEM
399 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.physmem').returns sample_physmem
422 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.physmem').returns sample_physmem
400423 end
401424
402425 after(:each) do
408431 sample_swapinfo = <<SWAP
409432 Device 1K-blocks Used Avail Capacity
410433 SWAP
411 Facter::Util::Resolution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
434 Facter::Core::Execution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
412435
413436 Facter.collection.internal_loader.load(:memory)
414437 end
436459 Device 1K-blocks Used Avail Capacity
437460 /dev/da0p3 2048540 0 1048540 0%
438461 SWAP
439 Facter::Util::Resolution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
462 Facter::Core::Execution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
440463
441464 Facter.collection.internal_loader.load(:memory)
442465 end
465488 /dev/da0p3 2048540 0 1048540 0%
466489 /dev/da0p4 3048540 0 1048540 0%
467490 SWAP
468 Facter::Util::Resolution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
491 Facter::Core::Execution.stubs(:exec).with('swapinfo -k').returns sample_swapinfo
469492
470493 Facter.collection.internal_loader.load(:memory)
471494 end
510533 Facter::Util::WMI.stubs(:execquery).returns([computer])
511534
512535 Facter.fact(:memorysize_mb).value.should == '3999.55'
513 Facter.fact(:MemoryTotal).value.should == '3.91 GB'
514 end
515 end
516
517 it "should use the memorysize fact for the memorytotal fact" do
518 Facter.fact("memorysize").expects(:value).once.returns "16.00 GB"
519 Facter::Util::Resolution.expects(:exec).never
520 Facter.fact(:memorytotal).value.should == "16.00 GB"
536 end
521537 end
522538 end
2424 example_behavior_for "netmask from ifconfig output",
2525 "Ubuntu 12.04", "255.255.255.255",
2626 "ifconfig_ubuntu_1204.txt"
27 end
28
29 context "on Darwin" do
30 before :each do
31 Facter.fact(:kernel).stubs(:value).returns("Darwin")
32 end
33
34 example_behavior_for "netmask from ifconfig output",
35 "Darwin 10.8.5", "255.255.252.0", "darwin_10_8_5.txt"
2736 end
2837
2938 context "on Windows" do
3939 "OpenIndiana" => "oi_151a",
4040 }.each_pair do |distribution, string|
4141 it "should be #{distribution} if uname -v is '#{string}'" do
42 Facter::Util::Resolution.stubs(:exec).with('uname -v').returns(string)
42 Facter::Core::Execution.stubs(:exec).with('uname -v').returns(string)
4343 Facter.fact(:operatingsystem).value.should == distribution
4444 end
4545 end
1212 end
1313 end
1414 end
15
16 context "on Solaris operatingsystems" do
17 before :each do
18 Facter.fact(:kernel).stubs(:value).returns("SunOS")
19 Facter.fact(:operatingsystem).stubs(:value).returns("Solaris")
20 end
21
22 it "should correctly derive from operatingsystemrelease on solaris 10" do
23 Facter.fact(:operatingsystemrelease).expects(:value).returns("10_u8")
24 Facter.fact(:operatingsystemmajrelease).value.should == "10"
25 end
26
27 it "should correctly derive from operatingsystemrelease on solaris 11 (old version scheme)" do
28 Facter.fact(:operatingsystemrelease).expects(:value).returns("11 11/11")
29 Facter.fact(:operatingsystemmajrelease).value.should == "11"
30 end
31
32 it "should correctly derive from operatingsystemrelease on solaris 11 (new version scheme)" do
33 Facter.fact(:operatingsystemrelease).expects(:value).returns("11.1")
34 Facter.fact(:operatingsystemmajrelease).value.should == "11"
35 end
36 end
1537 end
5050 Facter.fact(:kernelrelease).stubs(:value).returns("4.1.0")
5151 Facter.fact(:operatingsystem).stubs(:value).returns("VMwareESX")
5252
53 Facter::Util::Resolution.stubs(:exec).with('vmware -v').returns('foo')
53 Facter::Core::Execution.stubs(:exec).with('vmware -v').returns('foo')
5454
5555 Facter.fact(:operatingsystemrelease).value
5656 end
118118 'Solaris 10 10/09 s10x_u8wos_08a X86' => '10_u8',
119119 'Oracle Solaris 10 9/10 s10x_u9wos_14a X86' => '10_u9',
120120 'Oracle Solaris 10 8/11 s10x_u10wos_17b X86' => '10_u10',
121 'Oracle Solaris 11 11/11 X86' => '11 11/11',
122 'Oracle Solaris 11.1 SPARC' => '11.1'
121123 }.each do |fakeinput,expected_output|
122124 it "should be able to parse a release of #{fakeinput}" do
123125 Facter::Util::FileRead.stubs(:read).with('/etc/release').returns fakeinput
126128 end
127129
128130 context "malformed /etc/release files" do
129 before :each do
130 Facter::Util::Resolution.any_instance.stubs(:warn)
131 end
132131 it "should fallback to the kernelrelease fact if /etc/release is empty" do
133132 Facter::Util::FileRead.stubs(:read).with('/etc/release').
134133 raises EOFError
134 Facter.expects(:warn).with(regexp_matches(/Could not retrieve fact='operatingsystemrelease'.*EOFError/))
135135 Facter.fact(:operatingsystemrelease).value.
136136 should == Facter.fact(:kernelrelease).value
137137 end
139139 it "should fallback to the kernelrelease fact if /etc/release is not present" do
140140 Facter::Util::FileRead.stubs(:read).with('/etc/release').
141141 raises Errno::ENOENT
142 Facter.expects(:warn).with(regexp_matches(/Could not retrieve fact='operatingsystemrelease'.*No such file or directory/))
142143 Facter.fact(:operatingsystemrelease).value.
143144 should == Facter.fact(:kernelrelease).value
144145 end
152153 end
153154 end
154155
156 describe "with operatingsystem reported as Windows" do
157 require 'facter/util/wmi'
158 before do
159 Facter.fact(:kernel).stubs(:value).returns("windows")
160 end
161
162 {
163 ['5.2.3790', 1] => "XP",
164 ['6.0.6002', 1] => "Vista",
165 ['6.0.6002', 2] => "2008",
166 ['6.0.6002', 3] => "2008",
167 ['6.1.7601', 1] => "7",
168 ['6.1.7601', 2] => "2008 R2",
169 ['6.1.7601', 3] => "2008 R2",
170 ['6.2.9200', 1] => "8",
171 ['6.2.9200', 2] => "2012",
172 ['6.2.9200', 3] => "2012",
173 }.each do |os_values, expected_output|
174 it "should be #{expected_output} with Version #{os_values[0]} and ProductType #{os_values[1]}" do
175 os = mock('os', :version => os_values[0], :producttype => os_values[1])
176 Facter::Util::WMI.expects(:execquery).returns([os])
177 Facter.fact(:operatingsystemrelease).value.should == expected_output
178 end
179 end
180
181 {
182 ['5.2.3790', 2, ""] => "2003",
183 ['5.2.3790', 2, "R2"] => "2003 R2",
184 ['5.2.3790', 3, ""] => "2003",
185 ['5.2.3790', 3, "R2"] => "2003 R2",
186 }.each do |os_values, expected_output|
187 it "should be #{expected_output} with Version #{os_values[0]} and ProductType #{os_values[1]} and OtherTypeDescription #{os_values[2]}" do
188 os = mock('os', :version => os_values[0], :producttype => os_values[1], :othertypedescription => os_values[2])
189 Facter::Util::WMI.expects(:execquery).returns([os])
190 Facter.fact(:operatingsystemrelease).value.should == expected_output
191 end
192 end
193
194 it "reports '2003' if the WMI method othertypedescription does not exist" do
195 os = mock('os', :version => '5.2.3790', :producttype => 2)
196 os.stubs(:othertypedescription).raises(NoMethodError)
197
198 Facter::Util::WMI.expects(:execquery).returns([os])
199 Facter.fact(:operatingsystemrelease).value.should == '2003'
200 end
201
202 context "Unknown Windows version" do
203 before :each do
204 Facter.fact(:kernelrelease).stubs(:value).returns("X.Y.ZZZZ")
205 end
206
207 it "should be kernel version value with unknown values " do
208 os = mock('os', :version => "X.Y.ZZZZ")
209 Facter::Util::WMI.expects(:execquery).returns([os])
210 Facter.fact(:operatingsystemrelease).value.should == "X.Y.ZZZZ"
211 end
212 end
213 end
214
155215 context "Ubuntu" do
156 let(:issue) { "Ubuntu 10.04.4 LTS \\n \\l\n\n" }
216 let(:lsbrelease) { 'DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=10.04\nDISTRIB_CODENAME=lucid\nDISTRIB_DESCRIPTION="Ubuntu 10.04.4 LTS"'}
157217 before :each do
158218 Facter.fact(:kernel).stubs(:value).returns("Linux")
159219 Facter.fact(:operatingsystem).stubs(:value).returns("Ubuntu")
160220 end
161221
162222 it "Returns only the major and minor version (not patch version)" do
163 Facter::Util::FileRead.stubs(:read).with("/etc/issue").returns(issue)
223 Facter::Util::FileRead.stubs(:read).with("/etc/lsb-release").returns(lsbrelease)
164224 Facter.fact(:operatingsystemrelease).value.should == "10.04"
165225 end
166226 end
1111
1212 it "should return one physical CPU" do
1313 Dir.stubs(:glob).with("/sys/devices/system/cpu/cpu*/topology/physical_package_id").returns(["/sys/devices/system/cpu/cpu0/topology/physical_package_id"])
14 Facter::Util::Resolution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu0/topology/physical_package_id").returns("0")
14 Facter::Core::Execution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu0/topology/physical_package_id").returns("0")
1515
16 Facter.fact(:physicalprocessorcount).value.should == 1
16 Facter.fact(:physicalprocessorcount).value.should == "1"
1717 end
1818
1919 it "should return four physical CPUs" do
2424 /sys/devices/system/cpu/cpu3/topology/physical_package_id
2525 })
2626
27 Facter::Util::Resolution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu0/topology/physical_package_id").returns("0")
28 Facter::Util::Resolution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu1/topology/physical_package_id").returns("1")
29 Facter::Util::Resolution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu2/topology/physical_package_id").returns("2")
30 Facter::Util::Resolution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu3/topology/physical_package_id").returns("3")
27 Facter::Core::Execution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu0/topology/physical_package_id").returns("0")
28 Facter::Core::Execution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu1/topology/physical_package_id").returns("1")
29 Facter::Core::Execution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu2/topology/physical_package_id").returns("2")
30 Facter::Core::Execution.stubs(:exec).with("cat /sys/devices/system/cpu/cpu3/topology/physical_package_id").returns("3")
3131
32 Facter.fact(:physicalprocessorcount).value.should == 4
32 Facter.fact(:physicalprocessorcount).value.should == "4"
3333 end
3434 end
3535
4242 Facter::Util::WMI.expects(:execquery).with("select Name from Win32_Processor").returns(ole)
4343 ole.stubs(:Count).returns(4)
4444
45 Facter.fact(:physicalprocessorcount).value.should == 4
45 Facter.fact(:physicalprocessorcount).value.should == "4"
4646 end
4747 end
4848
5757 Facter.fact(:kernel).stubs(:value).returns(:sunos)
5858 Facter.stubs(:value).with(:kernelrelease).returns(release)
5959
60 Facter::Util::Resolution.expects(:exec).with("/usr/sbin/psrinfo -p").returns("1")
60 Facter::Core::Execution.expects(:exec).with("/usr/sbin/psrinfo -p").returns("1")
6161 Facter.fact(:physicalprocessorcount).value.should == "1"
6262 end
6363 end
6767 Facter.fact(:kernel).stubs(:value).returns(:sunos)
6868 Facter.stubs(:value).with(:kernelrelease).returns(release)
6969
70 Facter::Util::Resolution.expects(:exec).with("/usr/sbin/psrinfo").returns(psrinfo)
70 Facter::Core::Execution.expects(:exec).with("/usr/sbin/psrinfo").returns(psrinfo)
7171 Facter.fact(:physicalprocessorcount).value.should == "2"
7272 end
7373 end
11
22 require 'facter/util/processor'
33 require 'spec_helper'
4
5 def cpuinfo_fixture(filename)
6 File.open(fixtures('cpuinfo', filename)).readlines
7 end
4 require 'facter_spec/cpuinfo'
85
96 describe "Processor facts" do
107 describe "on Windows" do
6764 end
6865 end
6966
70 describe "on Solaris" do
71 before :each do
72 Facter.collection.internal_loader.load(:processor)
73 Facter.fact(:kernel).stubs(:value).returns(:sunos)
74 Facter.stubs(:value).with(:kernelrelease).returns("5.10")
75 end
76
77 it "should detect the correct processor count on x86_64" do
78 fixture_data = File.read(fixtures('processorcount','solaris-x86_64-kstat-cpu-info'))
79 Facter::Util::Resolution.expects(:exec).with("/usr/bin/kstat cpu_info").returns(fixture_data)
80 Facter.fact(:processorcount).value.should == 8
81 end
82
83 it "should detect the correct processor count on sparc" do
84 fixture_data = File.read(fixtures('processorcount','solaris-sparc-kstat-cpu-info'))
85 Facter::Util::Resolution.expects(:exec).with("/usr/bin/kstat cpu_info").returns(fixture_data)
86 Facter.fact(:processorcount).value.should == 8
87 end
88 end
67 describe "on Linux" do
68 include FacterSpec::Cpuinfo
69
70 shared_context 'Linux processor stubs' do
71 before :each do
72 Facter.collection.internal_loader.load(:processor)
73
74 Facter.fact(:kernel).stubs(:value).returns 'Linux'
75 Facter.fact(:operatingsystem).stubs(:value).returns 'Linux'
76 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
77 end
78 end
79
80 shared_examples_for 'a /proc/cpuinfo based processor fact' do |processor_fact|
81 include_context 'Linux processor stubs'
82
83 it "should be 1 in SPARC fixture" do
84 Facter.fact(:architecture).stubs(:value).returns("sparc")
85 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("sparc"))
86
87 Facter.fact(processor_fact).value.should == "1"
88 end
89
90 it "should be 2 in ppc64 fixture on Linux" do
91 Facter.fact(:architecture).stubs(:value).returns("ppc64")
92 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("ppc64"))
93
94 Facter.fact(processor_fact).value.should == "2"
95 end
96
97 it "should be 2 in panda-armel fixture on Linux" do
98 Facter.fact(:architecture).stubs(:value).returns("arm")
99 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("panda-armel"))
100
101 Facter.fact(processor_fact).value.should == "2"
102 end
103
104 it "should be 1 in bbg3-armel fixture on Linux" do
105 Facter.fact(:architecture).stubs(:value).returns("arm")
106 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("bbg3-armel"))
107
108 Facter.fact(processor_fact).value.should == "1"
109 end
110
111 it "should be 1 in beaglexm-armel fixture on Linux" do
112 Facter.fact(:architecture).stubs(:value).returns("arm")
113 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("beaglexm-armel"))
114
115 Facter.fact(processor_fact).value.should == "1"
116 end
117
118 it "should be 1 in amd64solo fixture on Linux" do
119 Facter.fact(:architecture).stubs(:value).returns("amd64")
120 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64solo"))
121
122 Facter.fact(processor_fact).value.should == "1"
123 end
124
125 it "should be 2 in amd64dual fixture on Linux" do
126 Facter.fact(:architecture).stubs(:value).returns("amd64")
127 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64dual"))
128
129 Facter.fact(processor_fact).value.should == "2"
130 end
131
132 it "should be 3 in amd64tri fixture on Linux" do
133 Facter.fact(:architecture).stubs(:value).returns("amd64")
134 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64tri"))
135
136 Facter.fact(processor_fact).value.should == "3"
137 end
138
139 it "should be 4 in amd64quad fixture on Linux" do
140 Facter.fact(:architecture).stubs(:value).returns("amd64")
141 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64quad"))
142
143 Facter.fact(processor_fact).value.should == "4"
144 end
145 end
146
147 it_behaves_like 'a /proc/cpuinfo based processor fact', :processorcount
148
149 def sysfs_cpu_stubs(count)
150 (0...count).map { |index| "/sys/devices/system/cpu/cpu#{index}" }
151 end
152
153 describe 'when /proc/cpuinfo returns 0 processors (#2945)' do
154 include_context 'Linux processor stubs'
155
156 before do
157 File.stubs(:readlines).with("/proc/cpuinfo").returns([])
158 File.stubs(:exists?).with("/sys/devices/system/cpu").returns(true)
159 end
160
161 it "should be 2 via sysfs when cpu0 and cpu1 are present" do
162 Dir.stubs(:glob).with("/sys/devices/system/cpu/cpu[0-9]*").returns(
163 sysfs_cpu_stubs(2)
164 )
165
166 Facter.fact(:processorcount).value.should == "2"
167 end
168
169 it "should be 16 via sysfs when cpu0 through cpu15 are present" do
170 Dir.stubs(:glob).with("/sys/devices/system/cpu/cpu[0-9]*").returns(
171 sysfs_cpu_stubs(16)
172 )
173
174 Facter.fact(:processorcount).value.should == "16"
175 end
176 end
177 end
178
89179
90180 describe "on Unixes" do
91181 before :each do
92182 Facter.collection.internal_loader.load(:processor)
93183 end
94184
95 it "should be 1 in SPARC fixture" do
96 Facter.fact(:kernel).stubs(:value).returns("Linux")
97 Facter.fact(:operatingsystem).stubs(:value).returns("Linux")
98 Facter.fact(:architecture).stubs(:value).returns("sparc")
99 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
100 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("sparc"))
101
102 Facter.fact(:processorcount).value.should == "1"
103 end
104
105 it "should be 2 in ppc64 fixture on Linux" do
106 Facter.fact(:kernel).stubs(:value).returns("Linux")
107 Facter.fact(:architecture).stubs(:value).returns("ppc64")
108 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
109 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("ppc64"))
110
111 Facter.fact(:processorcount).value.should == "2"
112 end
113
114 it "should be 2 in panda-armel fixture on Linux" do
115 Facter.fact(:kernel).stubs(:value).returns("Linux")
116 Facter.fact(:architecture).stubs(:value).returns("arm")
117 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
118 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("panda-armel"))
119
120 Facter.fact(:processorcount).value.should == "2"
121 end
122
123 it "should be 1 in bbg3-armel fixture on Linux" do
124 Facter.fact(:kernel).stubs(:value).returns("Linux")
125 Facter.fact(:architecture).stubs(:value).returns("arm")
126 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
127 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("bbg3-armel"))
128
129 Facter.fact(:processorcount).value.should == "1"
130 end
131
132 it "should be 1 in beaglexm-armel fixture on Linux" do
133 Facter.fact(:kernel).stubs(:value).returns("Linux")
134 Facter.fact(:architecture).stubs(:value).returns("arm")
135 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
136 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("beaglexm-armel"))
137
138 Facter.fact(:processorcount).value.should == "1"
139 end
140
141 it "should be 1 in amd64solo fixture on Linux" do
142 Facter.fact(:kernel).stubs(:value).returns("Linux")
143 Facter.fact(:architecture).stubs(:value).returns("amd64")
144 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
145 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64solo"))
146
147 Facter.fact(:processorcount).value.should == "1"
148 end
149
150 it "should be 2 in amd64dual fixture on Linux" do
151 Facter.fact(:kernel).stubs(:value).returns("Linux")
152 Facter.fact(:architecture).stubs(:value).returns("amd64")
153 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
154 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64dual"))
155
156 Facter.fact(:processorcount).value.should == "2"
157 end
158
159 it "should be 3 in amd64tri fixture on Linux" do
160 Facter.fact(:kernel).stubs(:value).returns("Linux")
161 Facter.fact(:architecture).stubs(:value).returns("amd64")
162 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
163 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64tri"))
164
165 Facter.fact(:processorcount).value.should == "3"
166 end
167
168 it "should be 4 in amd64quad fixture on Linux" do
169 Facter.fact(:kernel).stubs(:value).returns("Linux")
170 Facter.fact(:architecture).stubs(:value).returns("amd64")
171 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
172 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64quad"))
173
174 Facter.fact(:processorcount).value.should == "4"
175 end
176
177185 it "should be 2 on dual-processor Darwin box" do
178186 Facter.fact(:kernel).stubs(:value).returns("Darwin")
179 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.ncpu").returns('2')
187 Facter::Core::Execution.stubs(:execute).with("sysctl -n hw.ncpu", anything).returns('2')
180188
181189 Facter.fact(:processorcount).value.should == "2"
182190 end
183191
184192 it "should be 2 on dual-processor OpenBSD box" do
185193 Facter.fact(:kernel).stubs(:value).returns("OpenBSD")
186 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.ncpu").returns('2')
194 Facter::Core::Execution.stubs(:execute).with("sysctl -n hw.ncpu", anything).returns('2')
187195
188196 Facter.fact(:processorcount).value.should == "2"
189197 end
190198
191199 it "should be 2 on dual-processor FreeBSD box" do
192200 Facter.fact(:kernel).stubs(:value).returns("FreeBSD")
193 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.ncpu").returns('2')
201 Facter::Core::Execution.stubs(:execute).with("sysctl -n hw.ncpu", anything).returns('2')
194202
195203 Facter.fact(:processorcount).value.should == "2"
196204 end
197205
198206 it "should print the correct CPU Model on FreeBSD" do
199207 Facter.fact(:kernel).stubs(:value).returns("FreeBSD")
200 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.model").returns('SomeVendor CPU 3GHz')
208 Facter::Core::Execution.stubs(:execute).with("sysctl -n hw.model", anything).returns('SomeVendor CPU 3GHz')
201209
202210 Facter.fact(:processor).value.should == "SomeVendor CPU 3GHz"
203211 end
204
205212
206213 it "should be 2 on dual-processor DragonFly box" do
207214 Facter.fact(:kernel).stubs(:value).returns("DragonFly")
208 Facter::Util::Resolution.stubs(:exec).with("sysctl -n hw.ncpu").returns('2')
215 Facter::Core::Execution.stubs(:execute).with("sysctl -n hw.ncpu", anything).returns('2')
209216
210217 Facter.fact(:processorcount).value.should == "2"
211218 end
212
213 it "should be 2 via sysfs when cpu0 and cpu1 are present" do
214 Facter.fact(:kernel).stubs(:value).returns("Linux")
215 File.stubs(:exists?).with('/sys/devices/system/cpu').returns(true)
216 ## sysfs method is only used if cpuinfo method returned no processors
217 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
218 File.stubs(:readlines).with("/proc/cpuinfo").returns([])
219 Dir.stubs(:glob).with("/sys/devices/system/cpu/cpu[0-9]*").returns(%w{
220 /sys/devices/system/cpu/cpu0
221 /sys/devices/system/cpu/cpu1
222 })
223
224 Facter.fact(:processorcount).value.should == "2"
225 end
226
227 it "should be 16 via sysfs when cpu0 through cpu15 are present" do
228 Facter.fact(:kernel).stubs(:value).returns("Linux")
229 File.stubs(:exists?).with('/sys/devices/system/cpu').returns(true)
230 ## sysfs method is only used if cpuinfo method returned no processors
231 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
232 File.stubs(:readlines).with("/proc/cpuinfo").returns([])
233 Dir.stubs(:glob).with("/sys/devices/system/cpu/cpu[0-9]*").returns(%w{
234 /sys/devices/system/cpu/cpu0
235 /sys/devices/system/cpu/cpu1
236 /sys/devices/system/cpu/cpu2
237 /sys/devices/system/cpu/cpu3
238 /sys/devices/system/cpu/cpu4
239 /sys/devices/system/cpu/cpu5
240 /sys/devices/system/cpu/cpu6
241 /sys/devices/system/cpu/cpu7
242 /sys/devices/system/cpu/cpu8
243 /sys/devices/system/cpu/cpu9
244 /sys/devices/system/cpu/cpu10
245 /sys/devices/system/cpu/cpu11
246 /sys/devices/system/cpu/cpu12
247 /sys/devices/system/cpu/cpu13
248 /sys/devices/system/cpu/cpu14
249 /sys/devices/system/cpu/cpu15
250 })
251
252 Facter.fact(:processorcount).value.should == "16"
253 end
254
255 describe "on solaris" do
256 before :all do
257 @fixture_kstat_sparc = File.read(fixtures('processorcount','solaris-sparc-kstat-cpu-info'))
258 @fixture_kstat_x86_64 = File.read(fixtures('processorcount','solaris-x86_64-kstat-cpu-info'))
259 end
260
261 let(:psrinfo) do
262 "0 on-line since 10/16/2012 14:06:12\n" +
263 "1 on-line since 10/16/2012 14:06:14\n"
264 end
265
266 let(:kstat_sparc) { @fixture_kstat_sparc }
267 let(:kstat_x86_64) { @fixture_kstat_x86_64 }
268
269 %w{ 5.8 5.9 5.10 5.11 }.each do |release|
270 %w{ sparc x86_64 }.each do |arch|
271 it "uses kstat on release #{release} (#{arch})" do
272 Facter.fact(:kernel).stubs(:value).returns(:sunos)
273 Facter.stubs(:value).with(:kernelrelease).returns(release)
274
275 Facter::Util::Resolution.expects(:exec).with("/usr/bin/kstat cpu_info").returns(self.send("kstat_#{arch}".intern))
276 Facter.fact(:processorcount).value.should == 8
277 end
278 end
279 end
280
281 %w{ 5.5.1 5.6 5.7 }.each do |release|
282 it "uses psrinfo on release #{release}" do
283 Facter.fact(:kernel).stubs(:value).returns(:sunos)
219 end
220
221 describe "on solaris" do
222 before :each do
223 Facter::Util::Processor.stubs(:kernel_fact_value).returns :sunos
224 Facter.fact(:kernel).stubs(:value).returns(:sunos)
225 Facter.collection.internal_loader.load(:processor)
226 end
227
228 before :all do
229 @fixture_kstat_sparc = File.read(fixtures('processorcount','solaris-sparc-kstat-cpu-info'))
230 @fixture_kstat_x86_64 = File.read(fixtures('processorcount','solaris-x86_64-kstat-cpu-info'))
231 end
232
233 let(:kstat_sparc) { @fixture_kstat_sparc }
234 let(:kstat_x86_64) { @fixture_kstat_x86_64 }
235
236 %w{ 5.8 5.9 5.10 5.11 }.each do |release|
237 %w{ sparc x86_64 }.each do |arch|
238 it "uses kstat on release #{release} (#{arch})" do
284239 Facter.stubs(:value).with(:kernelrelease).returns(release)
285240
286 Facter::Util::Resolution.expects(:exec).with("/usr/sbin/psrinfo").returns(psrinfo)
287 Facter.fact(:physicalprocessorcount).value.should == "2"
288 end
241 Facter::Core::Execution.expects(:exec).with("/usr/sbin/psrinfo").never
242 Facter::Core::Execution.expects(:exec).with("/usr/bin/kstat cpu_info").returns(self.send("kstat_#{arch}".intern))
243 Facter.fact(:processorcount).value.should == '8'
244 end
245 end
246 end
247
248 %w{ 5.5.1 5.6 5.7 }.each do |release|
249 it "uses psrinfo on release #{release}" do
250 Facter.stubs(:value).with(:kernelrelease).returns(release)
251
252 fixture_data = File.read(fixtures('processorcount','solaris-psrinfo'))
253 Facter::Core::Execution.expects(:exec).with("/usr/bin/kstat cpu_info").never
254 Facter::Core::Execution.expects(:exec).with("/usr/sbin/psrinfo").returns(fixture_data)
255 Facter.fact(:processorcount).value.should == '24'
289256 end
290257 end
291258 end
332299 examples << [File.read(fixtures('hpux','machinfo','ia64-rx6600')), "Intel(R) Itanium 2 9100 series processor (1.59 GHz, 18 MB)"]
333300 examples << [File.read(fixtures('hpux','machinfo','ia64-rx8640')), "Intel(R) Itanium 2 9100 series"]
334301 examples << [File.read(fixtures('hpux','machinfo','hppa-rp4440')), "PA-RISC 8800 processor (1000 MHz, 64 MB)"]
302 examples << [File.read(fixtures('hpux','machinfo','superdome-server-SD32B')), "Intel(R) Itanium 2 9000 series"]
303 examples << [File.read(fixtures('hpux','machinfo','superdome2-16s')), "Intel(R) Itanium(R) Processor 9340 (1.6 GHz, 15 MB)"]
335304 examples
336305 end
337306
9595
9696 Facter.fact(:selinux_config_policy).value.should == "targeted"
9797 end
98
99 it "should ensure legacy selinux_mode facts returns same value as selinux_config_policy fact" do
100 Facter.fact(:selinux_config_policy).stubs(:value).returns("targeted")
101
102 Facter.fact(:selinux_mode).value.should == "targeted"
103 end
10498 end
10599
106100 def sestatus_is(status)
107 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/sestatus').returns(status)
101 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/sestatus').returns(status)
108102 end
109103
110104 def mounts_does_not_exist
113107
114108 def mounts_contains(*lines)
115109 FileTest.expects(:exists?).with("/proc/self/mounts").returns true
116 Facter::Util::Resolution.expects(:exec).with("cat /proc/self/mounts").returns(lines.join("\n"))
110 Facter::Core::Execution.expects(:exec).with("cat /proc/self/mounts").returns(lines.join("\n"))
117111 end
118112
119113 end
0 #! /usr/bin/env ruby -S rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33 require 'facter/ssh'
2121 # fingerprints extracted from ssh-keygen -r '' -f /etc/ssh/ssh_host_dsa_key.pub
2222 { 'SSHRSAKey' => [ 'ssh_host_rsa_key.pub' , "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrs+KtR8hjasELsyCiiBplUeIi77hEHzTSQt1ALG7N4IgtMg27ZAcq0tl2/O9ZarQuClc903pgionbM9Q98CtAIoqgJwdtsor7ETRmzwrcY/mvI7ne51UzQy4Eh9WrplfpNyg+EVO0FUC7mBcay6JY30QKasePp+g4MkwK5cuTzOCzd9up9KELonlH7tTm2L0YI4HhZugwVoTFulCAZvPICxSk1B/fEKyGSZVfY/UxZNqg9g2Wyvq5u40xQ5eO882UwhB3w4IbmRnPKcyotAcqOJxA7hToMKtEmFct+vjHE8T37w8axE/1X9mdvy8IZbkEBL1cupqqb8a8vU1QTg1z", "SSHFP 1 1 1e4f163a1747d0d1a08a29972c9b5d94ee5705d0\nSSHFP 1 2 4e834c91e423d6085ed6dfb880a59e2f1b04f17c1dc17da07708af67c5ab6045" ],
2323 'SSHDSAKey' => [ 'ssh_host_dsa_key.pub' , "ssh-dss AAAAB3NzaC1kc3MAAACBAKjmRez14aZT6OKhHrsw19s7u30AdghwHFQbtC+L781YjJ3UV0/WQoZ8NaDL4ovuvW23RuO49tsqSNcVHg+PtRiN2iTVAS2h55TFhaPKhTs+i0NH3p3Ze8LNSYuz8uK7a+nTxysz47GYTHiE1ke8KXe5wGKDO1TO/MUgpDbwx72LAAAAFQD9yMJCnZMiKzA7J1RNkwvgCyBKSQAAAIAtWBAsuRM0F2fdCe+F/JmgyryQmRIT5vP8E1ww3t3ywdLHklN7UMkaEKBW/TN/jj1JOGXtZ2v5XI+0VNoNKD/7dnCGzNViRT/jjfyVi6l5UMg4Q52Gv0RXJoBJpxNqFOU2niSsy8hioyE39W6LJYWJtQozGpH/KKgkCSvxBn5hlAAAAIB1yo/YD0kQICOO0KE+UMMaKtV7FwyedFJsxsWYwZfHXGwWskf0d2+lPhd9qwdbmSvySE8Qrlvu+W+X8AipwGkItSnj16ORF8kO3lfABa+7L4BLDtumt7ybjBPcHOy3n28dd07TmMtyWvLjOb0mcxPo+TwDLtHd3L/3C1Dh41jRPg==\n", "SSHFP 2 1 f63dfe8da99f50ffbcfa40a61161cee29d109f70\nSSHFP 2 2 5f57aa6be9baddd71b6049ed5d8639664a7ddf92ce293e3887f16ad0f2d459d9" ],
24 'SSHECDSAKey' => [ 'ssh_host_ecdsa_key.pub' , 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIuKHtgXQUIrXSVNKC7uY+ZOF7jjfqYNU7Cb/IncDOZ7jW44dxsfBzRJwS5sTHERjBinJskY87mmwY07NFF5GoE=', "SSHFP 3 1 091a088fd3500ad9e35ce201c5101646cbf6ff98\nSSHFP 3 2 1dd2aa8f29b539337316e2862b28c196c68ffe0af78fccf9e50625635677e50f"]
24 'SSHECDSAKey' => [ 'ssh_host_ecdsa_key.pub' , 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIuKHtgXQUIrXSVNKC7uY+ZOF7jjfqYNU7Cb/IncDOZ7jW44dxsfBzRJwS5sTHERjBinJskY87mmwY07NFF5GoE=', "SSHFP 3 1 091a088fd3500ad9e35ce201c5101646cbf6ff98\nSSHFP 3 2 1dd2aa8f29b539337316e2862b28c196c68ffe0af78fccf9e50625635677e50f"],
25 'SSHED25519Key' => [ 'ssh_host_ed25519_key.pub' , 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkxkUMKV0H7Z0KDgfMs+iKQFwJhKUDg8GImV/BwN48X', "SSHFP 4 1 216d49ff3581a42c7a2d4064f2356b375367d493\nSSHFP 4 2 95e3aa6f86bc2dcc46f1e9e5ea930c790afc0669fcf237c4d7b0c8e386ef2790"]
2526 }.each_pair do |fact, data|
2627 describe "#{fact}" do
2728 let(:filename) { data[0] }
55 describe "Uniqueid fact" do
66 it "should match hostid on Solaris" do
77 Facter.fact(:kernel).stubs(:value).returns("SunOS")
8 Facter::Util::Resolution.stubs(:exec).with("hostid").returns("Larry")
8 Facter::Core::Execution.stubs(:execute).with("hostid", anything).returns("Larry")
99
1010 Facter.fact(:uniqueid).value.should == "Larry"
1111 end
1212
1313 it "should match hostid on Linux" do
1414 Facter.fact(:kernel).stubs(:value).returns("Linux")
15 Facter::Util::Resolution.stubs(:exec).with("hostid").returns("Curly")
15 Facter::Core::Execution.stubs(:execute).with("hostid", anything).returns("Curly")
1616
1717 Facter.fact(:uniqueid).value.should == "Curly"
1818 end
1919
2020 it "should match hostid on AIX" do
2121 Facter.fact(:kernel).stubs(:value).returns("AIX")
22 Facter::Util::Resolution.stubs(:exec).with("hostid").returns("Moe")
22 Facter::Core::Execution.stubs(:execute).with("hostid", anything).returns("Moe")
2323
2424 Facter.fact(:uniqueid).value.should == "Moe"
2525 end
2626 end
2727
2828 it "should accept options" do
29 collection.add(:myname, :ldapname => "whatever") { }
30 end
31
32 it "should set any appropriate options on the fact instances" do
33 # Use a real fact instance, because we're using respond_to?
29 collection.add(:myname, :timeout => 1) { }
30 end
31
32 it "passes resolution specific options to the fact" do
3433 fact = Facter::Util::Fact.new(:myname)
35
36 collection.add(:myname, :ldapname => "testing")
37 collection.fact(:myname).ldapname.should == "testing"
38 end
39
40 it "should set appropriate options on the resolution instance" do
41 fact = Facter::Util::Fact.new(:myname)
42 Facter::Util::Fact.expects(:new).with(:myname).returns fact
43
44 resolve = Facter::Util::Resolution.new(:myname) {}
45 fact.expects(:add).returns resolve
34 Facter::Util::Fact.expects(:new).with(:myname, {:timeout => 'myval'}).returns fact
35
36 fact.expects(:add).with({:timeout => 'myval'})
4637
4738 collection.add(:myname, :timeout => "myval") {}
48 end
49
50 it "should not pass fact-specific options to resolutions" do
51 fact = Facter::Util::Fact.new(:myname)
52 Facter::Util::Fact.expects(:new).with(:myname).returns fact
53
54 resolve = Facter::Util::Resolution.new(:myname) {}
55 fact.expects(:add).returns resolve
56
57 fact.expects(:ldapname=).with("foo")
58 resolve.expects(:timeout=).with("myval")
59
60 collection.add(:myname, :timeout => "myval", :ldapname => "foo") {}
61 end
62
63 it "should fail if invalid options are provided" do
64 lambda { collection.add(:myname, :foo => :bar) }.should raise_error(ArgumentError)
6539 end
6640
6741 describe "and a block is provided" do
6842 it "should use the block to add a resolution to the fact" do
6943 fact = mock 'fact'
44 fact.stubs(:extract_ldapname_option!)
7045 Facter::Util::Fact.expects(:new).returns fact
7146
7247 fact.expects(:add)
7550 end
7651
7752 it "should discard resolutions that throw an exception when added" do
78 lambda {
53 Facter.expects(:warn).with(regexp_matches(/Unable to add resolve .* kaboom!/))
54 expect {
7955 collection.add('yay') do
80 raise
81 setcode { 'yay' }
56 raise "kaboom!"
8257 end
83 }.should_not raise_error
84 collection.value('yay').should be_nil
58 }.to_not raise_error
59 expect(collection.value('yay')).to be_nil
60 end
61 end
62 end
63
64 describe "when only defining facts" do
65 it "creates a new fact if no such fact exists" do
66 fact = Facter::Util::Fact.new(:newfact)
67 Facter::Util::Fact.expects(:new).with(:newfact, {}).returns fact
68 expect(collection.define_fact(:newfact)).to equal fact
69 end
70
71 it "returns an existing fact if the fact has already been defined" do
72 fact = collection.define_fact(:newfact)
73 expect(collection.define_fact(:newfact)).to equal fact
74 end
75
76 it "passes options to newly generated facts" do
77 Facter.stubs(:warnonce)
78 fact = collection.define_fact(:newfact, :ldapname => 'NewFact')
79 expect(fact.ldapname).to eq 'NewFact'
80 end
81
82 it "logs a warning if the fact could not be defined" do
83 Facter.expects(:warn).with("Unable to add fact newfact: kaboom!")
84
85 collection.define_fact(:newfact) do
86 raise "kaboom!"
8587 end
8688 end
8789 end
208210 end
209211
210212 describe "external facts" do
211 let(:collection) { Facter::Util::Collection.new(internal_loader, SingleFactLoader.new(:test_fact, "fact value")) }
213 let(:external_loader) { SingleFactLoader.new(:test_fact, "fact value") }
214 let(:collection) { Facter::Util::Collection.new(internal_loader, external_loader) }
212215
213216 it "loads when a specific fact is requested" do
214217 collection.fact(:test_fact).value.should == "fact value"
223226 collection.each { |fact_name, fact_value| facts << [fact_name, fact_value] }
224227
225228 facts.should == [["test_fact", "fact value"]]
229 end
230
231 it "are loaded only once" do
232 external_loader.expects(:load).with(collection)
233
234 collection.load_all
235 collection.load_all
236 end
237
238 it "are reloaded after flushing" do
239 external_loader.expects(:load).with(collection).twice
240
241 collection.load_all
242 collection.flush
243 collection.load_all
226244 end
227245 end
228246
3333 end
3434
3535 describe "external_facts_dirs" do
36 before :each do
37 Facter::Util::Root.stubs(:root?).returns(true)
38 end
39
3640 it "should return the default value for linux" do
3741 Facter::Util::Config.stubs(:is_windows?).returns(false)
3842 Facter::Util::Config.stubs(:windows_data_dir).returns(nil)
43 Facter::Util::Config.setup_default_ext_facts_dirs
3944 Facter::Util::Config.external_facts_dirs.should == ["/etc/facter/facts.d", "/etc/puppetlabs/facter/facts.d"]
4045 end
4146
4247 it "should return the default value for windows 2008" do
4348 Facter::Util::Config.stubs(:is_windows?).returns(true)
4449 Facter::Util::Config.stubs(:windows_data_dir).returns("C:\\ProgramData")
50 Facter::Util::Config.setup_default_ext_facts_dirs
4551 Facter::Util::Config.external_facts_dirs.should == [File.join("C:\\ProgramData", 'PuppetLabs', 'facter', 'facts.d')]
4652 end
4753
4854 it "should return the default value for windows 2003R2" do
4955 Facter::Util::Config.stubs(:is_windows?).returns(true)
5056 Facter::Util::Config.stubs(:windows_data_dir).returns("C:\\Documents")
57 Facter::Util::Config.setup_default_ext_facts_dirs
5158 Facter::Util::Config.external_facts_dirs.should == [File.join("C:\\Documents", 'PuppetLabs', 'facter', 'facts.d')]
5259 end
60
61 it "returns the users home directory when not root" do
62 Facter::Util::Root.stubs(:root?).returns(false)
63 Facter::Util::Config.setup_default_ext_facts_dirs
64 Facter::Util::Config.external_facts_dirs.should == [File.expand_path(File.join("~", ".facter", "facts.d"))]
65 end
66
67 it "includes additional values when user appends to the list" do
68 Facter::Util::Config.setup_default_ext_facts_dirs
69 original_values = Facter::Util::Config.external_facts_dirs.dup
70 new_value = '/usr/share/newdir'
71 Facter::Util::Config.external_facts_dirs << new_value
72 Facter::Util::Config.external_facts_dirs.should == original_values + [new_value]
73 end
74
75 it "should only output new values when explicitly set" do
76 Facter::Util::Config.setup_default_ext_facts_dirs
77 new_value = ['/usr/share/newdir']
78 Facter::Util::Config.external_facts_dirs = new_value
79 Facter::Util::Config.external_facts_dirs.should == new_value
80 end
81
5382 end
5483 end
122122 it "should return false if none of the provided ranges matches the fact's value" do
123123 confined(8, (5..7)).should be_false
124124 end
125
126 it "should accept and evaluate a block argument against the fact" do
127 @fact.expects(:value).returns 'foo'
128 confine = Facter::Util::Confine.new :yay do |f| f === 'foo' end
129 confine.true?.should be_true
130 end
131
132 it "should return false if the block raises a StandardError when checking a fact" do
133 @fact.stubs(:value).returns 'foo'
134 confine = Facter::Util::Confine.new :yay do |f| raise StandardError end
135 confine.true?.should be_false
136 end
137
138 it "should accept and evaluate only a block argument" do
139 Facter::Util::Confine.new { true }.true?.should be_true
140 Facter::Util::Confine.new { false }.true?.should be_false
141 end
142
143 it "should return false if the block raises a StandardError" do
144 Facter::Util::Confine.new { raise StandardError }.true?.should be_false
145 end
125146 end
126147 end
5454
5555 %w{bak orig}.each do |ext|
5656 it "should ignore files with an extension of '#{ext}'" do
57 Facter.expects(:warn).with(regexp_matches(/#{ext}/))
5758 write_to_file("data" + ".#{ext}", "foo=bar")
5859
5960 subject.load(collection)
1616
1717 it "should succeed if arp table contains fe:ff:ff:ff:ff:ff" do
1818 ec2arp = my_fixture_read("linux-arp-ec2.out")
19 Facter::Util::Resolution.expects(:exec).with("arp -an").\
19 Facter::Core::Execution.expects(:exec).with("arp -an").\
2020 at_least_once.returns(ec2arp)
2121 Facter::Util::EC2.has_ec2_arp?.should == true
2222 end
2323
2424 it "should succeed if arp table contains FE:FF:FF:FF:FF:FF" do
2525 ec2arp = my_fixture_read("centos-arp-ec2.out")
26 Facter::Util::Resolution.expects(:exec).with("arp -an").\
26 Facter::Core::Execution.expects(:exec).with("arp -an").\
2727 at_least_once.returns(ec2arp)
2828 Facter::Util::EC2.has_ec2_arp?.should == true
2929 end
3030
3131 it "should fail if arp table does not contain fe:ff:ff:ff:ff:ff" do
3232 ec2arp = my_fixture_read("linux-arp-not-ec2.out")
33 Facter::Util::Resolution.expects(:exec).with("arp -an").
33 Facter::Core::Execution.expects(:exec).with("arp -an").
3434 at_least_once.returns(ec2arp)
3535 Facter::Util::EC2.has_ec2_arp?.should == false
3636 end
4444
4545 it "should succeed if arp table contains fe-ff-ff-ff-ff-ff" do
4646 ec2arp = my_fixture_read("windows-2008-arp-a.out")
47 Facter::Util::Resolution.expects(:exec).with("arp -a").\
47 Facter::Core::Execution.expects(:exec).with("arp -a").\
4848 at_least_once.returns(ec2arp)
4949 Facter::Util::EC2.has_ec2_arp?.should == true
5050 end
5151
5252 it "should fail if arp table does not contain fe-ff-ff-ff-ff-ff" do
5353 ec2arp = my_fixture_read("windows-2008-arp-a-not-ec2.out")
54 Facter::Util::Resolution.expects(:exec).with("arp -a").
54 Facter::Core::Execution.expects(:exec).with("arp -a").
5555 at_least_once.returns(ec2arp)
5656 Facter::Util::EC2.has_ec2_arp?.should == false
5757 end
6565 it "should fail if arp table does not contain fe:ff:ff:ff:ff:ff" do
6666 ec2arp = my_fixture_read("solaris8_arp_a_not_ec2.out")
6767
68 Facter::Util::Resolution.expects(:exec).with("arp -a").
68 Facter::Core::Execution.expects(:exec).with("arp -a").
6969 at_least_once.returns(ec2arp)
7070
7171 Facter::Util::EC2.has_ec2_arp?.should == false
33 require 'facter/util/fact'
44
55 describe Facter::Util::Fact do
6 it "should require a name" do
7 lambda { Facter::Util::Fact.new }.should raise_error(ArgumentError)
6
7 subject(:fact) { Facter::Util::Fact.new("yay") }
8
9 let(:resolution) { Facter::Util::Resolution.new("yay", fact) }
10
11 it "requires a name" do
12 expect { Facter::Util::Fact.new }.to raise_error(ArgumentError)
813 end
914
10 it "should always downcase the name and convert it to a symbol" do
11 Facter::Util::Fact.new("YayNess").name.should == :yayness
15 it "downcases and converts the name to a symbol" do
16 expect(Facter::Util::Fact.new("YayNess").name).to eq :yayness
1217 end
1318
14 it "should default to its name converted to a string as its ldapname" do
15 Facter::Util::Fact.new("YayNess").ldapname.should == "yayness"
19 it "issues a deprecation warning for use of ldapname" do
20 Facter.expects(:warnonce).with("ldapname is deprecated and will be removed in a future version")
21 Facter::Util::Fact.new("YayNess", :ldapname => "fooness")
1622 end
1723
18 it "should allow specifying the ldap name at initialization" do
19 Facter::Util::Fact.new("YayNess", :ldapname => "fooness").ldapname.should == "fooness"
20 end
21
22 it "should fail if an unknown option is provided" do
23 lambda { Facter::Util::Fact.new('yay', :foo => :bar) }.should raise_error(ArgumentError)
24 end
25
26 it "should have a method for adding resolution mechanisms" do
27 Facter::Util::Fact.new("yay").should respond_to(:add)
28 end
29
30 describe "when adding resolution mechanisms" do
31 before do
32 @fact = Facter::Util::Fact.new("yay")
33
34 @resolution = Facter::Util::Resolution.new("yay")
35 end
36
37 it "should be to create a new resolution instance with a block" do
38 Facter::Util::Resolution.expects(:new).returns @resolution
39
40 @fact.add { }
41 end
42 it "should instance_eval the passed block on the new resolution" do
43 @fact.add {
44 setcode { "foo" }
45 }
46 @fact.value.should == "foo"
47 end
48
49 it "should re-sort the resolutions by weight, so the most restricted resolutions are first" do
50 @fact.add { self.value = "1"; self.weight = 1 }
51 @fact.add { self.value = "2"; self.weight = 2 }
52 @fact.add { self.value = "0"; self.weight = 0 }
53 @fact.value.should == "2"
24 describe "when adding resolution mechanisms using #add" do
25 it "delegates to #define_resolution with an anonymous resolution" do
26 subject.expects(:define_resolution).with(nil, {})
27 subject.add
5428 end
5529 end
5630
57 it "should be able to return a value" do
58 Facter::Util::Fact.new("yay").should respond_to(:value)
31 describe "looking up resolutions by name" do
32 subject(:fact) { described_class.new('yay') }
33
34 it "returns nil if no such resolution exists" do
35 expect(fact.resolution('nope')).to be_nil
36 end
37
38 it "never returns anonymous resolutions" do
39 fact.add() { setcode { 'anonymous' } }
40
41 expect(fact.resolution(nil)).to be_nil
42 end
43 end
44
45 describe "adding resolution mechanisms by name" do
46
47 let(:res) do
48 stub 'resolution',
49 :name => 'named',
50 :set_options => nil,
51 :resolution_type => :simple
52 end
53
54 it "creates a new resolution if no such resolution exists" do
55 Facter::Util::Resolution.expects(:new).once.with('named', fact).returns(res)
56
57 fact.define_resolution('named')
58
59 expect(fact.resolution('named')).to eq res
60 end
61
62 it "creates a simple resolution when the type is nil" do
63 fact.define_resolution('named')
64 expect(fact.resolution('named')).to be_a_kind_of Facter::Util::Resolution
65 end
66
67 it "creates a simple resolution when the type is :simple" do
68 fact.define_resolution('named', :type => :simple)
69 expect(fact.resolution('named')).to be_a_kind_of Facter::Util::Resolution
70 end
71
72 it "creates an aggregate resolution when the type is :aggregate" do
73 fact.define_resolution('named', :type => :aggregate)
74 expect(fact.resolution('named')).to be_a_kind_of Facter::Core::Aggregate
75 end
76
77 it "raises an error if there is an existing resolution with a different type" do
78 pending "We need to stop rescuing all errors when instantiating resolutions"
79 fact.define_resolution('named')
80 expect {
81 fact.define_resolution('named', :type => :aggregate)
82 }.to raise_error(ArgumentError, /Cannot return resolution.*already defined as simple/)
83 end
84
85 it "returns existing resolutions by name" do
86 Facter::Util::Resolution.expects(:new).once.with('named', fact).returns(res)
87
88 fact.define_resolution('named')
89 fact.define_resolution('named')
90
91 expect(fact.resolution('named')).to eq res
92 end
5993 end
6094
6195 describe "when returning a value" do
62 before do
63 @fact = Facter::Util::Fact.new("yay")
64 end
65
66 it "should return nil if there are no resolutions" do
96 it "returns nil if there are no resolutions" do
6797 Facter::Util::Fact.new("yay").value.should be_nil
6898 end
6999
70 it "should return the first value returned by a resolution" do
71 r1 = stub 'r1', :weight => 2, :value => nil, :suitable? => true
72 r2 = stub 'r2', :weight => 1, :value => "yay", :suitable? => true
73 r3 = stub 'r3', :weight => 0, :value => "foo", :suitable? => true
74 Facter::Util::Resolution.expects(:new).times(3).returns(r1).returns(r2).returns(r3)
75 @fact.add { }
76 @fact.add { }
77 @fact.add { }
78
79 @fact.value.should == "yay"
100 it "prefers the highest weight resolution" do
101 fact.add { has_weight 1; setcode { "1" } }
102 fact.add { has_weight 2; setcode { "2" } }
103 fact.add { has_weight 0; setcode { "0" } }
104 expect(fact.value).to eq "2"
80105 end
81106
82 it "should short-cut returning the value once one is found" do
83 r1 = stub 'r1', :weight => 2, :value => "foo", :suitable? => true
84 r2 = stub 'r2', :weight => 1, :suitable? => true # would fail if 'value' were asked for
85 Facter::Util::Resolution.expects(:new).times(2).returns(r1).returns(r2)
86 @fact.add { }
87 @fact.add { }
88
89 @fact.value
107 it "returns the first value returned by a resolution" do
108 fact.add { has_weight 1; setcode { "1" } }
109 fact.add { has_weight 2; setcode { nil } }
110 fact.add { has_weight 0; setcode { "0" } }
111 expect(fact.value).to eq "1"
90112 end
91113
92 it "should skip unsuitable resolutions" do
93 r1 = stub 'r1', :weight => 2, :suitable? => false # would fail if 'value' were asked for'
94 r2 = stub 'r2', :weight => 1, :value => "yay", :suitable? => true
95 Facter::Util::Resolution.expects(:new).times(2).returns(r1).returns(r2)
96 @fact.add { }
97 @fact.add { }
114 it "skips unsuitable resolutions" do
115 fact.add { has_weight 1; setcode { "1" } }
116 fact.add do
117 def suitable?; false; end
118 has_weight 2
119 setcode { 2 }
120 end
98121
99 @fact.value.should == "yay"
100 end
101
102 it "should return nil if the value is the empty string" do
103 r1 = stub 'r1', :suitable? => true, :value => ""
104 Facter::Util::Resolution.expects(:new).returns r1
105 @fact.add { }
106
107 @fact.value.should be_nil
122 expect(fact.value).to eq "1"
108123 end
109124 end
110125
112127 subject do
113128 Facter::Util::Fact.new(:foo)
114129 end
115 context 'basic facts using setcode' do
116 it "flushes the cached value when invoked" do
117 system = mock('some_system_call')
118 system.expects(:data).twice.returns(100,200)
119130
120 subject.add { setcode { system.data } }
121 5.times { subject.value.should == 100 }
122 subject.flush
123 subject.value.should == 200
124 end
125 end
126 context 'facts using setcode and on_flush' do
127 it 'invokes the block passed to on_flush' do
128 model = { :data => "Hello World" }
129 subject.add do
130 on_flush { model[:data] = "FLUSHED!" }
131 setcode { model[:data] }
132 end
133 subject.value.should == "Hello World"
134 subject.flush
135 subject.value.should == "FLUSHED!"
136 end
131 it "invokes #flush on all resolutions" do
132 simple = subject.add(:type => :simple)
133 simple.expects(:flush)
134
135 aggregate = subject.add(:type => :aggregate)
136 aggregate.expects(:flush)
137
138 subject.flush
137139 end
138140 end
139141 end
3333 let(:name) { 'Local Area Connection' }
3434 let(:index) { 7 }
3535 let(:nic_config) { mock('nic_config', :Index => index) }
36 let(:nic) { mock('nic', :NetConnectionId => name ) }
36 let(:nic) { stub('nic', :NetConnectionId => name ) }
37 let(:nic_empty_NetConnectionId) { stub('nic', :NetConnectionId => '' ) }
38 let(:nic_nil_NetConnectionId) { stub('nic', :NetConnectionId => nil ) }
39 let(:wmi_query) {"SELECT * FROM Win32_NetworkAdapter WHERE Index = #{index} AND NetEnabled = TRUE"}
3740
3841 it "should return an array of only connected interfaces" do
3942 Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY).
4043 returns([nic_config])
41 Facter::Util::WMI.expects(:execquery).with("SELECT * FROM Win32_NetworkAdapter WHERE Index = #{index}").
44 Facter::Util::WMI.expects(:execquery).with(wmi_query).
4245 returns([nic])
4346
4447 described_class.interfaces.should == [name]
4548 end
49
50 it "should not return an interface with an empty NetConnectionId" do
51 Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY).
52 returns([nic_config])
53 Facter::Util::WMI.expects(:execquery).with(wmi_query).
54 returns([nic_empty_NetConnectionId])
55
56 described_class.interfaces.should == []
57 end
58
59 it "should not return an interface with a nil NetConnectionId" do
60 Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY).
61 returns([nic_config])
62 Facter::Util::WMI.expects(:execquery).with(wmi_query).
63 returns([nic_nil_NetConnectionId])
64
65 described_class.interfaces.should == []
66 end
67
68 context "when the adapter configuration is enabled but the underlying adapter is not enabled" do
69 it "should not return an interface" do
70 Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY).
71 returns([nic_config])
72 Facter::Util::WMI.expects(:execquery).with(wmi_query).
73 returns([])
74
75 described_class.interfaces.should == []
76 end
77 end
4678 end
4779 end
400400
401401 describe "exec_ifconfig" do
402402 it "uses get_ifconfig" do
403 Facter::Core::Execution.stubs(:exec)
404
403405 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig").once
404406
405407 Facter::Util::IP.exec_ifconfig
406408 end
409
407410 it "support additional arguments" do
408411 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig")
409412
410 Facter::Util::Resolution.stubs(:exec).with("/sbin/ifconfig -a")
413 Facter::Core::Execution.stubs(:exec).with("/sbin/ifconfig -a")
411414
412415 Facter::Util::IP.exec_ifconfig(["-a"])
413416 end
417
414418 it "joins multiple arguments correctly" do
415419 Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig")
416420
417 Facter::Util::Resolution.stubs(:exec).with("/sbin/ifconfig -a -e -i -j")
421 Facter::Core::Execution.stubs(:exec).with("/sbin/ifconfig -a -e -i -j")
418422
419423 Facter::Util::IP.exec_ifconfig(["-a","-e","-i","-j"])
420424 end
11
22 require 'spec_helper'
33 require 'facter/util/loader'
4
5 # loader subclass for making assertions about file/directory ordering
6 class TestLoader < Facter::Util::Loader
7 def loaded_files
8 @loaded_files ||= []
9 end
10
11 def load_file(file)
12 loaded_files << file
13 super
14 end
15 end
16
174
185 describe Facter::Util::Loader do
196 before :each do
207 Facter::Util::Loader.any_instance.unstub(:load_all)
218 end
229
10 def loader_from(places)
11 env = places[:env] || {}
12 search_path = places[:search_path] || []
13 loader = Facter::Util::Loader.new(env)
14 loader.stubs(:search_path).returns search_path
15 loader
16 end
17
2318 it "should have a method for loading individual facts by name" do
2419 Facter::Util::Loader.new.should respond_to(:load)
2520 end
3328 end
3429
3530 describe "#valid_seach_path?" do
36 before :each do
37 @loader = Facter::Util::Loader.new
38 @settings = mock 'settings'
39 @settings.stubs(:value).returns "/eh"
40 end
41
42 it "should cache the result of a previous check" do
43 Pathname.any_instance.expects(:absolute?).returns(true).once
44
45 # we explicitly want two calls here to check that we get
46 # the second from the cache
47 @loader.should be_valid_search_path "/foo"
48 @loader.should be_valid_search_path "/foo"
49 end
31 let(:loader) { Facter::Util::Loader.new }
5032
5133 # Used to have test for " " as a directory since that should
5234 # be a relative directory, but on Windows in both 1.8.7 and
6749 ' \/',
6850 ].each do |dir|
6951 it "should be false for relative path #{dir}" do
70 @loader.should_not be_valid_search_path dir
52 loader.should_not be_valid_search_path dir
7153 end
7254 end
7355 [
8365 '/ /..',
8466 ].each do |dir|
8567 it "should be true for absolute path #{dir}" do
86 @loader.should be_valid_search_path dir
68 loader.should be_valid_search_path dir
8769 end
8870 end
8971 end
9072
9173 describe "when determining the search path" do
92 before do
93 @loader = Facter::Util::Loader.new
94 @settings = mock 'settings'
95 @settings.stubs(:value).returns "/eh"
96 end
74 let(:loader) { Facter::Util::Loader.new }
9775
9876 it "should include the facter subdirectory of all paths in ruby LOAD_PATH" do
99 dirs = $LOAD_PATH.collect { |d| File.join(d, "facter") }
100 @loader.stubs(:valid_search_path?).returns(true)
101 paths = @loader.search_path
77 dirs = $LOAD_PATH.collect { |d| File.expand_path('facter', d) }
78 loader.stubs(:valid_search_path?).returns(true)
79 File.stubs(:directory?).returns true
80
81 paths = loader.search_path
10282
10383 dirs.each do |dir|
10484 paths.should be_include(dir)
10585 end
10686 end
10787
108 it "should warn the user when an invalid search path has been excluded" do
109 dirs = $LOAD_PATH.collect { |d| File.join(d, "facter") }
110 @loader.stubs(:valid_search_path?).returns(false)
111 dirs.each do |dir|
112 Facter.expects(:debugonce).with("Relative directory #{dir} removed from search path.").once
113 end
114 paths = @loader.search_path
115 end
116
11788 it "should exclude invalid search paths" do
11889 dirs = $LOAD_PATH.collect { |d| File.join(d, "facter") }
119 @loader.stubs(:valid_search_path?).returns(false)
120 paths = @loader.search_path
90 loader.stubs(:valid_search_path?).returns(false)
91 paths = loader.search_path
12192 dirs.each do |dir|
12293 paths.should_not be_include(dir)
12394 end
12596
12697 it "should include all search paths registered with Facter" do
12798 Facter.expects(:search_path).returns %w{/one /two}
128 paths = @loader.search_path
99 loader.stubs(:valid_search_path?).returns true
100
101 File.stubs(:directory?).returns false
102 File.stubs(:directory?).with('/one').returns true
103 File.stubs(:directory?).with('/two').returns true
104
105 paths = loader.search_path
129106 paths.should be_include("/one")
130107 paths.should be_include("/two")
131108 end
132109
110 it "should warn on invalid search paths registered with Facter" do
111 Facter.expects(:search_path).returns %w{/one two/three}
112 loader.stubs(:valid_search_path?).returns false
113 loader.stubs(:valid_search_path?).with('/one').returns true
114 loader.stubs(:valid_search_path?).with('two/three').returns false
115 Facter.expects(:warn).with('Excluding two/three from search path. Fact file paths must be an absolute directory').once
116
117 File.stubs(:directory?).returns false
118 File.stubs(:directory?).with('/one').returns true
119
120 paths = loader.search_path
121 paths.should be_include("/one")
122 paths.should_not be_include("two/three")
123 end
124
125 it "should strip paths that are valid paths but not are not present" do
126 Facter.expects(:search_path).returns %w{/one /two}
127 loader.stubs(:valid_search_path?).returns false
128 loader.stubs(:valid_search_path?).with('/one').returns true
129 loader.stubs(:valid_search_path?).with('/two').returns true
130
131 File.stubs(:directory?).returns false
132 File.stubs(:directory?).with('/one').returns true
133 File.stubs(:directory?).with('/two').returns false
134
135 paths = loader.search_path
136 paths.should be_include("/one")
137 paths.should_not be_include('/two')
138 end
139
133140 describe "and the FACTERLIB environment variable is set" do
134141 it "should include all paths in FACTERLIB" do
135 Facter::Util::Resolution.with_env "FACTERLIB" => "/one/path#{File::PATH_SEPARATOR}/two/path" do
136 paths = @loader.search_path
137 %w{/one/path /two/path}.each do |dir|
138 paths.should be_include(dir)
139 end
142 loader = Facter::Util::Loader.new("FACTERLIB" => "/one/path#{File::PATH_SEPARATOR}/two/path")
143
144 File.stubs(:directory?).returns false
145 File.stubs(:directory?).with('/one/path').returns true
146 File.stubs(:directory?).with('/two/path').returns true
147
148 loader.stubs(:valid_search_path?).returns true
149 paths = loader.search_path
150 %w{/one/path /two/path}.each do |dir|
151 paths.should be_include(dir)
140152 end
141153 end
142154 end
143155 end
144156
145157 describe "when loading facts" do
146 before do
147 @loader = Facter::Util::Loader.new
148 @loader.stubs(:search_path).returns []
149 end
150
151158 it "should load values from the matching environment variable if one is present" do
159 loader = loader_from(:env => { "facter_testing" => "yayness" })
160
152161 Facter.expects(:add).with("testing")
153162
154 Facter::Util::Resolution.with_env "facter_testing" => "yayness" do
155 @loader.load(:testing)
156 end
163 loader.load(:testing)
157164 end
158165
159166 it "should load any files in the search path with names matching the fact name" do
160 @loader.expects(:search_path).returns %w{/one/dir /two/dir}
161 FileTest.stubs(:exist?).returns false
162 FileTest.expects(:exist?).with("/one/dir/testing.rb").returns true
163 FileTest.expects(:exist?).with("/two/dir/testing.rb").returns true
167 loader = loader_from(:search_path => %w{/one/dir /two/dir})
168
169 loader.expects(:search_path).returns %w{/one/dir /two/dir}
170 File.stubs(:file?).returns false
171 File.expects(:file?).with("/one/dir/testing.rb").returns true
164172
165173 Kernel.expects(:load).with("/one/dir/testing.rb")
166 Kernel.expects(:load).with("/two/dir/testing.rb")
167
168 @loader.load(:testing)
169 end
170
171 it 'should load any ruby files in directories matching the fact name in the search path in sorted order regardless of the order returned by Dir.entries' do
172 @loader = TestLoader.new
173
174 @loader.stubs(:search_path).returns %w{/one/dir}
175 FileTest.stubs(:exist?).returns false
176 FileTest.stubs(:directory?).with("/one/dir/testing").returns true
177 @loader.stubs(:search_path).returns %w{/one/dir}
174
175 loader.load(:testing)
176 end
177
178 it 'should not load any ruby files from subdirectories matching the fact name in the search path' do
179 loader = Facter::Util::Loader.new
180 File.stubs(:file?).returns false
181 File.expects(:file?).with("/one/dir/testing.rb").returns true
182 Kernel.expects(:load).with("/one/dir/testing.rb")
183
184 File.stubs(:directory?).with("/one/dir/testing").returns true
185 loader.stubs(:search_path).returns %w{/one/dir}
178186
179187 Dir.stubs(:entries).with("/one/dir/testing").returns %w{foo.rb bar.rb}
180188 %w{/one/dir/testing/foo.rb /one/dir/testing/bar.rb}.each do |f|
182190 Kernel.stubs(:load).with(f)
183191 end
184192
185 @loader.load(:testing)
186 @loader.loaded_files.should == %w{/one/dir/testing/bar.rb /one/dir/testing/foo.rb}
187 end
188
189 it "should load any ruby files in directories matching the fact name in the search path" do
190 @loader.expects(:search_path).returns %w{/one/dir}
191 FileTest.stubs(:exist?).returns false
192 FileTest.expects(:directory?).with("/one/dir/testing").returns true
193
194 Dir.expects(:entries).with("/one/dir/testing").returns %w{two.rb}
195
196 Kernel.expects(:load).with("/one/dir/testing/two.rb")
197
198 @loader.load(:testing)
193 loader.load(:testing)
199194 end
200195
201196 it "should not load files that don't end in '.rb'" do
202 @loader.expects(:search_path).returns %w{/one/dir}
203 FileTest.stubs(:exist?).returns false
204 FileTest.expects(:directory?).with("/one/dir/testing").returns true
205
206 Dir.expects(:entries).with("/one/dir/testing").returns %w{one}
207
197 loader = Facter::Util::Loader.new
198 loader.expects(:search_path).returns %w{/one/dir}
199 File.stubs(:file?).returns false
200 File.expects(:file?).with("/one/dir/testing.rb").returns false
201 File.expects(:exist?).with("/one/dir/testing").never
208202 Kernel.expects(:load).never
209203
210 @loader.load(:testing)
204 loader.load(:testing)
211205 end
212206 end
213207
214208 describe "when loading all facts" do
209 let(:loader) { Facter::Util::Loader.new }
210
215211 before :each do
216 @loader = Facter::Util::Loader.new
217 @loader.stubs(:search_path).returns []
218
219 FileTest.stubs(:directory?).returns true
220 end
221
222 it "should skip directories that do not exist" do
223 @loader.expects(:search_path).returns %w{/one/dir}
224
225 FileTest.expects(:directory?).with("/one/dir").returns false
226
227 Dir.expects(:entries).with("/one/dir").never
228
229 @loader.load_all
212 loader.stubs(:search_path).returns []
213
214 File.stubs(:directory?).returns true
230215 end
231216
232217 it "should load all files in all search paths" do
233 @loader.expects(:search_path).returns %w{/one/dir /two/dir}
234
235 Dir.expects(:entries).with("/one/dir").returns %w{a.rb b.rb}
236 Dir.expects(:entries).with("/two/dir").returns %w{c.rb d.rb}
237
238 %w{/one/dir/a.rb /one/dir/b.rb /two/dir/c.rb /two/dir/d.rb}.each { |f| Kernel.expects(:load).with(f) }
239
240 @loader.load_all
241 end
242
243 it "should load all files in all subdirectories in all search paths" do
244 @loader.expects(:search_path).returns %w{/one/dir /two/dir}
245
246 Dir.expects(:entries).with("/one/dir").returns %w{a}
247 Dir.expects(:entries).with("/two/dir").returns %w{b}
248
249 %w{/one/dir/a /two/dir/b}.each { |f| File.expects(:directory?).with(f).returns true }
250
251 Dir.expects(:entries).with("/one/dir/a").returns %w{c.rb}
252 Dir.expects(:entries).with("/two/dir/b").returns %w{d.rb}
253
254 %w{/one/dir/a/c.rb /two/dir/b/d.rb}.each { |f| Kernel.expects(:load).with(f) }
255
256 @loader.load_all
257 end
258
259 it 'should load all files in sorted order for any given directory regardless of the order returned by Dir.entries' do
260 @loader = TestLoader.new
261
262 @loader.stubs(:search_path).returns %w{/one/dir}
263 Dir.stubs(:entries).with("/one/dir").returns %w{foo.rb bar.rb}
264
265 %w{/one/dir}.each { |f| File.stubs(:directory?).with(f).returns true }
266
267 %w{/one/dir/foo.rb /one/dir/bar.rb}.each do |f|
268 File.stubs(:directory?).with(f).returns false
218 loader = loader_from(:search_path => %w{/one/dir /two/dir})
219
220 Dir.expects(:glob).with('/one/dir/*.rb').returns %w{/one/dir/a.rb /one/dir/b.rb}
221 Dir.expects(:glob).with('/two/dir/*.rb').returns %w{/two/dir/c.rb /two/dir/d.rb}
222
223 %w{/one/dir/a.rb /one/dir/b.rb /two/dir/c.rb /two/dir/d.rb}.each do |f|
224 File.expects(:file?).with(f).returns true
269225 Kernel.expects(:load).with(f)
270226 end
271227
272 @loader.load_all
273
274 @loader.loaded_files.should == %w{/one/dir/bar.rb /one/dir/foo.rb}
275 end
276
277 it "should not load files in the util subdirectory" do
278 @loader.expects(:search_path).returns %w{/one/dir}
279
280 Dir.expects(:entries).with("/one/dir").returns %w{util}
281
282 File.expects(:directory?).with("/one/dir/util").returns true
283
284 Dir.expects(:entries).with("/one/dir/util").never
285
286 @loader.load_all
287 end
288
289 it "should not load files in a lib subdirectory" do
290 @loader.expects(:search_path).returns %w{/one/dir}
291
292 Dir.expects(:entries).with("/one/dir").returns %w{lib}
293
294 File.expects(:directory?).with("/one/dir/lib").returns true
295
296 Dir.expects(:entries).with("/one/dir/lib").never
297
298 @loader.load_all
299 end
300
301 it "should not load files in '.' or '..'" do
302 @loader.expects(:search_path).returns %w{/one/dir}
303
304 Dir.expects(:entries).with("/one/dir").returns %w{. ..}
305
306 File.expects(:entries).with("/one/dir/.").never
307 File.expects(:entries).with("/one/dir/..").never
308
309 @loader.load_all
228 loader.load_all
229 end
230
231 it "should not try to load subdirectories of search paths" do
232 loader.expects(:search_path).returns %w{/one/dir /two/dir}
233
234 # a.rb is a directory
235 Dir.expects(:glob).with('/one/dir/*.rb').returns %w{/one/dir/a.rb /one/dir/b.rb}
236 File.expects(:file?).with('/one/dir/a.rb').returns false
237 File.expects(:file?).with('/one/dir/b.rb').returns true
238 Kernel.expects(:load).with('/one/dir/b.rb')
239
240 # c.rb is a directory
241 Dir.expects(:glob).with('/two/dir/*.rb').returns %w{/two/dir/c.rb /two/dir/d.rb}
242 File.expects(:file?).with('/two/dir/c.rb').returns false
243 File.expects(:file?).with('/two/dir/d.rb').returns true
244 Kernel.expects(:load).with('/two/dir/d.rb')
245
246 loader.load_all
310247 end
311248
312249 it "should not raise an exception when a file is unloadable" do
313 @loader.expects(:search_path).returns %w{/one/dir}
314 Dir.expects(:entries).with("/one/dir").returns %w{a.rb}
250 loader.expects(:search_path).returns %w{/one/dir}
251
252 Dir.expects(:glob).with('/one/dir/*.rb').returns %w{/one/dir/a.rb}
253 File.expects(:file?).with('/one/dir/a.rb').returns true
315254
316255 Kernel.expects(:load).with("/one/dir/a.rb").raises(LoadError)
317 @loader.expects(:warn)
318
319 lambda { @loader.load_all }.should_not raise_error
256 Facter.expects(:warn)
257
258 expect { loader.load_all }.to_not raise_error
320259 end
321260
322261 it "should load all facts from the environment" do
323262 Facter::Util::Resolution.with_env "facter_one" => "yayness", "facter_two" => "boo" do
324 @loader.load_all
263 loader.load_all
325264 end
326265 Facter.value(:one).should == 'yayness'
327266 Facter.value(:two).should == 'boo'
328267 end
329268
330269 it "should only load all facts one time" do
331 @loader.expects(:load_env).once
332 @loader.load_all
333 @loader.load_all
270 loader = loader_from(:env => {})
271 loader.expects(:load_env).once
272
273 loader.load_all
274 loader.load_all
334275 end
335276 end
336277
337278 it "should load facts on the facter search path only once" do
338279 facterlibdir = File.expand_path(File.dirname(__FILE__) + '../../../fixtures/unit/util/loader')
339 Facter::Util::Resolution.with_env 'FACTERLIB' => facterlibdir do
340 Facter::Util::Loader.new.load_all
341 Facter.value(:nosuchfact).should be_nil
342 end
280
281 Facter::Util::Loader.new('FACTERLIB' => facterlibdir).load_all
282
283 Facter.value(:nosuchfact).should be_nil
343284 end
344285 end
6666
6767 it "should warn about the lack of default" do
6868 Facter.expects(:warn).with("Could not find a default route. Using first non-loopback interface")
69 Facter::Util::Macaddress::Darwin.stubs(:default_interface).returns('')
7069 Facter::Util::Macaddress::Darwin.macaddress
7170 end
7271
7372 it "should return the macaddress of the first non-loopback interface" do
73 Facter.expects(:warn).with("Could not find a default route. Using first non-loopback interface")
7474 Facter::Util::Macaddress::Darwin.macaddress.should == fallback_macaddress
7575 end
7676 end
8989 include FacterSpec::WindowsNetwork
9090
9191 before :each do
92 Facter.fact(:kernel).stubs(:value).returns(:windows)
92 Facter.fact(:kernel).stubs(:value).returns('windows')
9393 Facter::Util::Registry.stubs(:hklm_read).returns(nic_bindings)
9494 end
9595
2626 end
2727
2828 it "should be able to retrieve profiler data as xml for a given data field" do
29 Facter::Util::Resolution.expects(:exec).with("/usr/sbin/system_profiler -xml foo").returns "yay"
29 Facter::Core::Execution.expects(:exec).with("/usr/sbin/system_profiler -xml foo").returns "yay"
3030 Facter::Util::Macosx.profiler_xml("foo").should == "yay"
3131 end
3232
4444 STDERR.stubs(:<<)
4545 expect {
4646 Facter::Util::Macosx.intern_xml('<bad}|%-->xml<--->')
47 }.to raise_error(RuntimeError, /A plist file could not be properly read by Facter::Util::CFPropertyList/)
47 }.to raise_error(RuntimeError, /A plist file could not be properly read by CFPropertyList/)
4848 end
4949
5050 describe "when collecting profiler data" do
7979 describe "when working out software version" do
8080
8181 before do
82 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -productName").returns "Mac OS X"
83 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -buildVersion").returns "9J62"
82 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -productName").returns "Mac OS X"
83 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -buildVersion").returns "9J62"
8484 end
8585
8686 it "should have called sw_vers three times when determining software version" do
87 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "10.5.7"
87 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "10.5.7"
8888 Facter::Util::Macosx.sw_vers
8989 end
9090
9191 it "should return a hash with the correct keys when determining software version" do
92 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "10.5.7"
92 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "10.5.7"
9393 Facter::Util::Macosx.sw_vers.keys.sort.should == ["macosx_productName",
9494 "macosx_buildVersion",
9595 "macosx_productversion_minor",
9898 end
9999
100100 it "should split a product version of 'x.y.z' into separate hash entries correctly" do
101 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "1.2.3"
101 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "1.2.3"
102102 sw_vers = Facter::Util::Macosx.sw_vers
103103 sw_vers["macosx_productversion_major"].should == "1.2"
104104 sw_vers["macosx_productversion_minor"].should == "3"
105105 end
106106
107107 it "should treat a product version of 'x.y' as 'x.y.0" do
108 Facter::Util::Resolution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "2.3"
108 Facter::Core::Execution.expects(:exec).with("/usr/bin/sw_vers -productVersion").returns "2.3"
109109 Facter::Util::Macosx.sw_vers["macosx_productversion_minor"].should == "0"
110110 end
111111 end
1717 end
1818
1919 it "should parse prtdiag output on a sunfire v120" do
20 Facter::Util::Resolution.stubs(:exec).returns(my_fixture_read("solaris_sunfire_v120_prtdiag"))
20 Facter::Core::Execution.stubs(:exec).returns(my_fixture_read("solaris_sunfire_v120_prtdiag"))
2121 Facter::Manufacturer.prtdiag_sparc_find_system_info()
2222 Facter.value(:manufacturer).should == "Sun Microsystems"
2323 Facter.value(:productname).should == "Sun Fire V120 (UltraSPARC-IIe 648MHz)"
2424 end
2525
2626 it "should parse prtdiag output on a t5220" do
27 Facter::Util::Resolution.stubs(:exec).returns(my_fixture_read("solaris_t5220_prtdiag"))
27 Facter::Core::Execution.stubs(:exec).returns(my_fixture_read("solaris_t5220_prtdiag"))
2828 Facter::Manufacturer.prtdiag_sparc_find_system_info()
2929 Facter.value(:manufacturer).should == "Sun Microsystems"
3030 Facter.value(:productname).should == "SPARC Enterprise T5220"
3535 Facter.fact(:kernel).stubs(:value).returns("SunOS")
3636 Facter.fact(:hardwareisa).stubs(:value).returns("sparc")
3737
38 Facter::Util::Resolution.stubs(:exec).with(regexp_matches(/prtdiag/)).returns(nil)
38 Facter::Core::Execution.stubs(:exec).with(regexp_matches(/prtdiag/)).returns(nil)
3939 Facter::Manufacturer.prtdiag_sparc_find_system_info()
4040 Facter.value(:manufacturer).should_not == "Sun Microsystems"
4141 end
+0
-42
spec/unit/util/monkey_patches_spec.rb less more
0 require 'spec_helper'
1 require 'tempfile'
2
3 describe 'Monkey Patches' do
4 let(:subject) { "a b c d e f\ng h i j" }
5
6 context 'String' do
7 it "should respond to lines" do
8 subject.lines.to_a.should == ["a b c d e f\n", "g h i j"]
9 end
10 it "should accept a block" do
11 our_lines = []
12 subject.lines do |line| our_lines << line end
13 our_lines.should == ["a b c d e f\n", "g h i j"]
14 end
15 end
16
17 context 'IO' do
18 it "should respond to lines" do
19 our_lines = nil
20 Tempfile.open("lines") do | file |
21 file.write(subject)
22 file.flush
23 file.rewind
24 our_lines = file.lines.to_a
25 end
26 our_lines.should == ["a b c d e f\n", "g h i j"]
27 end
28 it "should accept a block" do
29 our_lines = []
30 file = Tempfile.new("lines")
31 file.write(subject)
32 file.flush
33 file.rewind
34 file.lines.each do |line| our_lines << line end
35 file.unlink
36 our_lines.should == ["a b c d e f\n", "g h i j"]
37 end
38 end
39
40 end
41
0 # encoding: utf-8
1
2 require 'spec_helper'
3 require 'facter/util/normalization'
4
5 describe Facter::Util::Normalization do
6
7 subject { described_class }
8
9 def utf16(str)
10 if String.method_defined?(:encode) && defined?(::Encoding)
11 str.encode(Encoding::UTF_16LE)
12 else
13 str
14 end
15 end
16
17 def utf8(str)
18 if String.method_defined?(:encode) && defined?(::Encoding)
19 str.encode(Encoding::UTF_8)
20 else
21 str
22 end
23 end
24
25 describe "validating strings" do
26 describe "and string encoding is supported", :if => String.instance_methods.include?(:encoding) do
27 it "accepts strings that are ASCII and match their encoding and converts them to UTF-8" do
28 str = "ASCII".encode(Encoding::ASCII)
29 normalized_str = subject.normalize(str)
30 expect(normalized_str.encoding).to eq(Encoding::UTF_8)
31 end
32
33 it "accepts strings that are UTF-8 and match their encoding" do
34 str = "let's make a ☃!".encode(Encoding::UTF_8)
35 expect(subject.normalize(str)).to eq(str)
36 end
37
38 it "converts valid non UTF-8 strings to UTF-8" do
39 str = "let's make a ☃!".encode(Encoding::UTF_16LE)
40 enc = subject.normalize(str).encoding
41 expect(enc).to eq(Encoding::UTF_8)
42 end
43
44 it "normalizes a frozen string returning a non-frozen string" do
45 str = "factvalue".encode(Encoding::UTF_16LE).freeze
46 normalized_str = subject.normalize(str)
47 expect(normalized_str.encoding).to eq(Encoding::UTF_8)
48 expect(normalized_str).to_not be_frozen
49 end
50
51 it "rejects strings that are not UTF-8 and do not match their claimed encoding" do
52 invalid_shift_jis = "\xFF\x5C!".force_encoding(Encoding::SHIFT_JIS)
53 expect {
54 subject.normalize(invalid_shift_jis)
55 }.to raise_error(Facter::Util::Normalization::NormalizationError, /String encoding Shift_JIS is not UTF-8 and could not be converted to UTF-8/)
56 end
57
58 it "rejects strings that claim to be UTF-8 encoded but aren't" do
59 str = "\255ay!".force_encoding(Encoding::UTF_8)
60 expect {
61 subject.normalize(str)
62 }.to raise_error(Facter::Util::Normalization::NormalizationError, /String.*doesn't match the reported encoding UTF-8/)
63 end
64 end
65
66 describe "and string encoding is not supported", :unless => String.instance_methods.include?(:encoding) do
67 it "accepts strings that are UTF-8 and match their encoding" do
68 str = "let's make a ☃!"
69 expect(subject.normalize(str)).to eq(str)
70 end
71
72 it "rejects strings that are not UTF-8" do
73 str = "let's make a \255\255\255!"
74 expect {
75 subject.normalize(str)
76 }.to raise_error(Facter::Util::Normalization::NormalizationError, /String .* is not valid UTF-8/)
77 end
78 end
79 end
80
81 describe "normalizing arrays" do
82 it "normalizes each element in the array" do
83 arr = [utf16('first'), utf16('second'), [utf16('third'), utf16('fourth')]]
84 expected_arr = [utf8('first'), utf8('second'), [utf8('third'), utf8('fourth')]]
85
86 expect(subject.normalize_array(arr)).to eq(expected_arr)
87 end
88 end
89
90 describe "normalizing hashes" do
91 it "normalizes each element in the array" do
92 hsh = {utf16('first') => utf16('second'), utf16('third') => [utf16('fourth'), utf16('fifth')]}
93 expected_hsh = {utf8('first') => utf8('second'), utf8('third') => [utf8('fourth'), utf8('fifth')]}
94
95 expect(subject.normalize_hash(hsh)).to eq(expected_hsh)
96 end
97 end
98
99 [1, 1.0, true, false, nil].each do |val|
100 it "accepts #{val.inspect}:#{val.class}" do
101 expect(subject.normalize(val)).to eq(val)
102 end
103 end
104
105 [:sym, Object.new, Set.new].each do |val|
106 it "rejects #{val.inspect}:#{val.class}" do
107 expect {
108 subject.normalize(val)
109 }.to raise_error(Facter::Util::Normalization::NormalizationError, /Expected .*but was #{val.class}/ )
110 end
111 end
112 end
3434 let(:data) do {"one" => "two", "three" => "four"} end
3535
3636 describe "yaml" do
37 let(:data_in_yaml) do YAML.dump(data) end
38 let(:data_file) do "/tmp/foo.yaml" end
37 let(:data_in_yaml) { YAML.dump(data) }
38 let(:data_file) { "/tmp/foo.yaml" }
3939
4040 it "should return a hash of whatever is stored on disk" do
4141 File.stubs(:read).with(data_file).returns(data_in_yaml)
5151 end
5252
5353 describe "json" do
54 let(:data_in_json) do JSON.dump(data) end
55 let(:data_file) do "/tmp/foo.json" end
54 let(:data_in_json) { JSON.dump(data) }
55 let(:data_file) { "/tmp/foo.json" }
5656
5757 it "should return a hash of whatever is stored on disk" do
5858 pending("this test requires the json library") unless Facter.json?
6262 end
6363
6464 describe "txt" do
65 let(:data_file) do "/tmp/foo.txt" end
65 let(:data_file) { "/tmp/foo.txt" }
6666
6767 shared_examples_for "txt parser" do
6868 it "should return a hash of whatever is stored on disk" do
7272 end
7373
7474 context "well formed data" do
75 let(:data_in_txt) do "one=two\nthree=four\n" end
75 let(:data_in_txt) { "one=two\nthree=four\n" }
7676 it_behaves_like "txt parser"
7777 end
78
78
7979 context "extra equal sign" do
80 let(:data_in_txt) do "one=two\nthree=four=five\n" end
80 let(:data_in_txt) { "one=two\nthree=four=five\n" }
8181 let(:data) do {"one" => "two", "three" => "four=five"} end
8282 it_behaves_like "txt parser"
8383 end
8484
8585 context "extra data" do
86 let(:data_in_txt) do "one=two\nfive\nthree=four\n" end
86 let(:data_in_txt) { "one=two\nfive\nthree=four\n" }
8787 it_behaves_like "txt parser"
8888 end
8989 end
9090
9191 describe "scripts" do
92 let :cmd do "/tmp/foo.sh" end
93 let :data_in_txt do "one=two\nthree=four\n" end
94
95 before :each do
96 Facter::Util::Resolution.stubs(:exec).with(cmd).returns(data_in_txt)
97 File.stubs(:executable?).with(cmd).returns(true)
98 end
99
100 it "should return a hash of whatever is returned by the executable" do
101 pending("this test does not run on windows") if Facter::Util::Config.is_windows?
102 File.stubs(:file?).with(cmd).returns(true)
103 Facter::Util::Parser.parser_for(cmd).results.should == data
92 let(:ext) { Facter::Util::Config.is_windows? ? '.bat' : '.sh' }
93 let(:cmd) { "/tmp/foo#{ext}" }
94 let(:data_in_txt) { "one=two\nthree=four\n" }
95
96 def expects_script_to_return(path, content, result)
97 Facter::Core::Execution.stubs(:exec).with(path).returns(content)
98 File.stubs(:executable?).with(path).returns(true)
99 File.stubs(:file?).with(path).returns(true)
100
101 Facter::Util::Parser.parser_for(path).results.should == result
102 end
103
104 def expects_parser_to_return_nil_for_directory(path)
105 File.stubs(:file?).with(path).returns(false)
106
107 Facter::Util::Parser.parser_for(path).results.should be_nil
108 end
109
110 it "returns a hash of whatever is returned by the executable" do
111 expects_script_to_return(cmd, data_in_txt, data)
104112 end
105113
106114 it "should not parse a directory" do
107 File.stubs(:file?).with(cmd).returns(false)
108 Facter::Util::Parser.parser_for(cmd).results.should be_nil
109 end
110
111 context "on Windows" do
112 let :cmd do "/tmp/foo.bat" end
115 expects_parser_to_return_nil_for_directory(cmd)
116 end
117
118 it "returns an empty hash when the script returns nil" do
119 expects_script_to_return(cmd, nil, {})
120 end
121
122 it "quotes scripts with spaces" do
123 path = "/h a s s p a c e s#{ext}"
124
125 Facter::Core::Execution.expects(:exec).with("\"#{path}\"").returns(data_in_txt)
126
127 expects_script_to_return(path, data_in_txt, data)
128 end
129
130 context "exe, bat, cmd, and com files" do
131 let :cmds do ["/tmp/foo.bat", "/tmp/foo.cmd", "/tmp/foo.exe", "/tmp/foo.com"] end
113132
114133 before :each do
134 cmds.each {|cmd|
135 File.stubs(:executable?).with(cmd).returns(true)
136 File.stubs(:file?).with(cmd).returns(true)
137 }
138 end
139
140 it "should return nothing parser if not on windows" do
141 Facter::Util::Config.stubs(:is_windows?).returns(false)
142 cmds.each {|cmd| Facter::Util::Parser.parser_for(cmd).should be_an_instance_of(Facter::Util::Parser::NothingParser) }
143 end
144
145 it "should return script parser if on windows" do
115146 Facter::Util::Config.stubs(:is_windows?).returns(true)
116 end
117
118 let :parser do
119 Facter::Util::Parser.parser_for(cmd)
147 cmds.each {|cmd| Facter::Util::Parser.parser_for(cmd).should be_an_instance_of(Facter::Util::Parser::ScriptParser) }
148 end
149 end
150
151 context "exe, bat, cmd, and com files" do
152 let :cmds do ["/tmp/foo.bat", "/tmp/foo.cmd", "/tmp/foo.exe", "/tmp/foo.com"] end
153
154 before :each do
155 cmds.each {|cmd|
156 File.stubs(:executable?).with(cmd).returns(true)
157 File.stubs(:file?).with(cmd).returns(true)
158 }
159 end
160
161 it "should return nothing parser if not on windows" do
162 Facter::Util::Config.stubs(:is_windows?).returns(false)
163 cmds.each {|cmd| Facter::Util::Parser.parser_for(cmd).should be_an_instance_of(Facter::Util::Parser::NothingParser) }
164 end
165
166 it "should return script parser if on windows" do
167 Facter::Util::Config.stubs(:is_windows?).returns(true)
168 cmds.each {|cmd| Facter::Util::Parser.parser_for(cmd).should be_an_instance_of(Facter::Util::Parser::ScriptParser) }
169 end
170
171 end
172
173 describe "powershell parser" do
174 let(:ps1) { "/tmp/foo.ps1" }
175
176 def expects_to_parse_powershell(cmd, content, result)
177 Facter::Util::Config.stubs(:is_windows?).returns(true)
178 Facter::Core::Execution.stubs(:exec).returns(content)
179 File.stubs(:file?).with(ps1).returns(true)
180
181 Facter::Util::Parser.parser_for(cmd).results.should == result
120182 end
121183
122184 it "should not parse a directory" do
123 File.stubs(:file?).with(cmd).returns(false)
124 Facter::Util::Parser.parser_for(cmd).results.should be_nil
125 end
126
127 it "should return the data properly" do
128 File.stubs(:file?).with(cmd).returns(true)
129 parser.results.should == data
130 end
131 end
132 end
133
134 describe "powershell parser" do
135 let :ps1 do "/tmp/foo.ps1" end
136 let :data_in_ps1 do "one=two\nthree=four\n" end
137
138 before :each do
139 Facter::Util::Config.stubs(:is_windows?).returns(true)
140 Facter::Util::Resolution.stubs(:exec).returns(data_in_ps1)
141 end
142
143 let :parser do
144 Facter::Util::Parser.parser_for(ps1)
145 end
146
147 it "should not parse a directory" do
148 File.stubs(:file?).with(ps1).returns(false)
149 Facter::Util::Parser.parser_for(ps1).results.should be_nil
150 end
151
152 it "should return data properly" do
153 File.stubs(:file?).with(ps1).returns(true)
154 parser.results.should == data
185 expects_parser_to_return_nil_for_directory(ps1)
186 end
187
188 it "should parse output from powershell" do
189 expects_to_parse_powershell(ps1, data_in_txt, data)
190 end
155191 end
156192 end
157193
11
22 require 'spec_helper'
33 require 'facter/util/processor'
4
5 def cpuinfo_fixture(filename)
6 File.open(fixtures('cpuinfo', filename)).readlines
7 end
4 require 'facter_spec/cpuinfo'
85
96 describe Facter::Util::Processor do
107 describe "on linux" do
8 include FacterSpec::Cpuinfo
9
1110 before :each do
1211 Facter.fact(:kernel).stubs(:value).returns("Linux")
1312 File.stubs(:exists?).with("/proc/cpuinfo").returns(true)
1918 end
2019
2120 it "should get the processor description from the amd64solo fixture" do
22 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64solo"))
21 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64solo"))
2322 Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz"
2423 end
2524
2625 it "should get the processor descriptions from the amd64dual fixture" do
27 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64dual"))
26 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64dual"))
2827
2928 Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz"
3029 Facter::Util::Processor.enum_cpuinfo[1].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz"
3130 end
3231
3332 it "should get the processor descriptions from the amd64tri fixture" do
34 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64tri"))
33 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64tri"))
3534
3635 Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz"
3736 Facter::Util::Processor.enum_cpuinfo[1].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz"
3938 end
4039
4140 it "should get the processor descriptions from the amd64quad fixture" do
42 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture("amd64quad"))
41 File.stubs(:readlines).with("/proc/cpuinfo").returns(cpuinfo_fixture_readlines("amd64quad"))
4342
4443 Facter::Util::Processor.enum_cpuinfo[0].should == "Quad-Core AMD Opteron(tm) Processor 2374 HE"
4544 Facter::Util::Processor.enum_cpuinfo[1].should == "Quad-Core AMD Opteron(tm) Processor 2374 HE"
7069
7170 it "should get the processor description on Solaris (x86)" do
7271 Facter.fact(:architecture).stubs(:value).returns("i86pc")
73 Facter::Util::Resolution.stubs(:exec).with("/usr/bin/kstat cpu_info").returns(my_fixture_read("solaris-i86pc"))
72 Facter::Core::Execution.stubs(:exec).with("/usr/bin/kstat cpu_info").returns(my_fixture_read("solaris-i86pc"))
7473
7574 Facter::Util::Processor.enum_kstat[0].should == "Intel(r) Core(tm) i5 CPU M 450 @ 2.40GHz"
7675 end
7776
7877 it "should get the processor description on Solaris (SPARC64)" do
7978 Facter.fact(:architecture).stubs(:value).returns("sun4u")
80 Facter::Util::Resolution.stubs(:exec).with("/usr/bin/kstat cpu_info").returns(my_fixture_read("solaris-sun4u"))
79 Facter::Core::Execution.stubs(:exec).with("/usr/bin/kstat cpu_info").returns(my_fixture_read("solaris-sun4u"))
8180
8281 Facter::Util::Processor.enum_kstat[0].should == "SPARC64-VII"
8382 Facter::Util::Processor.enum_kstat[1].should == "SPARC64-VII"
55 describe Facter::Util::Resolution do
66 include FacterSpec::ConfigHelper
77
8 it "should require a name" do
9 lambda { Facter::Util::Resolution.new }.should raise_error(ArgumentError)
8 subject(:resolution) { described_class.new(:foo, stub_fact) }
9
10 let(:stub_fact) { stub('fact', :name => :stubfact) }
11
12 it "requires a name" do
13 expect { Facter::Util::Resolution.new }.to raise_error(ArgumentError)
1014 end
1115
12 it "should have a name" do
13 Facter::Util::Resolution.new("yay").name.should == "yay"
16 it "requires a fact" do
17 expect { Facter::Util::Resolution.new('yay') }.to raise_error(ArgumentError)
1418 end
1519
16 it "should be able to set the value" do
17 resolve = Facter::Util::Resolution.new("yay")
18 resolve.value = "foo"
19 resolve.value.should == "foo"
20 it "can return its name" do
21 expect(resolution.name).to eq :foo
2022 end
2123
22 it "should have a method for setting the weight" do
23 Facter::Util::Resolution.new("yay").should respond_to(:has_weight)
24 it "can explicitly set a value" do
25 resolution.value = "foo"
26 expect(resolution.value).to eq "foo"
2427 end
2528
26 it "should have a method for setting the code" do
27 Facter::Util::Resolution.new("yay").should respond_to(:setcode)
28 end
29
30 it "should support a timeout value" do
31 Facter::Util::Resolution.new("yay").should respond_to(:timeout=)
32 end
33
34 it "should default to a timeout of 0 seconds" do
35 Facter::Util::Resolution.new("yay").limit.should == 0
36 end
37
38 it "should default to nil for code" do
39 Facter::Util::Resolution.new("yay").code.should be_nil
40 end
41
42 it "should default to nil for interpreter" do
43 Facter.expects(:warnonce).with("The 'Facter::Util::Resolution.interpreter' method is deprecated and will be removed in a future version.")
44 Facter::Util::Resolution.new("yay").interpreter.should be_nil
45 end
46
47 it "should provide a 'limit' method that returns the timeout" do
48 res = Facter::Util::Resolution.new("yay")
49 res.timeout = "testing"
50 res.limit.should == "testing"
51 end
52
53
54 describe "when overriding environment variables" do
55 it "should execute the caller's block with the specified env vars" do
56 test_env = { "LANG" => "C", "FOO" => "BAR" }
57 Facter::Util::Resolution.with_env test_env do
58 test_env.keys.each do |key|
59 ENV[key].should == test_env[key]
60 end
61 end
62 end
63
64 it "should restore pre-existing environment variables to their previous values" do
65 orig_env = {}
66 new_env = {}
67 # an arbitrary sentinel value to use to temporarily set the environment vars to
68 sentinel_value = "Abracadabra"
69
70 # grab some values from the existing ENV (arbitrarily choosing 3 here)
71 ENV.keys.first(3).each do |key|
72 # save the original values so that we can test against them later
73 orig_env[key] = ENV[key]
74 # create bogus temp values for the chosen keys
75 new_env[key] = sentinel_value
76 end
77
78 # verify that, during the 'with_env', the new values are used
79 Facter::Util::Resolution.with_env new_env do
80 orig_env.keys.each do |key|
81 ENV[key].should == new_env[key]
82 end
83 end
84
85 # verify that, after the 'with_env', the old values are restored
86 orig_env.keys.each do |key|
87 ENV[key].should == orig_env[key]
88 end
89 end
90
91 it "should not be affected by a 'return' statement in the yield block" do
92 @sentinel_var = :resolution_test_foo.to_s
93
94 # the intent of this test case is to test a yield block that contains a return statement. However, it's illegal
95 # to use a return statement outside of a method, so we need to create one here to give scope to the 'return'
96 def handy_method()
97 ENV[@sentinel_var] = "foo"
98 new_env = { @sentinel_var => "bar" }
99
100 Facter::Util::Resolution.with_env new_env do
101 ENV[@sentinel_var].should == "bar"
102 return
103 end
104 end
105
106 handy_method()
107
108 ENV[@sentinel_var].should == "foo"
109
110 end
29 it "defaults to nil for code" do
30 expect(resolution.code).to be_nil
11131 end
11232
11333 describe "when setting the code" do
11434 before do
11535 Facter.stubs(:warnonce)
116 @resolve = Facter::Util::Resolution.new("yay")
11736 end
11837
119 it "should deprecate the interpreter argument to 'setcode'" do
120 Facter.expects(:warnonce).with("The interpreter parameter to 'setcode' is deprecated and will be removed in a future version.")
121 @resolve.setcode "foo", "bar"
122 @resolve.interpreter.should == "bar"
38 it "creates a block when given a command" do
39 resolution.setcode "foo"
40 expect(resolution.code).to be_a_kind_of Proc
12341 end
12442
125 it "should deprecate the interpreter= method" do
126 Facter.expects(:warnonce).with("The 'Facter::Util::Resolution.interpreter=' method is deprecated and will be removed in a future version.")
127 @resolve.interpreter = "baz"
128 @resolve.interpreter.should == "baz"
43 it "stores the provided block when given a block" do
44 block = lambda { }
45 resolution.setcode(&block)
46 resolution.code.should equal(block)
12947 end
13048
131 it "should deprecate the interpreter method" do
132 Facter.expects(:warnonce).with("The 'Facter::Util::Resolution.interpreter' method is deprecated and will be removed in a future version.")
133 @resolve.interpreter
49
50 it "prefers a command over a block" do
51 block = lambda { }
52 resolution.setcode("foo", &block)
53 expect(resolution.code).to_not eq block
13454 end
13555
136 it "should set the code to any provided string" do
137 @resolve.setcode "foo"
138 @resolve.code.should == "foo"
139 end
140
141 it "should set the code to any provided block" do
142 block = lambda { }
143 @resolve.setcode(&block)
144 @resolve.code.should equal(block)
145 end
146
147 it "should prefer the string over a block" do
148 @resolve.setcode("foo") { }
149 @resolve.code.should == "foo"
150 end
151
152 it "should fail if neither a string nor block has been provided" do
153 lambda { @resolve.setcode }.should raise_error(ArgumentError)
56 it "fails if neither a string nor block has been provided" do
57 expect { resolution.setcode }.to raise_error(ArgumentError)
15458 end
15559 end
15660
157 describe 'callbacks when flushing facts' do
158 class FlushFakeError < StandardError; end
159
160 subject do
161 Facter::Util::Resolution.new("jeff")
61 describe "when returning the value" do
62 it "returns any value that has been provided" do
63 resolution.value = "foo"
64 expect(resolution.value).to eq "foo"
16265 end
16366
164 context '#on_flush' do
165 it 'accepts a block with on_flush' do
166 subject.on_flush() { raise NotImplementedError }
67 describe "and setcode has not been called" do
68 it "returns nil" do
69 expect(resolution.value).to be_nil
16770 end
16871 end
16972
170 context '#flush' do
171 it 'calls the block passed to on_flush' do
172 subject.on_flush() { raise FlushFakeError }
173 expect { subject.flush }.to raise_error FlushFakeError
73 describe "and the code is a string" do
74 it "returns the result of executing the code" do
75 resolution.setcode "/bin/foo"
76 Facter::Core::Execution.expects(:execute).once.with("/bin/foo", anything).returns "yup"
77
78 expect(resolution.value).to eq "yup"
79 end
80 end
81
82 describe "and the code is a block" do
83 it "returns the value returned by the block" do
84 resolution.setcode { "yayness" }
85 expect(resolution.value).to eq "yayness"
17486 end
17587 end
17688 end
17789
178 it "should be able to return a value" do
179 Facter::Util::Resolution.new("yay").should respond_to(:value)
180 end
181
182 describe "when returning the value" do
183 before do
184 @resolve = Facter::Util::Resolution.new("yay")
90 describe "setting options" do
91 it "can set the value" do
92 resolution.set_options(:value => 'something')
93 expect(resolution.value).to eq 'something'
18594 end
18695
187 it "should return any value that has been provided" do
188 @resolve.value = "foo"
189 @resolve.value.should == "foo"
96 it "can set the timeout" do
97 resolution.set_options(:timeout => 314)
98 expect(resolution.limit).to eq 314
19099 end
191100
192 describe "and setcode has not been called" do
193 it "should return nil" do
194 Facter::Util::Resolution.expects(:exec).with(nil, nil).never
195 @resolve.value.should be_nil
196 end
101 it "can set the weight" do
102 resolution.set_options(:weight => 27)
103 expect(resolution.weight).to eq 27
197104 end
198105
199 describe "and the code is a string" do
200 describe "on windows" do
201 before do
202 given_a_configuration_of(:is_windows => true)
203 end
204
205 it "should return the result of executing the code" do
206 @resolve.setcode "/bin/foo"
207 Facter::Util::Resolution.expects(:exec).once.with("/bin/foo").returns "yup"
208
209 @resolve.value.should == "yup"
210 end
211
212 it "should return nil if the value is an empty string" do
213 @resolve.setcode "/bin/foo"
214 Facter::Util::Resolution.expects(:exec).once.returns ""
215 @resolve.value.should be_nil
216 end
217 end
218
219 describe "on non-windows systems" do
220 before do
221 given_a_configuration_of(:is_windows => false)
222 end
223
224 it "should return the result of executing the code" do
225 @resolve.setcode "/bin/foo"
226 Facter::Util::Resolution.expects(:exec).once.with("/bin/foo").returns "yup"
227
228 @resolve.value.should == "yup"
229 end
230
231 it "should return nil if the value is an empty string" do
232 @resolve.setcode "/bin/foo"
233 Facter::Util::Resolution.expects(:exec).once.returns ""
234 @resolve.value.should be_nil
235 end
236 end
237 end
238
239 describe "and the code is a block" do
240 it "should warn but not fail if the code fails" do
241 @resolve.setcode { raise "feh" }
242 @resolve.expects(:warn)
243 @resolve.value.should be_nil
244 end
245
246 it "should return the value returned by the block" do
247 @resolve.setcode { "yayness" }
248 @resolve.value.should == "yayness"
249 end
250
251 it "should return nil if the value is an empty string" do
252 @resolve.setcode { "" }
253 @resolve.value.should be_nil
254 end
255
256 it "should return nil if the value is an empty block" do
257 @resolve.setcode { "" }
258 @resolve.value.should be_nil
259 end
260
261 it "should use its limit method to determine the timeout, to avoid conflict when a 'timeout' method exists for some other reason" do
262 @resolve.expects(:timeout).never
263 @resolve.expects(:limit).returns "foo"
264 Timeout.expects(:timeout).with("foo")
265
266 @resolve.setcode { sleep 2; "raise This is a test"}
267 @resolve.value
268 end
269
270 it "should timeout after the provided timeout" do
271 @resolve.expects(:warn)
272 @resolve.timeout = 0.1
273 @resolve.setcode { sleep 2; raise "This is a test" }
274 Thread.expects(:new).yields
275
276 @resolve.value.should be_nil
277 end
278
279 it "should waitall to avoid zombies if the timeout is exceeded" do
280 @resolve.stubs(:warn)
281 @resolve.timeout = 0.1
282 @resolve.setcode { sleep 2; raise "This is a test" }
283
284 Thread.expects(:new).yields
285 Process.expects(:waitall)
286
287 @resolve.value
288 end
106 it "fails on unhandled options" do
107 expect do
108 resolution.set_options(:foo => 'bar')
109 end.to raise_error(ArgumentError, /Invalid resolution options.*foo/)
289110 end
290111 end
291112
292 it "should return its value when converted to a string" do
293 @resolve = Facter::Util::Resolution.new("yay")
294 @resolve.expects(:value).returns "myval"
295 @resolve.to_s.should == "myval"
296 end
297
298 it "should allow the adding of confines" do
299 Facter::Util::Resolution.new("yay").should respond_to(:confine)
300 end
301
302 it "should provide a method for returning the number of confines" do
303 @resolve = Facter::Util::Resolution.new("yay")
304 @resolve.confine "one" => "foo", "two" => "fee"
305 @resolve.weight.should == 2
306 end
307
308 it "should return 0 confines when no confines have been added" do
309 Facter::Util::Resolution.new("yay").weight.should == 0
310 end
311
312 it "should provide a way to set the weight" do
313 @resolve = Facter::Util::Resolution.new("yay")
314 @resolve.has_weight(45)
315 @resolve.weight.should == 45
316 end
317
318 it "should allow the weight to override the number of confines" do
319 @resolve = Facter::Util::Resolution.new("yay")
320 @resolve.confine "one" => "foo", "two" => "fee"
321 @resolve.weight.should == 2
322 @resolve.has_weight(45)
323 @resolve.weight.should == 45
324 end
325
326 it "should have a method for determining if it is suitable" do
327 Facter::Util::Resolution.new("yay").should respond_to(:suitable?)
328 end
329
330 describe "when adding confines" do
331 before do
332 @resolve = Facter::Util::Resolution.new("yay")
113 describe "evaluating" do
114 it "evaluates the block in the context of the given resolution" do
115 subject.expects(:has_weight).with(5)
116 subject.evaluate { has_weight(5) }
333117 end
334118
335 it "should accept a hash of fact names and values" do
336 lambda { @resolve.confine :one => "two" }.should_not raise_error
337 end
338
339 it "should create a Util::Confine instance for every argument in the provided hash" do
340 Facter::Util::Confine.expects(:new).with("one", "foo")
341 Facter::Util::Confine.expects(:new).with("two", "fee")
342
343 @resolve.confine "one" => "foo", "two" => "fee"
344 end
345
346 end
347
348 describe "when determining suitability" do
349 before do
350 @resolve = Facter::Util::Resolution.new("yay")
351 end
352
353 it "should always be suitable if no confines have been added" do
354 @resolve.should be_suitable
355 end
356
357 it "should be unsuitable if any provided confines return false" do
358 confine1 = mock 'confine1', :true? => true
359 confine2 = mock 'confine2', :true? => false
360 Facter::Util::Confine.expects(:new).times(2).returns(confine1).then.returns(confine2)
361 @resolve.confine :one => :two, :three => :four
362
363 @resolve.should_not be_suitable
364 end
365
366 it "should be suitable if all provided confines return true" do
367 confine1 = mock 'confine1', :true? => true
368 confine2 = mock 'confine2', :true? => true
369 Facter::Util::Confine.expects(:new).times(2).returns(confine1).then.returns(confine2)
370 @resolve.confine :one => :two, :three => :four
371
372 @resolve.should be_suitable
373 end
374 end
375
376 it "should have a class method for executing code" do
377 Facter::Util::Resolution.should respond_to(:exec)
378 end
379
380 # taken from puppet: spec/unit/util_spec.rb
381 describe "#absolute_path?" do
382 context "when run on unix", :as_platform => :posix do
383 %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path|
384 it "should return true for #{path}" do
385 Facter::Util::Resolution.should be_absolute_path(path)
386 end
119 it "raises a warning if the resolution is evaluated twice" do
120 Facter.expects(:warn).with do |msg|
121 expect(msg).to match /Already evaluated foo at.*reevaluating anyways/
387122 end
388123
389 %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path|
390 it "should return false for #{path}" do
391 Facter::Util::Resolution.should_not be_absolute_path(path)
392 end
393 end
394 end
395
396 context "when run on windows", :as_platform => :windows do
397 %w[C:/foo C:\foo \\\\Server\Foo\Bar \\\\?\C:\foo\bar //Server/Foo/Bar //?/C:/foo/bar /\?\C:/foo\bar \/Server\Foo/Bar c:/foo//bar//baz].each do |path|
398 it "should return true for #{path}" do
399 Facter::Util::Resolution.should be_absolute_path(path)
400 end
401 end
402
403 %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|
404 it "should return false for #{path}" do
405 Facter::Util::Resolution.should_not be_absolute_path(path)
406 end
407 end
408 end
409 end
410
411 describe "#search_paths" do
412 context "on windows", :as_platform => :windows do
413 it "should use the PATH environment variable to determine locations" do
414 ENV.expects(:[]).with('PATH').returns 'C:\Windows;C:\Windows\System32'
415 Facter::Util::Resolution.search_paths.should == %w{C:\Windows C:\Windows\System32}
416 end
417 end
418
419 context "on posix", :as_platform => :posix do
420 it "should use the PATH environment variable plus /sbin and /usr/sbin on unix" do
421 ENV.expects(:[]).with('PATH').returns "/bin:/usr/bin"
422 Facter::Util::Resolution.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
423 end
424 end
425 end
426
427 describe "#which" do
428 context "when run on posix", :as_platform => :posix do
429 before :each do
430 Facter::Util::Resolution.stubs(:search_paths).returns [ '/bin', '/sbin', '/usr/sbin']
431 end
432
433 context "and provided with an absolute path" do
434 it "should return the binary if executable" do
435 File.expects(:executable?).with('/opt/foo').returns true
436 Facter::Util::Resolution.which('/opt/foo').should == '/opt/foo'
437 end
438
439 it "should return nil if the binary is not executable" do
440 File.expects(:executable?).with('/opt/foo').returns false
441 Facter::Util::Resolution.which('/opt/foo').should be_nil
442 end
443 end
444
445 context "and not provided with an absolute path" do
446 it "should return the absolute path if found" do
447 File.expects(:executable?).with('/bin/foo').returns false
448 File.expects(:executable?).with('/sbin/foo').returns true
449 File.expects(:executable?).with('/usr/sbin/foo').never
450 Facter::Util::Resolution.which('foo').should == '/sbin/foo'
451 end
452
453 it "should return nil if not found" do
454 File.expects(:executable?).with('/bin/foo').returns false
455 File.expects(:executable?).with('/sbin/foo').returns false
456 File.expects(:executable?).with('/usr/sbin/foo').returns false
457 Facter::Util::Resolution.which('foo').should be_nil
458 end
459 end
460 end
461
462 context "when run on windows", :as_platform => :windows do
463 before :each do
464 Facter::Util::Resolution.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
465 ENV.stubs(:[]).with('PATHEXT').returns nil
466 end
467
468 context "and provided with an absolute path" do
469 it "should return the binary if executable" do
470 File.expects(:executable?).with('C:\Tools\foo.exe').returns true
471 File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns true
472 Facter::Util::Resolution.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
473 Facter::Util::Resolution.which('\\\\remote\dir\foo.exe').should == '\\\\remote\dir\foo.exe'
474 end
475
476 it "should return the binary with added extension if executable" do
477 ['.COM', '.BAT', '.CMD', '' ].each do |ext|
478 File.stubs(:executable?).with('C:\Windows\system32\netsh'+ext).returns false
479 end
480 File.expects(:executable?).with('C:\Windows\system32\netsh.EXE').returns true
481
482 Facter.expects(:warnonce).with('Using Facter::Util::Resolution.which with an absolute path like C:\\Windows\\system32\\netsh but no fileextension is deprecated. Please add the correct extension (.EXE)')
483 Facter::Util::Resolution.which('C:\Windows\system32\netsh').should == 'C:\Windows\system32\netsh.EXE'
484 end
485
486 it "should return nil if the binary is not executable" do
487 File.expects(:executable?).with('C:\Tools\foo.exe').returns false
488 File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns false
489 Facter::Util::Resolution.which('C:\Tools\foo.exe').should be_nil
490 Facter::Util::Resolution.which('\\\\remote\dir\foo.exe').should be_nil
491 end
492 end
493
494 context "and not provided with an absolute path" do
495 it "should return the absolute path if found" do
496 File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
497 File.expects(:executable?).with('C:\Windows\foo.exe').returns true
498 File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').never
499 Facter::Util::Resolution.which('foo.exe').should == 'C:\Windows\foo.exe'
500 end
501
502 it "should return the absolute path with file extension if found" do
503 ['.COM', '.EXE', '.BAT', '.CMD', '' ].each do |ext|
504 File.stubs(:executable?).with('C:\Windows\system32\foo'+ext).returns false
505 File.stubs(:executable?).with('C:\Windows\System32\Wbem\foo'+ext).returns false
506 end
507 ['.COM', '.BAT', '.CMD', '' ].each do |ext|
508 File.stubs(:executable?).with('C:\Windows\foo'+ext).returns false
509 end
510 File.stubs(:executable?).with('C:\Windows\foo.EXE').returns true
511
512 Facter::Util::Resolution.which('foo').should == 'C:\Windows\foo.EXE'
513 end
514
515 it "should return nil if not found" do
516 File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
517 File.expects(:executable?).with('C:\Windows\foo.exe').returns false
518 File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').returns false
519 Facter::Util::Resolution.which('foo.exe').should be_nil
520 end
521 end
522 end
523
524 describe "#expand_command" do
525 context "on windows", :as_platform => :windows do
526 it "should expand binary" do
527 Facter::Util::Resolution.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
528 Facter::Util::Resolution.expand_command(
529 'cmd /c echo foo > C:\bar'
530 ).should == 'C:\Windows\System32\cmd /c echo foo > C:\bar'
531 end
532
533 it "should expand double quoted binary" do
534 Facter::Util::Resolution.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
535 Facter::Util::Resolution.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
536 end
537
538 it "should not expand single quoted binary" do
539 Facter::Util::Resolution.expects(:which).with('\'C:\My').returns nil
540 Facter::Util::Resolution.expand_command('\'C:\My Tools\foo.exe\' /a /b').should be_nil
541 end
542
543 it "should quote expanded binary if found in path with spaces" do
544 Facter::Util::Resolution.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
545 Facter::Util::Resolution.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
546 end
547
548 it "should return nil if not found" do
549 Facter::Util::Resolution.expects(:which).with('foo').returns nil
550 Facter::Util::Resolution.expand_command('foo /a | stuff >> /dev/null').should be_nil
551 end
552 end
553
554 context "on unix", :as_platform => :posix do
555 it "should expand binary" do
556 Facter::Util::Resolution.expects(:which).with('foo').returns '/bin/foo'
557 Facter::Util::Resolution.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
558 end
559
560 it "should expand double quoted binary" do
561 Facter::Util::Resolution.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
562 Facter::Util::Resolution.expand_command(%q{"/tmp/my foo" bar}).should == %q{"/tmp/my foo" bar}
563 end
564
565 it "should expand single quoted binary" do
566 Facter::Util::Resolution.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
567 Facter::Util::Resolution.expand_command(%q{'my foo' -a}).should == %q{'/home/bob/my path/my foo' -a}
568 end
569
570 it "should quote expanded binary if found in path with spaces" do
571 Facter::Util::Resolution.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
572 Facter::Util::Resolution.expand_command('foo.sh /a /b').should == %q{'/home/bob/my tools/foo.sh' /a /b}
573 end
574
575 it "should return nil if not found" do
576 Facter::Util::Resolution.expects(:which).with('foo').returns nil
577 Facter::Util::Resolution.expand_command('foo -a | stuff >> /dev/null').should be_nil
578 end
579 end
580 end
581
582 end
583
584 # It's not possible, AFAICT, to mock %x{}, so I can't really test this bit.
585 describe "when executing code" do
586 # set up some command strings, making sure we get the right version for both unix and windows
587 echo_command = Facter::Util::Config.is_windows? ? 'cmd.exe /c "echo foo"' : 'echo foo'
588 echo_env_var_command = Facter::Util::Config.is_windows? ? 'cmd.exe /c "echo %%%s%%"' : 'echo $%s'
589
590 it "should deprecate the interpreter parameter" do
591 Facter.expects(:warnonce).with("The interpreter parameter to 'exec' is deprecated and will be removed in a future version.")
592 Facter::Util::Resolution.exec("/something", "/bin/perl")
593 end
594
595 # execute a simple echo command
596 it "should execute the binary" do
597 Facter::Util::Resolution.exec(echo_command).should == "foo"
598 end
599
600 it "should override the LANG environment variable" do
601 Facter::Util::Resolution.exec(echo_env_var_command % 'LANG').should == "C"
602 end
603
604 it "should respect other overridden environment variables" do
605 Facter::Util::Resolution.with_env( {"FOO" => "foo"} ) do
606 Facter::Util::Resolution.exec(echo_env_var_command % 'FOO').should == "foo"
607 end
608 end
609
610 it "should restore overridden LANG environment variable after execution" do
611 # we're going to call with_env in a nested fashion, to make sure that the environment gets restored properly
612 # at each level
613 Facter::Util::Resolution.with_env( {"LANG" => "foo"} ) do
614 # Resolution.exec always overrides 'LANG' for its own execution scope
615 Facter::Util::Resolution.exec(echo_env_var_command % 'LANG').should == "C"
616 # But after 'exec' completes, we should see our value restored
617 ENV['LANG'].should == "foo"
618 # Now we'll do a nested call to with_env
619 Facter::Util::Resolution.with_env( {"LANG" => "bar"} ) do
620 # During 'exec' it should still be 'C'
621 Facter::Util::Resolution.exec(echo_env_var_command % 'LANG').should == "C"
622 # After exec it should be restored to our current value for this level of the nesting...
623 ENV['LANG'].should == "bar"
624 end
625 # Now we've dropped out of one level of nesting,
626 ENV['LANG'].should == "foo"
627 # Call exec one more time just for kicks
628 Facter::Util::Resolution.exec(echo_env_var_command % 'LANG').should == "C"
629 # One last check at our current nesting level.
630 ENV['LANG'].should == "foo"
631 end
632 end
633
634 context "when run on unix", :as_platform => :posix do
635 context "binary is present" do
636 it "should run the command if path to binary is absolute" do
637 Facter::Util::Resolution.expects(:expand_command).with('/usr/bin/uname -m').returns('/usr/bin/uname -m')
638 Facter::Util::Resolution.expects(:`).with('/usr/bin/uname -m').returns 'x86_64'
639 Facter::Util::Resolution.exec('/usr/bin/uname -m').should == 'x86_64'
640 end
641
642 it "should run the expanded command if path to binary not absolute" do
643 Facter::Util::Resolution.expects(:expand_command).with('uname -m').returns('/usr/bin/uname -m')
644 Facter::Util::Resolution.expects(:`).with('/usr/bin/uname -m').returns 'x86_64'
645 Facter::Util::Resolution.exec('uname -m').should == 'x86_64'
646 end
647 end
648
649 context "binary is not present" do
650 it "should not run the command if path to binary is absolute" do
651 Facter::Util::Resolution.expects(:expand_command).with('/usr/bin/uname -m').returns nil
652 Facter::Util::Resolution.expects(:`).with('/usr/bin/uname -m').never
653 Facter::Util::Resolution.exec('/usr/bin/uname -m').should be_nil
654 end
655 it "should not run the command if path to binary is not absolute" do
656 Facter::Util::Resolution.expects(:expand_command).with('uname -m').returns nil
657 Facter::Util::Resolution.expects(:`).with('uname -m').never
658 Facter::Util::Resolution.exec('uname -m').should be_nil
659 end
660 end
661 end
662
663 context "when run on windows", :as_platform => :windows do
664 context "binary is present" do
665 it "should run the command if path to binary is absolute" do
666 Facter::Util::Resolution.expects(:expand_command).with(%q{C:\Windows\foo.exe /a /b}).returns(%q{C:\Windows\foo.exe /a /b})
667 Facter::Util::Resolution.expects(:`).with(%q{C:\Windows\foo.exe /a /b}).returns 'bar'
668 Facter::Util::Resolution.exec(%q{C:\Windows\foo.exe /a /b}).should == 'bar'
669 end
670
671 it "should run the expanded command if path to binary not absolute" do
672 Facter::Util::Resolution.expects(:expand_command).with(%q{foo.exe /a /b}).returns(%q{C:\Windows\foo.exe /a /b})
673 Facter::Util::Resolution.expects(:`).with(%q{C:\Windows\foo.exe /a /b}).returns 'bar'
674 Facter::Util::Resolution.exec(%q{foo.exe /a /b}).should == 'bar'
675 end
676 end
677
678 context "binary is not present" do
679 it "should not run the command if path to binary is absolute" do
680 Facter::Util::Resolution.expects(:expand_command).with(%q{C:\Windows\foo.exe /a /b}).returns nil
681 Facter::Util::Resolution.expects(:`).with(%q{C:\Windows\foo.exe /a /b}).never
682 Facter::Util::Resolution.exec(%q{C:\Windows\foo.exe /a /b}).should be_nil
683 end
684 it "should try to run the command and return output of a shell-builtin" do
685 Facter::Util::Resolution.expects(:expand_command).with(%q{echo foo}).returns nil
686 Facter::Util::Resolution.expects(:`).with(%q{echo foo}).returns 'foo'
687 Facter.expects(:warnonce).with 'Using Facter::Util::Resolution.exec with a shell built-in is deprecated. Most built-ins can be replaced with native ruby commands. If you really have to run a built-in, pass "cmd /c your_builtin" as a command (command responsible for this message was "echo foo")'
688 Facter::Util::Resolution.exec(%q{echo foo}).should == 'foo'
689 end
690 it "should try to run the command and return nil if not shell-builtin" do
691 Facter::Util::Resolution.expects(:expand_command).with(%q{echo foo}).returns nil
692 Facter::Util::Resolution.stubs(:`).with(%q{echo foo}).raises Errno::ENOENT, 'some_error_message'
693 Facter.expects(:warnonce).never
694 Facter::Util::Resolution.exec(%q{echo foo}).should be_nil
695 end
696 end
124 subject.evaluate { }
125 subject.evaluate { }
697126 end
698127 end
699128 end
4646
4747 describe '#refresh' do
4848 it 'executes the zoneadm_cmd' do
49 Facter::Util::Resolution.expects(:exec).with(subject.zoneadm_cmd).returns(zone_list)
49 Facter::Core::Execution.expects(:execute).with(subject.zoneadm_cmd, {:on_fail => nil}).returns(zone_list)
5050 subject.refresh
5151 end
5252 end
6868 it 'uses a single read of the system information for all of the dynamically generated zone facts' do
6969 given_initial_zone_facts # <= single read happens here
7070
71 Facter::Util::Resolution.expects(:exec).never
71 Facter::Core::Execution.expects(:execute).never
7272 Facter.fact(:zone_zoneA_id).value
7373 Facter.fact(:zone_local_id).value
7474 end
100100 given_initial_zone_facts
101101 when_facts_have_been_resolved_then_flushed
102102
103 Facter::Util::Resolution.expects(:exec).once.returns(zone_list2)
103 Facter::Core::Execution.expects(:execute).once.returns(zone_list2)
104104 Facter.fact(:zones).value
105105 Facter.fact(:zone_zoneA_id).value
106106 Facter.fact(:zone_local_id).value
110110 end
111111
112112 def given_initial_zone_facts
113 Facter::Util::Resolution.stubs(:exec).
114 with(subject.zoneadm_cmd).
113 Facter::Core::Execution.stubs(:execute).
114 with(subject.zoneadm_cmd, {:on_fail => nil}).
115115 returns(zone_list)
116116 described_class.add_facts
117117 end
120120 Facter.fact(:zones).value
121121 Facter.fact(:zone_zoneA_id).value
122122 Facter.fact(:zone_local_id).value
123 Facter::Util::Resolution.stubs(:exec).returns(zone_list2)
123 Facter::Core::Execution.stubs(:execute).returns(zone_list2)
124124 Facter.flush
125125 end
126126 end
9898
9999 test_cases.each do |uptime, expected|
100100 it "should return #{expected} for #{uptime}" do
101 Facter::Util::Resolution.stubs(:exec).with('uptime 2>/dev/null').returns(uptime)
101 Facter::Core::Execution.stubs(:execute).with('uptime 2>/dev/null', {:on_fail => nil}).returns(uptime)
102102 Facter.fact(:uptime_seconds).value.should == expected
103103 end
104104 end
105105
106106 describe "nor is 'uptime' command" do
107107 before :each do
108 Facter::Util::Uptime.stubs(:uptime_executable_cmd).returns("cat \"#{@nonexistent_file}\"")
108 Facter::Core::Execution.stubs(:execute).with('uptime 2>/dev/null', {:on_fail => nil}).returns(nil)
109109 end
110110
111111 it "should return nil" do
0 require 'spec_helper'
1 require 'facter/util/values'
2
3 describe Facter::Util::Values do
4 describe 'deep_freeze' do
5 it "it dups and freezes strings" do
6 input = "hi"
7 output = described_class.deep_freeze(input)
8 expect(input).to_not be_frozen
9 expect(output).to be_frozen
10 end
11
12 it "freezes arrays and each element in the array" do
13 input = %w[one two three]
14 output = described_class.deep_freeze(input)
15
16 input.each { |entry| expect(entry).to_not be_frozen }
17 output.each { |entry| expect(entry).to be_frozen }
18
19 expect(input).to_not be_frozen
20 expect(output).to be_frozen
21 end
22
23 it "freezes hashes and each key and value in the hash" do
24 input = {'one' => 'two', 'three' => 'four'}
25
26 output = described_class.deep_freeze(input)
27
28 input.each_pair do |key, val|
29 # Ruby freezes all string keys, so these will always be frozen
30 expect(key).to be_frozen
31 expect(val).to_not be_frozen
32 end
33
34 output.each_pair do |key, val|
35 expect(key).to be_frozen
36 expect(val).to be_frozen
37 end
38
39 expect(input).to_not be_frozen
40 expect(output).to be_frozen
41 end
42
43 it "raises an error when given a structure that cannot be deeply frozen" do
44 expect {
45 described_class.deep_freeze(Set.new)
46 }.to raise_error(Facter::Util::Values::DeepFreezeError, /Cannot deep freeze.*Set/)
47 end
48 end
49
50 describe 'deep_merge' do
51 it "non-destructively concatenates arrays" do
52 first = %w[foo bar]
53 second = %w[baz quux]
54
55 expect(described_class.deep_merge(first, second)).to eq %w[foo bar baz quux]
56 expect(first).to eq %w[foo bar]
57 expect(second).to eq %w[baz quux]
58 end
59
60 it "returns the left value if the right value is nil" do
61 expect(described_class.deep_merge("left", nil)).to eq "left"
62 end
63
64 it "returns the right value if the left value is nil" do
65 expect(described_class.deep_merge(nil, "right")).to eq "right"
66 end
67
68 it "returns nil if both values are nil" do
69 expect(described_class.deep_merge(nil, nil)).to be_nil
70 end
71
72 describe "with hashes" do
73 it "merges when keys do not overlap" do
74
75 first = {:foo => 'bar'}
76 second = {:baz => 'quux'}
77
78 expect(described_class.deep_merge(first, second)).to eq(:foo => 'bar', :baz => 'quux')
79 expect(first).to eq(:foo => 'bar')
80 expect(second).to eq(:baz => 'quux')
81 end
82
83 it "concatenates arrays when both keys are arrays" do
84 first = {:foo => %w[bar]}
85 second = {:foo => %w[baz quux]}
86
87 expect(described_class.deep_merge(first, second)).to eq(:foo => %w[bar baz quux])
88 expect(first).to eq(:foo => %w[bar])
89 expect(second).to eq(:foo => %w[baz quux])
90 end
91
92 it "merges hashes when both keys are hashes" do
93 first = {:foo => {:pb => 'lead', :ag => 'silver'}}
94 second = {:foo => {:au => 'gold', :na => 'sodium'}}
95
96 expect(described_class.deep_merge(first, second)).to eq(
97 :foo => {
98 :pb => 'lead',
99 :ag => 'silver',
100 :au => 'gold',
101 :na => 'sodium'
102 }
103 )
104 end
105
106 it "prints the data structure path if an error is raised" do
107 first = {:foo => {:bar => {:baz => {:quux => true}}}}
108 second = {:foo => {:bar => {:baz => {:quux => false}}}}
109
110 expect {
111 described_class.deep_merge(first, second)
112 }.to raise_error(Facter::Util::Values::DeepMergeError, /Cannot merge .*at .*foo.*bar.*baz.*quux/)
113 end
114 end
115
116 describe "with unmergable scalar values" do
117 [
118 [true, false],
119 [1, 2],
120 ['up', 'down']
121 ].each do |(left, right)|
122 it "raises an error when merging #{left}:#{left.class} and #{right}:#{right.class}" do
123 expect {
124 described_class.deep_merge(left, right)
125 }.to raise_error(Facter::Util::Values::DeepMergeError, /Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}/)
126 end
127 end
128 end
129 end
130 end
2929 it "should identify openvzhn when /proc/self/status has envID of 0" do
3030 Facter::Util::Virtual.stubs(:openvz?).returns(true)
3131 FileTest.stubs(:exists?).with("/proc/self/status").returns(true)
32 Facter::Util::Resolution.stubs(:exec).with('grep "envID" /proc/self/status').returns("envID: 0")
32 Facter::Core::Execution.stubs(:exec).with('grep "envID" /proc/self/status').returns("envID: 0")
3333 Facter::Util::Virtual.openvz_type().should == "openvzhn"
3434 end
3535
3636 it "should identify openvzve when /proc/self/status has envID of 0" do
3737 Facter::Util::Virtual.stubs(:openvz?).returns(true)
3838 FileTest.stubs(:exists?).with('/proc/self/status').returns(true)
39 Facter::Util::Resolution.stubs(:exec).with('grep "envID" /proc/self/status').returns("envID: 666")
39 Facter::Core::Execution.stubs(:exec).with('grep "envID" /proc/self/status').returns("envID: 666")
4040 Facter::Util::Virtual.openvz_type().should == "openvzve"
4141 end
4242
4343 it "should not attempt to identify openvz when /proc/self/status has no envID" do
4444 Facter::Util::Virtual.stubs(:openvz?).returns(true)
4545 FileTest.stubs(:exists?).with('/proc/self/status').returns(true)
46 Facter::Util::Resolution.stubs(:exec).with('grep "envID" /proc/self/status').returns("")
46 Facter::Core::Execution.stubs(:exec).with('grep "envID" /proc/self/status').returns("")
4747 Facter::Util::Virtual.openvz_type().should be_nil
4848 end
4949
5050 it "should identify Solaris zones when non-global zone" do
51 Facter::Util::Resolution.stubs(:exec).with("/sbin/zonename").returns("somezone")
51 Facter::Core::Execution.stubs(:exec).with("/sbin/zonename").returns("somezone")
5252 Facter::Util::Virtual.should be_zone
5353 end
5454
5555 it "should not identify Solaris zones when global zone" do
56 Facter::Util::Resolution.stubs(:exec).with("/sbin/zonename").returns("global")
56 Facter::Core::Execution.stubs(:exec).with("/sbin/zonename").returns("global")
5757 Facter::Util::Virtual.should_not be_zone
5858 end
5959
9090
9191 it "should identify kvm" do
9292 Facter::Util::Virtual.stubs(:kvm?).returns(true)
93 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("something")
93 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("something")
9494 Facter::Util::Virtual.kvm_type().should == "kvm"
9595 end
9696
201201 it "should detect kvm on FreeBSD" do
202202 FileTest.stubs(:exists?).with("/proc/cpuinfo").returns(false)
203203 Facter.fact(:kernel).stubs(:value).returns("FreeBSD")
204 Facter::Util::Resolution.stubs(:exec).with("/sbin/sysctl -n hw.model").returns("QEMU Virtual CPU version 0.12.4")
204 Facter::Core::Execution.stubs(:exec).with("/sbin/sysctl -n hw.model").returns("QEMU Virtual CPU version 0.12.4")
205205 Facter::Util::Virtual.should be_kvm
206206 end
207207
208208 it "should detect kvm on OpenBSD" do
209209 FileTest.stubs(:exists?).with("/proc/cpuinfo").returns(false)
210210 Facter.fact(:kernel).stubs(:value).returns("OpenBSD")
211 Facter::Util::Resolution.stubs(:exec).with("/sbin/sysctl -n hw.model").returns('QEMU Virtual CPU version (cpu64-rhel6) ("AuthenticAMD" 686-class, 512KB L2 cache)')
211 Facter::Core::Execution.stubs(:exec).with("/sbin/sysctl -n hw.model").returns('QEMU Virtual CPU version (cpu64-rhel6) ("AuthenticAMD" 686-class, 512KB L2 cache)')
212212 Facter::Util::Virtual.should be_kvm
213213 end
214214
215215 it "should identify FreeBSD jail when in jail" do
216216 Facter.fact(:kernel).stubs(:value).returns("FreeBSD")
217 Facter::Util::Resolution.stubs(:exec).with("/sbin/sysctl -n security.jail.jailed").returns("1")
217 Facter::Core::Execution.stubs(:exec).with("/sbin/sysctl -n security.jail.jailed").returns("1")
218218 Facter::Util::Virtual.should be_jail
219219 end
220220
221221 it "should not identify GNU/kFreeBSD jail when not in jail" do
222222 Facter.fact(:kernel).stubs(:value).returns("GNU/kFreeBSD")
223 Facter::Util::Resolution.stubs(:exec).with("/bin/sysctl -n security.jail.jailed").returns("0")
223 Facter::Core::Execution.stubs(:exec).with("/bin/sysctl -n security.jail.jailed").returns("0")
224224 Facter::Util::Virtual.should_not be_jail
225225 end
226226
227227 it "should detect hpvm on HP-UX" do
228228 Facter.fact(:kernel).stubs(:value).returns("HP-UX")
229 Facter::Util::Resolution.stubs(:exec).with("/usr/bin/getconf MACHINE_MODEL").returns('ia64 hp server Integrity Virtual Machine')
229 Facter::Core::Execution.stubs(:exec).with("/usr/bin/getconf MACHINE_MODEL").returns('ia64 hp server Integrity Virtual Machine')
230230 Facter::Util::Virtual.should be_hpvm
231231 end
232232
233233 it "should not detect hpvm on HP-UX when not in hpvm" do
234234 Facter.fact(:kernel).stubs(:value).returns("HP-UX")
235 Facter::Util::Resolution.stubs(:exec).with("/usr/bin/getconf MACHINE_MODEL").returns('ia64 hp server rx660')
235 Facter::Core::Execution.stubs(:exec).with("/usr/bin/getconf MACHINE_MODEL").returns('ia64 hp server rx660')
236236 Facter::Util::Virtual.should_not be_hpvm
237237 end
238238
262262 shared_examples_for "virt-what" do |kernel, path, null_device|
263263 before(:each) do
264264 Facter.fact(:kernel).stubs(:value).returns(kernel)
265 Facter::Util::Resolution.expects(:which).with("virt-what").returns(path)
266 Facter::Util::Resolution.expects(:exec).with("#{path} 2>#{null_device}")
265 Facter::Core::Execution.expects(:which).with("virt-what").returns(path)
266 Facter::Core::Execution.expects(:exec).with("#{path} 2>#{null_device}")
267267 end
268268
269269 it "on #{kernel} virt-what is at #{path} and stderr is sent to #{null_device}" do
277277 it "should strip out warnings on stdout from virt-what" do
278278 virt_what_warning = "virt-what: this script must be run as root"
279279 Facter.fact(:kernel).stubs(:value).returns('linux')
280 Facter::Util::Resolution.expects(:which).with('virt-what').returns "/usr/bin/virt-what"
281 Facter::Util::Resolution.expects(:exec).with('/usr/bin/virt-what 2>/dev/null').returns virt_what_warning
280 Facter::Core::Execution.expects(:which).with('virt-what').returns "/usr/bin/virt-what"
281 Facter::Core::Execution.expects(:exec).with('/usr/bin/virt-what 2>/dev/null').returns virt_what_warning
282282 Facter::Util::Virtual.virt_what.should_not match /^virt-what: /
283283 end
284284 end
33 require 'facter/util/vlans'
44
55 describe Facter::Util::Vlans do
6 it "should return a list of vlans on Linux" do
7 linux_vlanconfig = my_fixture_read("linux_vlan_config")
8 Facter::Util::Vlans.stubs(:get_vlan_config).returns(linux_vlanconfig)
9 Facter::Util::Vlans.get_vlans().should == %{400,300,200,100}
6 let(:vlan_file) { "/proc/net/vlan/config" }
7
8 describe "reading the vlan configuration" do
9 it "uses the contents of /proc/net/vlan/config" do
10 File.expects(:exist?).with(vlan_file).returns true
11 File.expects(:readable?).with(vlan_file).returns true
12 File.expects(:read).with(vlan_file).returns "vlan contents here"
13
14 expect(Facter::Util::Vlans.get_vlan_config).to eq "vlan contents here"
15 end
16
17 it "returns nil when /proc/net/vlan/config is absent" do
18 File.expects(:exist?).with(vlan_file).returns false
19 expect(Facter::Util::Vlans.get_vlan_config).to be_nil
20 end
21 end
22
23 describe "parsing the vlan configuration" do
24 let(:vlan_content) { my_fixture_read("linux_vlan_config") }
25
26 it "returns a list of vlans on Linux when vlans are configured" do
27 Facter::Util::Vlans.stubs(:get_vlan_config).returns(vlan_content)
28 expect(Facter::Util::Vlans.get_vlans()).to eq %{400,300,200,100}
29 end
30
31 it "returns nil when no vlans are configured" do
32 Facter::Util::Vlans.stubs(:get_vlan_config).returns(nil)
33 expect(Facter::Util::Vlans.get_vlans()).to be_nil
34 end
35
36 it "returns nil when only the vlan header is returned" do
37 Facter::Util::Vlans.stubs(:get_vlan_config).returns(my_fixture_read("centos-5-no-vlans"))
38 expect(Facter::Util::Vlans.get_vlans()).to be_nil
39 end
1040 end
1141 end
66 describe ".get_domains" do
77 it "should return a list of running Xen Domains on Xen0" do
88 xen0_domains = my_fixture_read("xendomains")
9 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/xm list 2>/dev/null').returns(xen0_domains)
9 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/xm list 2>/dev/null').returns(xen0_domains)
1010 Facter::Util::Xendomains.get_domains.should == %{web01,mailserver}
1111 end
1212
1313 describe "when xm list isn't executable" do
1414 it "should be nil" do
15 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/xm list 2>/dev/null').returns(nil)
15 Facter::Core::Execution.stubs(:exec).with('/usr/sbin/xm list 2>/dev/null').returns(nil)
1616 Facter::Util::Xendomains.get_domains.should == nil
1717 end
1818 end
7777 Facter.fact(:kernel).stubs(:value).returns("Linux")
7878 Facter.fact(:operatingsystem).stubs(:value).returns("Linux")
7979
80 Facter::Util::Resolution.stubs(:exec).with("vmware -v").returns false
80 Facter::Core::Execution.stubs(:exec).with("vmware -v").returns false
8181
8282 FileTest.stubs(:exists?).with("/proc/sys/xen").returns false
8383 FileTest.stubs(:exists?).with("/sys/bus/xen").returns false
9696 end
9797
9898 it "should be vmware with VMware vendor name from lspci 2>/dev/null" do
99 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("00:0f.0 VGA compatible controller: VMware Inc [VMware SVGA II] PCI Display Adapter")
99 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("00:0f.0 VGA compatible controller: VMware Inc [VMware SVGA II] PCI Display Adapter")
100100 Facter.fact(:virtual).value.should == "vmware"
101101 end
102102
103103 it "should be virtualbox with VirtualBox vendor name from lspci 2>/dev/null" do
104 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("00:02.0 VGA compatible controller: InnoTek Systemberatung GmbH VirtualBox Graphics Adapter")
104 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("00:02.0 VGA compatible controller: InnoTek Systemberatung GmbH VirtualBox Graphics Adapter")
105105 Facter.fact(:virtual).value.should == "virtualbox"
106106 end
107107
108108 it "should be vmware with VMWare vendor name from dmidecode" do
109 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
110 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("On Board Device 1 Information\nType: Video\nStatus: Disabled\nDescription: VMware SVGA II")
109 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
110 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("On Board Device 1 Information\nType: Video\nStatus: Disabled\nDescription: VMware SVGA II")
111111 Facter.fact(:virtual).value.should == "vmware"
112112 end
113113
127127 end
128128
129129 it "should be xenhvm with Xen HVM vendor name from lspci 2>/dev/null" do
130 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("00:03.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)")
130 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("00:03.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)")
131131 Facter.fact(:virtual).value.should == "xenhvm"
132132 end
133133
134134 it "should be xenhvm with Xen HVM vendor name from dmidecode" do
135 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
136 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("System Information\nManufacturer: Xen\nProduct Name: HVM domU")
135 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
136 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("System Information\nManufacturer: Xen\nProduct Name: HVM domU")
137137 Facter.fact(:virtual).value.should == "xenhvm"
138138 end
139139
140140 it "should be parallels with Parallels vendor name from dmidecode" do
141 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
142 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("On Board Device Information\nType: Video\nStatus: Disabled\nDescription: Parallels Video Adapter")
141 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
142 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("On Board Device Information\nType: Video\nStatus: Disabled\nDescription: Parallels Video Adapter")
143143 Facter.fact(:virtual).value.should == "parallels"
144144 end
145145
146146 it "should be virtualbox with VirtualBox vendor name from dmidecode" do
147 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
148 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("BIOS Information\nVendor: innotek GmbH\nVersion: VirtualBox\n\nSystem Information\nManufacturer: innotek GmbH\nProduct Name: VirtualBox\nFamily: Virtual Machine")
147 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
148 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("BIOS Information\nVendor: innotek GmbH\nVersion: VirtualBox\n\nSystem Information\nManufacturer: innotek GmbH\nProduct Name: VirtualBox\nFamily: Virtual Machine")
149149 Facter.fact(:virtual).value.should == "virtualbox"
150150 end
151151
152152 it "should be rhev with RHEV Hypervisor product name from dmidecode" do
153153 Facter.fact(:kernel).stubs(:value).returns("Linux")
154 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
155 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("Product Name: RHEV Hypervisor")
154 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
155 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("Product Name: RHEV Hypervisor")
156156 Facter.fact(:virtual).value.should == "rhev"
157157 end
158158
159159 it "should be ovirt with oVirt Node product name from dmidecode" do
160160 Facter.fact(:kernel).stubs(:value).returns("Linux")
161 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
162 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("Product Name: oVirt Node")
161 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
162 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("Product Name: oVirt Node")
163163 Facter.fact(:virtual).value.should == "ovirt"
164164 end
165165
166166 it "should be hyperv with Microsoft vendor name from lspci 2>/dev/null" do
167 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("00:08.0 VGA compatible controller: Microsoft Corporation Hyper-V virtual VGA")
167 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("00:08.0 VGA compatible controller: Microsoft Corporation Hyper-V virtual VGA")
168168 Facter.fact(:virtual).value.should == "hyperv"
169169 end
170170
171171 it "should be hyperv with Microsoft vendor name from dmidecode" do
172 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
173 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("System Information\nManufacturer: Microsoft Corporation\nProduct Name: Virtual Machine")
172 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
173 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("System Information\nManufacturer: Microsoft Corporation\nProduct Name: Virtual Machine")
174174 Facter.fact(:virtual).value.should == "hyperv"
175175 end
176176
185185 end
186186
187187 it "should be gce with gce vendor name from lspci 2>/dev/null" do
188 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("00:05.0 Class 8007: Google, Inc. Device 6442")
188 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("00:05.0 Class 8007: Google, Inc. Device 6442")
189189 Facter.fact(:virtual).value.should == "gce"
190190 end
191191 end
206206 end
207207
208208 it "(#20236) is vmware when dmidecode contains vmware and lspci returns insufficient information" do
209 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns("garbage\ninformation\n")
210 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns("On Board Device 1 Information\nType: Video\nStatus: Disabled\nDescription: VMware SVGA II")
209 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns("garbage\ninformation\n")
210 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns("On Board Device 1 Information\nType: Video\nStatus: Disabled\nDescription: VMware SVGA II")
211211 Facter.fact(:virtual).value.should eq("vmware")
212212 end
213213 end
214214
215215 describe "on Solaris" do
216216 before(:each) do
217 Facter::Util::Resolution.stubs(:exec).with("vmware -v").returns false
217 Facter::Core::Execution.stubs(:exec).with("vmware -v").returns false
218218 Facter.fact(:kernel).stubs(:value).returns("SunOS")
219219 end
220220
221221 it "should be vmware with VMWare vendor name from prtdiag" do
222222 Facter.fact(:hardwaremodel).stubs(:value).returns(nil)
223 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
224 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns(nil)
225 Facter::Util::Resolution.stubs(:exec).with('prtdiag').returns("System Configuration: VMware, Inc. VMware Virtual Platform")
223 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
224 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns(nil)
225 Facter::Core::Execution.stubs(:exec).with('prtdiag').returns("System Configuration: VMware, Inc. VMware Virtual Platform")
226226 Facter.fact(:virtual).value.should == "vmware"
227227 end
228228
229229 it "should be parallels with Parallels vendor name from prtdiag" do
230230 Facter.fact(:hardwaremodel).stubs(:value).returns(nil)
231 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
232 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns(nil)
233 Facter::Util::Resolution.stubs(:exec).with('prtdiag').returns("System Configuration: Parallels Virtual Platform")
231 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
232 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns(nil)
233 Facter::Core::Execution.stubs(:exec).with('prtdiag').returns("System Configuration: Parallels Virtual Platform")
234234 Facter.fact(:virtual).value.should == "parallels"
235235 end
236236
237237 it "should be virtualbox with VirtualBox vendor name from prtdiag" do
238238 Facter.fact(:hardwaremodel).stubs(:value).returns(nil)
239 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
240 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns(nil)
241 Facter::Util::Resolution.stubs(:exec).with('prtdiag').returns("System Configuration: innotek GmbH VirtualBox")
239 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
240 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns(nil)
241 Facter::Core::Execution.stubs(:exec).with('prtdiag').returns("System Configuration: innotek GmbH VirtualBox")
242242 Facter.fact(:virtual).value.should == "virtualbox"
243243 end
244244 end
245245
246246 describe "on OpenBSD" do
247247 before do
248 Facter::Util::Resolution.stubs(:exec).with("vmware -v").returns false
248 Facter::Core::Execution.stubs(:exec).with("vmware -v").returns false
249249 Facter.fact(:kernel).stubs(:value).returns("OpenBSD")
250250 Facter.fact(:hardwaremodel).stubs(:value).returns(nil)
251 Facter::Util::Resolution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
252 Facter::Util::Resolution.stubs(:exec).with('dmidecode').returns(nil)
251 Facter::Core::Execution.stubs(:exec).with('lspci 2>/dev/null').returns(nil)
252 Facter::Core::Execution.stubs(:exec).with('dmidecode 2> /dev/null').returns(nil)
253253 end
254254
255255 it "should be parallels with Parallels product name from sysctl" do
256 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("Parallels Virtual Platform")
256 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("Parallels Virtual Platform")
257257 Facter.fact(:virtual).value.should == "parallels"
258258 end
259259
260260 it "should be vmware with VMware product name from sysctl" do
261 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("VMware Virtual Platform")
261 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("VMware Virtual Platform")
262262 Facter.fact(:virtual).value.should == "vmware"
263263 end
264264
265265 it "should be virtualbox with VirtualBox product name from sysctl" do
266 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("VirtualBox")
266 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("VirtualBox")
267267 Facter.fact(:virtual).value.should == "virtualbox"
268268 end
269269
270270 it "should be xenhvm with Xen HVM product name from sysctl" do
271 Facter::Util::Resolution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("HVM domU")
271 Facter::Core::Execution.stubs(:exec).with('sysctl -n hw.product 2>/dev/null').returns("HVM domU")
272272 Facter.fact(:virtual).value.should == "xenhvm"
273273 end
274274 end
302302 computersystem = mock('computersystem', :model => 'VMware Virtual Platform')
303303 Facter::Util::WMI.expects(:execquery).returns([computersystem])
304304 Facter.fact(:virtual).value.should == "vmware"
305 end
306
307 it "resolves as Xen with a manufacturer name like xen" do
308 computersystem = mock('computersystem', :model => nil, :manufacturer => 'Xen')
309 Facter::Util::WMI.expects(:execquery).returns([computersystem])
310 Facter.fact(:virtual).value.should == "xen"
305311 end
306312 end
307313
382388 Facter.fact(:is_virtual).value.should == "true"
383389 end
384390
391 it "should be true when running on vserver" do
392 Facter.fact(:kernel).stubs(:value).returns("Linux")
393 Facter.fact(:virtual).stubs(:value).returns("vserver")
394 Facter.fact(:is_virtual).value.should == "true"
395 end
396
385397 it "should be true when running on kvm" do
386398 Facter.fact(:kernel).stubs(:value).returns("Linux")
387399 Facter.fact(:virtual).stubs(:value).returns("kvm")
431443 Facter.fact(:is_virtual).value.should == "false"
432444 end
433445
446 it "should be false on vserver host nodes" do
447 Facter.fact(:kernel).stubs(:value).returns("Linux")
448 Facter.fact(:virtual).stubs(:value).returns("vserver_host")
449 Facter.fact(:is_virtual).value.should == "false"
450 end
451
434452 it "should be true when running on hyperv" do
435453 Facter.fact(:kernel).stubs(:value).returns("Linux")
436454 Facter.fact(:virtual).stubs(:value).returns("hyperv")
1414 # Solaris 11 11/11 (ga) 33 5
1515
1616 before :each do
17 Facter::Util::Resolution.stubs(:which).with("zfs").returns("/usr/bin/zfs")
17 Facter::Core::Execution.stubs(:which).with("zfs").returns("/usr/bin/zfs")
1818 end
1919
2020 it "should return correct version on Solaris 10" do
21 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('solaris_10'))
21 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('solaris_10'))
2222 Facter.fact(:zfs_version).value.should == "3"
2323 end
2424
2525 it "should return correct version on Solaris 11" do
26 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('solaris_11'))
26 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('solaris_11'))
2727 Facter.fact(:zfs_version).value.should == "5"
2828 end
2929
3030 it "should return correct version on FreeBSD 8.2" do
31 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('freebsd_8.2'))
31 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('freebsd_8.2'))
3232 Facter.fact(:zfs_version).value.should == "4"
3333 end
3434
3535 it "should return correct version on FreeBSD 9.0" do
36 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('freebsd_9.0'))
36 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('freebsd_9.0'))
3737 Facter.fact(:zfs_version).value.should == "5"
3838 end
3939
4040 it "should return correct version on Linux with ZFS-fuse" do
41 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
41 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
4242 Facter.fact(:zfs_version).value.should == "4"
4343 end
4444
4545 it "should return nil if zfs command is not available" do
46 Facter::Util::Resolution.stubs(:which).with("zfs").returns(nil)
47 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
46 Facter::Core::Execution.stubs(:which).with("zfs").returns(nil)
47 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
4848 Facter.fact(:zfs_version).value.should == nil
4949 end
5050
5151 it "should return nil if zfs fails to run" do
52 Facter::Util::Resolution.stubs(:exec).with("zfs upgrade -v").returns(nil)
52 Facter::Core::Execution.stubs(:exec).with("zfs upgrade -v").returns('')
5353 Facter.fact(:zfs_version).value.should == nil
5454 end
5555
5656 it "handles the zfs command becoming available at a later point in time" do
5757 # Simulate Puppet configuring the zfs tools from a persistent daemon by
5858 # simulating three sequential responses to which('zfs').
59 Facter::Util::Resolution.stubs(:which).
59 Facter::Core::Execution.stubs(:which).
6060 with("zfs").
6161 returns(nil,nil,"/usr/bin/zfs")
62 Facter::Util::Resolution.stubs(:exec).
62 Facter::Core::Execution.stubs(:exec).
6363 with("zfs upgrade -v").
6464 returns(my_fixture_read('linux-fuse_0.6.9'))
6565
0 #!/usr/bin/env rspec
0 #! /usr/bin/env ruby
11
22 require 'spec_helper'
33 require 'facter'
66
77 it "should return global zone" do
88 Facter.fact(:kernel).stubs(:value).returns("SunOS")
9 Facter::Util::Resolution.stubs(:exec).with("zonename").returns('global')
9 Facter::Core::Execution.stubs(:execute).with("zonename", anything).returns('global')
1010
1111 Facter.fact(:zonename).value.should == "global"
1212 end
99 -:local:configured:/::native:shared
1010 -:zoneA:stopped:/::native:shared
1111 EOF
12 Facter::Util::Resolution.stubs(:exec).with('/usr/sbin/zoneadm list -cp').returns(zone_list)
12 Facter::Core::Execution.stubs(:execute).with('/usr/sbin/zoneadm list -cp', {:on_fail => nil}).returns(zone_list)
1313 Facter.collection.internal_loader.load(:zones)
1414 end
1515
1414 # Solaris 11 11/11 (ga) 33 5
1515
1616 before :each do
17 Facter::Util::Resolution.stubs(:which).with("zpool").returns("/usr/bin/zpool")
17 Facter::Core::Execution.stubs(:which).with("zpool").returns("/usr/bin/zpool")
1818 end
1919
2020 it "should return correct version on Solaris 10" do
21 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('solaris_10'))
21 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('solaris_10'))
2222 Facter.fact(:zpool_version).value.should == "22"
2323 end
2424
2525 it "should return correct version on Solaris 11" do
26 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('solaris_11'))
26 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('solaris_11'))
2727 Facter.fact(:zpool_version).value.should == "33"
2828 end
2929
3030 it "should return correct version on FreeBSD 8.2" do
31 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('freebsd_8.2'))
31 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('freebsd_8.2'))
3232 Facter.fact(:zpool_version).value.should == "15"
3333 end
3434
3535 it "should return correct version on FreeBSD 9.0" do
36 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('freebsd_9.0'))
36 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('freebsd_9.0'))
3737 Facter.fact(:zpool_version).value.should == "28"
3838 end
3939
4040 it "should return correct version on Linux with ZFS-fuse" do
41 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
41 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
4242 Facter.fact(:zpool_version).value.should == "23"
4343 end
4444
4545 it "should return nil if zpool is not available" do
46 Facter::Util::Resolution.stubs(:which).with("zpool").returns(nil)
47 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
46 Facter::Core::Execution.stubs(:which).with("zpool").returns(nil)
47 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns(my_fixture_read('linux-fuse_0.6.9'))
4848 Facter.fact(:zpool_version).value.should == nil
4949 end
5050
5151 it "should return nil if zpool fails to run" do
52 Facter::Util::Resolution.stubs(:exec).with("zpool upgrade -v").returns(nil)
52 Facter::Core::Execution.stubs(:exec).with("zpool upgrade -v").returns('')
5353 Facter.fact(:zpool_version).value.should == nil
5454 end
5555
5656 it "handles the zpool command becoming available" do
5757 # Simulate Puppet configuring the zfs tools from a persistent daemon by
5858 # simulating three sequential responses to which('zpool').
59 Facter::Util::Resolution.stubs(:which).
59 Facter::Core::Execution.stubs(:which).
6060 with("zpool").
6161 returns(nil,nil,"/usr/bin/zpool")
62 Facter::Util::Resolution.stubs(:exec).
62 Facter::Core::Execution.stubs(:exec).
6363 with("zpool upgrade -v").
6464 returns(my_fixture_read('linux-fuse_0.6.9'))
6565