Imported Upstream version 2.0.1
Stig Sandbeck Mathisen
10 years ago
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 | |
2 | 1 | |
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. | |
19 | 8 | |
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 | |
28 | 10 | |
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 | |
43 | 17 | |
44 | 1. Decide what to base your work on. | |
18 | ## Making Changes | |
45 | 19 | |
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. | |
63 | 31 | |
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 | |
79 | 34 | |
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. | |
81 | 40 | |
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 | ```` | |
88 | 45 | |
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. | |
91 | 48 | |
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 | |
95 | 50 | |
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 | |
100 | 56 | |
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 | |
108 | 58 | |
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) | |
190 | 62 | * [General GitHub documentation](http://help.github.com/) |
191 | ||
192 | 63 | * [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" | |
5 | 1 | |
6 | 2 | # C Ruby (MRI) or Rubinius, but NOT Windows |
7 | 3 | platforms :ruby do |
8 | 4 | gem 'watchr', :group => :development |
9 | 5 | gem 'pry', :group => :development |
10 | 6 | gem 'yard', :group => :development |
11 | gem 'redcarpet', :group => :development | |
7 | gem 'redcarpet', '<= 2.3.0', :group => :development | |
12 | 8 | end |
13 | 9 | |
14 | 10 | group :development, :test do |
15 | gem 'rake' | |
16 | gem 'facter', ">= 1.0.0", :path => File.expand_path("..", __FILE__) | |
11 | gem 'rake', "~> 10.1.0" | |
17 | 12 | gem 'rspec', "~> 2.11.0" |
18 | 13 | gem 'mocha', "~> 0.10.5" |
19 | gem 'json', "~> 1.7" | |
14 | gem 'json', "~> 1.7", :platforms => :ruby | |
20 | 15 | gem 'puppetlabs_spec_helper' |
21 | 16 | end |
22 | 17 | |
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 | |
29 | 30 | end |
31 | ||
32 | gem 'facter', ">= 1.0.0", :path => File.expand_path("..", __FILE__) | |
30 | 33 | |
31 | 34 | if File.exists? "#{__FILE__}.local" |
32 | 35 | eval(File.read("#{__FILE__}.local"), binding) |
2 | 2 | |
3 | 3 | This package is largely meant to be a library for collecting facts about your |
4 | 4 | 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. | |
7 | 6 | |
8 | 7 | See `bin/facter` for an example of the interface. |
9 | 8 |
6 | 6 | $LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks') |
7 | 7 | |
8 | 8 | require 'rake' |
9 | Dir['tasks/**/*.rake'].each { |t| load t } | |
9 | 10 | |
10 | 11 | begin |
11 | 12 | load File.join(File.dirname(__FILE__), 'ext', 'packaging', 'packaging.rake') |
22 | 23 | end |
23 | 24 | end |
24 | 25 | |
25 | Dir['tasks/**/*.rake'].each { |t| load t } | |
26 | 26 | |
27 | 27 | build_defs_file = 'ext/build_defaults.yaml' |
28 | 28 | 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 |
13 | 13 | end |
14 | 14 | } |
15 | 15 | |
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 } | |
19 | 21 | |
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 | |
24 | 33 | 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 |
0 | 0 | #!/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 | |
59 | 1 | |
60 | 2 | # Bundler and rubygems maintain a set of directories from which to |
61 | 3 | # load gems. If Bundler is loaded, let it determine what can be |
1 | 1 | packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=master' |
2 | 2 | packaging_repo: 'packaging' |
3 | 3 | 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' | |
5 | 5 | pbuild_conf: '/etc/pbuilderrc' |
6 | 6 | packager: 'puppetlabs' |
7 | 7 | gpg_name: 'info@puppetlabs.com' |
8 | 8 | gpg_key: '4BD6EC30' |
9 | 9 | sign_tar: FALSE |
10 | 10 | # 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' | |
12 | 12 | yum_host: 'yum.puppetlabs.com' |
13 | 13 | yum_repo_path: '/opt/repository/yum/' |
14 | 14 | 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 | |
1 | 1 | |
2 | 2 | * Update to version |
3 | 3 | |
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 | |
5 | 5 | |
6 | 6 | facter (1.7.2-1puppetlabs2) lucid unstable sid wheezy lucid squeeze precise; urgency=low |
7 | 7 |
6 | 6 | BINDIR=$(shell /usr/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["bindir"]') |
7 | 7 | |
8 | 8 | 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 | |
1 | 1 | 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" | |
3 | 3 | set name=pkg.description value="You can prove anything with facts!" |
4 | 4 | set name=info.classification value="org.opensolaris.category.2008:Applications/System Utilities" |
5 | 5 | set name=org.opensolaris.consolidation value="puppet" |
0 | 0 | directories: |
1 | 1 | lib: |
2 | path: 'usr/lib/ruby/site_ruby/1.8' | |
2 | path: 'Library/Ruby/Site' | |
3 | 3 | owner: 'root' |
4 | 4 | group: 'wheel' |
5 | 5 | perms: '0644' |
7 | 7 | # ${3} is the destination volume so that this works correctly |
8 | 8 | # when being installed to volumes other than the current OS. |
9 | 9 | |
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 -%> | |
15 | 14 | |
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 | ||
21 | 24 | # 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 -%> | |
27 | 30 | |
28 | 31 | # remove old doc files |
29 | /bin/rm -Rf "${3}<%= @apple_docdir %>/<%=@package_name%>" | |
32 | /bin/rm -Rf "${3}<%= @apple_docdir %>/<%= @package_name %>" |
12 | 12 | gem_test_files: 'spec/**/*' |
13 | 13 | gem_executables: 'facter' |
14 | 14 | 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 | |
1 | 1 | # of sitelibdir |
2 | %if 0%{?fedora} >= 17 | |
2 | %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 | |
3 | 3 | %global facter_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendorlibdir"]') |
4 | 4 | %else |
5 | 5 | %global facter_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["sitelibdir"]') |
6 | 6 | %endif |
7 | 7 | |
8 | 8 | # 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 | |
11 | 11 | |
12 | 12 | Summary: Ruby module for collecting simple facts about a host operating system |
13 | 13 | Name: facter |
23 | 23 | |
24 | 24 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) |
25 | 25 | |
26 | Requires: ruby >= 1.8.5 | |
26 | Requires: ruby >= 1.8.7 | |
27 | 27 | Requires: which |
28 | 28 | # dmidecode and pciutils are not available on all arches |
29 | 29 | %ifarch %ix86 x86_64 ia64 |
31 | 31 | Requires: pciutils |
32 | 32 | %endif |
33 | 33 | 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 | |
39 | 39 | BuildRequires: rubygem-rdoc |
40 | 40 | %else |
41 | 41 | BuildRequires: ruby-rdoc |
69 | 69 | |
70 | 70 | |
71 | 71 | %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 | |
74 | 74 | |
75 | 75 | * Mon Apr 01 2013 Matthaus Owens <matthaus@puppetlabs.com> - 1:1.7.0-0.1rc1 |
76 | 76 | - Add dependency on virt-what to facter for better virutalization detection |
159 | 159 | opts.on('--[no-]man', 'Presents the creation of man pages.', 'Default on.') do |onman| |
160 | 160 | InstallOptions.man = onman |
161 | 161 | 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 | |
166 | 162 | opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir| |
167 | 163 | InstallOptions.destdir = destdir |
168 | 164 | end |
232 | 228 | mandir = RbConfig::CONFIG['mandir'] |
233 | 229 | end |
234 | 230 | |
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) | |
247 | 232 | bindir = join(destdir, bindir) |
248 | 233 | mandir = join(destdir, mandir) |
249 | 234 | sitelibdir = join(destdir, sitelibdir) |
0 | require 'optparse' | |
1 | require 'facter' | |
2 | require 'facter/util/formatter' | |
3 | ||
0 | 4 | module Facter |
1 | 5 | module Application |
2 | 6 | |
6 | 10 | begin |
7 | 11 | Facter::Util::Config.ext_fact_loader = Facter::Util::DirectoryLoader.loader_for(dir) |
8 | 12 | 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.") | |
10 | 14 | exit(1) |
11 | 15 | end |
12 | 16 | end |
16 | 20 | end |
17 | 21 | |
18 | 22 | def self.run(argv) |
19 | require 'optparse' | |
20 | require 'facter' | |
21 | ||
22 | 23 | options = parse(argv) |
23 | 24 | |
24 | 25 | # Accept fact names to return from the command line |
34 | 35 | begin |
35 | 36 | facts[name] = Facter.value(name) |
36 | 37 | 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) | |
39 | 40 | end |
40 | 41 | end |
41 | 42 | end |
43 | 44 | # Print everything if they didn't ask for specific facts. |
44 | 45 | facts ||= Facter.to_hash |
45 | 46 | |
46 | # Print the facts as YAML and exit | |
47 | output = nil | |
48 | ||
47 | 49 | 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) | |
71 | 55 | 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) | |
76 | 61 | |
77 | 62 | 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) | |
84 | 65 | end |
85 | 66 | |
86 | 67 | private |
92 | 73 | # @return [Hash] options hash |
93 | 74 | def self.parse(argv) |
94 | 75 | 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 | |
106 | 167 | puts Facter.version |
107 | 168 | exit(0) |
108 | 169 | end |
109 | 170 | |
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) | |
122 | 176 | end |
123 | end.parse!(argv) | |
177 | end | |
178 | ||
179 | parser.parse!(argv) | |
124 | 180 | |
125 | 181 | options |
126 | 182 | rescue OptionParser::InvalidOption => e |
3 | 3 | # Return the CPU hardware architecture. |
4 | 4 | # |
5 | 5 | # 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 | |
7 | 8 | # Gentoo and Debian call "x86_86" "amd64". |
8 | 9 | # Gentoo also calls "i386" "x86". |
9 | 10 | # |
10 | 11 | # Caveats: |
11 | 12 | # |
12 | 13 | |
14 | require 'facter/util/architecture' | |
15 | ||
13 | 16 | Facter.add(:architecture) do |
14 | 17 | setcode do |
15 | 18 | model = Facter.value(:hardwaremodel) |
16 | 19 | case model |
17 | 20 | # 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 | |
18 | 31 | when "x86_64" |
19 | 32 | case Facter.value(:operatingsystem) |
20 | 33 | 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 = "[0;32m" | |
8 | # @api private | |
9 | RESET = "[0m" | |
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 |
40 | 40 | basic_hostname |
41 | 41 | end |
42 | 42 | |
43 | if name = Facter::Util::Resolution.exec(hostname_command) \ | |
43 | if name = Facter::Core::Execution.exec(hostname_command) \ | |
44 | 44 | and name =~ /.*?\.(.+$)/ |
45 | 45 | |
46 | 46 | 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') \ | |
48 | 48 | and domain =~ /.+/ |
49 | 49 | |
50 | 50 | return_value = domain |
63 | 63 | return_value ||= domain |
64 | 64 | return_value ||= search |
65 | 65 | end |
66 | return_value = '' if return_value.nil? | |
67 | return_value.gsub(/\.$/, '') | |
66 | ||
67 | if return_value | |
68 | return_value.gsub(/\.$/, '') | |
69 | end | |
68 | 70 | end |
69 | 71 | end |
70 | 72 | |
72 | 74 | confine :kernel => :windows |
73 | 75 | setcode do |
74 | 76 | require 'facter/util/registry' |
75 | domain = "" | |
77 | ||
78 | domain = nil | |
76 | 79 | 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 | |
79 | 84 | require 'facter/util/wmi' |
80 | 85 | Facter::Util::WMI.execquery("select DNSDomain from Win32_NetworkAdapterConfiguration where IPEnabled = True").each { |nic| |
81 | 86 | if nic.DNSDomain && nic.DNSDomain.length > 0 |
85 | 90 | } |
86 | 91 | end |
87 | 92 | |
88 | domain ||= '' | |
89 | 93 | |
90 | domain.gsub(/\.$/, '') | |
94 | if domain | |
95 | domain.gsub(/\.$/, '') | |
96 | end | |
91 | 97 | end |
92 | 98 | end |
17 | 17 | # This is due to some problems with IO#read in Ruby and reading content of |
18 | 18 | # the "proc" file system that was reported more than once in the past ... |
19 | 19 | 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| | |
21 | 21 | # Remove bloat ... |
22 | 22 | line.strip! |
23 | 23 |
17 | 17 | Facter.add(:hardwaremodel) do |
18 | 18 | confine :operatingsystem => :aix |
19 | 19 | setcode do |
20 | model = Facter::Util::Resolution.exec('lsattr -El sys0 -a modelname') | |
20 | model = Facter::Core::Execution.exec('lsattr -El sys0 -a modelname') | |
21 | 21 | if model =~ /modelname\s(\S+)\s/ |
22 | 22 | $1 |
23 | 23 | end |
10 | 10 | # Caveats: |
11 | 11 | # |
12 | 12 | |
13 | Facter.add(:hostname, :ldapname => "cn") do | |
13 | Facter.add(:hostname) do | |
14 | 14 | setcode do |
15 | 15 | hostname = nil |
16 | if name = Facter::Util::Resolution.exec('hostname') | |
16 | if name = Facter::Core::Execution.execute('hostname') | |
17 | 17 | if name =~ /(.*?)\./ |
18 | 18 | hostname = $1 |
19 | 19 | else |
26 | 26 | |
27 | 27 | Facter.add(:hostname) do |
28 | 28 | 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' | |
32 | 30 | end |
14 | 14 | # |
15 | 15 | |
16 | 16 | require 'facter/util/ip' |
17 | require 'facter/util/macaddress' | |
17 | 18 | |
18 | 19 | # Note that most of this only works on a fixed list of platforms; notably, Darwin |
19 | 20 | # is missing. |
33 | 34 | %w{ipaddress ipaddress6 macaddress netmask mtu}.each do |label| |
34 | 35 | Facter.add(label + "_" + Facter::Util::IP.alphafy(interface)) do |
35 | 36 | 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 | |
37 | 42 | end |
38 | 43 | end |
39 | 44 | end |
120 | 120 | end |
121 | 121 | end |
122 | 122 | |
123 | Facter.add(:ipaddress, :ldapname => "iphostnumber", :timeout => 2) do | |
123 | Facter.add(:ipaddress, :timeout => 2) do | |
124 | 124 | setcode do |
125 | 125 | if Facter.value(:kernel) == 'windows' |
126 | 126 | require 'win32/resolv' |
154 | 154 | if hostname = Facter.value(:hostname) |
155 | 155 | # we need Hostname to exist for this to work |
156 | 156 | host = nil |
157 | if host = Facter::Util::Resolution.exec("host #{hostname}") | |
157 | if host = Facter::Core::Execution.execute("host #{hostname}") | |
158 | 158 | list = host.chomp.split(/\s/) |
159 | 159 | if defined? list[-1] and |
160 | 160 | list[-1] =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ |
15 | 15 | if Facter::Util::Config.is_windows? |
16 | 16 | 'windows' |
17 | 17 | else |
18 | Facter::Util::Resolution.exec("uname -s") | |
18 | Facter::Core::Execution.exec("uname -s") | |
19 | 19 | end |
20 | 20 | end |
21 | 21 | end |
22 | 22 | Facter.add(:kernelrelease) do |
23 | 23 | confine :kernel => "hp-ux" |
24 | 24 | setcode do |
25 | version = Facter::Util::Resolution.exec('uname -r') | |
25 | version = Facter::Core::Execution.execute('uname -r') | |
26 | 26 | version[2..-1] |
27 | 27 | end |
28 | 28 | 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') | |
2 | 2 | |
3 | 3 | # Convert virtinfo parseable output format to array of arrays. |
4 | 4 | # DOMAINROLE|impl=LDoms|control=true|io=true|service=true|root=true |
11 | 11 | |
12 | 12 | Facter.add(:lsbdistcodename) do |
13 | 13 | 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' | |
17 | 15 | end |
11 | 11 | |
12 | 12 | Facter.add(:lsbdistdescription) do |
13 | 13 | confine :kernel => [ :linux, :"gnu/kfreebsd" ] |
14 | confine do | |
15 | Facter::Core::Execution.which("lsb_release") | |
16 | end | |
17 | ||
14 | 18 | 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') | |
16 | 20 | # the output may be quoted (at least it is on gentoo) |
17 | 21 | output.sub(/^"(.*)"$/,'\1') |
18 | 22 | end |
11 | 11 | |
12 | 12 | Facter.add(:lsbdistid) do |
13 | 13 | 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' | |
17 | 15 | end |
11 | 11 | |
12 | 12 | Facter.add(:lsbdistrelease) do |
13 | 13 | 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' | |
17 | 15 | end |
11 | 11 | |
12 | 12 | Facter.add(:lsbrelease) do |
13 | 13 | 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' | |
17 | 15 | end |
8 | 8 | |
9 | 9 | require 'facter/util/macaddress' |
10 | 10 | 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 | |
24 | 11 | |
25 | 12 | Facter.add(:macaddress) do |
26 | 13 | confine :kernel => 'Linux' |
51 | 38 | confine :osfamily => "Solaris" |
52 | 39 | setcode do |
53 | 40 | ether = [] |
54 | output = Facter::Util::Resolution.exec("/usr/bin/netstat -np") | |
41 | output = Facter::Core::Execution.exec("/usr/bin/netstat -np") | |
55 | 42 | output.each_line do |s| |
56 | 43 | ether.push($1) if s =~ /(?:SPLA)\s+(\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})/ |
57 | 44 | end |
23 | 23 | # at this point in time. |
24 | 24 | # In particular, Installed Software might be an interesting addition. |
25 | 25 | |
26 | require 'facter/util/macosx' | |
26 | if Facter.value(:kernel) == "Darwin" | |
27 | require 'facter/util/macosx' | |
27 | 28 | |
28 | if Facter.value(:kernel) == "Darwin" | |
29 | 29 | Facter::Util::Macosx.hardware_overview.each do |fact, value| |
30 | 30 | Facter.add("sp_#{fact}") do |
31 | 31 | confine :kernel => :darwin |
85 | 85 | Facter.add("SwapEncrypted") do |
86 | 86 | confine :kernel => :Darwin |
87 | 87 | setcode do |
88 | swap = Facter::Util::Resolution.exec('sysctl vm.swapusage') | |
88 | swap = Facter::Core::Execution.exec('sysctl vm.swapusage') | |
89 | 89 | encrypted = false |
90 | 90 | if swap =~ /\(encrypted\)/ then encrypted = true; end |
91 | 91 | encrypted |
98 | 98 | Facter.add("memorysize_mb") do |
99 | 99 | confine :kernel => :sunos |
100 | 100 | # 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') | |
102 | 102 | phymem = "" |
103 | 103 | pconf.each_line do |line| |
104 | 104 | if line =~ /^Memory size:\s+(\d+) Megabytes/ |
142 | 142 | Facter.add("swapsize_mb") do |
143 | 143 | confine :kernel => :dragonfly |
144 | 144 | 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 | |
147 | 147 | "%.2f" % [(swaptotal.to_f / 1024.0) / 1024.0] |
148 | 148 | end |
149 | 149 | end |
151 | 151 | Facter.add("swapfree_mb") do |
152 | 152 | confine :kernel => :dragonfly |
153 | 153 | 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 | |
158 | 158 | swapfree = swaptotal - swap_anon_use - swap_cache_use |
159 | 159 | "%.2f" % [(swapfree.to_f / 1024.0) / 1024.0] |
160 | 160 | end |
161 | 161 | 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 |
15 | 15 | setcode do |
16 | 16 | # Use uname -v because /etc/release can change in zones under SmartOS. |
17 | 17 | # 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') | |
19 | 19 | if output =~ /^joyent_/ |
20 | 20 | "SmartOS" |
21 | 21 | elsif output =~ /^oi_/ |
1 | 1 | # |
2 | 2 | # Purpose: Returns the major release of the operating system. |
3 | 3 | # |
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 | |
6 | 9 | # |
7 | 10 | # This should be the same as lsbmajdistrelease, but on minimal systems there |
8 | 11 | # are too many dependencies to use LSB |
12 | 15 | #"Debian" "Fedora" "Gentoo" "Mandrake" "Mandriva" "MeeGo" "OEL" "OpenSuSE" |
13 | 16 | #"OracleLinux" "OVS" "PSBM" "RedHat" "Scientific" "Slackware" "Slamd64" "SLC" |
14 | 17 | #"SLED" "SLES" "SuSE" "Ubuntu" "VMWareESX" |
18 | ||
15 | 19 | Facter.add(:operatingsystemmajrelease) do |
16 | 20 | confine :operatingsystem => [ |
17 | 21 | :Amazon, |
30 | 34 | Facter.value('operatingsystemrelease').split('.').first |
31 | 35 | end |
32 | 36 | 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 |
4 | 4 | # Resolution: |
5 | 5 | # On RedHat derivatives, returns their '/etc/<variant>-release' file. |
6 | 6 | # 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. | |
8 | 8 | # On Suse, derivatives, parses '/etc/SuSE-release' for a selection of version |
9 | 9 | # information. |
10 | 10 | # On Slackware, parses '/etc/slackware-version'. |
60 | 60 | Facter.add(:operatingsystemrelease) do |
61 | 61 | confine :operatingsystem => %w{Ubuntu} |
62 | 62 | 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+))?)/) | |
65 | 65 | # Return only the major and minor version numbers. This behavior must |
66 | 66 | # be preserved for compatibility reasons. |
67 | 67 | match[2] |
140 | 140 | Facter.add(:operatingsystemrelease) do |
141 | 141 | confine :operatingsystem => %w{VMwareESX} |
142 | 142 | setcode do |
143 | release = Facter::Util::Resolution.exec('vmware -v') | |
143 | release = Facter::Core::Execution.exec('vmware -v') | |
144 | 144 | if match = /VMware ESX .*?(\d.*)/.match(release) |
145 | 145 | match[1] |
146 | 146 | end |
180 | 180 | setcode do |
181 | 181 | if release = Facter::Util::FileRead.read('/etc/release') |
182 | 182 | 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 | |
183 | 186 | if match = /\s+s(\d+)[sx]?(_u\d+)?.*(?:SPARC|X86)/.match(line) |
184 | 187 | 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 | |
187 | 225 | end |
188 | 226 | end |
189 | 227 |
38 | 38 | lookup_pattern = "#{sysfs_cpu_directory}" + |
39 | 39 | "/cpu*/topology/physical_package_id" |
40 | 40 | |
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 | |
42 | 42 | |
43 | 43 | else |
44 | 44 | # |
47 | 47 | # We assume that /proc/cpuinfo has what we need and is so then we need |
48 | 48 | # to make sure that we only count unique entries ... |
49 | 49 | # |
50 | str = Facter::Util::Resolution.exec("grep 'physical.\\+:' /proc/cpuinfo") | |
50 | str = Facter::Core::Execution.exec("grep 'physical.\\+:' /proc/cpuinfo") | |
51 | 51 | |
52 | if str then str.scan(/\d+/).uniq.size; end | |
52 | if str then str.scan(/\d+/).uniq.size.to_s; end | |
53 | 53 | end |
54 | 54 | end |
55 | 55 | end |
58 | 58 | confine :kernel => :windows |
59 | 59 | setcode do |
60 | 60 | 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 | |
62 | 62 | end |
63 | 63 | end |
64 | 64 | |
65 | 65 | Facter.add('physicalprocessorcount') do |
66 | 66 | confine :kernel => :sunos |
67 | 67 | |
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. | |
68 | 71 | 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. | |
72 | 72 | kernelrelease = Facter.value(:kernelrelease) |
73 | 73 | (major_version, minor_version) = kernelrelease.split(".").map { |str| str.to_i } |
74 | cmd = "/usr/sbin/psrinfo" | |
75 | result = nil | |
76 | 74 | 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") | |
78 | 76 | 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 | |
81 | 79 | end |
82 | 80 | end |
83 | 81 | end |
21 | 21 | require 'thread' |
22 | 22 | require 'facter/util/processor' |
23 | 23 | |
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) | |
26 | 27 | processor_list = case Facter::Util::Processor.kernel_fact_value |
27 | 28 | when "AIX" |
28 | 29 | Facter::Util::Processor.aix_processor_list |
88 | 89 | |
89 | 90 | Facter.add("Processor") do |
90 | 91 | 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" | |
101 | 93 | end |
102 | 94 | |
103 | 95 | Facter.add("ProcessorCount") do |
104 | 96 | confine :kernel => :Darwin |
105 | setcode do | |
106 | Facter::Util::Resolution.exec("sysctl -n hw.ncpu") | |
107 | end | |
97 | setcode "sysctl -n hw.ncpu" | |
108 | 98 | end |
109 | 99 | |
110 | 100 | if Facter.value(:kernel) == "windows" |
147 | 137 | |
148 | 138 | Facter.add("Processor") do |
149 | 139 | confine :kernel => [:dragonfly,:freebsd] |
150 | setcode do | |
151 | Facter::Util::Resolution.exec("sysctl -n hw.model") | |
152 | end | |
140 | setcode "sysctl -n hw.model" | |
153 | 141 | end |
154 | 142 | |
155 | 143 | 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" | |
160 | 146 | end |
161 | 147 | |
162 | 148 | Facter.add("ProcessorCount") do |
167 | 153 | result = nil |
168 | 154 | |
169 | 155 | 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") | |
171 | 157 | result = kstat.scan(/\bcore_id\b\s+\d+/).uniq.length |
172 | 158 | end |
173 | 159 | else |
174 | if output = Facter::Util::Resolution.exec("/usr/sbin/psrinfo") then | |
160 | if output = Facter::Core::Execution.exec("/usr/sbin/psrinfo") then | |
175 | 161 | result = output.split("\n").length |
176 | 162 | end |
177 | 163 | end |
178 | 164 | |
179 | result | |
165 | result.to_s | |
180 | 166 | end |
181 | 167 | end |
21 | 21 | # a hang. Reading from other parts of /proc does not seem to cause this problem. |
22 | 22 | # The work around is to read the file in another process. |
23 | 23 | # -- 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/ } | |
25 | 25 | if selinux_line |
26 | 26 | path = selinux_line.split[1] |
27 | 27 | end |
71 | 71 | confine :selinux => :true |
72 | 72 | setcode do |
73 | 73 | result = 'unknown' |
74 | mode = Facter::Util::Resolution.exec(sestatus_cmd) | |
74 | mode = Facter::Core::Execution.exec(sestatus_cmd) | |
75 | 75 | mode.each_line { |l| result = $1 if l =~ /^Current mode\:\s+(\w+)$/i } |
76 | 76 | result.chomp |
77 | 77 | end |
81 | 81 | confine :selinux => :true |
82 | 82 | setcode do |
83 | 83 | result = 'unknown' |
84 | mode = Facter::Util::Resolution.exec(sestatus_cmd) | |
84 | mode = Facter::Core::Execution.exec(sestatus_cmd) | |
85 | 85 | mode.each_line { |l| result = $1 if l =~ /^Mode from config file\:\s+(\w+)$/i } |
86 | 86 | result.chomp |
87 | 87 | end |
91 | 91 | confine :selinux => :true |
92 | 92 | setcode do |
93 | 93 | result = 'unknown' |
94 | mode = Facter::Util::Resolution.exec(sestatus_cmd) | |
94 | mode = Facter::Core::Execution.exec(sestatus_cmd) | |
95 | 95 | mode.each_line { |l| result = $1 if l =~ /^Policy from config file\:\s+(\w+)$/i } |
96 | 96 | result.chomp |
97 | 97 | end |
98 | 98 | 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 |
10 | 10 | ## Facts related to SSH |
11 | 11 | ## |
12 | 12 | |
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 | ||
15 | 18 | Facter.add(name) do |
16 | 19 | setcode do |
17 | 20 | 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 | 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 | 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 | 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 | 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 | # -*- 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 | # -*- 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 | # -*- 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 | # -*- 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 | # -*- 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 | # -*- 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 | # -*- 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 | # -*- coding: utf-8 -*- | |
1 | ||
2 | require File.join(File.dirname(__FILE__), 'cfpropertylist', 'lib', 'rbCFPropertyList.rb') | |
3 | ||
4 | ||
5 | # eof |
3 | 3 | |
4 | 4 | # Manage which facts exist and how we access them. Largely just a wrapper |
5 | 5 | # around a hash of facts. |
6 | # | |
7 | # @api private | |
6 | 8 | class Facter::Util::Collection |
7 | 9 | |
8 | 10 | def initialize(internal_loader, external_loader) |
11 | 13 | @external_loader = external_loader |
12 | 14 | end |
13 | 15 | |
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. | |
16 | 17 | def [](name) |
17 | 18 | value(name) |
18 | 19 | end |
19 | 20 | |
21 | # Define a new fact or extend an existing fact. | |
22 | # | |
23 | # @param name [Symbol] The name of the fact to define | |
24 | # @param options [Hash] A hash of options to set on the fact | |
25 | # | |
26 | # @return [Facter::Util::Fact] The fact that was defined | |
27 | def define_fact(name, options = {}, &block) | |
28 | fact = create_or_return_fact(name, options) | |
29 | ||
30 | 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 | ||
20 | 39 | # Add a resolution mechanism for a named fact. This does not distinguish |
21 | 40 | # 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 | |
22 | 46 | def add(name, options = {}, &block) |
23 | name = canonize(name) | |
47 | fact = create_or_return_fact(name, options) | |
24 | 48 | |
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) | |
61 | 50 | |
62 | 51 | return fact |
63 | 52 | end |
77 | 66 | |
78 | 67 | # Return a fact by name. |
79 | 68 | def fact(name) |
80 | name = canonize(name) | |
69 | name = canonicalize(name) | |
81 | 70 | |
82 | 71 | # Try to load the fact if necessary |
83 | 72 | load(name) unless @facts[name] |
95 | 84 | # Flush all cached values. |
96 | 85 | def flush |
97 | 86 | @facts.each { |name, fact| fact.flush } |
87 | @external_facts_loaded = nil | |
98 | 88 | end |
99 | 89 | |
100 | 90 | # Return a list of all of the facts. |
105 | 95 | |
106 | 96 | def load(name) |
107 | 97 | internal_loader.load(name) |
108 | external_loader.load(self) | |
98 | load_external_facts | |
109 | 99 | end |
110 | 100 | |
111 | 101 | # Load all known facts. |
112 | 102 | def load_all |
113 | 103 | internal_loader.load_all |
114 | external_loader.load(self) | |
104 | load_external_facts | |
115 | 105 | end |
116 | 106 | |
117 | 107 | def internal_loader |
142 | 132 | |
143 | 133 | private |
144 | 134 | |
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) | |
148 | 151 | name.to_s.downcase.to_sym |
149 | 152 | 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 | |
150 | 160 | end |
28 | 28 | end |
29 | 29 | end |
30 | 30 | |
31 | def self.external_facts_dirs=(dir) | |
32 | @external_facts_dirs = dir | |
33 | end | |
34 | ||
31 | 35 | 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 | |
35 | 47 | else |
36 | [File.join(windows_dir, 'PuppetLabs', 'facter', 'facts.d')] | |
48 | @external_facts_dirs = [File.expand_path(File.join("~", ".facter", "facts.d"))] | |
37 | 49 | end |
38 | 50 | 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 | |
39 | 60 | end |
40 | ||
41 | if Facter::Util::Config.is_windows? | |
42 | require 'rubygems' | |
43 | require 'win32/dir' | |
44 | end |
9 | 9 | |
10 | 10 | # Add the restriction. Requires the fact name, an operator, and the value |
11 | 11 | # 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 | |
15 | 24 | @fact = fact |
16 | 25 | @values = values |
26 | @block = block | |
17 | 27 | end |
18 | 28 | |
19 | 29 | def to_s |
30 | return @block.to_s if @block | |
20 | 31 | return "'%s' '%s'" % [@fact, @values.join(",")] |
21 | 32 | end |
22 | 33 | |
23 | 34 | # Evaluate the fact, returning true or false. |
35 | # if we have a block paramter then we only evaluate that instead | |
24 | 36 | 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 | ||
25 | 46 | unless fact = Facter[@fact] |
26 | 47 | Facter.debug "No fact for %s" % @fact |
27 | 48 | return false |
30 | 51 | |
31 | 52 | return false if value.nil? |
32 | 53 | |
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 | |
34 | 64 | end |
35 | 65 | end |
51 | 51 | "arp -an" |
52 | 52 | end |
53 | 53 | |
54 | if arp_table = Facter::Util::Resolution.exec(arp_command) | |
54 | if arp_table = Facter::Core::Execution.exec(arp_command) | |
55 | 55 | return true if arp_table.match(mac_address_re) |
56 | 56 | end |
57 | 57 | return false |
0 | 0 | require 'facter' |
1 | 1 | 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 | |
3 | 10 | 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 | |
9 | 26 | def initialize(name, options = {}) |
10 | 27 | @name = name.to_s.downcase.intern |
11 | 28 | |
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) | |
21 | 30 | |
22 | 31 | @ldapname ||= @name.to_s |
23 | 32 | |
27 | 36 | @value = nil |
28 | 37 | end |
29 | 38 | |
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 | |
50 | 91 | def flush |
51 | 92 | @resolves.each { |r| r.flush } |
52 | 93 | @value = nil |
53 | 94 | end |
54 | 95 | |
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 | |
57 | 101 | def value |
58 | 102 | return @value if @value |
59 | 103 | |
74 | 118 | end |
75 | 119 | end |
76 | 120 | |
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 | |
77 | 129 | |
78 | 130 | private |
79 | 131 | |
105 | 157 | |
106 | 158 | def find_first_real_value(resolutions) |
107 | 159 | resolutions.each do |resolve| |
108 | value = normalize_value(resolve.value) | |
160 | value = resolve.value | |
109 | 161 | if not value.nil? |
110 | 162 | return value |
111 | 163 | end |
125 | 177 | end |
126 | 178 | end |
127 | 179 | |
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 | |
130 | 202 | end |
131 | 203 | end |
0 | 0 | module Facter |
1 | 1 | module Util |
2 | ## | |
2 | ||
3 | 3 | # {Facter::Util::FileRead} is a utility module intended to provide easily |
4 | 4 | # mockable methods that delegate to simple file read methods. The intent is to |
5 | 5 | # avoid the need to execute the `cat` system command or `File.read` directly in |
6 | 6 | # Ruby, as mocking these behaviors can have wide-ranging effects. |
7 | 7 | # |
8 | 8 | # 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 ...') | |
10 | 10 | # |
11 | 11 | # @api public |
12 | 12 | module FileRead |
13 | ## | |
14 | 13 | # read returns the raw content of a file as a string. If the file does not |
15 | 14 | # exist, or the process does not have permission to read the file then nil is |
16 | 15 | # returned. |
17 | 16 | # |
18 | 17 | # @api public |
19 | 18 | # |
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. | |
23 | 24 | def self.read(path) |
24 | 25 | File.read(path) |
25 | 26 | 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 |
42 | 42 | interface_names = [] |
43 | 43 | |
44 | 44 | 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? | |
47 | 47 | end |
48 | 48 | end |
49 | 49 |
95 | 95 | # |
96 | 96 | # @return [String] the output of `ifconfig #{arguments} 2>/dev/null` or nil |
97 | 97 | 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(' ')}") | |
99 | 99 | end |
100 | 100 | ## |
101 | 101 | # get_ifconfig looks up the ifconfig binary |
109 | 109 | # hpux_netstat_in is a delegate method that allows us to stub netstat -in |
110 | 110 | # without stubbing exec. |
111 | 111 | def self.hpux_netstat_in |
112 | Facter::Util::Resolution.exec("/bin/netstat -in") | |
112 | Facter::Core::Execution.exec("/bin/netstat -in") | |
113 | 113 | end |
114 | 114 | |
115 | 115 | def self.get_infiniband_macaddress(interface) |
159 | 159 | end |
160 | 160 | |
161 | 161 | def self.hpux_lanscan |
162 | Facter::Util::Resolution.exec("/usr/sbin/lanscan") | |
162 | Facter::Core::Execution.exec("/usr/sbin/lanscan") | |
163 | 163 | end |
164 | 164 | |
165 | 165 | def self.get_output_for_interface_and_label(interface, label) |
4 | 4 | # Load facts on demand. |
5 | 5 | class Facter::Util::Loader |
6 | 6 | |
7 | def initialize | |
7 | def initialize(environment_vars = ENV) | |
8 | 8 | @loaded = [] |
9 | @valid_path = {} | |
9 | @environment_vars = environment_vars | |
10 | 10 | end |
11 | 11 | |
12 | 12 | # Load all resolutions for a single fact. |
13 | # | |
14 | # @api public | |
15 | # @param name [Symbol] | |
13 | 16 | def load(fact) |
14 | 17 | # Now load from the search path |
15 | 18 | shortname = fact.to_s.downcase |
16 | 19 | load_env(shortname) |
17 | 20 | |
18 | 21 | filename = shortname + ".rb" |
19 | search_path.each do |dir| | |
20 | # Load individual files | |
21 | file = File.join(dir, filename) | |
22 | 22 | |
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) | |
24 | 28 | |
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 | |
28 | 31 | end |
29 | 32 | end |
30 | 33 | |
31 | 34 | # Load all facts from all directories. |
35 | # | |
36 | # @api public | |
32 | 37 | def load_all |
33 | 38 | return if defined?(@loaded_all) |
34 | 39 | |
35 | 40 | load_env |
36 | 41 | |
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) | |
46 | 49 | end |
47 | 50 | end |
48 | 51 | end |
50 | 53 | @loaded_all = true |
51 | 54 | end |
52 | 55 | |
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>] | |
54 | 69 | 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) | |
59 | 75 | end |
60 | 76 | |
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) } | |
63 | 78 | |
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 | |
68 | 85 | end |
86 | ||
87 | search_paths.delete_if { |path| ! File.directory?(path) } | |
88 | ||
89 | search_paths.uniq | |
69 | 90 | 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? | |
77 | 91 | |
78 | 92 | private |
79 | 93 | |
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? | |
86 | 101 | end |
87 | 102 | |
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 | |
88 | 107 | def load_file(file) |
89 | 108 | return if @loaded.include? file |
109 | ||
90 | 110 | # We have to specify Kernel.load, because we have a load method. |
91 | 111 | begin |
92 | 112 | # Store the file path so we don't try to reload it |
96 | 116 | # Don't store the path if the file can't be loaded |
97 | 117 | # in case it's loadable later on. |
98 | 118 | @loaded.delete(file) |
99 | warn "Error loading fact #{file} #{detail}" | |
119 | Facter.log_exception(detail, "Error loading fact #{file}: #{detail.message}") | |
100 | 120 | end |
101 | 121 | end |
102 | 122 | |
104 | 124 | # all will be loaded. |
105 | 125 | def load_env(fact = nil) |
106 | 126 | # Load from the environment, if possible |
107 | ENV.each do |name, value| | |
127 | @environment_vars.each do |name, value| | |
108 | 128 | # Skip anything that doesn't match our regex. |
109 | 129 | next unless name =~ /^facter_?(\w+)$/i |
110 | 130 | env_name = $1 |
5 | 5 | ## |
6 | 6 | |
7 | 7 | module Facter::Util::Macosx |
8 | require 'thread' | |
9 | require 'facter/util/cfpropertylist' | |
8 | require 'cfpropertylist' | |
10 | 9 | require 'facter/util/resolution' |
11 | 10 | |
12 | 11 | Plist_Xml_Doctype = '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' |
15 | 14 | # by looking at the _name key of the _items dict for each _dataType |
16 | 15 | |
17 | 16 | 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}") | |
19 | 18 | end |
20 | 19 | |
21 | 20 | def self.intern_xml(xml) |
25 | 24 | xml.gsub!( bad_xml_doctype, Plist_Xml_Doctype ) |
26 | 25 | Facter.debug("Had to fix plist with incorrect DOCTYPE declaration") |
27 | 26 | end |
28 | plist = Facter::Util::CFPropertyList::List.new | |
27 | plist = CFPropertyList::List.new | |
29 | 28 | begin |
30 | 29 | 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 | |
33 | 32 | end |
34 | Facter::Util::CFPropertyList.native_types(plist.value) | |
33 | CFPropertyList.native_types(plist.value) | |
35 | 34 | end |
36 | 35 | |
37 | 36 | # Return an xml result, modified as we need it. |
57 | 56 | def self.sw_vers |
58 | 57 | ver = Hash.new |
59 | 58 | [ "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 | |
61 | 60 | end |
62 | 61 | productversion = ver["macosx_productVersion"] |
63 | 62 | if not productversion.nil? |
53 | 53 | name.each do |sysctlkey,facterkey| |
54 | 54 | Facter.add(facterkey) do |
55 | 55 | 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" | |
59 | 57 | end |
60 | 58 | end |
61 | 59 | end |
62 | 60 | |
63 | 61 | def self.prtdiag_sparc_find_system_info() |
64 | 62 | # 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') | |
66 | 64 | |
67 | 65 | # System Configuration: Sun Microsystems sun4u Sun SPARC Enterprise M3000 Server |
68 | 66 | if output and output =~ /^System Configuration:\s+(.+?)\s+(sun\d+\S+)\s+(.+)/ |
79 | 77 | end |
80 | 78 | |
81 | 79 | Facter.add('serialnumber') do |
82 | setcode do | |
83 | Facter::Util::Resolution.exec("/usr/sbin/sneep") | |
84 | end | |
80 | setcode "/usr/sbin/sneep" | |
85 | 81 | end |
86 | 82 | end |
87 | 83 |
34 | 34 | def self.vmstat_find_free_memory(args = []) |
35 | 35 | cmd = 'vmstat' |
36 | 36 | 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] | |
38 | 38 | if row =~ /^\s*\d+\s*\d+\s*\d+\s*\d+\s*(\d+)/ |
39 | 39 | memfree = $1 |
40 | 40 | end |
51 | 51 | pagesize = 0 |
52 | 52 | memspecfree = 0 |
53 | 53 | |
54 | vmstats = Facter::Util::Resolution.exec('vm_stat') | |
54 | vmstats = Facter::Core::Execution.exec('vm_stat') | |
55 | 55 | vmstats.each_line do |vmline| |
56 | 56 | case |
57 | 57 | when vmline =~ /page\ssize\sof\s(\d+)\sbytes/ |
66 | 66 | freemem = ( memfree + memspecfree ) * pagesize |
67 | 67 | end |
68 | 68 | |
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 | ||
69 | 77 | def self.mem_free(kernel = Facter.value(:kernel)) |
70 | 78 | output = mem_free_info(kernel) |
71 | 79 | scale_mem_free_value output, kernel |
79 | 87 | vmstat_find_free_memory(["-H"]) |
80 | 88 | when /Darwin/i |
81 | 89 | vmstat_darwin_find_free_memory() |
90 | when /AIX/i | |
91 | svmon_aix_find_free_memory() | |
82 | 92 | end |
83 | 93 | end |
84 | 94 | |
85 | 95 | def self.scale_mem_free_value (value, kernel) |
86 | 96 | case kernel |
87 | when /OpenBSD/i, /FreeBSD/i, /SunOS/i, /Dragonfly/i | |
97 | when /OpenBSD/i, /FreeBSD/i, /SunOS/i, /Dragonfly/i, /AIX/i | |
88 | 98 | value.to_f / 1024.0 |
89 | 99 | when /Darwin/i |
90 | 100 | value.to_f / 1024.0 / 1024.0 |
101 | 111 | def self.mem_size_info(kernel = Facter.value(:kernel)) |
102 | 112 | case kernel |
103 | 113 | 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") | |
109 | 119 | 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 | |
111 | 125 | end |
112 | 126 | end |
113 | 127 | |
115 | 129 | case kernel |
116 | 130 | when /OpenBSD/i, /FreeBSD/i, /Darwin/i, /Dragonfly/i |
117 | 131 | value.to_f / 1024.0 / 1024.0 |
132 | when /AIX/i | |
133 | value.to_f / 1024.0 | |
118 | 134 | else |
119 | 135 | value.to_f |
120 | 136 | end |
133 | 149 | def self.swap_info(kernel = Facter.value(:kernel)) |
134 | 150 | case kernel |
135 | 151 | 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 | |
137 | 153 | 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') | |
143 | 159 | when /SunOS/i |
144 | Facter::Util::Resolution.exec('/usr/sbin/swap -l') | |
160 | Facter::Core::Execution.exec('/usr/sbin/swap -l 2>/dev/null') | |
145 | 161 | end |
146 | 162 | end |
147 | 163 |
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 |
7 | 7 | when 'Linux' |
8 | 8 | ops = { |
9 | 9 | :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, | |
11 | 11 | :munge => nil, |
12 | 12 | } |
13 | 13 | when 'SunOS' |
14 | 14 | ops = { |
15 | 15 | :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, | |
17 | 17 | :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') } |
18 | 18 | } |
19 | 19 | when 'FreeBSD','NetBSD','OpenBSD', 'Darwin', 'GNU/kFreeBSD', 'DragonFly' |
20 | 20 | ops = { |
21 | 21 | :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, | |
23 | 23 | :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') } |
24 | 24 | } |
25 | 25 | 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 | ||
5 | 0 | module Facter |
6 | 1 | module Util |
7 | 2 | |
3 | # An external fact loader that doesn't load anything | |
4 | # This makes it possible to disable loading | |
5 | # of external facts | |
8 | 6 | class NothingLoader |
9 | ||
10 | 7 | def load(collection) |
11 | 8 | end |
12 | 9 | end |
54 | 54 | def results |
55 | 55 | parse_results |
56 | 56 | 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}") | |
60 | 58 | nil |
61 | 59 | end |
62 | 60 | |
67 | 65 | |
68 | 66 | module KeyValuePairOutputFormat |
69 | 67 | def self.parse(output) |
68 | return {} if output.nil? | |
69 | ||
70 | 70 | result = {} |
71 | 71 | re = /^(.+?)=(.+)$/ |
72 | 72 | output.each_line do |line| |
99 | 99 | end |
100 | 100 | |
101 | 101 | class JsonParser < Base |
102 | def results | |
102 | def parse_results | |
103 | 103 | if Facter.json? |
104 | 104 | JSON.load(content) |
105 | 105 | else |
115 | 115 | end |
116 | 116 | |
117 | 117 | 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 | |
120 | 126 | end |
121 | 127 | end |
122 | 128 | |
124 | 130 | if Facter::Util::Config.is_windows? |
125 | 131 | extension_matches?(filename, %w{bat cmd com exe}) && File.file?(filename) |
126 | 132 | else |
127 | File.executable?(filename) && File.file?(filename) | |
133 | File.executable?(filename) && File.file?(filename) && ! extension_matches?(filename, %w{bat cmd com exe}) | |
128 | 134 | end |
129 | 135 | end |
130 | 136 | |
131 | 137 | # Executes and parses the key value output of Powershell scripts |
132 | 138 | class PowershellParser < Base |
133 | 139 | # Returns a hash of facts from powershell output |
134 | def results | |
140 | def parse_results | |
135 | 141 | 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) | |
137 | 143 | end |
138 | 144 | end |
139 | 145 |
50 | 50 | # Writes the serialized object's plist to the specified filename. |
51 | 51 | def self.save_plist(obj, filename) |
52 | 52 | File.open(filename, 'wb') do |f| |
53 | f.write(obj.to_plist) | |
53 | f.write(Plist::Emit.dump(obj)) | |
54 | 54 | end |
55 | 55 | end |
56 | 56 |
46 | 46 | end |
47 | 47 | |
48 | 48 | ## |
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 | |
50 | 50 | # effort to make the processorX facts easier to test by stubbing only the |
51 | 51 | # behaviors we need to stub to get the output of the system command. |
52 | 52 | 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 | |
58 | 58 | # an effort to make the processorX facts easier to test. See also the |
59 | 59 | # {lsdev} method. |
60 | 60 | def self.lsattr(command="lsattr -El proc0 -a type") |
61 | Facter::Util::Resolution.exec(command) | |
61 | Facter::Core::Execution.exec(command) | |
62 | 62 | end |
63 | 63 | |
64 | 64 | ## |
86 | 86 | elsif line.match(/\d+\s+((?:PA-RISC|Intel).*processors.*)/) then |
87 | 87 | cpu = $1.to_s |
88 | 88 | cpu.sub!(/processors/, "processor") |
89 | elsif line.match(/\s+(Intel.*Processor.*)/) then | |
90 | cpu = $1.to_s | |
89 | 91 | end |
90 | 92 | end |
91 | 93 | end |
178 | 180 | private_class_method :read_unistd_h |
179 | 181 | |
180 | 182 | ## |
181 | # machinfo delegates directly to Facter::Util::Resolution.exec, as with lsdev | |
183 | # machinfo delegates directly to Facter::Core::Execution.exec, as with lsdev | |
182 | 184 | # above. |
183 | 185 | 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. | |
189 | 191 | 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. | |
195 | 197 | 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. | |
201 | 203 | 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. | |
207 | 209 | def self.getconf_cpu_chip_type(command="getconf CPU_CHIP_TYPE") |
208 | Facter::Util::Resolution.exec(command) | |
210 | Facter::Core::Execution.exec(command) | |
209 | 211 | end |
210 | 212 | |
211 | 213 | def self.enum_cpuinfo |
269 | 271 | processor_num = -1 |
270 | 272 | processor_list = [] |
271 | 273 | 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') | |
273 | 275 | if kstat |
274 | 276 | kstat.each_line do |l| |
275 | 277 | 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. | |
5 | 0 | require 'facter/util/confine' |
6 | 1 | require 'facter/util/config' |
2 | require 'facter/util/normalization' | |
3 | require 'facter/core/execution' | |
4 | require 'facter/core/resolvable' | |
5 | require 'facter/core/suitable' | |
7 | 6 | |
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 | |
9 | 22 | |
10 | class Facter::Util::Resolution | |
11 | attr_accessor :interpreter, :code, :name, :timeout | |
12 | attr_writer :value, :weight | |
23 | extend Facter::Core::Execution | |
13 | 24 | |
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 | |
28 | 32 | end |
29 | 33 | |
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 | |
69 | 36 | |
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 | |
81 | 43 | |
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 | |
198 | 48 | |
199 | 49 | # 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) | |
201 | 56 | @name = name |
57 | @fact = fact | |
202 | 58 | @confines = [] |
203 | 59 | @value = nil |
204 | 60 | @timeout = 0 |
205 | 61 | @weight = nil |
206 | 62 | end |
207 | 63 | |
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(':') | |
212 | 83 | else |
213 | @confines.length | |
84 | @last_evaluated = true | |
214 | 85 | end |
215 | 86 | end |
216 | 87 | |
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 | |
223 | 92 | |
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}" | |
235 | 107 | end |
236 | 108 | end |
237 | 109 | |
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. | |
246 | 112 | # |
247 | # Please see the Solaris zones fact for an example of how this feature may be | |
248 | # used. | |
113 | # @return [void] | |
249 | 114 | # |
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. | |
252 | 125 | # |
253 | 126 | # @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 | |
256 | 142 | end |
257 | 143 | |
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 | |
268 | 145 | |
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() | |
283 | 153 | 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 | |
327 | 154 | end |
328 | 155 | end |
92 | 92 | # |
93 | 93 | # @return [Hash] the parsed output of the zoneadm command |
94 | 94 | def refresh |
95 | @zoneadm_output = Facter::Util::Resolution.exec(zoneadm_cmd) | |
95 | @zoneadm_output = Facter::Core::Execution.execute(zoneadm_cmd, {:on_fail => nil}) | |
96 | 96 | parse! |
97 | 97 | end |
98 | 98 |
19 | 19 | private |
20 | 20 | |
21 | 21 | 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? | |
23 | 25 | output.chomp.split(" ").first.to_i |
24 | 26 | end |
25 | 27 | end |
26 | 28 | |
27 | 29 | 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 | |
29 | 32 | compute_uptime(Time.at(output.match(/\d+/)[0].to_i)) |
30 | 33 | end |
31 | 34 | end |
32 | 35 | |
33 | 36 | 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 | |
35 | 40 | up=0 |
36 | 41 | if output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+):(\d+)/ |
37 | 42 | # Regexp handles Solaris, AIX, HP-UX, and Tru64. |
0 | # A util module for facter containing helper methods | |
1 | 0 | module Facter |
2 | 1 | module Util |
2 | # A util module for facter containing helper methods | |
3 | 3 | module Values |
4 | 4 | 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 | |
5 | 71 | |
6 | 72 | def convert(value) |
7 | 73 | value = value.to_s if value.is_a?(Symbol) |
3 | 3 | ## |
4 | 4 | # virt_what is a delegating helper method intended to make it easier to stub |
5 | 5 | # the system call without affecting other calls to |
6 | # Facter::Util::Resolution.exec | |
6 | # Facter::Core::Execution.exec | |
7 | 7 | # |
8 | 8 | # Per https://bugzilla.redhat.com/show_bug.cgi?id=719611 when run as a |
9 | 9 | # non-root user the virt-what command may emit an error message on stdout, |
11 | 11 | # method ensures stderr is redirected and that error messages are stripped |
12 | 12 | # from stdout. |
13 | 13 | def self.virt_what(command = "virt-what") |
14 | command = Facter::Util::Resolution.which(command) | |
14 | command = Facter::Core::Execution.which(command) | |
15 | 15 | return unless command |
16 | 16 | |
17 | 17 | if Facter.value(:kernel) == 'windows' |
19 | 19 | else |
20 | 20 | redirected_cmd = "#{command} 2>/dev/null" |
21 | 21 | end |
22 | output = Facter::Util::Resolution.exec redirected_cmd | |
22 | output = Facter::Core::Execution.exec redirected_cmd | |
23 | 23 | output.gsub(/^virt-what: .*$/, '') if output |
24 | 24 | end |
25 | 25 | |
26 | 26 | ## |
27 | 27 | # 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 | |
29 | 29 | def self.lspci(command = "lspci 2>/dev/null") |
30 | Facter::Util::Resolution.exec command | |
30 | Facter::Core::Execution.exec command | |
31 | 31 | end |
32 | 32 | |
33 | 33 | def self.openvz? |
39 | 39 | return false unless self.openvz? |
40 | 40 | return false unless FileTest.exists?( '/proc/self/status' ) |
41 | 41 | |
42 | envid = Facter::Util::Resolution.exec( 'grep "envID" /proc/self/status' ) | |
42 | envid = Facter::Core::Execution.exec( 'grep "envID" /proc/self/status' ) | |
43 | 43 | if envid =~ /^envID:\s+0$/i |
44 | 44 | return 'openvzhn' |
45 | 45 | elsif envid =~ /^envID:\s+(\d+)$/i |
55 | 55 | |
56 | 56 | def self.zone? |
57 | 57 | return true if FileTest.directory?("/.SUNWnative") |
58 | z = Facter::Util::Resolution.exec("/sbin/zonename") | |
58 | z = Facter::Core::Execution.exec("/sbin/zonename") | |
59 | 59 | return false unless z |
60 | 60 | return z.chomp != 'global' |
61 | 61 | end |
91 | 91 | txt = if FileTest.exists?("/proc/cpuinfo") |
92 | 92 | File.read("/proc/cpuinfo") |
93 | 93 | 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") | |
95 | 95 | end |
96 | 96 | (txt =~ /QEMU Virtual CPU/) ? true : false |
97 | 97 | end |
122 | 122 | when "FreeBSD" then "/sbin" |
123 | 123 | when "GNU/kFreeBSD" then "/bin" |
124 | 124 | 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" | |
126 | 126 | end |
127 | 127 | |
128 | 128 | 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/ | |
130 | 130 | end |
131 | 131 | |
132 | 132 | def self.zlinux? |
1 | 1 | # |
2 | 2 | module Facter::Util::Vlans |
3 | 3 | 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 | |
9 | 7 | end |
10 | 8 | |
11 | 9 | 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] | |
17 | 15 | end |
18 | 16 | end |
17 | vlans.join(',') unless vlans.empty? | |
19 | 18 | end |
20 | ||
21 | vlans.join(',') | |
22 | 19 | end |
23 | 20 | 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 |
1 | 1 | # |
2 | 2 | module Facter::Util::Xendomains |
3 | 3 | 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') | |
5 | 5 | domains = xm_list.split("\n").reject { |line| line =~ /^(Name|Domain-0)/ } |
6 | 6 | domains.map { |line| line.split(/\s/)[0] }.join(',') |
7 | 7 | end |
0 | 0 | module Facter |
1 | 1 | if not defined? FACTERVERSION then |
2 | FACTERVERSION = '1.7.3' | |
2 | FACTERVERSION = '2.0.1' | |
3 | 3 | end |
4 | 4 | |
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. | |
8 | 6 | # |
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: | |
11 | 10 | # |
12 | 11 | # require 'facter/version' |
13 | 12 | # version = Facter.version |
14 | 13 | # |
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. | |
18 | 17 | # |
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. | |
25 | 24 | # |
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. | |
31 | 30 | # |
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. | |
37 | 36 | # |
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. | |
44 | 43 | # |
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: | |
47 | 46 | # |
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 | |
51 | 50 | # |
52 | 51 | # @api public |
53 | 52 | # |
61 | 60 | @facter_version ||= FACTERVERSION |
62 | 61 | end |
63 | 62 | |
63 | # Sets the Facter version | |
64 | # | |
65 | # @return [void] | |
66 | # | |
67 | # @api private | |
64 | 68 | def self.version=(version) |
65 | 69 | @facter_version = version |
66 | 70 | end |
67 | 71 | |
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 | |
70 | 73 | # same directory as this source code file. |
71 | 74 | # |
72 | 75 | # @api private |
73 | 76 | # |
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 | |
75 | 78 | # file does not exist. |
76 | 79 | def self.read_version_file(path) |
77 | 80 | if File.exists?(path) |
54 | 54 | Facter.add("virtual") do |
55 | 55 | confine :kernel => 'SunOS' |
56 | 56 | has_weight 10 |
57 | self.timeout = 6 | |
58 | ||
57 | 59 | setcode do |
58 | 60 | next "zone" if Facter::Util::Virtual.zone? |
59 | 61 | |
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') | |
64 | 63 | if output |
65 | 64 | lines = output.split("\n") |
66 | 65 | next "parallels" if lines.any? {|l| l =~ /Parallels/ } |
91 | 90 | confine :kernel => 'OpenBSD' |
92 | 91 | has_weight 10 |
93 | 92 | 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') | |
95 | 94 | if output |
96 | 95 | lines = output.split("\n") |
97 | 96 | next "parallels" if lines.any? {|l| l =~ /Parallels/ } |
132 | 131 | end |
133 | 132 | |
134 | 133 | # Parse dmidecode |
135 | output = Facter::Util::Resolution.exec('dmidecode') | |
134 | output = Facter::Core::Execution.exec('dmidecode 2> /dev/null') | |
136 | 135 | if output |
137 | 136 | lines = output.split("\n") |
138 | 137 | next "parallels" if lines.any? {|l| l =~ /Parallels/ } |
144 | 143 | next "ovirt" if lines.any? {|l| l =~ /Product Name: oVirt Node/ } |
145 | 144 | end |
146 | 145 | |
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 | |
147 | 157 | # 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") | |
149 | 159 | if output |
150 | 160 | mdata = output.match /(\S+)\s+(\S+)/ |
151 | 161 | next "#{mdata[1]}_#{mdata[2]}".downcase if mdata |
152 | 162 | end |
153 | ||
154 | # Default to 'physical' | |
155 | next 'physical' | |
156 | 163 | end |
157 | 164 | end |
158 | 165 | |
162 | 169 | require 'facter/util/wmi' |
163 | 170 | result = nil |
164 | 171 | 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 | ||
174 | 189 | break |
175 | 190 | end |
191 | result ||= "physical" | |
192 | ||
176 | 193 | result |
177 | 194 | end |
178 | 195 | end |
241 | 258 | confine :kernel => %w{Linux FreeBSD OpenBSD SunOS HP-UX Darwin GNU/kFreeBSD windows} |
242 | 259 | |
243 | 260 | 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} | |
245 | 262 | |
246 | 263 | if physical_types.include? Facter.value(:virtual) |
247 | 264 | "false" |
1 | 1 | |
2 | 2 | Facter.add('zfs_version') do |
3 | 3 | 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? | |
7 | 7 | end |
8 | 8 | end |
9 | 9 | end |
1 | 1 | |
2 | 2 | Facter.add('zpool_version') do |
3 | 3 | 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? | |
7 | 7 | end |
8 | 8 | end |
9 | 9 | end |
15 | 15 | |
16 | 16 | require 'facter/version' |
17 | 17 | |
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 | |
18 | 29 | 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 | |
20 | 32 | module Util; end |
21 | 33 | |
22 | 34 | require 'facter/util/fact' |
23 | 35 | require 'facter/util/collection' |
24 | require 'facter/util/monkey_patches' | |
25 | 36 | |
26 | 37 | include Comparable |
27 | 38 | include Enumerable |
28 | 39 | |
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 = "[0;32m" | |
43 | RESET = "[0m" | |
44 | @@debug = 0 | |
45 | @@timing = 0 | |
46 | @@messages = {} | |
47 | @@debug_messages = {} | |
40 | require 'facter/core/logging' | |
41 | extend Facter::Core::Logging | |
48 | 42 | |
49 | 43 | # module methods |
50 | 44 | |
45 | # Accessor for the collection object which holds all the facts | |
46 | # @return [Facter::Util::Collection] the collection of facts | |
47 | # | |
48 | # @api private | |
51 | 49 | def self.collection |
52 | 50 | unless defined?(@collection) and @collection |
53 | 51 | @collection = Facter::Util::Collection.new( |
57 | 55 | @collection |
58 | 56 | end |
59 | 57 | |
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 | |
93 | 61 | def self.json? |
94 | 62 | begin |
95 | 63 | require 'json' |
99 | 67 | end |
100 | 68 | end |
101 | 69 | |
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 | |
104 | 80 | def self.[](name) |
105 | 81 | collection.fact(name) |
106 | 82 | end |
107 | 83 | |
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 | |
126 | 159 | def self.add(name, options = {}, &block) |
127 | 160 | collection.add(name, options, &block) |
128 | 161 | end |
129 | 162 | |
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 | |
130 | 171 | def self.each |
131 | 172 | # Make sure all facts are loaded. |
132 | 173 | collection.load_all |
136 | 177 | end |
137 | 178 | end |
138 | 179 | |
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 | |
171 | 185 | def self.clear |
172 | 186 | Facter.flush |
173 | 187 | Facter.reset |
174 | 188 | end |
175 | 189 | |
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 | |
241 | 196 | def self.reset |
242 | 197 | @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 | |
246 | 206 | def self.loadfacts |
247 | 207 | collection.load_all |
248 | 208 | end |
249 | 209 | |
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 | |
253 | 218 | def self.search(*dirs) |
254 | 219 | @search_path += dirs |
255 | 220 | end |
256 | 221 | |
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 | |
258 | 227 | def self.search_path |
259 | 228 | @search_path.dup |
260 | 229 | 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 | |
261 | 260 | 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 | |
3 | 2 | . |
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" | |
45 | 79 | Luke Kanies |
46 | .SH COPYRIGHT | |
47 | .sp | |
48 | Copyright (c) 2012 Puppet Labs, Inc Licensed under the Apache 2.0 | |
49 | license | |
50 | 80 | . |
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 | 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 |
7 | 7 | Facter::Application.parse(['architecture', 'kernel']).should == {} |
8 | 8 | end |
9 | 9 | |
10 | [:yaml, :json, :trace].each do |option_key| | |
10 | [:yaml, :json].each do |option_key| | |
11 | 11 | it "sets options[:#{option_key}] when given --#{option_key}" do |
12 | 12 | options = Facter::Application.parse(["--#{option_key}"]) |
13 | 13 | options[option_key].should be_true |
28 | 28 | Facter.should be_debugging |
29 | 29 | Facter.debugging(false) |
30 | 30 | 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) | |
31 | 38 | end |
32 | 39 | |
33 | 40 | ['-t', '--timing'].each do |option| |
51 | 58 | Facter::Application.parse(argv) |
52 | 59 | argv.should == ['uptime', 'virtual'] |
53 | 60 | 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 | |
54 | 99 | end |
55 | 100 | end |
0 | 0 | #! /usr/bin/env ruby |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | require 'facter/util/architecture' | |
3 | 4 | |
4 | 5 | describe "Architecture fact" do |
5 | 6 | |
51 | 52 | end |
52 | 53 | end |
53 | 54 | |
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 | ||
54 | 62 | 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 |
23 | 23 | let(:dnsdomain_command) { "dnsdomainname 2> /dev/null" } |
24 | 24 | |
25 | 25 | 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) | |
27 | 27 | end |
28 | 28 | |
29 | 29 | 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) | |
31 | 31 | end |
32 | 32 | |
33 | 33 | before do |
196 | 196 | it "should return nil" do |
197 | 197 | expects_dnsdomains([nil]) |
198 | 198 | |
199 | Facter::Util::Resolution.stubs(:exec).with(hostname_command).returns('sometest') | |
199 | Facter::Core::Execution.stubs(:exec).with(hostname_command).returns('sometest') | |
200 | 200 | FileTest.stubs(:exists?).with("/etc/resolv.conf").returns(false) |
201 | 201 | |
202 | 202 | Facter.fact(:domain).value.should be_nil |
287 | 287 | |
288 | 288 | describe scenario[:scenario] do |
289 | 289 | 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]) | |
292 | 292 | resolv_conf_contains( |
293 | 293 | "search #{scenario[:resolve_search]}", |
294 | 294 | "domain #{scenario[:resolve_domain]}" |
135 | 135 | end |
136 | 136 | |
137 | 137 | it "should return nil if open fails" do |
138 | Facter.stubs(:warn) # do not pollute test output | |
138 | 139 | Facter.expects(:warn).with('Could not retrieve ec2 metadata: host unreachable') |
139 | Facter::Util::Resolution.any_instance.stubs(:warn) # do not pollute test output | |
140 | 140 | |
141 | 141 | Object.any_instance.expects(:open). |
142 | 142 | with("#{api_prefix}/2008-02-01/meta-data/"). |
2 | 2 | require 'spec_helper' |
3 | 3 | |
4 | 4 | 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 | |
10 | 6 | Facter.collection.should equal(Facter.collection) |
11 | 7 | end |
12 | 8 | |
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 | |
16 | 66 | end |
17 | 67 | |
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 | |
62 | 69 | Facter.collection.stubs(:load_all) |
63 | 70 | Facter.collection.stubs(:each).yields "foo" |
64 | 71 | result = [] |
66 | 73 | result.should == %w{foo} |
67 | 74 | end |
68 | 75 | |
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! } | |
75 | 78 | |
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 | |
256 | 80 | Facter.search "/my/dir" |
257 | 81 | end |
258 | 82 | |
259 | it "should allow registration of multiple directories" do | |
83 | it "allows registration of multiple directories" do | |
260 | 84 | Facter.search "/my/dir", "/other/dir" |
261 | 85 | end |
262 | 86 | |
263 | it "should return all registered directories when asked" do | |
87 | it "returns all registered directories when asked" do | |
264 | 88 | Facter.search "/my/dir", "/other/dir" |
265 | 89 | Facter.search_path.should == %w{/my/dir /other/dir} |
266 | 90 | end |
267 | 91 | 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 | |
268 | 107 | end |
0 | #! /usr/bin/env ruby -S rspec | |
0 | #! /usr/bin/env ruby | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | |
13 | 13 | before :each do |
14 | 14 | Facter.fact(:kernel).stubs(:value).returns('Linux') |
15 | 15 | fixture_data = my_fixture_read('linux') |
16 | Facter::Util::Resolution.expects(:exec) \ | |
16 | Facter::Core::Execution.expects(:exec) \ | |
17 | 17 | .with('cat /proc/filesystems 2> /dev/null').returns(fixture_data) |
18 | 18 | Facter.collection.internal_loader.load(:filesystems) |
19 | 19 | end |
5 | 5 | describe "Hardwareisa fact" do |
6 | 6 | it "should match uname -p on Linux" do |
7 | 7 | 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") | |
9 | 9 | |
10 | 10 | Facter.fact(:hardwareisa).value.should == "Inky" |
11 | 11 | end |
12 | 12 | |
13 | 13 | it "should match uname -p on Darwin" do |
14 | 14 | 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") | |
16 | 16 | |
17 | 17 | Facter.fact(:hardwareisa).value.should == "Blinky" |
18 | 18 | end |
19 | 19 | |
20 | 20 | it "should match uname -p on SunOS" do |
21 | 21 | 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") | |
23 | 23 | |
24 | 24 | Facter.fact(:hardwareisa).value.should == "Pinky" |
25 | 25 | end |
26 | 26 | |
27 | 27 | it "should match uname -p on FreeBSD" do |
28 | 28 | 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") | |
30 | 30 | |
31 | 31 | Facter.fact(:hardwareisa).value.should == "Clyde" |
32 | 32 | end |
33 | 33 | |
34 | 34 | it "should match uname -m on HP-UX" do |
35 | 35 | 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") | |
37 | 37 | |
38 | 38 | Facter.fact(:hardwareisa).value.should == "Pac-Man" |
39 | 39 | end |
5 | 5 | describe "Hardwaremodel fact" do |
6 | 6 | it "should match uname -m by default" do |
7 | 7 | 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") | |
9 | 9 | |
10 | 10 | Facter.fact(:hardwaremodel).value.should == "Inky" |
11 | 11 | end |
22 | 22 | Facter::Util::WMI.expects(:execquery).returns([cpu]) |
23 | 23 | |
24 | 24 | Facter.fact(:hardwaremodel).value.should == "i486" |
25 | end | |
25 | end | |
26 | 26 | |
27 | 27 | it "should detect i686" do |
28 | 28 | cpu = mock('cpu', :Architecture => 0, :Level => 6) |
10 | 10 | end |
11 | 11 | |
12 | 12 | 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 | |
14 | 14 | Facter.fact(:hostname).value.should be_nil |
15 | 15 | end |
16 | 16 | |
17 | 17 | 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') | |
19 | 19 | Facter.fact(:hostname).value.should == "host1" |
20 | 20 | end |
21 | 21 | |
22 | 22 | 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') | |
24 | 24 | Facter.fact(:hostname).value.should == "host1" |
25 | 25 | end |
26 | 26 | end |
32 | 32 | end |
33 | 33 | |
34 | 34 | 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") | |
36 | 36 | Facter.fact(:hostname).value.should == "host1" |
37 | 37 | end |
38 | 38 | end |
11 | 11 | it "should return the current user" do |
12 | 12 | given_a_configuration_of(:is_windows => k == 'windows') |
13 | 13 | 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' | |
15 | 15 | |
16 | 16 | Facter.fact(:id).value.should == 'bar' |
17 | 17 | end |
20 | 20 | |
21 | 21 | it "should return the current user on Solaris" do |
22 | 22 | 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' | |
25 | 25 | |
26 | 26 | Facter.fact(:id).value.should == 'bar' |
27 | 27 | end |
30 | 30 | Facter::Util::IP.stubs(:get_interfaces).returns ["Local Area Connection", "Loopback \"Pseudo-Interface\" (#1)"] |
31 | 31 | Facter.fact(:interfaces).value.should == %{Local_Area_Connection,Loopback__Pseudo_Interface____1_} |
32 | 32 | 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 | |
33 | 43 | end |
34 | 44 | |
35 | 45 | describe "Netmask handling on Linux" do |
14 | 14 | end |
15 | 15 | |
16 | 16 | 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') | |
18 | 18 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig") |
19 | 19 | Facter::Util::IP.stubs(:exec_ifconfig).with(["-a"]). |
20 | 20 | returns(ifconfig_fixture('darwin_ifconfig_all_with_multiple_interfaces')) |
23 | 23 | end |
24 | 24 | |
25 | 25 | 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') | |
27 | 27 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig") |
28 | 28 | Facter::Util::IP.stubs(:exec_ifconfig).with(["2>/dev/null"]). |
29 | 29 | returns(ifconfig_fixture('linux_ifconfig_all_with_multiple_interfaces')) |
32 | 32 | end |
33 | 33 | |
34 | 34 | 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') | |
36 | 36 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig") |
37 | 37 | Facter::Util::IP.stubs(:exec_ifconfig).with(["2>/dev/null"]). |
38 | 38 | returns(ifconfig_fixture('ifconfig_net_tools_1.60.txt')) |
41 | 41 | end |
42 | 42 | |
43 | 43 | 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') | |
45 | 45 | Facter::Util::IP.stubs(:get_ifconfig).returns("/usr/sbin/ifconfig") |
46 | 46 | Facter::Util::IP.stubs(:exec_ifconfig).with(["-a"]). |
47 | 47 | returns(ifconfig_fixture('sunos_ifconfig_all_with_multiple_interfaces')) |
59 | 59 | context "when you have no active network adapter" do |
60 | 60 | it "should return nil if there are no active (or any) network adapters" do |
61 | 61 | 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) | |
63 | 63 | |
64 | 64 | Facter.value(:ipaddress).should == nil |
65 | 65 | end |
0 | #! /usr/bin/env ruby -S rspec | |
0 | #! /usr/bin/env ruby | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | |
15 | 15 | describe "on everything else" do |
16 | 16 | it "should return the kernel using 'uname -s'" do |
17 | 17 | 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") | |
19 | 19 | |
20 | 20 | Facter.fact(:kernel).value.should == 'test_kernel' |
21 | 21 | end |
0 | #! /usr/bin/env ruby -S rspec | |
0 | #! /usr/bin/env ruby | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | |
4 | describe "Kernel release fact" do | |
4 | describe "Kernel release fact" do | |
5 | 5 | |
6 | describe "on Windows" do | |
7 | before do | |
6 | describe "on Windows" do | |
7 | before do | |
8 | 8 | Facter.fact(:kernel).stubs(:value).returns("windows") |
9 | 9 | require 'facter/util/wmi' |
10 | 10 | version = stubs 'version' |
11 | 11 | version.stubs(:Version).returns("test_kernel") |
12 | 12 | Facter::Util::WMI.stubs(:execquery).with("SELECT Version from Win32_OperatingSystem").returns([version]) |
13 | 13 | end |
14 | ||
15 | it "should return the kernel release" do | |
14 | ||
15 | it "should return the kernel release" do | |
16 | 16 | Facter.fact(:kernelrelease).value.should == "test_kernel" |
17 | end | |
18 | end | |
17 | end | |
18 | end | |
19 | 19 | |
20 | describe "on AIX" do | |
21 | before do | |
20 | describe "on AIX" do | |
21 | before do | |
22 | 22 | 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 | |
27 | 27 | Facter.fact(:kernelrelease).value.should == "test_kernel" |
28 | end | |
29 | end | |
28 | end | |
29 | end | |
30 | 30 | |
31 | 31 | describe "on HP-UX" do |
32 | 32 | 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 | ||
37 | 37 | it "should remove preceding letters" do |
38 | 38 | Facter.fact(:kernelrelease).value.should == "11.31" |
39 | end | |
40 | end | |
39 | end | |
40 | end | |
41 | 41 | |
42 | describe "on everything else" do | |
42 | describe "on everything else" do | |
43 | 43 | before do |
44 | 44 | 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 | ||
48 | 48 | it "should return the kernel release" do |
49 | 49 | 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 | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | |
4 | 4 | describe "Kernel version fact" do |
5 | ||
5 | ||
6 | 6 | describe "on Solaris/Sun OS" do |
7 | 7 | before do |
8 | 8 | 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 | |
13 | 13 | Facter.fact(:kernelversion).value.should == "1.234.5" |
14 | end | |
14 | end | |
15 | 15 | end |
16 | ||
16 | ||
17 | 17 | describe "on everything else" do |
18 | before do | |
18 | before do | |
19 | 19 | Facter.fact(:kernel).stubs(:value).returns('linux') |
20 | 20 | 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 | |
24 | 24 | Facter.fact(:kernelversion).value.should == "1.23.4" |
25 | end | |
25 | end | |
26 | 26 | end |
27 | end | |
27 | end | |
28 | 28 | |
29 | 29 | |
30 | 30 |
14 | 14 | before :each do |
15 | 15 | # For virtinfo documentation: |
16 | 16 | # 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"). | |
18 | 19 | returns(ldom_fixtures('ldom_v1')) |
19 | 20 | Facter.collection.internal_loader.load(:ldom) |
20 | 21 | end |
62 | 63 | |
63 | 64 | describe "when running on non ldom hardware" do |
64 | 65 | before :each do |
65 | Facter::Util::Resolution.stubs(:exec).with("virtinfo -ap").returns(nil) | |
66 | Facter::Core::Execution.stubs(:which).with("virtinfo").returns(nil) | |
66 | 67 | Facter.collection.internal_loader.load(:ldom) |
67 | 68 | end |
68 | 69 |
9 | 9 | Facter.fact(:kernel).stubs(:value).returns kernel |
10 | 10 | end |
11 | 11 | |
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' | |
15 | 15 | end |
16 | 16 | |
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 | |
20 | 20 | end |
21 | 21 | end |
22 | 22 | end |
9 | 9 | Facter.fact(:kernel).stubs(:value).returns kernel |
10 | 10 | end |
11 | 11 | |
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' | |
15 | 16 | end |
16 | 17 | |
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 | |
20 | 21 | end |
21 | 22 | end |
22 | 23 | end |
23 | ||
24 | 24 | end |
9 | 9 | Facter.fact(:kernel).stubs(:value).returns kernel |
10 | 10 | end |
11 | 11 | |
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' | |
15 | 15 | end |
16 | 16 | |
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 | |
20 | 20 | end |
21 | 21 | end |
22 | 22 | end |
10 | 10 | end |
11 | 11 | |
12 | 12 | 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' | |
14 | 14 | Facter.fact(:lsbdistrelease).value.should == '2.1' |
15 | 15 | end |
16 | 16 | |
17 | 17 | 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 | |
19 | 19 | Facter.fact(:lsbdistrelease).value.should be_nil |
20 | 20 | end |
21 | 21 | end |
9 | 9 | Facter.fact(:kernel).stubs(:value).returns kernel |
10 | 10 | end |
11 | 11 | |
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' | |
15 | 15 | end |
16 | 16 | |
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 | |
20 | 20 | end |
21 | 21 | end |
22 | 22 | end |
47 | 47 | before(:each) do |
48 | 48 | Facter.clear |
49 | 49 | 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') | |
51 | 51 | sample_vm_stat = <<VMSTAT |
52 | 52 | Mach Virtual Memory Statistics: (page size of 4096 bytes) |
53 | 53 | Pages free: 28430. |
63 | 63 | Pageouts: 1384383. |
64 | 64 | Object cache: 14 hits of 2619925 lookups (0% hit rate) |
65 | 65 | 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)") | |
68 | 68 | |
69 | 69 | Facter.collection.internal_loader.load(:memory) |
70 | 70 | end |
153 | 153 | /dev/hd6 10, 2 512MB 508MB |
154 | 154 | SWAP |
155 | 155 | |
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) | |
157 | 171 | |
158 | 172 | Facter.collection.internal_loader.load(:memory) |
159 | 173 | end |
189 | 203 | Facter.fact(:swapfree_mb).value.should == "508.00" |
190 | 204 | end |
191 | 205 | 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 | ||
192 | 215 | end |
193 | 216 | |
194 | 217 | |
198 | 221 | Facter.fact(:kernel).stubs(:value).returns("OpenBSD") |
199 | 222 | |
200 | 223 | 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) | |
202 | 225 | |
203 | 226 | vmstat = <<EOS |
204 | 227 | procs memory page disks traps cpu |
205 | 228 | r b w avm fre flt re pi po fr sr cd0 sd0 int sys cs us sy id |
206 | 229 | 0 0 0 11048 181028 39 0 0 0 0 0 0 1 3 90 17 0 0 100 |
207 | 230 | 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') | |
211 | 234 | |
212 | 235 | Facter.collection.internal_loader.load(:memory) |
213 | 236 | end |
243 | 266 | System Peripherals (Software Nodes): |
244 | 267 | |
245 | 268 | 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 | |
247 | 270 | |
248 | 271 | vmstat_lines = <<VMSTAT |
249 | 272 | kthr memory page disk faults cpu |
250 | 273 | r b w swap free re mf pi po fr de sr s0 s3 -- -- in sy cs us sy id |
251 | 274 | 0 0 0 1154552 476224 8 19 0 0 0 0 0 0 0 0 0 460 294 236 1 2 97 |
252 | 275 | VMSTAT |
253 | Facter::Util::Resolution.stubs(:exec).with('vmstat').returns(vmstat_lines) | |
276 | Facter::Core::Execution.stubs(:exec).with('vmstat').returns(vmstat_lines) | |
254 | 277 | end |
255 | 278 | |
256 | 279 | after(:each) do |
263 | 286 | swapfile dev swaplo blocks free |
264 | 287 | /dev/swap 4294967295,4294967295 16 2097136 2097136 |
265 | 288 | 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 | |
267 | 290 | |
268 | 291 | Facter.collection.internal_loader.load(:memory) |
269 | 292 | end |
292 | 315 | /dev/swap 4294967295,4294967295 16 2097136 2097136 |
293 | 316 | /dev/swap2 4294967295,4294967295 16 2097136 2097136 |
294 | 317 | 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 | |
296 | 319 | Facter.collection.internal_loader.load(:memory) |
297 | 320 | end |
298 | 321 | |
315 | 338 | |
316 | 339 | describe "when no swap exists" do |
317 | 340 | 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 "" | |
319 | 342 | |
320 | 343 | Facter.collection.internal_loader.load(:memory) |
321 | 344 | end |
344 | 367 | Facter.fact(:kernel).stubs(:value).returns("dragonfly") |
345 | 368 | |
346 | 369 | 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") | |
351 | 374 | |
352 | 375 | vmstat = <<EOS |
353 | 376 | procs memory page disks faults cpu |
354 | 377 | r b w avm fre flt re pi po fr sr da0 sg1 in sy cs us sy id |
355 | 378 | 0 0 0 33152 13940 1902120 2198 53119 11642 6544597 5460994 0 0 6148243 7087927 3484264 0 1 9 |
356 | 379 | 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') | |
360 | 383 | |
361 | 384 | Facter.collection.internal_loader.load(:memory) |
362 | 385 | end |
392 | 415 | r b w avm fre flt re pi po fr sr da0 cd0 in sy cs us sy id |
393 | 416 | 1 0 0 207600 656640 10 0 0 0 13 0 0 0 51 164 257 0 1 99 |
394 | 417 | 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 | |
396 | 419 | sample_physmem = <<PHYSMEM |
397 | 420 | 1056276480 |
398 | 421 | 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 | |
400 | 423 | end |
401 | 424 | |
402 | 425 | after(:each) do |
408 | 431 | sample_swapinfo = <<SWAP |
409 | 432 | Device 1K-blocks Used Avail Capacity |
410 | 433 | 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 | |
412 | 435 | |
413 | 436 | Facter.collection.internal_loader.load(:memory) |
414 | 437 | end |
436 | 459 | Device 1K-blocks Used Avail Capacity |
437 | 460 | /dev/da0p3 2048540 0 1048540 0% |
438 | 461 | 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 | |
440 | 463 | |
441 | 464 | Facter.collection.internal_loader.load(:memory) |
442 | 465 | end |
465 | 488 | /dev/da0p3 2048540 0 1048540 0% |
466 | 489 | /dev/da0p4 3048540 0 1048540 0% |
467 | 490 | 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 | |
469 | 492 | |
470 | 493 | Facter.collection.internal_loader.load(:memory) |
471 | 494 | end |
510 | 533 | Facter::Util::WMI.stubs(:execquery).returns([computer]) |
511 | 534 | |
512 | 535 | 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 | |
521 | 537 | end |
522 | 538 | end |
24 | 24 | example_behavior_for "netmask from ifconfig output", |
25 | 25 | "Ubuntu 12.04", "255.255.255.255", |
26 | 26 | "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" | |
27 | 36 | end |
28 | 37 | |
29 | 38 | context "on Windows" do |
39 | 39 | "OpenIndiana" => "oi_151a", |
40 | 40 | }.each_pair do |distribution, string| |
41 | 41 | 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) | |
43 | 43 | Facter.fact(:operatingsystem).value.should == distribution |
44 | 44 | end |
45 | 45 | end |
12 | 12 | end |
13 | 13 | end |
14 | 14 | 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 | |
15 | 37 | end |
50 | 50 | Facter.fact(:kernelrelease).stubs(:value).returns("4.1.0") |
51 | 51 | Facter.fact(:operatingsystem).stubs(:value).returns("VMwareESX") |
52 | 52 | |
53 | Facter::Util::Resolution.stubs(:exec).with('vmware -v').returns('foo') | |
53 | Facter::Core::Execution.stubs(:exec).with('vmware -v').returns('foo') | |
54 | 54 | |
55 | 55 | Facter.fact(:operatingsystemrelease).value |
56 | 56 | end |
118 | 118 | 'Solaris 10 10/09 s10x_u8wos_08a X86' => '10_u8', |
119 | 119 | 'Oracle Solaris 10 9/10 s10x_u9wos_14a X86' => '10_u9', |
120 | 120 | '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' | |
121 | 123 | }.each do |fakeinput,expected_output| |
122 | 124 | it "should be able to parse a release of #{fakeinput}" do |
123 | 125 | Facter::Util::FileRead.stubs(:read).with('/etc/release').returns fakeinput |
126 | 128 | end |
127 | 129 | |
128 | 130 | context "malformed /etc/release files" do |
129 | before :each do | |
130 | Facter::Util::Resolution.any_instance.stubs(:warn) | |
131 | end | |
132 | 131 | it "should fallback to the kernelrelease fact if /etc/release is empty" do |
133 | 132 | Facter::Util::FileRead.stubs(:read).with('/etc/release'). |
134 | 133 | raises EOFError |
134 | Facter.expects(:warn).with(regexp_matches(/Could not retrieve fact='operatingsystemrelease'.*EOFError/)) | |
135 | 135 | Facter.fact(:operatingsystemrelease).value. |
136 | 136 | should == Facter.fact(:kernelrelease).value |
137 | 137 | end |
139 | 139 | it "should fallback to the kernelrelease fact if /etc/release is not present" do |
140 | 140 | Facter::Util::FileRead.stubs(:read).with('/etc/release'). |
141 | 141 | raises Errno::ENOENT |
142 | Facter.expects(:warn).with(regexp_matches(/Could not retrieve fact='operatingsystemrelease'.*No such file or directory/)) | |
142 | 143 | Facter.fact(:operatingsystemrelease).value. |
143 | 144 | should == Facter.fact(:kernelrelease).value |
144 | 145 | end |
152 | 153 | end |
153 | 154 | end |
154 | 155 | |
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 | ||
155 | 215 | 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"'} | |
157 | 217 | before :each do |
158 | 218 | Facter.fact(:kernel).stubs(:value).returns("Linux") |
159 | 219 | Facter.fact(:operatingsystem).stubs(:value).returns("Ubuntu") |
160 | 220 | end |
161 | 221 | |
162 | 222 | 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) | |
164 | 224 | Facter.fact(:operatingsystemrelease).value.should == "10.04" |
165 | 225 | end |
166 | 226 | end |
11 | 11 | |
12 | 12 | it "should return one physical CPU" do |
13 | 13 | 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") | |
15 | 15 | |
16 | Facter.fact(:physicalprocessorcount).value.should == 1 | |
16 | Facter.fact(:physicalprocessorcount).value.should == "1" | |
17 | 17 | end |
18 | 18 | |
19 | 19 | it "should return four physical CPUs" do |
24 | 24 | /sys/devices/system/cpu/cpu3/topology/physical_package_id |
25 | 25 | }) |
26 | 26 | |
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") | |
31 | 31 | |
32 | Facter.fact(:physicalprocessorcount).value.should == 4 | |
32 | Facter.fact(:physicalprocessorcount).value.should == "4" | |
33 | 33 | end |
34 | 34 | end |
35 | 35 | |
42 | 42 | Facter::Util::WMI.expects(:execquery).with("select Name from Win32_Processor").returns(ole) |
43 | 43 | ole.stubs(:Count).returns(4) |
44 | 44 | |
45 | Facter.fact(:physicalprocessorcount).value.should == 4 | |
45 | Facter.fact(:physicalprocessorcount).value.should == "4" | |
46 | 46 | end |
47 | 47 | end |
48 | 48 | |
57 | 57 | Facter.fact(:kernel).stubs(:value).returns(:sunos) |
58 | 58 | Facter.stubs(:value).with(:kernelrelease).returns(release) |
59 | 59 | |
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") | |
61 | 61 | Facter.fact(:physicalprocessorcount).value.should == "1" |
62 | 62 | end |
63 | 63 | end |
67 | 67 | Facter.fact(:kernel).stubs(:value).returns(:sunos) |
68 | 68 | Facter.stubs(:value).with(:kernelrelease).returns(release) |
69 | 69 | |
70 | Facter::Util::Resolution.expects(:exec).with("/usr/sbin/psrinfo").returns(psrinfo) | |
70 | Facter::Core::Execution.expects(:exec).with("/usr/sbin/psrinfo").returns(psrinfo) | |
71 | 71 | Facter.fact(:physicalprocessorcount).value.should == "2" |
72 | 72 | end |
73 | 73 | end |
1 | 1 | |
2 | 2 | require 'facter/util/processor' |
3 | 3 | require 'spec_helper' |
4 | ||
5 | def cpuinfo_fixture(filename) | |
6 | File.open(fixtures('cpuinfo', filename)).readlines | |
7 | end | |
4 | require 'facter_spec/cpuinfo' | |
8 | 5 | |
9 | 6 | describe "Processor facts" do |
10 | 7 | describe "on Windows" do |
67 | 64 | end |
68 | 65 | end |
69 | 66 | |
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 | ||
89 | 179 | |
90 | 180 | describe "on Unixes" do |
91 | 181 | before :each do |
92 | 182 | Facter.collection.internal_loader.load(:processor) |
93 | 183 | end |
94 | 184 | |
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 | ||
177 | 185 | it "should be 2 on dual-processor Darwin box" do |
178 | 186 | 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') | |
180 | 188 | |
181 | 189 | Facter.fact(:processorcount).value.should == "2" |
182 | 190 | end |
183 | 191 | |
184 | 192 | it "should be 2 on dual-processor OpenBSD box" do |
185 | 193 | 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') | |
187 | 195 | |
188 | 196 | Facter.fact(:processorcount).value.should == "2" |
189 | 197 | end |
190 | 198 | |
191 | 199 | it "should be 2 on dual-processor FreeBSD box" do |
192 | 200 | 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') | |
194 | 202 | |
195 | 203 | Facter.fact(:processorcount).value.should == "2" |
196 | 204 | end |
197 | 205 | |
198 | 206 | it "should print the correct CPU Model on FreeBSD" do |
199 | 207 | 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') | |
201 | 209 | |
202 | 210 | Facter.fact(:processor).value.should == "SomeVendor CPU 3GHz" |
203 | 211 | end |
204 | ||
205 | 212 | |
206 | 213 | it "should be 2 on dual-processor DragonFly box" do |
207 | 214 | 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') | |
209 | 216 | |
210 | 217 | Facter.fact(:processorcount).value.should == "2" |
211 | 218 | 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 | |
284 | 239 | Facter.stubs(:value).with(:kernelrelease).returns(release) |
285 | 240 | |
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' | |
289 | 256 | end |
290 | 257 | end |
291 | 258 | end |
332 | 299 | examples << [File.read(fixtures('hpux','machinfo','ia64-rx6600')), "Intel(R) Itanium 2 9100 series processor (1.59 GHz, 18 MB)"] |
333 | 300 | examples << [File.read(fixtures('hpux','machinfo','ia64-rx8640')), "Intel(R) Itanium 2 9100 series"] |
334 | 301 | 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)"] | |
335 | 304 | examples |
336 | 305 | end |
337 | 306 |
95 | 95 | |
96 | 96 | Facter.fact(:selinux_config_policy).value.should == "targeted" |
97 | 97 | 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 | |
104 | 98 | end |
105 | 99 | |
106 | 100 | 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) | |
108 | 102 | end |
109 | 103 | |
110 | 104 | def mounts_does_not_exist |
113 | 107 | |
114 | 108 | def mounts_contains(*lines) |
115 | 109 | 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")) | |
117 | 111 | end |
118 | 112 | |
119 | 113 | end |
0 | #! /usr/bin/env ruby -S rspec | |
0 | #! /usr/bin/env ruby | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | require 'facter/ssh' |
21 | 21 | # fingerprints extracted from ssh-keygen -r '' -f /etc/ssh/ssh_host_dsa_key.pub |
22 | 22 | { '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" ], |
23 | 23 | '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"] | |
25 | 26 | }.each_pair do |fact, data| |
26 | 27 | describe "#{fact}" do |
27 | 28 | let(:filename) { data[0] } |
5 | 5 | describe "Uniqueid fact" do |
6 | 6 | it "should match hostid on Solaris" do |
7 | 7 | 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") | |
9 | 9 | |
10 | 10 | Facter.fact(:uniqueid).value.should == "Larry" |
11 | 11 | end |
12 | 12 | |
13 | 13 | it "should match hostid on Linux" do |
14 | 14 | 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") | |
16 | 16 | |
17 | 17 | Facter.fact(:uniqueid).value.should == "Curly" |
18 | 18 | end |
19 | 19 | |
20 | 20 | it "should match hostid on AIX" do |
21 | 21 | 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") | |
23 | 23 | |
24 | 24 | Facter.fact(:uniqueid).value.should == "Moe" |
25 | 25 | end |
26 | 26 | end |
27 | 27 | |
28 | 28 | 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 | |
34 | 33 | 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'}) | |
46 | 37 | |
47 | 38 | 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) | |
65 | 39 | end |
66 | 40 | |
67 | 41 | describe "and a block is provided" do |
68 | 42 | it "should use the block to add a resolution to the fact" do |
69 | 43 | fact = mock 'fact' |
44 | fact.stubs(:extract_ldapname_option!) | |
70 | 45 | Facter::Util::Fact.expects(:new).returns fact |
71 | 46 | |
72 | 47 | fact.expects(:add) |
75 | 50 | end |
76 | 51 | |
77 | 52 | 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 { | |
79 | 55 | collection.add('yay') do |
80 | raise | |
81 | setcode { 'yay' } | |
56 | raise "kaboom!" | |
82 | 57 | 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!" | |
85 | 87 | end |
86 | 88 | end |
87 | 89 | end |
208 | 210 | end |
209 | 211 | |
210 | 212 | 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) } | |
212 | 215 | |
213 | 216 | it "loads when a specific fact is requested" do |
214 | 217 | collection.fact(:test_fact).value.should == "fact value" |
223 | 226 | collection.each { |fact_name, fact_value| facts << [fact_name, fact_value] } |
224 | 227 | |
225 | 228 | 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 | |
226 | 244 | end |
227 | 245 | end |
228 | 246 |
33 | 33 | end |
34 | 34 | |
35 | 35 | describe "external_facts_dirs" do |
36 | before :each do | |
37 | Facter::Util::Root.stubs(:root?).returns(true) | |
38 | end | |
39 | ||
36 | 40 | it "should return the default value for linux" do |
37 | 41 | Facter::Util::Config.stubs(:is_windows?).returns(false) |
38 | 42 | Facter::Util::Config.stubs(:windows_data_dir).returns(nil) |
43 | Facter::Util::Config.setup_default_ext_facts_dirs | |
39 | 44 | Facter::Util::Config.external_facts_dirs.should == ["/etc/facter/facts.d", "/etc/puppetlabs/facter/facts.d"] |
40 | 45 | end |
41 | 46 | |
42 | 47 | it "should return the default value for windows 2008" do |
43 | 48 | Facter::Util::Config.stubs(:is_windows?).returns(true) |
44 | 49 | Facter::Util::Config.stubs(:windows_data_dir).returns("C:\\ProgramData") |
50 | Facter::Util::Config.setup_default_ext_facts_dirs | |
45 | 51 | Facter::Util::Config.external_facts_dirs.should == [File.join("C:\\ProgramData", 'PuppetLabs', 'facter', 'facts.d')] |
46 | 52 | end |
47 | 53 | |
48 | 54 | it "should return the default value for windows 2003R2" do |
49 | 55 | Facter::Util::Config.stubs(:is_windows?).returns(true) |
50 | 56 | Facter::Util::Config.stubs(:windows_data_dir).returns("C:\\Documents") |
57 | Facter::Util::Config.setup_default_ext_facts_dirs | |
51 | 58 | Facter::Util::Config.external_facts_dirs.should == [File.join("C:\\Documents", 'PuppetLabs', 'facter', 'facts.d')] |
52 | 59 | 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 | ||
53 | 82 | end |
54 | 83 | end |
122 | 122 | it "should return false if none of the provided ranges matches the fact's value" do |
123 | 123 | confined(8, (5..7)).should be_false |
124 | 124 | 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 | |
125 | 146 | end |
126 | 147 | end |
54 | 54 | |
55 | 55 | %w{bak orig}.each do |ext| |
56 | 56 | it "should ignore files with an extension of '#{ext}'" do |
57 | Facter.expects(:warn).with(regexp_matches(/#{ext}/)) | |
57 | 58 | write_to_file("data" + ".#{ext}", "foo=bar") |
58 | 59 | |
59 | 60 | subject.load(collection) |
16 | 16 | |
17 | 17 | it "should succeed if arp table contains fe:ff:ff:ff:ff:ff" do |
18 | 18 | 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").\ | |
20 | 20 | at_least_once.returns(ec2arp) |
21 | 21 | Facter::Util::EC2.has_ec2_arp?.should == true |
22 | 22 | end |
23 | 23 | |
24 | 24 | it "should succeed if arp table contains FE:FF:FF:FF:FF:FF" do |
25 | 25 | 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").\ | |
27 | 27 | at_least_once.returns(ec2arp) |
28 | 28 | Facter::Util::EC2.has_ec2_arp?.should == true |
29 | 29 | end |
30 | 30 | |
31 | 31 | it "should fail if arp table does not contain fe:ff:ff:ff:ff:ff" do |
32 | 32 | 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"). | |
34 | 34 | at_least_once.returns(ec2arp) |
35 | 35 | Facter::Util::EC2.has_ec2_arp?.should == false |
36 | 36 | end |
44 | 44 | |
45 | 45 | it "should succeed if arp table contains fe-ff-ff-ff-ff-ff" do |
46 | 46 | 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").\ | |
48 | 48 | at_least_once.returns(ec2arp) |
49 | 49 | Facter::Util::EC2.has_ec2_arp?.should == true |
50 | 50 | end |
51 | 51 | |
52 | 52 | it "should fail if arp table does not contain fe-ff-ff-ff-ff-ff" do |
53 | 53 | 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"). | |
55 | 55 | at_least_once.returns(ec2arp) |
56 | 56 | Facter::Util::EC2.has_ec2_arp?.should == false |
57 | 57 | end |
65 | 65 | it "should fail if arp table does not contain fe:ff:ff:ff:ff:ff" do |
66 | 66 | ec2arp = my_fixture_read("solaris8_arp_a_not_ec2.out") |
67 | 67 | |
68 | Facter::Util::Resolution.expects(:exec).with("arp -a"). | |
68 | Facter::Core::Execution.expects(:exec).with("arp -a"). | |
69 | 69 | at_least_once.returns(ec2arp) |
70 | 70 | |
71 | 71 | Facter::Util::EC2.has_ec2_arp?.should == false |
3 | 3 | require 'facter/util/fact' |
4 | 4 | |
5 | 5 | 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) | |
8 | 13 | end |
9 | 14 | |
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 | |
12 | 17 | end |
13 | 18 | |
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") | |
16 | 22 | end |
17 | 23 | |
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 | |
54 | 28 | end |
55 | 29 | end |
56 | 30 | |
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 | |
59 | 93 | end |
60 | 94 | |
61 | 95 | 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 | |
67 | 97 | Facter::Util::Fact.new("yay").value.should be_nil |
68 | 98 | end |
69 | 99 | |
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" | |
80 | 105 | end |
81 | 106 | |
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" | |
90 | 112 | end |
91 | 113 | |
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 | |
98 | 121 | |
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" | |
108 | 123 | end |
109 | 124 | end |
110 | 125 | |
112 | 127 | subject do |
113 | 128 | Facter::Util::Fact.new(:foo) |
114 | 129 | 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) | |
119 | 130 | |
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 | |
137 | 139 | end |
138 | 140 | end |
139 | 141 | end |
33 | 33 | let(:name) { 'Local Area Connection' } |
34 | 34 | let(:index) { 7 } |
35 | 35 | 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"} | |
37 | 40 | |
38 | 41 | it "should return an array of only connected interfaces" do |
39 | 42 | Facter::Util::WMI.expects(:execquery).with(Facter::Util::IP::Windows::WMI_IP_INFO_QUERY). |
40 | 43 | 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). | |
42 | 45 | returns([nic]) |
43 | 46 | |
44 | 47 | described_class.interfaces.should == [name] |
45 | 48 | 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 | |
46 | 78 | end |
47 | 79 | end |
400 | 400 | |
401 | 401 | describe "exec_ifconfig" do |
402 | 402 | it "uses get_ifconfig" do |
403 | Facter::Core::Execution.stubs(:exec) | |
404 | ||
403 | 405 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig").once |
404 | 406 | |
405 | 407 | Facter::Util::IP.exec_ifconfig |
406 | 408 | end |
409 | ||
407 | 410 | it "support additional arguments" do |
408 | 411 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig") |
409 | 412 | |
410 | Facter::Util::Resolution.stubs(:exec).with("/sbin/ifconfig -a") | |
413 | Facter::Core::Execution.stubs(:exec).with("/sbin/ifconfig -a") | |
411 | 414 | |
412 | 415 | Facter::Util::IP.exec_ifconfig(["-a"]) |
413 | 416 | end |
417 | ||
414 | 418 | it "joins multiple arguments correctly" do |
415 | 419 | Facter::Util::IP.stubs(:get_ifconfig).returns("/sbin/ifconfig") |
416 | 420 | |
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") | |
418 | 422 | |
419 | 423 | Facter::Util::IP.exec_ifconfig(["-a","-e","-i","-j"]) |
420 | 424 | end |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | 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 | ||
17 | 4 | |
18 | 5 | describe Facter::Util::Loader do |
19 | 6 | before :each do |
20 | 7 | Facter::Util::Loader.any_instance.unstub(:load_all) |
21 | 8 | end |
22 | 9 | |
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 | ||
23 | 18 | it "should have a method for loading individual facts by name" do |
24 | 19 | Facter::Util::Loader.new.should respond_to(:load) |
25 | 20 | end |
33 | 28 | end |
34 | 29 | |
35 | 30 | 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 } | |
50 | 32 | |
51 | 33 | # Used to have test for " " as a directory since that should |
52 | 34 | # be a relative directory, but on Windows in both 1.8.7 and |
67 | 49 | ' \/', |
68 | 50 | ].each do |dir| |
69 | 51 | 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 | |
71 | 53 | end |
72 | 54 | end |
73 | 55 | [ |
83 | 65 | '/ /..', |
84 | 66 | ].each do |dir| |
85 | 67 | 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 | |
87 | 69 | end |
88 | 70 | end |
89 | 71 | end |
90 | 72 | |
91 | 73 | 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 } | |
97 | 75 | |
98 | 76 | 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 | |
102 | 82 | |
103 | 83 | dirs.each do |dir| |
104 | 84 | paths.should be_include(dir) |
105 | 85 | end |
106 | 86 | end |
107 | 87 | |
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 | ||
117 | 88 | it "should exclude invalid search paths" do |
118 | 89 | 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 | |
121 | 92 | dirs.each do |dir| |
122 | 93 | paths.should_not be_include(dir) |
123 | 94 | end |
125 | 96 | |
126 | 97 | it "should include all search paths registered with Facter" do |
127 | 98 | 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 | |
129 | 106 | paths.should be_include("/one") |
130 | 107 | paths.should be_include("/two") |
131 | 108 | end |
132 | 109 | |
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 | ||
133 | 140 | describe "and the FACTERLIB environment variable is set" do |
134 | 141 | 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) | |
140 | 152 | end |
141 | 153 | end |
142 | 154 | end |
143 | 155 | end |
144 | 156 | |
145 | 157 | describe "when loading facts" do |
146 | before do | |
147 | @loader = Facter::Util::Loader.new | |
148 | @loader.stubs(:search_path).returns [] | |
149 | end | |
150 | ||
151 | 158 | it "should load values from the matching environment variable if one is present" do |
159 | loader = loader_from(:env => { "facter_testing" => "yayness" }) | |
160 | ||
152 | 161 | Facter.expects(:add).with("testing") |
153 | 162 | |
154 | Facter::Util::Resolution.with_env "facter_testing" => "yayness" do | |
155 | @loader.load(:testing) | |
156 | end | |
163 | loader.load(:testing) | |
157 | 164 | end |
158 | 165 | |
159 | 166 | 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 | |
164 | 172 | |
165 | 173 | 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} | |
178 | 186 | |
179 | 187 | Dir.stubs(:entries).with("/one/dir/testing").returns %w{foo.rb bar.rb} |
180 | 188 | %w{/one/dir/testing/foo.rb /one/dir/testing/bar.rb}.each do |f| |
182 | 190 | Kernel.stubs(:load).with(f) |
183 | 191 | end |
184 | 192 | |
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) | |
199 | 194 | end |
200 | 195 | |
201 | 196 | 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 | |
208 | 202 | Kernel.expects(:load).never |
209 | 203 | |
210 | @loader.load(:testing) | |
204 | loader.load(:testing) | |
211 | 205 | end |
212 | 206 | end |
213 | 207 | |
214 | 208 | describe "when loading all facts" do |
209 | let(:loader) { Facter::Util::Loader.new } | |
210 | ||
215 | 211 | 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 | |
230 | 215 | end |
231 | 216 | |
232 | 217 | 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 | |
269 | 225 | Kernel.expects(:load).with(f) |
270 | 226 | end |
271 | 227 | |
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 | |
310 | 247 | end |
311 | 248 | |
312 | 249 | 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 | |
315 | 254 | |
316 | 255 | 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 | |
320 | 259 | end |
321 | 260 | |
322 | 261 | it "should load all facts from the environment" do |
323 | 262 | Facter::Util::Resolution.with_env "facter_one" => "yayness", "facter_two" => "boo" do |
324 | @loader.load_all | |
263 | loader.load_all | |
325 | 264 | end |
326 | 265 | Facter.value(:one).should == 'yayness' |
327 | 266 | Facter.value(:two).should == 'boo' |
328 | 267 | end |
329 | 268 | |
330 | 269 | 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 | |
334 | 275 | end |
335 | 276 | end |
336 | 277 | |
337 | 278 | it "should load facts on the facter search path only once" do |
338 | 279 | 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 | |
343 | 284 | end |
344 | 285 | end |
66 | 66 | |
67 | 67 | it "should warn about the lack of default" do |
68 | 68 | Facter.expects(:warn).with("Could not find a default route. Using first non-loopback interface") |
69 | Facter::Util::Macaddress::Darwin.stubs(:default_interface).returns('') | |
70 | 69 | Facter::Util::Macaddress::Darwin.macaddress |
71 | 70 | end |
72 | 71 | |
73 | 72 | 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") | |
74 | 74 | Facter::Util::Macaddress::Darwin.macaddress.should == fallback_macaddress |
75 | 75 | end |
76 | 76 | end |
89 | 89 | include FacterSpec::WindowsNetwork |
90 | 90 | |
91 | 91 | before :each do |
92 | Facter.fact(:kernel).stubs(:value).returns(:windows) | |
92 | Facter.fact(:kernel).stubs(:value).returns('windows') | |
93 | 93 | Facter::Util::Registry.stubs(:hklm_read).returns(nic_bindings) |
94 | 94 | end |
95 | 95 |
26 | 26 | end |
27 | 27 | |
28 | 28 | 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" | |
30 | 30 | Facter::Util::Macosx.profiler_xml("foo").should == "yay" |
31 | 31 | end |
32 | 32 | |
44 | 44 | STDERR.stubs(:<<) |
45 | 45 | expect { |
46 | 46 | 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/) | |
48 | 48 | end |
49 | 49 | |
50 | 50 | describe "when collecting profiler data" do |
79 | 79 | describe "when working out software version" do |
80 | 80 | |
81 | 81 | 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" | |
84 | 84 | end |
85 | 85 | |
86 | 86 | 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" | |
88 | 88 | Facter::Util::Macosx.sw_vers |
89 | 89 | end |
90 | 90 | |
91 | 91 | 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" | |
93 | 93 | Facter::Util::Macosx.sw_vers.keys.sort.should == ["macosx_productName", |
94 | 94 | "macosx_buildVersion", |
95 | 95 | "macosx_productversion_minor", |
98 | 98 | end |
99 | 99 | |
100 | 100 | 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" | |
102 | 102 | sw_vers = Facter::Util::Macosx.sw_vers |
103 | 103 | sw_vers["macosx_productversion_major"].should == "1.2" |
104 | 104 | sw_vers["macosx_productversion_minor"].should == "3" |
105 | 105 | end |
106 | 106 | |
107 | 107 | 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" | |
109 | 109 | Facter::Util::Macosx.sw_vers["macosx_productversion_minor"].should == "0" |
110 | 110 | end |
111 | 111 | end |
17 | 17 | end |
18 | 18 | |
19 | 19 | 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")) | |
21 | 21 | Facter::Manufacturer.prtdiag_sparc_find_system_info() |
22 | 22 | Facter.value(:manufacturer).should == "Sun Microsystems" |
23 | 23 | Facter.value(:productname).should == "Sun Fire V120 (UltraSPARC-IIe 648MHz)" |
24 | 24 | end |
25 | 25 | |
26 | 26 | 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")) | |
28 | 28 | Facter::Manufacturer.prtdiag_sparc_find_system_info() |
29 | 29 | Facter.value(:manufacturer).should == "Sun Microsystems" |
30 | 30 | Facter.value(:productname).should == "SPARC Enterprise T5220" |
35 | 35 | Facter.fact(:kernel).stubs(:value).returns("SunOS") |
36 | 36 | Facter.fact(:hardwareisa).stubs(:value).returns("sparc") |
37 | 37 | |
38 | Facter::Util::Resolution.stubs(:exec).with(regexp_matches(/prtdiag/)).returns(nil) | |
38 | Facter::Core::Execution.stubs(:exec).with(regexp_matches(/prtdiag/)).returns(nil) | |
39 | 39 | Facter::Manufacturer.prtdiag_sparc_find_system_info() |
40 | 40 | Facter.value(:manufacturer).should_not == "Sun Microsystems" |
41 | 41 | end |
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 |
34 | 34 | let(:data) do {"one" => "two", "three" => "four"} end |
35 | 35 | |
36 | 36 | 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" } | |
39 | 39 | |
40 | 40 | it "should return a hash of whatever is stored on disk" do |
41 | 41 | File.stubs(:read).with(data_file).returns(data_in_yaml) |
51 | 51 | end |
52 | 52 | |
53 | 53 | 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" } | |
56 | 56 | |
57 | 57 | it "should return a hash of whatever is stored on disk" do |
58 | 58 | pending("this test requires the json library") unless Facter.json? |
62 | 62 | end |
63 | 63 | |
64 | 64 | describe "txt" do |
65 | let(:data_file) do "/tmp/foo.txt" end | |
65 | let(:data_file) { "/tmp/foo.txt" } | |
66 | 66 | |
67 | 67 | shared_examples_for "txt parser" do |
68 | 68 | it "should return a hash of whatever is stored on disk" do |
72 | 72 | end |
73 | 73 | |
74 | 74 | 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" } | |
76 | 76 | it_behaves_like "txt parser" |
77 | 77 | end |
78 | ||
78 | ||
79 | 79 | 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" } | |
81 | 81 | let(:data) do {"one" => "two", "three" => "four=five"} end |
82 | 82 | it_behaves_like "txt parser" |
83 | 83 | end |
84 | 84 | |
85 | 85 | 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" } | |
87 | 87 | it_behaves_like "txt parser" |
88 | 88 | end |
89 | 89 | end |
90 | 90 | |
91 | 91 | 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) | |
104 | 112 | end |
105 | 113 | |
106 | 114 | 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 | |
113 | 132 | |
114 | 133 | 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 | |
115 | 146 | 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 | |
120 | 182 | end |
121 | 183 | |
122 | 184 | 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 | |
155 | 191 | end |
156 | 192 | end |
157 | 193 |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | require 'facter/util/processor' |
4 | ||
5 | def cpuinfo_fixture(filename) | |
6 | File.open(fixtures('cpuinfo', filename)).readlines | |
7 | end | |
4 | require 'facter_spec/cpuinfo' | |
8 | 5 | |
9 | 6 | describe Facter::Util::Processor do |
10 | 7 | describe "on linux" do |
8 | include FacterSpec::Cpuinfo | |
9 | ||
11 | 10 | before :each do |
12 | 11 | Facter.fact(:kernel).stubs(:value).returns("Linux") |
13 | 12 | File.stubs(:exists?).with("/proc/cpuinfo").returns(true) |
19 | 18 | end |
20 | 19 | |
21 | 20 | 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")) | |
23 | 22 | Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz" |
24 | 23 | end |
25 | 24 | |
26 | 25 | 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")) | |
28 | 27 | |
29 | 28 | Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz" |
30 | 29 | Facter::Util::Processor.enum_cpuinfo[1].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz" |
31 | 30 | end |
32 | 31 | |
33 | 32 | 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")) | |
35 | 34 | |
36 | 35 | Facter::Util::Processor.enum_cpuinfo[0].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz" |
37 | 36 | Facter::Util::Processor.enum_cpuinfo[1].should == "Intel(R) Core(TM)2 Duo CPU P8700 @ 2.53GHz" |
39 | 38 | end |
40 | 39 | |
41 | 40 | 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")) | |
43 | 42 | |
44 | 43 | Facter::Util::Processor.enum_cpuinfo[0].should == "Quad-Core AMD Opteron(tm) Processor 2374 HE" |
45 | 44 | Facter::Util::Processor.enum_cpuinfo[1].should == "Quad-Core AMD Opteron(tm) Processor 2374 HE" |
70 | 69 | |
71 | 70 | it "should get the processor description on Solaris (x86)" do |
72 | 71 | 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")) | |
74 | 73 | |
75 | 74 | Facter::Util::Processor.enum_kstat[0].should == "Intel(r) Core(tm) i5 CPU M 450 @ 2.40GHz" |
76 | 75 | end |
77 | 76 | |
78 | 77 | it "should get the processor description on Solaris (SPARC64)" do |
79 | 78 | 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")) | |
81 | 80 | |
82 | 81 | Facter::Util::Processor.enum_kstat[0].should == "SPARC64-VII" |
83 | 82 | Facter::Util::Processor.enum_kstat[1].should == "SPARC64-VII" |
5 | 5 | describe Facter::Util::Resolution do |
6 | 6 | include FacterSpec::ConfigHelper |
7 | 7 | |
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) | |
10 | 14 | end |
11 | 15 | |
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) | |
14 | 18 | end |
15 | 19 | |
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 | |
20 | 22 | end |
21 | 23 | |
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" | |
24 | 27 | end |
25 | 28 | |
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 | |
111 | 31 | end |
112 | 32 | |
113 | 33 | describe "when setting the code" do |
114 | 34 | before do |
115 | 35 | Facter.stubs(:warnonce) |
116 | @resolve = Facter::Util::Resolution.new("yay") | |
117 | 36 | end |
118 | 37 | |
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 | |
123 | 41 | end |
124 | 42 | |
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) | |
129 | 47 | end |
130 | 48 | |
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 | |
134 | 54 | end |
135 | 55 | |
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) | |
154 | 58 | end |
155 | 59 | end |
156 | 60 | |
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" | |
162 | 65 | end |
163 | 66 | |
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 | |
167 | 70 | end |
168 | 71 | end |
169 | 72 | |
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" | |
174 | 86 | end |
175 | 87 | end |
176 | 88 | end |
177 | 89 | |
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' | |
185 | 94 | end |
186 | 95 | |
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 | |
190 | 99 | end |
191 | 100 | |
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 | |
197 | 104 | end |
198 | 105 | |
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/) | |
289 | 110 | end |
290 | 111 | end |
291 | 112 | |
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) } | |
333 | 117 | end |
334 | 118 | |
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/ | |
387 | 122 | end |
388 | 123 | |
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 { } | |
697 | 126 | end |
698 | 127 | end |
699 | 128 | end |
46 | 46 | |
47 | 47 | describe '#refresh' do |
48 | 48 | 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) | |
50 | 50 | subject.refresh |
51 | 51 | end |
52 | 52 | end |
68 | 68 | it 'uses a single read of the system information for all of the dynamically generated zone facts' do |
69 | 69 | given_initial_zone_facts # <= single read happens here |
70 | 70 | |
71 | Facter::Util::Resolution.expects(:exec).never | |
71 | Facter::Core::Execution.expects(:execute).never | |
72 | 72 | Facter.fact(:zone_zoneA_id).value |
73 | 73 | Facter.fact(:zone_local_id).value |
74 | 74 | end |
100 | 100 | given_initial_zone_facts |
101 | 101 | when_facts_have_been_resolved_then_flushed |
102 | 102 | |
103 | Facter::Util::Resolution.expects(:exec).once.returns(zone_list2) | |
103 | Facter::Core::Execution.expects(:execute).once.returns(zone_list2) | |
104 | 104 | Facter.fact(:zones).value |
105 | 105 | Facter.fact(:zone_zoneA_id).value |
106 | 106 | Facter.fact(:zone_local_id).value |
110 | 110 | end |
111 | 111 | |
112 | 112 | 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}). | |
115 | 115 | returns(zone_list) |
116 | 116 | described_class.add_facts |
117 | 117 | end |
120 | 120 | Facter.fact(:zones).value |
121 | 121 | Facter.fact(:zone_zoneA_id).value |
122 | 122 | Facter.fact(:zone_local_id).value |
123 | Facter::Util::Resolution.stubs(:exec).returns(zone_list2) | |
123 | Facter::Core::Execution.stubs(:execute).returns(zone_list2) | |
124 | 124 | Facter.flush |
125 | 125 | end |
126 | 126 | end |
98 | 98 | |
99 | 99 | test_cases.each do |uptime, expected| |
100 | 100 | 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) | |
102 | 102 | Facter.fact(:uptime_seconds).value.should == expected |
103 | 103 | end |
104 | 104 | end |
105 | 105 | |
106 | 106 | describe "nor is 'uptime' command" do |
107 | 107 | 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) | |
109 | 109 | end |
110 | 110 | |
111 | 111 | 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 |
29 | 29 | it "should identify openvzhn when /proc/self/status has envID of 0" do |
30 | 30 | Facter::Util::Virtual.stubs(:openvz?).returns(true) |
31 | 31 | 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") | |
33 | 33 | Facter::Util::Virtual.openvz_type().should == "openvzhn" |
34 | 34 | end |
35 | 35 | |
36 | 36 | it "should identify openvzve when /proc/self/status has envID of 0" do |
37 | 37 | Facter::Util::Virtual.stubs(:openvz?).returns(true) |
38 | 38 | 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") | |
40 | 40 | Facter::Util::Virtual.openvz_type().should == "openvzve" |
41 | 41 | end |
42 | 42 | |
43 | 43 | it "should not attempt to identify openvz when /proc/self/status has no envID" do |
44 | 44 | Facter::Util::Virtual.stubs(:openvz?).returns(true) |
45 | 45 | 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("") | |
47 | 47 | Facter::Util::Virtual.openvz_type().should be_nil |
48 | 48 | end |
49 | 49 | |
50 | 50 | 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") | |
52 | 52 | Facter::Util::Virtual.should be_zone |
53 | 53 | end |
54 | 54 | |
55 | 55 | 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") | |
57 | 57 | Facter::Util::Virtual.should_not be_zone |
58 | 58 | end |
59 | 59 | |
90 | 90 | |
91 | 91 | it "should identify kvm" do |
92 | 92 | 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") | |
94 | 94 | Facter::Util::Virtual.kvm_type().should == "kvm" |
95 | 95 | end |
96 | 96 | |
201 | 201 | it "should detect kvm on FreeBSD" do |
202 | 202 | FileTest.stubs(:exists?).with("/proc/cpuinfo").returns(false) |
203 | 203 | 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") | |
205 | 205 | Facter::Util::Virtual.should be_kvm |
206 | 206 | end |
207 | 207 | |
208 | 208 | it "should detect kvm on OpenBSD" do |
209 | 209 | FileTest.stubs(:exists?).with("/proc/cpuinfo").returns(false) |
210 | 210 | 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)') | |
212 | 212 | Facter::Util::Virtual.should be_kvm |
213 | 213 | end |
214 | 214 | |
215 | 215 | it "should identify FreeBSD jail when in jail" do |
216 | 216 | 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") | |
218 | 218 | Facter::Util::Virtual.should be_jail |
219 | 219 | end |
220 | 220 | |
221 | 221 | it "should not identify GNU/kFreeBSD jail when not in jail" do |
222 | 222 | 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") | |
224 | 224 | Facter::Util::Virtual.should_not be_jail |
225 | 225 | end |
226 | 226 | |
227 | 227 | it "should detect hpvm on HP-UX" do |
228 | 228 | 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') | |
230 | 230 | Facter::Util::Virtual.should be_hpvm |
231 | 231 | end |
232 | 232 | |
233 | 233 | it "should not detect hpvm on HP-UX when not in hpvm" do |
234 | 234 | 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') | |
236 | 236 | Facter::Util::Virtual.should_not be_hpvm |
237 | 237 | end |
238 | 238 | |
262 | 262 | shared_examples_for "virt-what" do |kernel, path, null_device| |
263 | 263 | before(:each) do |
264 | 264 | 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}") | |
267 | 267 | end |
268 | 268 | |
269 | 269 | it "on #{kernel} virt-what is at #{path} and stderr is sent to #{null_device}" do |
277 | 277 | it "should strip out warnings on stdout from virt-what" do |
278 | 278 | virt_what_warning = "virt-what: this script must be run as root" |
279 | 279 | 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 | |
282 | 282 | Facter::Util::Virtual.virt_what.should_not match /^virt-what: / |
283 | 283 | end |
284 | 284 | end |
3 | 3 | require 'facter/util/vlans' |
4 | 4 | |
5 | 5 | 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 | |
10 | 40 | end |
11 | 41 | end |
6 | 6 | describe ".get_domains" do |
7 | 7 | it "should return a list of running Xen Domains on Xen0" do |
8 | 8 | 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) | |
10 | 10 | Facter::Util::Xendomains.get_domains.should == %{web01,mailserver} |
11 | 11 | end |
12 | 12 | |
13 | 13 | describe "when xm list isn't executable" do |
14 | 14 | 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) | |
16 | 16 | Facter::Util::Xendomains.get_domains.should == nil |
17 | 17 | end |
18 | 18 | end |
77 | 77 | Facter.fact(:kernel).stubs(:value).returns("Linux") |
78 | 78 | Facter.fact(:operatingsystem).stubs(:value).returns("Linux") |
79 | 79 | |
80 | Facter::Util::Resolution.stubs(:exec).with("vmware -v").returns false | |
80 | Facter::Core::Execution.stubs(:exec).with("vmware -v").returns false | |
81 | 81 | |
82 | 82 | FileTest.stubs(:exists?).with("/proc/sys/xen").returns false |
83 | 83 | FileTest.stubs(:exists?).with("/sys/bus/xen").returns false |
96 | 96 | end |
97 | 97 | |
98 | 98 | 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") | |
100 | 100 | Facter.fact(:virtual).value.should == "vmware" |
101 | 101 | end |
102 | 102 | |
103 | 103 | 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") | |
105 | 105 | Facter.fact(:virtual).value.should == "virtualbox" |
106 | 106 | end |
107 | 107 | |
108 | 108 | 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") | |
111 | 111 | Facter.fact(:virtual).value.should == "vmware" |
112 | 112 | end |
113 | 113 | |
127 | 127 | end |
128 | 128 | |
129 | 129 | 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)") | |
131 | 131 | Facter.fact(:virtual).value.should == "xenhvm" |
132 | 132 | end |
133 | 133 | |
134 | 134 | 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") | |
137 | 137 | Facter.fact(:virtual).value.should == "xenhvm" |
138 | 138 | end |
139 | 139 | |
140 | 140 | 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") | |
143 | 143 | Facter.fact(:virtual).value.should == "parallels" |
144 | 144 | end |
145 | 145 | |
146 | 146 | 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") | |
149 | 149 | Facter.fact(:virtual).value.should == "virtualbox" |
150 | 150 | end |
151 | 151 | |
152 | 152 | it "should be rhev with RHEV Hypervisor product name from dmidecode" do |
153 | 153 | 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") | |
156 | 156 | Facter.fact(:virtual).value.should == "rhev" |
157 | 157 | end |
158 | 158 | |
159 | 159 | it "should be ovirt with oVirt Node product name from dmidecode" do |
160 | 160 | 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") | |
163 | 163 | Facter.fact(:virtual).value.should == "ovirt" |
164 | 164 | end |
165 | 165 | |
166 | 166 | 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") | |
168 | 168 | Facter.fact(:virtual).value.should == "hyperv" |
169 | 169 | end |
170 | 170 | |
171 | 171 | 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") | |
174 | 174 | Facter.fact(:virtual).value.should == "hyperv" |
175 | 175 | end |
176 | 176 | |
185 | 185 | end |
186 | 186 | |
187 | 187 | 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") | |
189 | 189 | Facter.fact(:virtual).value.should == "gce" |
190 | 190 | end |
191 | 191 | end |
206 | 206 | end |
207 | 207 | |
208 | 208 | 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") | |
211 | 211 | Facter.fact(:virtual).value.should eq("vmware") |
212 | 212 | end |
213 | 213 | end |
214 | 214 | |
215 | 215 | describe "on Solaris" do |
216 | 216 | 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 | |
218 | 218 | Facter.fact(:kernel).stubs(:value).returns("SunOS") |
219 | 219 | end |
220 | 220 | |
221 | 221 | it "should be vmware with VMWare vendor name from prtdiag" do |
222 | 222 | 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") | |
226 | 226 | Facter.fact(:virtual).value.should == "vmware" |
227 | 227 | end |
228 | 228 | |
229 | 229 | it "should be parallels with Parallels vendor name from prtdiag" do |
230 | 230 | 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") | |
234 | 234 | Facter.fact(:virtual).value.should == "parallels" |
235 | 235 | end |
236 | 236 | |
237 | 237 | it "should be virtualbox with VirtualBox vendor name from prtdiag" do |
238 | 238 | 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") | |
242 | 242 | Facter.fact(:virtual).value.should == "virtualbox" |
243 | 243 | end |
244 | 244 | end |
245 | 245 | |
246 | 246 | describe "on OpenBSD" do |
247 | 247 | before do |
248 | Facter::Util::Resolution.stubs(:exec).with("vmware -v").returns false | |
248 | Facter::Core::Execution.stubs(:exec).with("vmware -v").returns false | |
249 | 249 | Facter.fact(:kernel).stubs(:value).returns("OpenBSD") |
250 | 250 | 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) | |
253 | 253 | end |
254 | 254 | |
255 | 255 | 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") | |
257 | 257 | Facter.fact(:virtual).value.should == "parallels" |
258 | 258 | end |
259 | 259 | |
260 | 260 | 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") | |
262 | 262 | Facter.fact(:virtual).value.should == "vmware" |
263 | 263 | end |
264 | 264 | |
265 | 265 | 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") | |
267 | 267 | Facter.fact(:virtual).value.should == "virtualbox" |
268 | 268 | end |
269 | 269 | |
270 | 270 | 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") | |
272 | 272 | Facter.fact(:virtual).value.should == "xenhvm" |
273 | 273 | end |
274 | 274 | end |
302 | 302 | computersystem = mock('computersystem', :model => 'VMware Virtual Platform') |
303 | 303 | Facter::Util::WMI.expects(:execquery).returns([computersystem]) |
304 | 304 | 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" | |
305 | 311 | end |
306 | 312 | end |
307 | 313 | |
382 | 388 | Facter.fact(:is_virtual).value.should == "true" |
383 | 389 | end |
384 | 390 | |
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 | ||
385 | 397 | it "should be true when running on kvm" do |
386 | 398 | Facter.fact(:kernel).stubs(:value).returns("Linux") |
387 | 399 | Facter.fact(:virtual).stubs(:value).returns("kvm") |
431 | 443 | Facter.fact(:is_virtual).value.should == "false" |
432 | 444 | end |
433 | 445 | |
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 | ||
434 | 452 | it "should be true when running on hyperv" do |
435 | 453 | Facter.fact(:kernel).stubs(:value).returns("Linux") |
436 | 454 | Facter.fact(:virtual).stubs(:value).returns("hyperv") |
14 | 14 | # Solaris 11 11/11 (ga) 33 5 |
15 | 15 | |
16 | 16 | 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") | |
18 | 18 | end |
19 | 19 | |
20 | 20 | 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')) | |
22 | 22 | Facter.fact(:zfs_version).value.should == "3" |
23 | 23 | end |
24 | 24 | |
25 | 25 | 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')) | |
27 | 27 | Facter.fact(:zfs_version).value.should == "5" |
28 | 28 | end |
29 | 29 | |
30 | 30 | 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')) | |
32 | 32 | Facter.fact(:zfs_version).value.should == "4" |
33 | 33 | end |
34 | 34 | |
35 | 35 | 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')) | |
37 | 37 | Facter.fact(:zfs_version).value.should == "5" |
38 | 38 | end |
39 | 39 | |
40 | 40 | 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')) | |
42 | 42 | Facter.fact(:zfs_version).value.should == "4" |
43 | 43 | end |
44 | 44 | |
45 | 45 | 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')) | |
48 | 48 | Facter.fact(:zfs_version).value.should == nil |
49 | 49 | end |
50 | 50 | |
51 | 51 | 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('') | |
53 | 53 | Facter.fact(:zfs_version).value.should == nil |
54 | 54 | end |
55 | 55 | |
56 | 56 | it "handles the zfs command becoming available at a later point in time" do |
57 | 57 | # Simulate Puppet configuring the zfs tools from a persistent daemon by |
58 | 58 | # simulating three sequential responses to which('zfs'). |
59 | Facter::Util::Resolution.stubs(:which). | |
59 | Facter::Core::Execution.stubs(:which). | |
60 | 60 | with("zfs"). |
61 | 61 | returns(nil,nil,"/usr/bin/zfs") |
62 | Facter::Util::Resolution.stubs(:exec). | |
62 | Facter::Core::Execution.stubs(:exec). | |
63 | 63 | with("zfs upgrade -v"). |
64 | 64 | returns(my_fixture_read('linux-fuse_0.6.9')) |
65 | 65 |
0 | #!/usr/bin/env rspec | |
0 | #! /usr/bin/env ruby | |
1 | 1 | |
2 | 2 | require 'spec_helper' |
3 | 3 | require 'facter' |
6 | 6 | |
7 | 7 | it "should return global zone" do |
8 | 8 | 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') | |
10 | 10 | |
11 | 11 | Facter.fact(:zonename).value.should == "global" |
12 | 12 | end |
9 | 9 | -:local:configured:/::native:shared |
10 | 10 | -:zoneA:stopped:/::native:shared |
11 | 11 | 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) | |
13 | 13 | Facter.collection.internal_loader.load(:zones) |
14 | 14 | end |
15 | 15 |
14 | 14 | # Solaris 11 11/11 (ga) 33 5 |
15 | 15 | |
16 | 16 | 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") | |
18 | 18 | end |
19 | 19 | |
20 | 20 | 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')) | |
22 | 22 | Facter.fact(:zpool_version).value.should == "22" |
23 | 23 | end |
24 | 24 | |
25 | 25 | 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')) | |
27 | 27 | Facter.fact(:zpool_version).value.should == "33" |
28 | 28 | end |
29 | 29 | |
30 | 30 | 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')) | |
32 | 32 | Facter.fact(:zpool_version).value.should == "15" |
33 | 33 | end |
34 | 34 | |
35 | 35 | 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')) | |
37 | 37 | Facter.fact(:zpool_version).value.should == "28" |
38 | 38 | end |
39 | 39 | |
40 | 40 | 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')) | |
42 | 42 | Facter.fact(:zpool_version).value.should == "23" |
43 | 43 | end |
44 | 44 | |
45 | 45 | 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')) | |
48 | 48 | Facter.fact(:zpool_version).value.should == nil |
49 | 49 | end |
50 | 50 | |
51 | 51 | 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('') | |
53 | 53 | Facter.fact(:zpool_version).value.should == nil |
54 | 54 | end |
55 | 55 | |
56 | 56 | it "handles the zpool command becoming available" do |
57 | 57 | # Simulate Puppet configuring the zfs tools from a persistent daemon by |
58 | 58 | # simulating three sequential responses to which('zpool'). |
59 | Facter::Util::Resolution.stubs(:which). | |
59 | Facter::Core::Execution.stubs(:which). | |
60 | 60 | with("zpool"). |
61 | 61 | returns(nil,nil,"/usr/bin/zpool") |
62 | Facter::Util::Resolution.stubs(:exec). | |
62 | Facter::Core::Execution.stubs(:exec). | |
63 | 63 | with("zpool upgrade -v"). |
64 | 64 | returns(my_fixture_read('linux-fuse_0.6.9')) |
65 | 65 |