diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | LICENSE | 17 | ||||
-rw-r--r-- | README | 9 | ||||
-rw-r--r-- | Rakefile | 277 | ||||
-rw-r--r-- | TODO | 4 | ||||
-rwxr-xr-x | bin/facter | 72 | ||||
-rw-r--r-- | etc/facter.conf | 5 | ||||
-rw-r--r-- | install.rb | 289 | ||||
-rw-r--r-- | lib/facter.rb | 766 | ||||
-rwxr-xr-x | tests/tc_facterbin.rb | 35 | ||||
-rw-r--r-- | tests/tc_simple.rb | 268 |
13 files changed, 2089 insertions, 0 deletions
@@ -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. @@ -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. @@ -0,0 +1,2 @@ +Unforantely, no install.rb yet. Hopefully soon. Just copy the libs into +your main ruby library directory. Or something. @@ -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 @@ -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 + @@ -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 = "[0;32m" + RESET = "[0m" + @@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 |