summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@1f5c1d6a-bddf-0310-8f58-fc49e503516a>2005-08-29 21:30:45 +0000
committerluke <luke@1f5c1d6a-bddf-0310-8f58-fc49e503516a>2005-08-29 21:30:45 +0000
commit7cec936bf10ea2f0c2f56c00839043ad9369d99b (patch)
tree5c16f28e53172aaaf9efd1eb936cfe59e93a4832
downloadfacter-7cec936bf10ea2f0c2f56c00839043ad9369d99b.tar.gz
facter-7cec936bf10ea2f0c2f56c00839043ad9369d99b.tar.xz
facter-7cec936bf10ea2f0c2f56c00839043ad9369d99b.zip
moving things to the trunk
git-svn-id: http://reductivelabs.com/svn/facter/trunk@58 1f5c1d6a-bddf-0310-8f58-fc49e503516a
-rw-r--r--CHANGES6
-rw-r--r--COPYING339
-rw-r--r--INSTALL2
-rw-r--r--LICENSE17
-rw-r--r--README9
-rw-r--r--Rakefile277
-rw-r--r--TODO4
-rwxr-xr-xbin/facter72
-rw-r--r--etc/facter.conf5
-rw-r--r--install.rb289
-rw-r--r--lib/facter.rb766
-rwxr-xr-xtests/tc_facterbin.rb35
-rw-r--r--tests/tc_simple.rb268
13 files changed, 2089 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..f8ad814
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,6 @@
+2.0:
+ Rewrote entirely. It's much simpler to use, and now supports
+ adding new fact resolution mechanisms at run-time.
+
+1.0b1:
+ Initial release.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..de89f69
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,2 @@
+Unforantely, no install.rb yet. Hopefully soon. Just copy the libs into
+your main ruby library directory. Or something.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..308f4e0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,17 @@
+Facter - Host Fact Detection and Reporting. Copyright (C) 2005 Reductive Labs LLC
+
+Reductive Labs can be contacted at: info@reductivelabs.com
+
+This program and entire repository is free software; you can
+redistribute it and/or modify it under the terms of the GNU
+General Public License as published by the Free Software
+Foundation; either version 2 of the License, or any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/README b/README
new file mode 100644
index 0000000..5462795
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+This package is largely meant to be a library for collecting facts about your
+system. These facts are mostly strings (i.e., not numbers), and are things
+like the output of 'uname', public ssh and cfengine keys, the number of
+processors, etc.
+
+It currently cannot collect very much information, but it is architected to be
+both OS and OS version specific.
+
+See bin/facter or http://madstop.com/svn/enhost for an example of the interface.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..0cd2889
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,277 @@
+# Rakefile for facter
+
+begin
+ require 'rubygems'
+ require 'rake/gempackagetask'
+rescue Exception
+ nil
+end
+
+require 'rake/clean'
+require 'rake/testtask'
+
+require 'rake/rdoctask'
+#CLEAN.include('**/*.o')
+CLOBBER.include('doc/*')
+
+def announce(msg='')
+ STDERR.puts msg
+end
+
+# Determine the current version
+
+if `ruby -Ilib ./bin/facter --version` =~ /\S+$/
+ CURRENT_VERSION = $&
+else
+ CURRENT_VERSION = "0.0.0"
+end
+
+if ENV['REL']
+ PKG_VERSION = ENV['REL']
+else
+ PKG_VERSION = CURRENT_VERSION
+end
+
+
+# The default task is run if rake is given no explicit arguments.
+
+desc "Default Task"
+task :default => :unittests
+
+# Test Tasks ---------------------------------------------------------
+
+task :u => :unittests
+task :a => :alltests
+
+task :alltests => :unittests
+
+Rake::TestTask.new(:unittests) do |t|
+ t.test_files = FileList['tests/tc*.rb']
+ t.warning = true
+ t.verbose = false
+end
+
+# SVN Tasks ----------------------------------------------------------
+# ... none.
+
+# Install rake using the standard install.rb script.
+
+desc "Install the application"
+task :install do
+ ruby "install.rb"
+end
+
+# Create a task to build the RDOC documentation tree.
+
+rd = Rake::RDocTask.new("rdoc") { |rdoc|
+ rdoc.rdoc_dir = 'html'
+ rdoc.template = 'css2'
+ rdoc.title = "Facter"
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
+ rdoc.rdoc_files.include('README', 'LICENSE', 'TODO', 'CHANGES')
+ rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
+}
+
+# ====================================================================
+# Create a task that will package the Rake software into distributable
+# tar, zip and gem files.
+
+PKG_FILES = FileList[
+ 'install.rb',
+ '[A-Z]*',
+ 'bin/**/*',
+ 'lib/**/*.rb',
+ 'test/**/*.rb',
+ 'doc/**/*',
+ 'etc/*'
+]
+PKG_FILES.delete_if {|item| item.include?(".svn")}
+
+if ! defined?(Gem)
+ puts "Package Target requires RubyGEMs"
+else
+ spec = Gem::Specification.new do |s|
+
+ #### Basic information.
+
+ s.name = 'facter'
+ s.version = PKG_VERSION
+ s.summary = "Facter collects Operating system facts."
+ s.description = <<-EOF
+ Facter is a module for collecting simple facts about a host
+ Operating system.
+ EOF
+
+ #### Dependencies and requirements.
+
+ #s.add_dependency('log4r', '> 1.0.4')
+ #s.requirements << ""
+
+ s.files = PKG_FILES.to_a
+
+ #### Load-time details: library and application (you will need one or both).
+
+ s.require_path = 'lib' # Use these for libraries.
+
+ s.bindir = "bin" # Use these for applications.
+ s.executables = ["facter"]
+ s.default_executable = "facter"
+
+ #### Documentation and testing.
+
+ s.has_rdoc = true
+ s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
+ s.rdoc_options <<
+ '--title' << 'Facter' <<
+ '--main' << 'README' <<
+ '--line-numbers'
+
+ #### Author and project details.
+
+ s.author = "Luke Kanies"
+ s.email = "dev@reductivelabs.com"
+ s.homepage = "http://reductivelabs.com/projects/facter"
+ #s.rubyforge_project = "facter"
+ end
+
+ Rake::GemPackageTask.new(spec) do |pkg|
+ #pkg.need_zip = true
+ pkg.need_tar = true
+ end
+end
+
+# Misc tasks =========================================================
+
+#ARCHIVEDIR = '/...'
+
+#task :archive => [:package] do
+# cp FileList["pkg/*.tgz", "pkg/*.zip", "pkg/*.gem"], ARCHIVEDIR
+#end
+
+# Define an optional publish target in an external file. If the
+# publish.rf file is not found, the publish targets won't be defined.
+
+#load "publish.rf" if File.exist? "publish.rf"
+
+# Support Tasks ------------------------------------------------------
+
+def egrep(pattern)
+ Dir['**/*.rb'].each do |fn|
+ count = 0
+ open(fn) do |f|
+ while line = f.gets
+ count += 1
+ if line =~ pattern
+ puts "#{fn}:#{count}:#{line}"
+ end
+ end
+ end
+ end
+end
+
+desc "Look for TODO and FIXME tags in the code"
+task :todo do
+ egrep "/#.*(FIXME|TODO|TBD)/"
+end
+
+#desc "Look for Debugging print lines"
+#task :dbg do
+# egrep /\bDBG|\bbreakpoint\b/
+#end
+
+#desc "List all ruby files"
+#task :rubyfiles do
+# puts Dir['**/*.rb'].reject { |fn| fn =~ /^pkg/ }
+# puts Dir['bin/*'].reject { |fn| fn =~ /CVS|(~$)|(\.rb$)/ }
+#end
+
+# --------------------------------------------------------------------
+# Creating a release
+
+desc "Make a new release"
+task :release => [
+ :prerelease,
+ :clobber,
+ :alltests,
+ :update_version,
+ :package,
+ :tag] do
+
+ announce
+ announce "**************************************************************"
+ announce "* Release #{PKG_VERSION} Complete."
+ announce "* Packages ready to upload."
+ announce "**************************************************************"
+ announce
+end
+
+# Validate that everything is ready to go for a release.
+task :prerelease do
+ announce
+ announce "**************************************************************"
+ announce "* Making RubyGem Release #{PKG_VERSION}"
+ announce "* (current version #{CURRENT_VERSION})"
+ announce "**************************************************************"
+ announce
+
+ # Is a release number supplied?
+ unless ENV['REL']
+ fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
+ end
+
+ # Is the release different than the current release.
+ # (or is REUSE set?)
+ if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE']
+ fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version"
+ end
+
+ # Are all source files checked in?
+ if ENV['RELTEST']
+ announce "Release Task Testing, skipping checked-in file test"
+ else
+ announce "Checking for unchecked-in files..."
+ data = `svn -q update`
+ unless data =~ /^$/
+ fail "SVN update is not clean ... do you have unchecked-in files?"
+ end
+ announce "No outstanding checkins found ... OK"
+ end
+end
+
+task :update_version => [:prerelease] do
+ if PKG_VERSION == CURRENT_VERSION
+ announce "No version change ... skipping version update"
+ else
+ announce "Updating Facter version to #{PKG_VERSION}"
+ open("lib/facter.rb") do |rakein|
+ open("lib/facter.rb.new", "w") do |rakeout|
+ rakein.each do |line|
+ if line =~ /^FACTERVERSION\s*=\s*/
+ rakeout.puts "FACTERVERSION = '#{PKG_VERSION}'"
+ else
+ rakeout.puts line
+ end
+ end
+ end
+ end
+ mv "lib/facter.rb.new", "lib/facter.rb"
+ if ENV['RELTEST']
+ announce "Release Task Testing, skipping commiting of new version"
+ else
+ sh %{svn commit -m "Updated to version #{PKG_VERSION}" lib/facter.rb}
+ end
+ end
+end
+
+desc "Tag all the SVN files with the latest release number (REL=x.y.z)"
+task :tag => [:prerelease] do
+ reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"
+ reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']
+ announce "Tagging SVN copy with [#{reltag}]"
+ if ENV['RELTEST']
+ announce "Release Task Testing, skipping SVN tagging"
+ else
+ #sh %{svn copy trunk/ tags/#{reltag}}
+ end
+end
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..9389e45
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+More documentation.
+
+The ability to specify fact names with strings or symbols; right now, only
+strings are supported
diff --git a/bin/facter b/bin/facter
new file mode 100755
index 0000000..ed37fb5
--- /dev/null
+++ b/bin/facter
@@ -0,0 +1,72 @@
+#!/usr/bin/env ruby
+
+#--------------------
+# duh, it's facter!
+#
+# $Id: facter,v 1.1.1.1 2004/03/21 21:06:27 luke Exp $
+
+require 'getoptlong'
+require 'facter'
+
+Facter.load
+
+$debug = 0
+
+config = nil
+
+result = GetoptLong.new(
+ [ "--version", "-v", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ]
+)
+
+result.each { |opt,arg|
+ case opt
+ when "--version"
+ puts "%s" % Facter.version
+ exit
+ when "--debug"
+ Facter.debugging(1)
+ when "--help"
+ puts "There is no help yet"
+ exit
+ else
+ raise "Invalid option '#{opt}'"
+ end
+}
+
+names = []
+
+unless config.nil?
+ File.open(config) { |file|
+ names = file.readlines.collect { |line|
+ line.chomp
+ }
+ }
+end
+
+ARGV.each { |item|
+ names.push item
+}
+
+facts = {}
+
+if names.empty?
+ Facter.each { |name,fact|
+ facts[name] = fact
+ }
+else
+ names.each { |name|
+ begin
+ facts[name] = Facter[name].value
+ rescue => error
+ STDERR.puts "Could not retrieve %s: #{error}" % name
+ exit 10
+ end
+ }
+end
+
+facts.each { |name,value|
+ puts "%s => %s" % [name,value]
+}
diff --git a/etc/facter.conf b/etc/facter.conf
new file mode 100644
index 0000000..9140d50
--- /dev/null
+++ b/etc/facter.conf
@@ -0,0 +1,5 @@
+Hostname
+OperatingSystem
+OperatingSystemRelease
+SSHDSAKey
+CfKey
diff --git a/install.rb b/install.rb
new file mode 100644
index 0000000..73a1a2e
--- /dev/null
+++ b/install.rb
@@ -0,0 +1,289 @@
+#! /usr/bin/env ruby
+#--
+# Copyright 2004 Austin Ziegler <ruby-install@halostatue.ca>
+# Install utility. Based on the original installation script for rdoc by the
+# Pragmatic Programmers.
+#
+# This program is free software. It may be redistributed and/or modified under
+# the terms of the GPL version 2 (or later) or the Ruby licence.
+#
+# Usage
+# -----
+# In most cases, if you have a typical project layout, you will need to do
+# absolutely nothing to make this work for you. This layout is:
+#
+# bin/ # executable files -- "commands"
+# lib/ # the source of the library
+# tests/ # unit tests
+#
+# The default behaviour:
+# 1) Run all unit test files (ending in .rb) found in all directories under
+# tests/.
+# 2) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd),
+# all .rb files in lib/, ./README, ./ChangeLog, and ./Install.
+# 3) Build ri documentation from all files in bin/ (excluding .bat and .cmd),
+# and all .rb files in lib/. This is disabled by default on Win32.
+# 4) Install commands from bin/ into the Ruby bin directory. On Windows, if a
+# if a corresponding batch file (.bat or .cmd) exists in the bin directory,
+# it will be copied over as well. Otherwise, a batch file (always .bat) will
+# be created to run the specified command.
+# 5) Install all library files ending in .rb from lib/ into Ruby's
+# site_lib/version directory.
+#
+# $Id: install.rb,v 1.6 2004/08/08 20:33:09 austin Exp $
+#++
+
+require 'rbconfig'
+require 'find'
+require 'fileutils'
+require 'optparse'
+require 'ostruct'
+
+InstallOptions = OpenStruct.new
+
+$loadedrdoc = false
+
+begin
+ require 'rdoc/rdoc'
+ $loadedrdoc = true
+rescue LoadError => detail
+ $stderr.puts "Could not load rdoc/rdoc: %s" % detail
+ InstallOptions.rdoc = false
+end
+
+
+def glob(list)
+ g = list.map { |i| Dir.glob(i) }
+ g.flatten!
+ g.compact!
+ g.reject! { |e| e =~ /CVS/ }
+ g
+end
+
+ # Set these values to what you want installed.
+#bins = glob(%w{bin/**/*}).reject { |e| e =~ /\.(bat|cmd)$/ }
+bins = ["bin/facter"]
+rdoc = glob(%w{bin/**/* lib/**/*.rb README CHANGELOG INSTALL}).reject { |e| e=~ /\.(bat|cmd)$/ }
+ri = glob(%w(bin/**/* lib/**/*.rb)).reject { |e| e=~ /\.(bat|cmd)$/ }
+libs = glob(%w{lib/**/*.rb})
+tests = glob(%w{tests/**/*.rb})
+
+def do_bins(bins, target, strip = 'bin/')
+ bins.each do |bf|
+ obf = bf.gsub(/#{strip}/, '')
+ install_binfile(bf, obf, target)
+ end
+end
+
+def do_libs(libs, strip = 'lib/')
+ libs.each do |lf|
+ olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, ''))
+ op = File.dirname(olf)
+ #if File.respond_to?(:makedirs)
+ FileUtils.makedirs(op)
+ #else
+ # recmkdir(op)
+ #end
+ File.chmod(0755, op)
+ FileUtils.install(lf, olf, :mode => 0755, :verbose => true)
+ end
+end
+
+##
+# Prepare the file installation.
+#
+def prepare_installation
+ InstallOptions.rdoc = true
+ if RUBY_PLATFORM == "i386-mswin32"
+ InstallOptions.ri = false
+ else
+ InstallOptions.ri = true
+ end
+ InstallOptions.tests = true
+
+ ARGV.options do |opts|
+ opts.banner = "Usage: #{File.basename($0)} [options]"
+ opts.separator ""
+ opts.on('--[no-]rdoc', 'Prevents the creation of RDoc output.', 'Default on.') do |onrdoc|
+ InstallOptions.rdoc = onrdoc
+ end
+ opts.on('--[no-]ri', 'Prevents the creation of RI output.', 'Default off on mswin32.') do |onri|
+ InstallOptions.ri = onri
+ end
+ opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default on.') do |ontest|
+ InstallOptions.tests = ontest
+ end
+ opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick|
+ InstallOptions.rdoc = false
+ InstallOptions.ri = false
+ InstallOptions.tests = false
+ end
+ opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full|
+ InstallOptions.rdoc = true
+ InstallOptions.ri = true
+ InstallOptions.tests = true
+ end
+ opts.separator("")
+ opts.on_tail('--help', "Shows this help text.") do
+ $stderr.puts opts
+ exit
+ end
+
+ opts.parse!
+ end
+
+ bds = [".", ENV['TMP'], ENV['TEMP'], "/tmp"]
+
+ version = [Config::CONFIG["MAJOR"], Config::CONFIG["MINOR"]].join(".")
+ ld = File.join(Config::CONFIG["libdir"], "ruby", version)
+
+ sd = Config::CONFIG["sitelibdir"]
+ if sd.nil?
+ sd = $:.find { |x| x =~ /site_ruby/ }
+ if sd.nil?
+ sd = File.join(ld, "site_ruby")
+ elsif sd !~ Regexp.quote(version)
+ sd = File.join(sd, version)
+ end
+ end
+
+ if (destdir = ENV['DESTDIR'])
+ bd = "#{destdir}#{Config::CONFIG['bindir']}"
+ sd = "#{destdir}#{sd}"
+ bds << bd
+
+ FileUtils.makedirs(bd)
+ FileUtils.makedirs(sd)
+ else
+ bds << Config::CONFIG['bindir']
+ end
+
+ InstallOptions.bin_dirs = bds.compact
+ InstallOptions.site_dir = sd
+ InstallOptions.bin_dir = bd
+ InstallOptions.lib_dir = ld
+
+ unless $loadedrdoc
+ InstallOptions.rdoc = false
+ InstallOptions.ri = false
+ end
+end
+
+##
+# Build the rdoc documentation. Also, try to build the RI documentation.
+#
+def build_rdoc(files)
+ r = RDoc::RDoc.new
+ r.document(["--main", "README", "--title", "Facter -- A Fact Collecter",
+ "--line-numbers"] + files)
+
+rescue RDoc::RDocError => e
+ $stderr.puts e.message
+rescue Exception => e
+ $stderr.puts "Couldn't build RDoc documentation\n#{e.message}"
+end
+
+def build_ri(files)
+ ri = RDoc::RDoc.new
+ ri.document(["--ri-site", "--merge"] + files)
+rescue RDoc::RDocError => e
+ $stderr.puts e.message
+rescue Exception => e
+ $stderr.puts e.class
+ $stderr.puts "Couldn't build Ri documentation\n#{e.message}"
+end
+
+def run_tests(test_list)
+ begin
+ require 'test/unit/ui/console/testrunner'
+ $:.unshift "lib"
+ test_list.each do |test|
+ next if File.directory?(test)
+ require test
+ end
+
+ tests = []
+ ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) }
+ tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) }
+ tests.delete_if { |o| o == Test::Unit::TestCase }
+
+ tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) }
+ $:.shift
+ rescue LoadError
+ puts "Missing testrunner library; skipping tests"
+ end
+end
+
+##
+# Install file(s) from ./bin to Config::CONFIG['bindir']. Patch it on the way
+# to insert a #! line; on a Unix install, the command is named as expected
+# (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under
+# windows, we add an '.rb' extension and let file associations do their stuff.
+def install_binfile(from, op_file, target)
+ tmp_dir = nil
+ InstallOptions.bin_dirs.each do |t|
+ if File.directory?(t) and File.writable_real?(t)
+ tmp_dir = t
+ break
+ end
+ end
+
+ fail "Cannot find a temporary directory" unless tmp_dir
+ tmp_file = File.join(tmp_dir, '_tmp')
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+
+ File.open(from) do |ip|
+ File.open(tmp_file, "w") do |op|
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+ op.puts "#!#{ruby}"
+ op.write ip.read
+ end
+ end
+
+ if Config::CONFIG["target_os"] =~ /win/io
+ installed_wrapper = false
+
+ if File.exists?("#{from}.bat")
+ FileUtils.install("#{from}.bat", File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true)
+ installed_wrapper = true
+ end
+
+ if File.exists?("#{from}.cmd")
+ FileUtils.install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), :mode => 0755, :verbose => true)
+ installed_wrapper = true
+ end
+
+ if not installed_wrapper
+ tmp_file2 = File.join(tmp_dir, '_tmp_wrapper')
+ cwn = File.join(Config::CONFIG['bindir'], op_file)
+ cwv = CMD_WRAPPER.gsub('<ruby>', ruby.gsub(%r{/}) { "\\" }).gsub!('<command>', cwn.gsub(%r{/}) { "\\" } )
+
+ File.open(tmp_file2, "wb") { |cw| cw.puts cwv }
+ FileUtils.install(tmp_file2, File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true)
+
+ File.unlink(tmp_file2)
+ installed_wrapper = true
+ end
+ end
+ FileUtils.install(tmp_file, File.join(target, op_file), :mode => 0755, :verbose => true)
+ File.unlink(tmp_file)
+end
+
+CMD_WRAPPER = <<-EOS
+@echo off
+if "%OS%"=="Windows_NT" goto WinNT
+<ruby> -x "<command>" %1 %2 %3 %4 %5 %6 %7 %8 %9
+goto done
+:WinNT
+<ruby> -x "<command>" %*
+goto done
+:done
+EOS
+
+prepare_installation
+
+run_tests(tests) if InstallOptions.tests
+build_rdoc(rdoc) if InstallOptions.rdoc
+build_ri(ri) if InstallOptions.ri
+do_bins(bins, Config::CONFIG['bindir'])
+do_libs(libs)
diff --git a/lib/facter.rb b/lib/facter.rb
new file mode 100644
index 0000000..e537ca4
--- /dev/null
+++ b/lib/facter.rb
@@ -0,0 +1,766 @@
+# $Id$
+#--
+# Copyright 2004 Luke Kanies <luke@madstop.com>
+#
+# This program is free software. It may be redistributed and/or modified under
+# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
+# Ruby licence.
+#
+#--
+
+#--------------------------------------------------------------------
+class Facter
+ include Comparable
+ include Enumerable
+
+ FACTERVERSION="1.0.0"
+ # = Facter 1.0
+ # Functions as a hash of 'facts' you might care about about your
+ # system, such as mac address, IP address, Video card, etc.
+ # returns them dynamically
+
+ # == Synopsis
+ #
+ # Generally, treat <tt>Facter</tt> as a hash:
+ # == Example
+ # require 'facter'
+ # puts Facter['operatingsystem']
+ #
+
+
+ @@facts = {}
+ GREEN = ""
+ RESET = ""
+ @@debug = 0
+ @@os = nil
+ @@osrel = nil
+
+ attr_accessor :name, :os, :osrel, :hardware, :searching
+
+ #----------------------------------------------------------------
+ # module methods
+ #----------------------------------------------------------------
+
+ def Facter.version
+ return FACTERVERSION
+ end
+
+ def Facter.debug(string)
+ if string.nil?
+ return
+ end
+ if @@debug != 0
+ puts GREEN + string + RESET
+ end
+ end
+
+ #----------------------------------------------------------------
+ def Facter.[](name)
+ if @@facts.include?(name.downcase)
+ return @@facts[name.downcase]
+ else
+ return Facter.add(name)
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def Facter.add(name)
+ fact = nil
+ dcname = name.downcase
+
+ if @@facts.include?(dcname)
+ fact = @@facts[dcname]
+ else
+ Facter.new(dcname)
+ fact = @@facts[dcname]
+ end
+
+ if block_given?
+ fact.add
+ end
+
+ return fact
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ class << self
+ include Enumerable
+ def each
+ @@facts.each { |name,fact|
+ if fact.suitable?
+ value = fact.value
+ unless value.nil?
+ yield name, fact.value
+ end
+ end
+ }
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def Facter.reset
+ @@facts.each { |name,fact|
+ @@facts.delete(name)
+ }
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ #
+ def Facter.debugging(bit)
+ if bit
+ case bit
+ when TrueClass: @@debug = 1
+ when FalseClass: @@debug = 0
+ when Fixnum:
+ if bit > 0
+ @@debug = 1
+ else
+ @@debug = 0
+ end
+ when String:
+ if bit.downcase == 'off'
+ @@debug = 0
+ else
+ @@debug = 1
+ end
+ else
+ @@debug = 0
+ end
+ else
+ @@debug = 0
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ #def Facter.init
+ # @@os = @@facts["operatingsystem"].value
+ # @@release = @@facts["operatingsystemrelease"].value
+ #end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def Facter.list
+ return @@facts.keys
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def Facter.os
+ return @@os
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def Facter.release
+ return @@release
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def <=>(other)
+ return self.value <=> other
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def initialize(name)
+ @name = name.downcase
+ if @@facts.include?(@name)
+ raise "A fact named %s already exists" % name
+ else
+ @@facts[@name] = self
+ end
+
+ @resolves = []
+ @searching = false
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ # as long as this doesn't work over the 'net, it should probably
+ # be optimized to throw away any resolutions not valid for the machine
+ # it's running on
+ # but that's not currently done...
+ def add
+ resolve = Resolution.new(@name)
+ yield(resolve)
+
+ # skip resolves that will never be suitable for us
+ return unless resolve.suitable?
+
+ # insert resolves in order of number of tags
+ inserted = false
+ @resolves.each_with_index { |r,index|
+ if resolve.length > r.length
+ @resolves.insert(index,resolve)
+ inserted = true
+ break
+ end
+ }
+
+ unless inserted
+ @resolves.push resolve
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ # iterate across all of the currently configured resolves
+ # sort the resolves in order of most tags first, so we're getting
+ # the most specific answers first, hopefully
+ def each
+# @resolves.sort { |a,b|
+# puts "for tag %s, alength is %s and blength is %s" %
+# [@name, a.length, b.length]
+# b.length <=> a.length
+# }.each { |resolve|
+# yield resolve
+# }
+ @resolves.each { |r| yield r }
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ # return a count of resolution mechanisms available
+ def count
+ return @resolves.length
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ # is this fact suitable for finding answers on this host?
+ # again, this should really be optimizing by throwing away everything
+ # not suitable, but...
+ def suitable?
+ return false if @resolves.length == 0
+
+ unless defined? @suitable
+ @suitable = false
+ self.each { |resolve|
+ if resolve.suitable?
+ @suitable = true
+ break
+ end
+ }
+ end
+
+ return @suitable
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ def value
+ # make sure we don't get stuck in recursive dependency loops
+ if @searching
+ Facter.debug "Caught recursion on %s" % @name
+
+ # return a cached value if we've got it
+ if @value
+ return @value
+ else
+ return nil
+ end
+ end
+ @value = nil
+ foundsuits = false
+
+ if @resolves.length == 0
+ Facter.debug "No resolves for %s" % @name
+ return nil
+ end
+
+ @searching = true
+ @resolves.each { |resolve|
+ #Facter.debug "Searching resolves for %s" % @name
+ if resolve.suitable?
+ @value = resolve.value
+ foundsuits = true
+ else
+ Facter.debug "Unsuitable resolve %s for %s" % [resolve,@name]
+ end
+ unless @value.nil? or @value == ""
+ break
+ end
+ }
+ @searching = false
+
+ unless foundsuits
+ Facter.debug "Found no suitable resolves of %s for %s" %
+ [@resolves.length,@name]
+ end
+
+ if @value.nil?
+ # nothing
+ Facter.debug("value for %s is still nil" % @name)
+ return nil
+ else
+ return @value
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ class Resolution
+ attr_accessor :interpreter, :code, :name
+
+ def Resolution.exec(code, interpreter = "/bin/sh")
+ if interpreter == "/bin/sh"
+ binary = code.split(/\s+/).shift
+
+ path = nil
+ if binary !~ /^\//
+ path = %x{which #{binary} 2>/dev/null}.chomp
+ if path == ""
+ # we don't have the binary necessary
+ return nil
+ end
+ else
+ path = binary
+ end
+
+ unless FileTest.exists?(path)
+ # our binary does not exist
+ return nil
+ end
+ out = nil
+ begin
+ out = %x{#{code}}.chomp
+ rescue => detail
+ $stderr.puts detail
+ end
+ if out == ""
+ return nil
+ else
+ return out
+ end
+ else
+ raise "non-sh interpreters are not currently supported"
+ end
+ end
+
+ def initialize(name)
+ @name = name
+ @tags = []
+ end
+
+ def length
+ @tags.length
+ end
+
+ def suitable?
+ unless defined? @suitable
+ @suitable = true
+ if @tags.length == 0
+ #Facter.debug "'%s' has no tags" % @name
+ return true
+ end
+ @tags.each { |tag|
+ unless tag.true?
+ #Facter.debug "'%s' is false" % tag
+ @suitable = false
+ end
+ }
+ end
+
+ return @suitable
+ end
+
+ def tag(fact,op,value)
+ @tags.push Tag.new(fact,op,value)
+ end
+
+ def to_s
+ return self.value()
+ end
+
+ # how we get a value for our resolution mechanism
+ def value
+ #puts "called %s value" % @name
+ value = nil
+ if @code.is_a?(Proc)
+ value = @code.call()
+ else
+ unless defined? @interpreter
+ @interpreter = "/bin/sh"
+ end
+ if @code.nil?
+ $stderr.puts "Code for %s is nil" % @name
+ else
+ value = Resolution.exec(@code,@interpreter)
+ end
+ end
+
+ if value == ""
+ value = nil
+ end
+
+ return value
+ end
+ end
+ #----------------------------------------------------------------
+
+ #----------------------------------------------------------------
+ class Tag
+ attr_accessor :fact, :op, :value
+
+ def initialize(fact,op,value)
+ @fact = fact
+ if op == "="
+ op = "=="
+ end
+ @op = op
+ @value = value
+ end
+
+ def to_s
+ return "'%s' %s '%s'" % [@fact,@op,@value]
+ end
+
+ def true?
+ value = Facter[@fact].value
+
+ if value.nil?
+ return false
+ end
+
+ str = "'%s' %s '%s'" % [value,@op,@value]
+ begin
+ if eval(str)
+ return true
+ else
+ return false
+ end
+ rescue => detail
+ $stderr.puts "Failed to test '%s': %s" % [str,detail]
+ return false
+ end
+ end
+ end
+ #----------------------------------------------------------------
+
+ # Load all of the default facts
+ def Facter.load
+ Facter["OperatingSystem"].add { |obj|
+ obj.code = 'uname -s'
+ }
+
+ Facter["OperatingSystemRelease"].add { |obj|
+ obj.code = 'uname -r'
+ }
+
+ Facter["HardwareModel"].add { |obj|
+ obj.code = 'uname -m'
+ #obj.os = "SunOS"
+ #obj.tag("operatingsystem","=","SunOS")
+ }
+
+ Facter["CfKey"].add { |obj|
+ obj.code = proc {
+ value = nil
+ ["/usr/local/etc/cfkey.pub",
+ "/etc/cfkey.pub",
+ "/var/cfng/keys/localhost.pub",
+ "/var/cfengine/ppkeys/localhost.pub"
+ ].each { |file|
+ if FileTest.file?(file)
+ File.open(file) { |openfile|
+ value = openfile.readlines.reject { |line|
+ line =~ /PUBLIC KEY/
+ }.collect { |line|
+ line.chomp
+ }.join("")
+ }
+ end
+ if value
+ break
+ end
+ }
+
+ value
+ }
+ }
+
+ Facter["Domain"].add { |obj|
+ obj.code = proc {
+ if defined? $domain and ! $domain.nil?
+ $domain
+ end
+ }
+ }
+ Facter["Domain"].add { |obj|
+ obj.code = proc {
+ domain = Resolution.exec('domainname')
+ # make sure it's a real domain
+ if domain =~ /.+\..+/
+ domain
+ else
+ nil
+ end
+ }
+ }
+ Facter["Domain"].add { |obj|
+ obj.code = proc {
+ value = nil
+ unless FileTest.exists?("/etc/resolv.conf")
+ return nil
+ end
+ File.open("/etc/resolv.conf") { |file|
+ # is the domain set?
+ file.each { |line|
+ if line =~ /domain\s+(\S+)/
+ value = $1
+ break
+ end
+ }
+ }
+ ! value and File.open("/etc/resolv.conf") { |file|
+ # is the search path set?
+ file.each { |line|
+ if line =~ /search\s+(\S+)/
+ value = $1
+ break
+ end
+ }
+ }
+ value
+ }
+ }
+ Facter["Hostname"].add { |obj|
+ obj.code = proc {
+ hostname = nil
+ name = Resolution.exec('hostname')
+ if name =~ /^([\w-]+)\.(.+)$/
+ hostname = $1
+ # the Domain class uses this
+ $domain = $2
+ else
+ hostname = name
+ end
+ hostname
+ }
+ }
+
+ Facter["IPHostNumber"].add { |obj|
+ obj.code = proc {
+ require 'resolv'
+
+ begin
+ hostname = Facter["hostname"].value
+ ip = Resolv.getaddress(hostname)
+ unless ip == "127.0.0.1"
+ ip
+ end
+ rescue Resolv::ResolvError
+ nil
+ rescue NoMethodError # i think this is a bug in resolv.rb?
+ nil
+ end
+ }
+ }
+ Facter["IPHostNumber"].add { |obj|
+ obj.code = proc {
+ hostname = Facter["hostname"].value
+ # crap, we need Hostname to exist for this to
+ # work
+ list = Resolution.exec("host #{hostname}").chomp.split(/\s/)
+ if defined? list[-1] and list[-1] =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/
+ list[-1]
+ end
+ }
+ }
+
+ ["/etc/ssh","/usr/local/etc/ssh","/etc","/usr/local/etc"].each { |dir|
+ {"SSHDSAKey" => "ssh_host_dsa_key.pub",
+ "SSHRSAKey" => "ssh_host_rsa_key.pub"}.each { |name,file|
+ Facter[name].add { |obj|
+ obj.code = proc {
+ value = nil
+ filepath = File.join(dir,file)
+ if FileTest.file?(filepath)
+ begin
+ value = File.open(filepath).read.chomp
+ rescue
+ return nil
+ end
+ end
+ return value
+ } # end of proc
+ } # end of add
+ } # end of hash each
+ } # end of dir each
+
+ Facter["UniqueId"].add { |obj|
+ obj.code = 'hostid'
+ obj.interpreter = '/bin/sh'
+ obj.tag("operatingsystem","=","SunOS")
+ #obj.os = "SunOS"
+ }
+ Facter["HardwareISA"].add { |obj|
+ obj.code = 'uname -p'
+ obj.interpreter = '/bin/sh'
+ #obj.os = "SunOS"
+ obj.tag("operatingsystem","=","SunOS")
+ }
+ Facter["MacAddress"].add { |obj|
+ #obj.os = "SunOS"
+ obj.tag("operatingsystem","=","SunOS")
+ obj.code = proc {
+ ether = nil
+ output = %x{/sbin/ifconfig -a}
+
+ output =~ /ether (\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2})/
+ ether = $1
+
+ ether
+ }
+ }
+ Facter["MacAddress"].add { |obj|
+ #obj.os = "Darwin"
+ obj.tag("operatingsystem","=","Darwin")
+ obj.code = proc {
+ ether = nil
+ output = %x{/sbin/ifconfig}
+
+ output.split(/^\S/).each { |str|
+ if str =~ /10baseT/ # we're wired
+ str =~ /ether (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/
+ ether = $1
+ end
+ }
+
+ ether
+ }
+ }
+ Facter["IPHostnumber"].add { |obj|
+ #obj.os = "Darwin"
+ obj.tag("operatingsystem","=","Darwin")
+ obj.code = proc {
+ ip = nil
+ output = %x{/sbin/ifconfig}
+
+ output.split(/^\S/).each { |str|
+ if str =~ /inet ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/
+ tmp = $1
+ unless tmp =~ /127\./
+ ip = tmp
+ break
+ end
+ end
+ }
+
+ ip
+ }
+ }
+ Facter["Hostname"].add { |obj|
+ #obj.os = "Darwin"
+ #obj.release = "R7"
+ obj.tag("operatingsystem","=","Darwin")
+ obj.tag("operatingsystemrelease","=","R7")
+ obj.code = proc {
+ hostname = nil
+ File.open(
+ "/Library/Preferences/SystemConfiguration/preferences.plist"
+ ) { |file|
+ found = 0
+ file.each { |line|
+ if line =~ /ComputerName/i
+ found = 1
+ next
+ end
+ if found == 1
+ if line =~ /<string>([\w|-]+)<\/string>/
+ hostname = $1
+ break
+ end
+ end
+ }
+ }
+
+ if hostname != nil
+ hostname
+ end
+ }
+ }
+ Facter["IPHostnumber"].add { |obj|
+ #obj.os = "Darwin"
+ #obj.release = "R6"
+ obj.tag("operatingsystem","=","Darwin")
+ obj.tag("operatingsystemrelease","=","R6")
+ obj.code = proc {
+ hostname = nil
+ File.open(
+ "/var/db/SystemConfiguration/preferences.xml"
+ ) { |file|
+ found = 0
+ file.each { |line|
+ if line =~ /ComputerName/i
+ found = 1
+ next
+ end
+ if found == 1
+ if line =~ /<string>([\w|-]+)<\/string>/
+ hostname = $1
+ break
+ end
+ end
+ }
+ }
+
+ if hostname != nil
+ hostname
+ end
+ }
+ }
+ Facter["IPHostnumber"].add { |obj|
+ #obj.os = "Darwin"
+ #obj.release = "R6"
+ obj.tag("operatingsystem","=","Darwin")
+ obj.tag("operatingsystemrelease","=","R6")
+ obj.code = proc {
+ ether = nil
+ output = %x{/sbin/ifconfig}
+
+ output =~ /HWaddr (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/
+ ether = $1
+
+ ether
+ }
+ }
+ Facter["Distro"].add { |obj|
+ #obj.os = "Linux"
+ obj.tag("operatingsystem","=","Linux")
+ obj.code = proc {
+ if FileTest.exists?("/etc/debian_version")
+ return "Debian"
+ elsif FileTest.exists?("/etc/gentoo-release")
+ return "Gentoo"
+ elsif FileTest.exists?("/etc/fedora-release")
+ return "Fedora"
+ elsif FileTest.exists?("/etc/redhat-release")
+ return "RedHat"
+ end
+ }
+ }
+ Facter["ps"].add { |obj|
+ obj.code = "echo 'ps -ef'"
+ }
+ Facter["ps"].add { |obj|
+ obj.tag("operatingsystem","=","Darwin")
+ obj.code = "echo 'ps -auxwww'"
+ }
+ end
+
+ Facter.load
+end
+
+# try to load a local fact library, if there happens to be one
+begin
+ require 'facter/local'
+rescue LoadError
+ # no worries
+end
diff --git a/tests/tc_facterbin.rb b/tests/tc_facterbin.rb
new file mode 100755
index 0000000..b4bde3f
--- /dev/null
+++ b/tests/tc_facterbin.rb
@@ -0,0 +1,35 @@
+#! /usr/bin/env ruby
+# $Id: $
+if __FILE__ == $0
+ $:.unshift '../lib'
+ $facterbase = ".."
+end
+
+require 'facter'
+require 'test/unit'
+
+# add the bin directory to our search path
+ENV["PATH"] += ":" + File.join($facterbase, "bin")
+
+# and then the library directories
+libdirs = $:.find_all { |dir|
+ dir =~ /facter/ or dir =~ /\.\./
+}
+ENV["RUBYLIB"] = libdirs.join(":")
+
+class TestFacterBin < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ def test_version
+ output = nil
+ assert_nothing_raised {
+ output = %x{facter --version 2>&1}.chomp
+ }
+ print output
+ assert ( output == Facter.version )
+ end
+end
diff --git a/tests/tc_simple.rb b/tests/tc_simple.rb
new file mode 100644
index 0000000..cda2557
--- /dev/null
+++ b/tests/tc_simple.rb
@@ -0,0 +1,268 @@
+#! /usr/bin/env ruby
+# $Id$
+#
+if __FILE__ == $0 # Make this library first!
+ $:.unshift '../lib'
+end
+
+require 'test/unit'
+require 'facter'
+
+if __FILE__ == $0
+ Facter.debugging(true)
+end
+
+class TestFacter < Test::Unit::TestCase
+ def setup
+ Facter.load
+ end
+
+ def teardown
+ # clear out the list of facts, so we start fresh for every test
+ Facter.reset
+ end
+
+ def test_version
+ #Could match /[0-9.]+/
+ #Strict match: /^[0-9]+(\.[0-9]+)*$/
+ #ok: 1.0.0 1.0 1
+ #notok: 1..0 1. .1 1a
+ assert(Facter.version =~ /^[0-9]+(\.[0-9]+)*$/ )
+ end
+
+ def test_notags_sh
+ assert_nothing_raised {
+ Facter["testing"].add { |fact|
+ fact.code = "echo yup"
+ }
+ }
+
+ assert_equal("yup", Facter["testing"].value)
+ end
+
+ def test_notags
+ assert_nothing_raised {
+ Facter["testing"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ }
+
+ assert_equal("foo", Facter["testing"].value)
+ end
+
+ def test_onetruetag
+ assert_nothing_raised {
+ Facter["required"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ Facter["testing"].add { |fact|
+ fact.code = proc { "bar" }
+ fact.tag("required","=","foo")
+ }
+ }
+
+ assert_equal("bar", Facter["testing"].value)
+ end
+
+ def test_onefalsetag
+ assert_nothing_raised {
+ Facter["required"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ Facter["testing"].add { |fact|
+ fact.code = proc { "bar" }
+ fact.tag("required","=","bar")
+ }
+ }
+
+ assert_equal(nil, Facter["testing"].value)
+ end
+
+ def test_recursivetags
+ assert_nothing_raised {
+ Facter["required"].add { |fact|
+ fact.code = proc { "foo" }
+ fact.tag("testing","=","foo")
+ }
+ Facter["testing"].add { |fact|
+ fact.code = proc { "bar" }
+ fact.tag("required","=","foo")
+ }
+ }
+
+ assert_equal(nil, Facter["testing"].value)
+ end
+
+ def test_multipleresolves
+ assert_nothing_raised {
+ Facter["funtest"].add { |fact|
+ fact.code = proc { "untagged" }
+ }
+ Facter["funtest"].add { |fact|
+ fact.code = proc { "tagged" }
+ fact.tag("operatingsystem","=", Facter["operatingsystem"].value)
+ }
+ }
+
+ assert_equal("tagged", Facter["funtest"].value)
+ end
+
+ def test_osname
+ assert_equal(
+ %x{uname -s}.chomp,
+ Facter["operatingsystem"].value
+ )
+ end
+
+ def test_osrel
+ assert_equal(
+ %x{uname -r}.chomp,
+ Facter["operatingsystemrelease"].value
+ )
+ end
+
+ def test_hostname
+ assert_equal(
+ %x{hostname}.chomp.sub(/\..+/,''),
+ Facter["hostname"].value
+ )
+ end
+
+ def test_upcase
+ Facter["Testing"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ assert_equal(
+ "foo",
+ Facter["Testing"].value
+ )
+ end
+
+ def test_doublecall
+ Facter["testing"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ assert_equal(
+ Facter["testing"].value,
+ Facter["testing"].value
+ )
+ end
+
+ def test_downcase
+ Facter["testing"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ assert_equal(
+ "foo",
+ Facter["testing"].value
+ )
+ end
+
+ def test_case_insensitivity
+ Facter["Testing"].add { |fact|
+ fact.code = proc { "foo" }
+ }
+ upcase = Facter["Testing"].value
+ downcase = Facter["testing"].value
+ assert_equal(upcase, downcase)
+ end
+
+ def test_adding
+ assert_nothing_raised() {
+ Facter["Funtest"].add { |obj|
+ obj.code = proc { return "funtest value" }
+ }
+ }
+
+ assert_equal(
+ "funtest value",
+ Facter["funtest"].value
+ )
+
+ assert_nothing_raised() {
+ code = proc { return "yaytest value" }
+ block = proc { |obj| obj.code = code }
+ Facter["Yaytest"].add(&block)
+ }
+
+ assert_equal(
+ "yaytest value",
+ Facter["yaytest"].value
+ )
+ end
+
+ def test_comparison
+ assert(
+ %x{uname -s}.chomp == Facter["operatingsystem"].value
+ )
+ assert(
+ %x{hostname}.chomp.sub(/\..+/,'') == Facter["hostname"].value
+ )
+ end
+
+ def test_adding2
+ assert_nothing_raised() {
+ Facter["bootest"].add { |obj|
+ obj.tag("operatingsystem", "=", Facter["operatingsystem"].value)
+ obj.code = "echo bootest"
+ }
+ }
+
+ assert_equal(
+ "bootest",
+ Facter["bootest"].value
+ )
+
+ assert_nothing_raised() {
+ Facter["bahtest"].add { |obj|
+ #obj.os = Facter["operatingsystem"].value
+ #obj.release = Facter["operatingsystemrelease"].value
+ obj.tag("operatingsystem", "=", Facter["operatingsystem"].value)
+ obj.tag("operatingsystemrelease", "=",
+ Facter["operatingsystemrelease"].value)
+ obj.code = "echo bahtest"
+ }
+ }
+
+ assert_equal(
+ "bahtest",
+ Facter["bahtest"].value
+ )
+
+ assert_nothing_raised() {
+ Facter["failure"].add { |obj|
+ #obj.os = Facter["operatingsystem"].value
+ #obj.release = "FakeRelease"
+ obj.tag("operatingsystem", "=", Facter["operatingsystem"].value)
+ obj.tag("operatingsystemrelease", "=", "FakeRelease")
+ obj.code = "echo failure"
+ }
+ }
+
+ assert_equal(
+ nil,
+ Facter["failure"].value
+ )
+ end
+
+ def test_distro
+ if Facter["operatingsystem"] == "Linux"
+ assert(Facter["Distro"])
+ end
+ end
+
+ def test_each
+ list = {}
+ assert_nothing_raised {
+ Facter.each { |name,fact|
+ list[name] = fact
+ }
+ }
+
+ list.each { |name,value|
+ assert(value.class != Facter)
+ assert(name)
+ assert(value)
+ }
+ end
+end