From 7e9c9b84a6449f70c768a2334c1a68ce73499cd0 Mon Sep 17 00:00:00 2001
From: astokes_rhn
Date: Wed, 25 Jul 2007 15:10:20 +0000
Subject: branching off for stokes work
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/sos/trunk@223 ef72aa8b-4018-0410-8976-d6e080ef94d8
---
trunk/src/ChangeLog | 248 ++++++++++
trunk/src/LICENSE | 340 +++++++++++++
trunk/src/Makefile | 95 ++++
trunk/src/README | 24 +
trunk/src/TODO | 21 +
trunk/src/example_plugins/example.py | 94 ++++
trunk/src/example_plugins/fsusage.py | 23 +
trunk/src/example_plugins/release.py | 21 +
trunk/src/example_plugins/runcommand.py | 45 ++
trunk/src/lib/sos/__init__.py | 0
trunk/src/lib/sos/helpers.py | 120 +++++
trunk/src/lib/sos/plugins/__init__.py | 0
trunk/src/lib/sos/plugins/amd.py | 31 ++
trunk/src/lib/sos/plugins/apache.py | 26 +
trunk/src/lib/sos/plugins/bootloader.py | 31 ++
trunk/src/lib/sos/plugins/cluster.py | 78 +++
trunk/src/lib/sos/plugins/devicemapper.py | 38 ++
trunk/src/lib/sos/plugins/filesys.py | 47 ++
trunk/src/lib/sos/plugins/ftp.py | 24 +
trunk/src/lib/sos/plugins/general.py | 43 ++
trunk/src/lib/sos/plugins/hardware.py | 55 +++
trunk/src/lib/sos/plugins/initrd.py | 28 ++
trunk/src/lib/sos/plugins/kernel.py | 131 +++++
trunk/src/lib/sos/plugins/ldap.py | 27 ++
trunk/src/lib/sos/plugins/libraries.py | 24 +
trunk/src/lib/sos/plugins/mail.py | 25 +
trunk/src/lib/sos/plugins/memory.py | 30 ++
trunk/src/lib/sos/plugins/named.py | 36 ++
trunk/src/lib/sos/plugins/networking.py | 75 +++
trunk/src/lib/sos/plugins/nfsserver.py | 27 ++
trunk/src/lib/sos/plugins/pam.py | 25 +
trunk/src/lib/sos/plugins/printing.py | 24 +
trunk/src/lib/sos/plugins/process.py | 24 +
trunk/src/lib/sos/plugins/radius.py | 29 ++
trunk/src/lib/sos/plugins/rhn.py | 91 ++++
trunk/src/lib/sos/plugins/rpm.py | 33 ++
trunk/src/lib/sos/plugins/samba.py | 26 +
trunk/src/lib/sos/plugins/satellite.py | 53 +++
trunk/src/lib/sos/plugins/selinux.py | 36 ++
trunk/src/lib/sos/plugins/sendmail.py | 27 ++
trunk/src/lib/sos/plugins/squid.py | 23 +
trunk/src/lib/sos/plugins/ssh.py | 27 ++
trunk/src/lib/sos/plugins/startup.py | 25 +
trunk/src/lib/sos/plugins/system.py | 31 ++
trunk/src/lib/sos/plugins/systemtap.py | 29 ++
trunk/src/lib/sos/plugins/x11.py | 26 +
trunk/src/lib/sos/plugins/xen.py | 82 ++++
trunk/src/lib/sos/plugins/xinetd.py | 27 ++
trunk/src/lib/sos/plugins/yum.py | 37 ++
trunk/src/lib/sos/plugintools.py | 568 ++++++++++++++++++++++
trunk/src/lib/sos/policyredhat.py | 151 ++++++
trunk/src/locale/en/LC_MESSAGES/sos.po | 99 ++++
trunk/src/locale/it/LC_MESSAGES/sos.po | 99 ++++
trunk/src/pylintrc | 354 ++++++++++++++
trunk/src/setup.py | 14 +
trunk/src/sos.spec | 145 ++++++
trunk/src/sosreport | 725 ++++++++++++++++++++++++++++
trunk/src/sosreport.1 | 55 +++
trunk/src/tools/msgfmt.py | 203 ++++++++
trunk/src/tools/pygettext.py | 762 ++++++++++++++++++++++++++++++
60 files changed, 5657 insertions(+)
create mode 100644 trunk/src/ChangeLog
create mode 100644 trunk/src/LICENSE
create mode 100644 trunk/src/Makefile
create mode 100644 trunk/src/README
create mode 100644 trunk/src/TODO
create mode 100755 trunk/src/example_plugins/example.py
create mode 100644 trunk/src/example_plugins/fsusage.py
create mode 100644 trunk/src/example_plugins/release.py
create mode 100755 trunk/src/example_plugins/runcommand.py
create mode 100644 trunk/src/lib/sos/__init__.py
create mode 100755 trunk/src/lib/sos/helpers.py
create mode 100644 trunk/src/lib/sos/plugins/__init__.py
create mode 100644 trunk/src/lib/sos/plugins/amd.py
create mode 100644 trunk/src/lib/sos/plugins/apache.py
create mode 100644 trunk/src/lib/sos/plugins/bootloader.py
create mode 100644 trunk/src/lib/sos/plugins/cluster.py
create mode 100644 trunk/src/lib/sos/plugins/devicemapper.py
create mode 100644 trunk/src/lib/sos/plugins/filesys.py
create mode 100644 trunk/src/lib/sos/plugins/ftp.py
create mode 100644 trunk/src/lib/sos/plugins/general.py
create mode 100644 trunk/src/lib/sos/plugins/hardware.py
create mode 100644 trunk/src/lib/sos/plugins/initrd.py
create mode 100644 trunk/src/lib/sos/plugins/kernel.py
create mode 100644 trunk/src/lib/sos/plugins/ldap.py
create mode 100644 trunk/src/lib/sos/plugins/libraries.py
create mode 100644 trunk/src/lib/sos/plugins/mail.py
create mode 100644 trunk/src/lib/sos/plugins/memory.py
create mode 100644 trunk/src/lib/sos/plugins/named.py
create mode 100644 trunk/src/lib/sos/plugins/networking.py
create mode 100644 trunk/src/lib/sos/plugins/nfsserver.py
create mode 100644 trunk/src/lib/sos/plugins/pam.py
create mode 100644 trunk/src/lib/sos/plugins/printing.py
create mode 100644 trunk/src/lib/sos/plugins/process.py
create mode 100644 trunk/src/lib/sos/plugins/radius.py
create mode 100644 trunk/src/lib/sos/plugins/rhn.py
create mode 100644 trunk/src/lib/sos/plugins/rpm.py
create mode 100644 trunk/src/lib/sos/plugins/samba.py
create mode 100644 trunk/src/lib/sos/plugins/satellite.py
create mode 100644 trunk/src/lib/sos/plugins/selinux.py
create mode 100644 trunk/src/lib/sos/plugins/sendmail.py
create mode 100644 trunk/src/lib/sos/plugins/squid.py
create mode 100644 trunk/src/lib/sos/plugins/ssh.py
create mode 100644 trunk/src/lib/sos/plugins/startup.py
create mode 100644 trunk/src/lib/sos/plugins/system.py
create mode 100644 trunk/src/lib/sos/plugins/systemtap.py
create mode 100644 trunk/src/lib/sos/plugins/x11.py
create mode 100644 trunk/src/lib/sos/plugins/xen.py
create mode 100644 trunk/src/lib/sos/plugins/xinetd.py
create mode 100644 trunk/src/lib/sos/plugins/yum.py
create mode 100644 trunk/src/lib/sos/plugintools.py
create mode 100755 trunk/src/lib/sos/policyredhat.py
create mode 100644 trunk/src/locale/en/LC_MESSAGES/sos.po
create mode 100644 trunk/src/locale/it/LC_MESSAGES/sos.po
create mode 100644 trunk/src/pylintrc
create mode 100644 trunk/src/setup.py
create mode 100644 trunk/src/sos.spec
create mode 100755 trunk/src/sosreport
create mode 100644 trunk/src/sosreport.1
create mode 100644 trunk/src/tools/msgfmt.py
create mode 100644 trunk/src/tools/pygettext.py
(limited to 'trunk/src')
diff --git a/trunk/src/ChangeLog b/trunk/src/ChangeLog
new file mode 100644
index 00000000..92947fd9
--- /dev/null
+++ b/trunk/src/ChangeLog
@@ -0,0 +1,248 @@
+2007-06-15 Navid Sheikhol-Eslami
+
+ * [BZ#241282] initial port to RHEL4 :) sos core now runs happily, plugins will probably need to be fixed as well.
+ * Initial commit of XML reporting to gather details about commands executed and files gathered.
+ * Exceptions in plugin.analyse() were not catched allowing a bad plugin to break sosreport.
+
+2007-06-15 Eugene Teo
+
+ * lib/sos/plugins/apache.py, lib/sos/plugins/nfsserver.py, lib/sos/plugins/selinux.py, lib/sos/plugins/xinetd.py, lib/sos/plugins/ssh.py, lib/sos/plugins/sendmail.py, lib/sos/plugins/samba.py, lib/sos/plugins/named.py, lib/sos/plugins/cluster.py:
+ - Edited apache.py to gather /var/log/httpd/* log files
+ - Added nfsserver.py to gather NFS server-related debugging information
+ - Edited selinux.py to gather /etc/selinux/* configuration files
+ - Added xinetd.py to gather xinetd-related information
+ - Added ssh.py to gather ssh-related information
+ - Added sendmail.py to gather sendmail information
+ - Edited samba.py to gather /var/log/samba/* log files
+ - Edited named.py to gather /etc/sysconfig/named
+ - Edited cluster.py to gather the output of fdisk -l to show the
+ shared storage devices that should be available to each system
+
+2007-05-28 Eugene Teo
+
+ * lib/sos/plugins/systemtap.py:
+ - Added systemtap.py to gather SystemTap pre-requisites information
+
+2007-05-28 Eugene Teo
+
+ * lib/sos/plugins/amd.py:
+ - Added amd.py to gather Amd automounter information
+
+2007-05-25 Eugene Teo
+
+ * lib/sos/plugins/xen.py, lib/sos/plugins/pam.py, lib/sos/plugins/memory.py:
+ - Edited xen.py to determine if CPU has PAE/Intel VT/AMD-V support
+ - Edited pam.py to gather configurations in /etc/security, and files
+ listing of /lib/security/pam_*so
+ - Edited memory.py to gather /proc/{vmstat,slabinfo}, and free -m
+ output
+
+2007-04-23 Navid Sheikhol-Eslami
+
+ * Running "multipath" without arguments might change the device-mapper maps, which we want to avoid if things are broken.
+
+2007-04-02 Navid Sheikhol-Eslami
+
+ * Replaced xen plugin with (better) version from Chris Lalancette
+
+2007-03-29 Navid Sheikhol-Eslami
+
+ * Added a checkenabled() function which can be used to disable a plugin at run-time.
+ * Disable the progress-bar if verbosity is enabled.
+
+2007-03-27 Navid Sheikhol-Eslami
+
+ * Fixed hardware plugin to use modules.pcimap instead of deprecated pcitable.
+ * Added a random suffix to sosreport tree to avoid overwriting an existing tree with same name.
+ * Better logging using python's logging module.
+ * Verbose logs included in sosreport (sos_logs/sos.log)
+
+2007-03-15 Navid Sheikhol-Eslami
+
+ * Implemented a progress bar (RFE BZ#219672) which can be disabled from the command line.
+ * Added check to see if the loaded module matches the copy on the file-system
+
+2007-03-14 Navid Sheikhol-Eslami
+
+ * fixed BZ#219877 (ncurses "Cancel" button makes sosreport exit)
+
+2007-03-07 Navid Sheikhol-Eslami
+
+ * Allow passing multiple comma-separated plugin names to -n (--noplugin) and -o (--onlyplugin) options.
+ * Added further commands' output to gather from lvm_dump
+
+
+2007-02-20 Navid Sheikhol-Eslami
+
+ * Added a specialized plugin for device-mapper related configuration files and command outputs (device-mapper, LVM and multipath)
+ * Added --onlyplugin option (-o) to selectively choose which plugins to load (complementary to existing --noplugin)
+ * Exit if no valid plugin was selected (rather than building an empty sosreport).
+
+2007-02-16 Navid Sheikhol-Eslami
+
+ * Strip out the shared secret (bindpw) from /etc/ldap.conf
+ * Collect parsed configuration tree directly from ccsd (useful for troubleshooting parsing issues)
+ * Scamble password information for fencing devices.
+
+2007-01-26 Navid Sheikhol-Eslami
+
+ * Added doRegexSub() to be called in postproc() to apply a regexp substitution to files
+ * Added radius plugin for freeradius data collection
+ * Ask full name to prevent errors when moving the sos tree before packaging
+ * Reformat tar file name is no ticket number is given
+
+2006-06-19 Steve Conklin
+
+ * ChangeLog, LICENSE, setup.py, sos.spec:
+ Added License file and bumped release
+
+2006-06-08 dlehman
+
+ * example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/template.py, lib/sos/helpers.py, lib/sos/plugins/apache.py, lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/libraries.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/samba.py, lib/sos/plugins/selinux.py, lib/sos/plugins/squid.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/tarball.py, lib/sos/plugins/x11.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.py, sosreport:
+ - Flesh out rhn plugin to handle Proxy or Satellite
+ - Add package queries to policyredhat.py (allPkgsByName, pkgByName, pkgNVRA)
+ - Add policy instance to the commons dict for access from plugins
+ - Use string objects' methods instead of the string module where possible
+ - Remove imports of unused string module
+ - Cleanup some typos, redundant initializations, &c
+
+2006-06-08 dlehman
+
+ * example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/template.py, lib/sos/helpers.py, lib/sos/plugins/apache.py, lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/libraries.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/samba.py, lib/sos/plugins/selinux.py, lib/sos/plugins/squid.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/tarball.py, lib/sos/plugins/x11.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.py, sosreport:
+ - Flesh out rhn plugin to handle Proxy or Satellite
+ - Add package queries to policyredhat.py (allPkgsByName, pkgByName, pkgNVRA)
+ - Add policy instance to the commons dict for access from plugins
+ - Use string objects' methods instead of the string module where possible
+ - Remove imports of unused string module
+ - Cleanup some typos, redundant initializations, &c
+
+2006-06-05 jwhiter
+
+ * lib/sos/plugins/system.py:
+ adding the abilit to capture the autofs maps to system.py
+
+2006-05-31 Steve Conklin
+
+ * ChangeLog, Makefile: New Makefile and ChangeLog (autogenerated)
+
+ * Changelog, setup.py, sos.spec:
+ Removed old Changelog file and sync'd version and Changelog in spec file
+
+ * lib/sos/plugins/networking.py, TODO, setup.py, sos.spec:
+ Final patches and version change before submission to Fedora
+
+2006-05-31 Steve Conklin
+
+ * Changelog, setup.py, sos.spec:
+ Removed old Changelog file and sync'd version and Changelog in spec file
+
+ * lib/sos/plugins/networking.py, TODO, setup.py, sos.spec:
+ Final patches and version change before submission to Fedora
+
+2006-05-26 Steve Conklin
+
+ * Changelog, lib/sos/helpers.py, lib/sos/plugintools.py, setup.py, sosreport:
+ Added pamadio's curses UI for selecting plugin options
+ Added flushing stdout after informational messages
+
+2006-05-26 jwhiter
+
+ * lib/sos/plugins/filesys.py:
+ - making the filesys.py plugin call 'blkid' when running at the request of L1, this will allow us to map labels with actual devices.
+
+ * lib/sos/plugins/tarball.py, lib/sos/plugintools.py, sosreport:
+ - Adding tarball.py to create a tarball of the report after it is run
+ - Updated sosreport to call postproc() in all the plugins, which handles the post run
+ - Added runExeInd() which will just run the exe and return the status to plugintools.py
+ - Added postproc() to plugintools.py so that plugins can implement it
+
+2006-05-25 Steve Conklin
+
+ * lib/sos/plugintools.py, sosreport:
+ Fixed file naming for commands to eliminate special chars and prevent
+ name collisions. Added sorting of plugins by name before reporting.
+
+ * TODO, lib/sos/plugins/kernel.py, lib/sos/plugintools.py, setup.py:
+ Fixed option handling
+
+2006-05-23 jwhiter
+
+ * Changelog, lib/sos/plugins/kernel.py, setup.py:
+ Adding jwb's patch to have sosreport grab sysrq data.
+
+2006-05-22 Steve Conklin
+
+ * lib/sos/plugins/apache.py: oops, forgot this file
+
+ * lib/sos/plugins/bootloader.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/samba.py, lib/sos/plugins/squid.py, lib/sos/plugins/x11.py, sos.spec:
+ Patch from jwb
+
+ * lib/sos/plugins/filesys.py, lib/sos/plugins/kernel.py:
+ jwb's patches for kernel.py and filesys.py
+
+ * sosreport: minor fix to the dir perms patch
+
+ * lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/libraries.py, lib/sos/plugins/memory.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/selinux.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/template.py, lib/sos/plugins/x11.py, Changelog, example_plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport:
+ Merged all of jwb's weekend patches. Make output dirs world readable, check before
+ executing executables, and added a lot of plugins.
+
+2006-05-19 Steve Conklin
+
+ * lib/sos/plugins/template.py: Removed unneeded variabled from plugin.
+
+ * Changelog, TODO, example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/runcommand.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport:
+ Applied jwb's fix, added his examples, improved html output
+
+2006-05-18 Steve Conklin
+
+ * example_plugins/example.py, example_plugins/runcommand.py, lib/sos/plugins/template.py:
+ Put instance variables in plugins in addition to base class
+
+ * example_plugins/example.py, example_plugins/runcommand.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport:
+ Removed separate pit class, and put everything having to do with the plugin in
+ pluginBase. Still incorrectly aggregates data across all plugins.
+
+ * README: Added Jwb as a contributor
+
+ * Changelog, TODO, lib/sos/plugins/template.py, lib/sos/plugintools.py, sosreport:
+ Implemented a base class for plugins
+
+2006-05-17 Steve Conklin
+
+ * Changelog, TODO, lib/sos/helpers.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, sosreport, tests/maketesttree.sh:
+ Cleaned up code, added comments, fixed dir copying bug, changed option
+ handling. See Changelog
+
+2006-05-16 Steve Conklin
+
+ * lib/sos/helpers.py, lib/sos/plugintools.py:
+ Missed an edit to change log file descriptor to the dictionary. Fixed.
+
+ * TODO: Added documentation to list
+
+ * TODO: Added need for example plugin
+
+2006-05-15 Steve Conklin
+
+ * README, TODO, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport:
+ Added a dictionary of things that need to be known to all, like paths for
+ reports.
+
+ Added Pierre Amadio's command line arg handling
+
+ Fixed incorrect handling of command completion status
+
+ Added html generation
+
+2006-05-09 Steve Conklin
+
+ * TODO: changed it
+
+ * setup.cfg: removed RFC file
+
+ * MANIFEST.in, Notes.txt, README, TODO, lib/sos/__init__.py, lib/sos/helpers.py, lib/sos/plugins/__init__.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.cfg, setup.py, sosreport:
+ Initial checkin of the sos project
+
+ * MANIFEST.in, Notes.txt, README, TODO, lib/sos/__init__.py, lib/sos/helpers.py, lib/sos/plugins/__init__.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.cfg, setup.py, sosreport:
+ New file.
+
diff --git a/trunk/src/LICENSE b/trunk/src/LICENSE
new file mode 100644
index 00000000..7a8e8abf
--- /dev/null
+++ b/trunk/src/LICENSE
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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.
+
+
+ Copyright (C)
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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) year 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.
+
+ , 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 Lesser General
+Public License instead of this License.
diff --git a/trunk/src/Makefile b/trunk/src/Makefile
new file mode 100644
index 00000000..99d2ed5d
--- /dev/null
+++ b/trunk/src/Makefile
@@ -0,0 +1,95 @@
+#
+# Makefile for sos system support tools
+#
+
+NAME = sos
+VERSION = $(shell awk '/^%define version / { print $$3 }' sos.spec)
+REPO = https://sos.108.redhat.com/svn/sos
+SVNTAG = r$(subst .,-,$(VERSION))
+SRCDIR = $(PWD)
+TOPDIR = $(PWD)/build/rpm-$(NAME)-$(VERSION)
+
+
+all:
+
+.PHONY: tag-release tarball release install version clean
+
+diff-tag:
+ svn diff $(REPO)/trunk/src $(REPO)/tags/$(SVNTAG)
+
+tag:
+ @if ( svn list $(REPO)/tags/$(SVNTAG)/Makefile &> /dev/null ); then \
+ echo "The repository already contains a tag for version $(VERSION)"; \
+ exit 1; \
+ fi
+ @svn copy $(REPO)/trunk/src $(REPO)/tags/$(SVNTAG) \
+ -m "Tagging the $(SVNTAG) release of the sos project"
+ @echo "Tagged as $(SVNTAG)"
+
+tag-force:
+ @echo svn del $(REPO)/tags/$(SVNTAG)
+ @echo make diff-tag
+
+tarball: clean
+ @echo "Creating an archive from HEAD of development"
+ @rm -rf /tmp/$(NAME)
+ @svn export -q $(REPO)/trunk/src /tmp/$(NAME) \
+ || echo GRRRrrrrr -- ignore [export aborted]
+ @mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION)
+ @cd /tmp; tar --bzip2 -cSpf $(NAME)-$(VERSION).tar.bz2 $(NAME)-$(VERSION)
+ @rm -rf /tmp/$(NAME)-$(VERSION)
+ @mv /tmp/$(NAME)-$(VERSION).tar.bz2 .
+ @echo " "
+ @echo "The final archive is ./$(NAME)-$(VERSION).tar.bz2."
+
+release: clean
+ @if ( ! svn list $(REPO)/tags/$(SVNTAG)/Makefile &> /dev/null ); then \
+ echo "There is no tag in the repository for this version, must be tagged before release"; \
+ exit 1; \
+ fi
+ @echo "Creating an archive from tag $(SVNTAG)"
+ @rm -rf /tmp/$(NAME)
+ @svn export -q $(REPO)/tags/$(SVNTAG) /tmp/$(NAME) \
+ || echo GRRRrrrrr -- ignore [export aborted]
+ @mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION)
+ @cd /tmp; tar --bzip2 -cSpf $(NAME)-$(VERSION).tar.bz2 $(NAME)-$(VERSION)
+ @rm -rf /tmp/$(NAME)-$(VERSION)
+ @cp /tmp/$(NAME)-$(VERSION).tar.bz2 .
+ @rm -f /tmp/$(NAME)-$(VERSION).tar.bz2
+ @echo " "
+ @echo "The final archive is ./$(NAME)-$(VERSION).tar.bz2."
+
+install:mo
+ python setup.py install
+
+version:
+ @echo "The version is $(NAME)-$(VERSION)"
+
+clean:
+ @rm -fv *~ .*~ changenew ChangeLog.old $(NAME)-$(VERSION).tar.bz2 sosreport.1.gz
+
+rpm: mo
+ @test -f sos.spec
+ @mkdir -p $(TOPDIR)/SOURCES $(TOPDIR)/SRPMS $(TOPDIR)/RPMS $(TOPDIR)/BUILD
+
+# this builds an RPM from the current working copy
+ @cd $(TOPDIR)/BUILD ; \
+ rm -rf $(NAME)-$(VERSION) ; \
+ ln -s $(SRCDIR) $(NAME)-$(VERSION) ; \
+ tar --gzip --exclude=.svn --exclude=svn-commit.tmp --exclude=$(NAME)-$(VERSION)/build --exclude=$(NAME)-$(VERSION)/dist \
+ -chSpf $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION) ; \
+ rm -f $(NAME)-$(VERSION)
+
+# this builds an RPM from HEAD
+# @rm -rf $(TOPDIR)/BUILD/$(NAME)-$(VERSION)
+# @svn export -q $(REPO)/trunk/src $(TOPDIR)/BUILD/$(NAME)-$(VERSION)
+# @cd $(TOPDIR)/BUILD ; tar --gzip -cSpf $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION); rm -rf $(NAME)-$(VERSION)
+
+ rpmbuild -ba --define="_topdir $(TOPDIR)" sos.spec
+ @mv $(TOPDIR)/RPMS/noarch/$(NAME)-$(VERSION)*.rpm $(TOPDIR)/SRPMS/$(NAME)-$(VERSION)*.rpm $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz dist/
+
+pot:
+ python tools/pygettext.py -o locale/sos.pot sosreport
+
+mo:
+ python tools/msgfmt.py locale/*/LC_MESSAGES/sos.po
diff --git a/trunk/src/README b/trunk/src/README
new file mode 100644
index 00000000..b65b07ed
--- /dev/null
+++ b/trunk/src/README
@@ -0,0 +1,24 @@
+This set of tools is designed to provide information to support
+organizations in an extensible manner, allowing third parties,
+package maintainers, and anyone else to provide plugins that will
+collect, analyze, and report information that is useful for supporting
+software packages.
+
+This project is hosted at http://sos.108.redhat.com. For the latest version,
+to contribute, and for more information, please visit there.
+
+To access to the public source code repository for this project run:
+
+ svn checkout https://sos.108.redhat.com/svn/sos/trunk sos
+
+(all the following as root)
+to install locally ==> python setup.py install
+to build an rpm ==> make rpm
+
+See the Makefile.
+
+Contributors:
+Steve Conklin
+Pierre Amadio
+John Berninger
+Navid Sheikhol-Eslami
diff --git a/trunk/src/TODO b/trunk/src/TODO
new file mode 100644
index 00000000..2f2d323f
--- /dev/null
+++ b/trunk/src/TODO
@@ -0,0 +1,21 @@
+To Do List:
+
+ * Gather statistics (time, files collected, commands run) per plugin
+
+ * Choose sane defaults to satisfy Red Hat support requirements (ie. what
+ plugins should be loaded by default, with what options, etc)
+
+ * Display on screen what files/commands are being gathered.
+
+ * Make sosreport a drop-in replacement for sysreport
+
+ * Allow to use a different rootdir than /
+
+ * Add support for distributions other than Fedora.
+
+ * Make it easier to select a policy module and perhaps optionally include
+ plugins from the library, to allow per-distribution customization.
+
+ * Documentation - plugin howto doc, inline doc in plugin template
+
+ * Review and test error handling for things like a full tmp file system
diff --git a/trunk/src/example_plugins/example.py b/trunk/src/example_plugins/example.py
new file mode 100755
index 00000000..1e650417
--- /dev/null
+++ b/trunk/src/example_plugins/example.py
@@ -0,0 +1,94 @@
+## example.py
+## An example sos plugin
+
+### 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.
+
+import sos.plugintools
+
+# Class name must be the same as file name and method names must not change
+class example(sos.plugintools.PluginBase):
+ """This is an example plugin for sos. Plugins gather, analyze, and report on various aspects
+ of system operation that are of interest. plugins are based on the PluginBase class, which
+ you should inspect if you wish to override any methods in your plugin. The methods of use
+ to plugin developers are:
+ collect() - use the functions sosCp and sosRunExe to gether information
+ analyze() - perform any special analysis you require beyond just saving the
+ information gathered by collect(). Use sosAlert() and sosAddCustomText()
+ to add information to the report.
+ report() - override this method if you wish to replace the default reporting
+
+ All plugins will use collect(), some will use analyze(), few will override report()
+ """
+
+ # Add your options here, indicate whether they are slow to run, and set whether they are enabled by default
+ # Options are python dictionaries that conatin a short name, long description, speed, and whether they are enabled by default
+ optionList = [("init.d", 'Gathers the init.d directory', 'slow', 0),
+ ('follicles', 'Gathers information about each follicle on every toe', 'slow', 0),
+ ('color', 'Gathers toenail polish color', 'fast', 0)]
+
+ def setup(self):
+ ''' First phase - Collect all the information we need.
+ Directories are copied recursively. arbitrary commands may be
+ executed using the susRunExe method. Information is automatically saved, and
+ links are presented in the report to each file or directory which has been
+ copied to the saved tree. Also, links are provided to the output from each command.
+ '''
+ # Here's how to copy files and directory trees
+ self.addCopySpec("/etc/hosts")
+ # this one saves a file path to the copy for later analysis
+ # FIXME: Need to figure out how to do this
+ # self.fooFilePath = self.copyFileOrDir("/proc/cpuinfo")
+
+ # Here's how to test your options and execute if enabled
+ if self.isOptionEnabled("init.d"):
+ self.addCopySpec("/etc/init.d") # copies a whole directory tree
+
+ # Here's how to execute a command
+ # you can save the path to the copied file for later analysis if desired
+ # FIXME: Need to figure out how to do this
+ self.psCmdDstFileName = self.runExe("/bin/ps -ef")
+ return
+
+ def analyze(self):
+ ''' This is optional and need not be defined.
+ If you wish to perform some analysis on either files
+ that were gathered or on the output of commands, then save the filenames on the
+ destination file system when gathering that information in the collect() method
+ and use them here
+ '''
+ # This is an example of opening and reading the output of a command that
+ # was run in the collect() method. Note that the output of the command is
+ # included in the report anyway
+ fd = open(self.fooFilePath)
+ lines = fd.readlines()
+ fd.close()
+ for line in lines:
+ if line.count("vendor_id"):
+ self.addCustomText("Vendor ID string is: %s \n" % line)
+ #
+ # Alerts can optionally be generated, and will be included in the
+ # report automatically
+ #
+ self.addAlert("This is an alert")
+ return
+
+# def report(self):
+# """ Usually, this doesn't even need to be defined, and you can inherit the
+# base class, unless you want to replace what sosGetResults()
+# does with your own custom report generator. If you are going to do that, have a good
+# look at that method to see what it does. Custom text and alerts can still be added
+# here using sosAddCustomText() and sosAddAlert()
+# This method returns html that will be included inline in the report
+# """
diff --git a/trunk/src/example_plugins/fsusage.py b/trunk/src/example_plugins/fsusage.py
new file mode 100644
index 00000000..2532c5f7
--- /dev/null
+++ b/trunk/src/example_plugins/fsusage.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+### 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.
+
+import sos.plugintools
+
+# Class name must be the same as file name and method names must not change
+class fsusage(sos.plugintools.PluginBase):
+ def setup(self):
+ self.psCmdDstFileName = self.collectExtOutput("/bin/df -al")
+ return
diff --git a/trunk/src/example_plugins/release.py b/trunk/src/example_plugins/release.py
new file mode 100644
index 00000000..db4d7581
--- /dev/null
+++ b/trunk/src/example_plugins/release.py
@@ -0,0 +1,21 @@
+### 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.
+
+import sos.plugintools
+
+# Class name must be the same as file name and method names must not change
+class release(sos.plugintools.PluginBase):
+ def setup(self):
+ self.addCopySpec("/etc/redhat-release")
+ return
diff --git a/trunk/src/example_plugins/runcommand.py b/trunk/src/example_plugins/runcommand.py
new file mode 100755
index 00000000..df8951d2
--- /dev/null
+++ b/trunk/src/example_plugins/runcommand.py
@@ -0,0 +1,45 @@
+## runcommand.py
+## An example plugin for sos
+
+### 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.
+
+import sos.plugintools
+
+# Class name must be the same as file name and method names must not change
+class runcommand(sos.plugintools.PluginBase):
+ """This is a very simple example plugin that just runs one command in a shell. That
+ command could be a script supplied with your package which outputs information of interest
+ to support. When the script or command is run, stdout is collected and included in the
+ report generated by sosreport, and stderr is collected and copied into the sos log.
+
+ This is useful for people who have minimal knowledge of python, who wish to write
+ collection tools in other languages, or who have existing tools.
+
+ If your script or command generates output files that you want included in the sosreport
+ collection of files, include sosCp calls to include them.
+
+ The only method required for this simple plugin is collect().
+
+ Your finished plugin should be included with your package and installed in python's
+ site-packages/sos/plugins/ directory
+ """
+ def setup(self):
+ ''' Run a command. Output is automatically included in the report.
+ '''
+ self.collectExtOutput("/path/to/my/script --myoption --anotheroption")
+
+ # if (for example) that command created files /foo/bar/baz.txt and
+ # we want to include that in our report, we include the next line:
+ #self.pit.sosCp("/foo/bar/baz.txt")
diff --git a/trunk/src/lib/sos/__init__.py b/trunk/src/lib/sos/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/trunk/src/lib/sos/helpers.py b/trunk/src/lib/sos/helpers.py
new file mode 100755
index 00000000..54a86dba
--- /dev/null
+++ b/trunk/src/lib/sos/helpers.py
@@ -0,0 +1,120 @@
+## helpers.py
+## Implement policies required for the sos system support tool
+
+## Copyright (C) 2006 Steve Conklin
+
+### 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.
+
+## Some code adapted from "Python Cookbook, 2nd ed", by Alex
+## Martelli, Anna Martelli Ravenscroft, and David Ascher
+## (O'Reilly Media, 2005) 0-596-00797-3
+##
+
+"""
+helper functions used by sosreport and plugins
+"""
+import os, popen2, fcntl, select, itertools, sys, commands
+from time import time
+from tempfile import mkdtemp
+
+def importPlugin(pluginname, name):
+ """ Import a plugin to extend capabilities of sosreport
+ """
+ try:
+ plugin = __import__(pluginname, globals(), locals(), [name])
+ except ImportError:
+ return None
+ return getattr(plugin, name)
+
+
+def sosFindTmpDir():
+ """Find a temp directory to form the root for our gathered information
+ and reports.
+ """
+ workingBase = mkdtemp("","sos_")
+ return workingBase
+
+
+def makeNonBlocking(afd):
+ """ Make the file descriptor non-blocking. This prevents deadlocks.
+ """
+ fl = fcntl.fcntl(afd, fcntl.F_GETFL)
+ try:
+ fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.O_NDELAY)
+ except AttributeError:
+ fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY)
+
+
+def sosGetCommandOutput(command):
+ """ Execute a command and gather stdin, stdout, and return status.
+ Adapted from Python Cookbook - O'Reilly
+ """
+ stime = time()
+ errdata = ''
+ status,outdata=commands.getstatusoutput(command)
+ return (status, outdata, time()-stime)
+
+
+# FIXME: this needs to be made clean and moved to the plugin tools, so
+# that it prints nice color output like sysreport if the progress bar
+# is not enabled.
+def sosStatus(stat):
+ """ Complete a status line that has been output to the console,
+ providing pass/fail indication.
+ """
+ if not stat:
+ print " [ OK ]"
+ else:
+ print " [ FAILED ]"
+ sys.stdout.flush()
+ return
+
+
+def allEqual(elements):
+ ''' return True if all the elements are equal, otherwise False. '''
+ first_element = elements[0]
+ for other_element in elements[1:]:
+ if other_element != first_element:
+ return False
+ return True
+
+
+def commonPrefix(*sequences):
+ ''' return a list of common elements at the start of all sequences,
+ then a list of lists that are the unique tails of each sequence. '''
+ # if there are no sequences at all, we're done
+ if not sequences:
+ return [], []
+ # loop in parallel on the sequences
+ common = []
+ for elements in itertools.izip(*sequences):
+ # unless all elements are equal, bail out of the loop
+ if not allEqual(elements):
+ break
+ # got one more common element, append it and keep looping
+ common.append(elements[0])
+ # return the common prefix and unique tails
+ return common, [ sequence[len(common):] for sequence in sequences ]
+
+def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir):
+ ''' return a relative path from path1 equivalent to path path2.
+ In particular: the empty string, if path1 == path2;
+ path2, if path1 and path2 have no common prefix.
+ '''
+ common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep))
+ if not common:
+ return path2 # leave path absolute if nothing at all in common
+ return sep.join( [pardir]*len(u1) + u2 )
+
diff --git a/trunk/src/lib/sos/plugins/__init__.py b/trunk/src/lib/sos/plugins/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/trunk/src/lib/sos/plugins/amd.py b/trunk/src/lib/sos/plugins/amd.py
new file mode 100644
index 00000000..1b1375cc
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/amd.py
@@ -0,0 +1,31 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class amd(sos.plugintools.PluginBase):
+ """Amd automounter information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/amd.*")
+ self.addCopySpec("/etc/rc.d/init.d/amd")
+ self.addCopySpec("/etc/sysconfig/amd")
+ self.collectExtOutput("/bin/rpm -qV am-utils")
+ self.collectExtOutput("/bin/egrep -e 'automount|pid.*nfs' /proc/mounts")
+ self.collectExtOutput("/bin/mount | egrep -e 'automount|pid.*nfs'")
+ self.collectExtOutput("/sbin/chkconfig --list amd")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/apache.py b/trunk/src/lib/sos/plugins/apache.py
new file mode 100644
index 00000000..c5250fd9
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/apache.py
@@ -0,0 +1,26 @@
+### 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.
+
+import sos.plugintools
+from threading import Thread
+
+class apache(sos.plugintools.PluginBase):
+ """Apache related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/httpd/conf/httpd.conf")
+ self.addCopySpec("/etc/httpd/conf.d/*.conf")
+ self.addCopySpec("/var/log/httpd/*")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/bootloader.py b/trunk/src/lib/sos/plugins/bootloader.py
new file mode 100644
index 00000000..a820069e
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/bootloader.py
@@ -0,0 +1,31 @@
+### 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.
+
+import sos.plugintools
+
+class bootloader(sos.plugintools.PluginBase):
+ """Bootloader information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/lilo.conf")
+ self.addCopySpec("/etc/milo.conf")
+ self.addCopySpec("/etc/silo.conf")
+ self.addCopySpec("/boot/grub/grub.conf")
+ self.addCopySpec("/boot/grub/device.map")
+ self.addCopySpec("/boot/efi/elilo.conf")
+ self.addCopySpec("/boot/yaboot.conf")
+
+ self.collectExtOutput("/sbin/lilo -q")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/cluster.py b/trunk/src/lib/sos/plugins/cluster.py
new file mode 100644
index 00000000..595d940a
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/cluster.py
@@ -0,0 +1,78 @@
+### 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.
+
+import sos.plugintools
+import commands, os
+
+class cluster(sos.plugintools.PluginBase):
+ """cluster suite and GFS related information
+ """
+ def checkenabled(self):
+ # enable if any related package is installed
+ for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins",
+ "rgmanager", "fence", "dlm", "dlm-kernel", "gulm",
+ "GFS", "GFS-kernel", "lvm2-cluster" ]:
+ if self.cInfo["policy"].pkgByName(pkg) != None:
+ return True
+
+ # enable if any related file is present
+ for fname in [ "/etc/cluster/cluster.conf" ]:
+ try: os.stat(fname)
+ except:pass
+ else: return True
+
+ # no data related to RHCS/GFS exists
+ return False
+
+ def diagnose(self):
+ rhelver = self.cInfo["policy"].pkgDictByName("fedora-release")[0]
+ if rhelver == "6":
+ # check if the minimum set of packages is installed
+ # for RHEL4 RHCS(ccs, cman, cman-kernel, magma, magma-plugins, (dlm, dlm-kernel) || gulm, perl-Net-Telnet, rgmanager, fence)
+ # RHEL4 GFS (GFS, GFS-kernel, ccs, lvm2-cluster, fence)
+ for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", "perl-Net-Telnet", "rgmanager", "fence" ]:
+ if self.cInfo["policy"].pkgByName(pkg) == None:
+ self.addDiagnose("required package is missing: %s" % pkg)
+
+ # check if all the needed daemons are active at sosreport time
+ # check if they are started at boot time in RHEL4 RHCS (cman, ccsd, rgmanager, fenced)
+ # and GFS (gfs, ccsd, clvmd, fenced)
+ for service in [ "cman", "ccsd", "rgmanager", "fence" ]:
+ if commands.getstatus("/sbin/service %s status" % service):
+ self.addDiagnose("service %s is not running" % service)
+
+ if not self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService(service):
+ self.addDiagnose("service %s is not started in default runlevel" % service)
+
+ # FIXME: what locking are we using ? check if packages exist
+# if self.cInfo["policy"].pkgByName(pkg) and self.cInfo["policy"].pkgByName(pkg) and not self.cInfo["policy"].pkgByName(pkg)
+
+ def setup(self):
+ self.collectExtOutput("/sbin/fdisk -l")
+ self.addCopySpec("/etc/cluster.conf")
+ self.addCopySpec("/etc/cluster.xml")
+ self.addCopySpec("/etc/cluster")
+ self.collectExtOutput("/usr/sbin/rg_test test /etc/cluster/cluster.conf")
+ self.addCopySpec("/proc/cluster")
+ self.collectExtOutput("/usr/bin/cman_tool status")
+ self.collectExtOutput("/usr/bin/cman_tool services")
+ self.collectExtOutput("/usr/bin/cman_tool -af nodes")
+ self.collectExtOutput("/usr/bin/ccs_tool lsnode")
+ self.collectExtOutput("/usr/bin/openais-cfgtool -s")
+ self.collectExtOutput("/usr/bin/clustat")
+ return
+
+ def postproc(self):
+ self.doRegexSub("/etc/cluster/cluster.conf", r"(\s*\/dev/null | /bin/grep -v Module 2>/dev/null').split('\n'):
+ if '' != kmod.strip():
+ runcmd = runcmd + " " + kmod
+ if len(runcmd):
+ self.collectExtOutput("/sbin/modinfo " + runcmd)
+ self.collectExtOutput("/sbin/ksyms")
+ self.addCopySpec("/proc/filesystems")
+ self.addCopySpec("/proc/ksyms")
+ self.addCopySpec("/proc/slabinfo")
+ kver = commands.getoutput('/bin/uname -r')
+ depfile = "/lib/modules/%s/modules.dep" % (kver,)
+ self.addCopySpec(depfile)
+ self.addCopySpec("/etc/conf.modules")
+ self.addCopySpec("/etc/modules.conf")
+ self.addCopySpec("/etc/modprobe.conf")
+ self.collectExtOutput("/usr/sbin/dmidecode", root_symlink = "dmidecode")
+ self.collectExtOutput("/usr/sbin/dkms status")
+ self.addCopySpec("/proc/cmdline")
+ self.addCopySpec("/proc/driver")
+ self.addCopySpec("/proc/sys/kernel/tainted")
+ # trigger some sysrq's. I'm not sure I like doing it this way, but
+ # since we end up with the sysrq dumps in syslog whether we run the
+ # syslog report before or after this, I suppose I can live with it.
+ if self.isOptionEnabled('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK) and os.access("/proc/sys/kernel/sysrq", os.R_OK):
+ sysrq_state = commands.getoutput("/bin/cat /proc/sys/kernel/sysrq")
+ commands.getoutput("/bin/echo 1 > /proc/sys/kernel/sysrq")
+ for key in ['m', 'p', 't']:
+ commands.getoutput("/bin/echo %s > /proc/sysrq-trigger" % (key,))
+ commands.getoutput("/bin/echo %s > /proc/sys/kernel/sysrq" % (sysrq_state,))
+ # No need to grab syslog here if we can't trigger sysrq, so keep this
+ # inside the if
+ self.addCopySpec("/var/log/messages")
+
+ return
+
+ def analyze(self):
+ infd = open("/proc/modules", "r")
+ modules = infd.readlines()
+ infd.close()
+
+ for modname in modules:
+ modname=modname.split(" ")[0]
+ modinfo_srcver = commands.getoutput("/sbin/modinfo -F srcversion %s" % modname)
+ if not os.access("/sys/module/%s/srcversion" % modname, os.R_OK):
+ continue
+ infd = open("/sys/module/%s/srcversion" % modname, "r")
+ sys_srcver = infd.read().strip("\n")
+ infd.close()
+ if modinfo_srcver != sys_srcver:
+ self.addAlert("Loaded module %s differs from the one present on the file-system")
+
+ # this would be a good moment to check the module's signature
+ # but at the moment there's no easy way to do that outside of
+ # the kernel. i will probably need to write a C lib (derived from
+ # the kernel sources to do this verification.
+
+ savedtaint = os.path.join(self.cInfo['dstroot'], "/proc/sys/kernel/tainted")
+ infd = open(savedtaint, "r")
+ line = infd.read()
+ infd.close()
+ line = line.strip()
+ if (line != "0"):
+ self.addAlert("Kernel taint flag is <%s>\n" % line)
+
+
+ infd = open(self.moduleFile, "r")
+ modules = infd.readlines()
+ infd.close()
+
+ #print(modules)
+ for tainter in self.taintList:
+ p = re.compile(tainter['regex'])
+ for line in modules:
+ if p.match(line) != None:
+ # found a taint match, create an alert
+ moduleName = line.split()[0]
+ self.addAlert("Check for tainted kernel by module %s, which is %s" % (moduleName, tainter['description']))
+ return
diff --git a/trunk/src/lib/sos/plugins/ldap.py b/trunk/src/lib/sos/plugins/ldap.py
new file mode 100644
index 00000000..318a3ba9
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/ldap.py
@@ -0,0 +1,27 @@
+### 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.
+
+import sos.plugintools
+
+class ldap(sos.plugintools.PluginBase):
+ """LDAP related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/ldap.conf")
+ self.addCopySpec("/etc/openldap")
+ return
+
+ def postproc(self):
+ self.doRegexSub("/etc/ldap.conf", r"(\s*bindpw\s*)\S+", r"\1***")
+ return
diff --git a/trunk/src/lib/sos/plugins/libraries.py b/trunk/src/lib/sos/plugins/libraries.py
new file mode 100644
index 00000000..70b63dba
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/libraries.py
@@ -0,0 +1,24 @@
+### 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.
+
+import sos.plugintools
+
+class libraries(sos.plugintools.PluginBase):
+ """information on shared libraries
+ """
+ def setup(self):
+ self.addCopySpec("/etc/ld.so.conf")
+ self.addCopySpec("/etc/ld.so.conf.d")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/mail.py b/trunk/src/lib/sos/plugins/mail.py
new file mode 100644
index 00000000..0d4dda29
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/mail.py
@@ -0,0 +1,25 @@
+### 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.
+
+import sos.plugintools
+
+class mail(sos.plugintools.PluginBase):
+ """mail server related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/mail")
+ self.addCopySpec("/etc/postfix/main.cf")
+ self.addCopySpec("/etc/postfix/master.cf")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/memory.py b/trunk/src/lib/sos/plugins/memory.py
new file mode 100644
index 00000000..7fbe39c3
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/memory.py
@@ -0,0 +1,30 @@
+### 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.
+
+import sos.plugintools
+
+class memory(sos.plugintools.PluginBase):
+ """memory usage information
+ """
+ def setup(self):
+ self.addCopySpec("/proc/pci")
+ self.addCopySpec("/proc/meminfo")
+ self.addCopySpec("/proc/vmstat")
+ self.addCopySpec("/proc/slabinfo")
+
+ self.collectExtOutput("/bin/dmesg | grep -e 'e820.' -e 'aperature.'")
+ self.collectExtOutput("/usr/bin/free", root_symlink = "free")
+ self.collectExtOutput("/usr/bin/free -m")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/named.py b/trunk/src/lib/sos/plugins/named.py
new file mode 100644
index 00000000..68d56ca0
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/named.py
@@ -0,0 +1,36 @@
+### 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.
+
+import sos.plugintools
+import commands
+import os
+
+class named(sos.plugintools.PluginBase):
+ """named related information
+ """
+ def setup(self):
+ dnsdir = ""
+ self.addCopySpec("/etc/named.boot")
+ self.addCopySpec("/etc/named.conf")
+ self.addCopySpec("/etc/sysconfig/named")
+ if os.access("/etc/named.conf", os.R_OK):
+ dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.conf | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'")
+ if os.access("/etc/named.boot", os.R_OK):
+ dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.boot | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'")
+ if '' != dnsdir.strip():
+ self.addCopySpec(dnsdir)
+ self.addForbiddenPath('/var/named/chroot/proc')
+ self.addForbiddenPath('/var/named/chroot/dev')
+ return
+
diff --git a/trunk/src/lib/sos/plugins/networking.py b/trunk/src/lib/sos/plugins/networking.py
new file mode 100644
index 00000000..54723663
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/networking.py
@@ -0,0 +1,75 @@
+### 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.
+
+import sos.plugintools
+import os,re,commands
+
+class networking(sos.plugintools.PluginBase):
+ """network related information
+ """
+ optionList = [("traceroute", "collects a traceroute to rhn.redhat.com", "slow", 0)]
+
+ def get_interface_name(self,ifconfigFile):
+ """Return a dictionary for which key are interface name according to the
+ output of ifconifg-a stored in ifconfigFile.
+ """
+ out={}
+ if(os.path.isfile(ifconfigFile)):
+ f=open(ifconfigFile,'r')
+ content=f.read()
+ f.close()
+ reg=re.compile(r"^(eth\d+)\D",re.MULTILINE)
+ for name in reg.findall(content):
+ out[name]=1
+ return out
+
+ def collectIPTable(self,tablename):
+ """ When running the iptables command, it unfortunately auto-loads
+ the modules before trying to get output. Some people explicitly
+ don't want this, so check if the modules are loaded before running
+ the command. If they aren't loaded, there can't possibly be any
+ relevant rules in that table """
+
+ cmd = "/sbin/iptables -t "+tablename+" -nvL"
+
+ (status, output) = commands.getstatusoutput("/sbin/lsmod | grep -q "+tablename)
+ if status == 0:
+ self.collectExtOutput(cmd)
+ else:
+ self.writeTextToCommand(cmd,"IPTables module "+tablename+" not loaded\n")
+
+ def setup(self):
+ self.addCopySpec("/etc/nsswitch.conf")
+ self.addCopySpec("/etc/yp.conf")
+ self.addCopySpec("/etc/inetd.conf")
+ self.addCopySpec("/etc/xinetd.conf")
+ self.addCopySpec("/etc/xinetd.d")
+ self.addCopySpec("/etc/host*")
+ self.addCopySpec("/etc/resolv.conf")
+ ifconfigFile=self.collectExtOutput("/sbin/ifconfig -a", root_symlink = "ifconfig")
+ self.collectExtOutput("/sbin/route -n", root_symlink = "route")
+ self.collectExtOutput("/sbin/ipchains -nvL")
+ self.collectIPTable("filter")
+ self.collectIPTable("nat")
+ self.collectIPTable("mangle")
+ self.collectExtOutput("/bin/netstat -nap")
+ if ifconfigFile:
+ for eth in self.get_interface_name(ifconfigFile):
+ self.collectExtOutput("/sbin/ethtool "+eth)
+ if self.isOptionEnabled("traceroute"):
+ # The semicolon prevents the browser from thinking this is a link when viewing the report
+ self.collectExtOutput("/bin/traceroute rhn.redhat.com;")
+
+ return
+
diff --git a/trunk/src/lib/sos/plugins/nfsserver.py b/trunk/src/lib/sos/plugins/nfsserver.py
new file mode 100644
index 00000000..6c894177
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/nfsserver.py
@@ -0,0 +1,27 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class nfsserver(sos.plugintools.PluginBase):
+ """NFS server-related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/exports")
+ self.collectExtOutput("/usr/sbin/rpcinfo -p localhost")
+ self.collectExtOutput("/usr/sbin/nfsstat")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/pam.py b/trunk/src/lib/sos/plugins/pam.py
new file mode 100644
index 00000000..8164bba3
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/pam.py
@@ -0,0 +1,25 @@
+### 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.
+
+import sos.plugintools
+
+class pam(sos.plugintools.PluginBase):
+ """PAM related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/pam.d")
+ self.addCopySpec("/etc/security")
+ self.collectExtOutput("/bin/ls -laF /lib/security/pam_*so")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/printing.py b/trunk/src/lib/sos/plugins/printing.py
new file mode 100644
index 00000000..76a476eb
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/printing.py
@@ -0,0 +1,24 @@
+### 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.
+
+import sos.plugintools
+
+class printing(sos.plugintools.PluginBase):
+ """printing related information (cups)
+ """
+ def setup(self):
+ self.addCopySpec("/etc/cups/*.conf")
+ self.addCopySpec("/var/log/cups")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/process.py b/trunk/src/lib/sos/plugins/process.py
new file mode 100644
index 00000000..7ed13a5e
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/process.py
@@ -0,0 +1,24 @@
+### 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.
+
+import sos.plugintools
+
+class process(sos.plugintools.PluginBase):
+ """process information
+ """
+ def setup(self):
+ self.collectExtOutput("/bin/ps auxww", root_symlink = "ps")
+ self.collectExtOutput("/usr/bin/pstree", root_symlink = "pstree")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/radius.py b/trunk/src/lib/sos/plugins/radius.py
new file mode 100644
index 00000000..7ab53810
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/radius.py
@@ -0,0 +1,29 @@
+## Copyright (C) 2007 Navid Sheikhol-Eslami
+
+### 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.
+
+import sos.plugintools
+
+class radius(sos.plugintools.PluginBase):
+ """radius related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/raddb")
+ self.addCopySpec("/etc/pam.d/radiusd")
+ return
+
+ def postproc(self):
+ self.doRegexSub("/etc/raddb/sql.conf", r"(\s*password\s*=\s*)\S+", r"\1***")
+ return
diff --git a/trunk/src/lib/sos/plugins/rhn.py b/trunk/src/lib/sos/plugins/rhn.py
new file mode 100644
index 00000000..049e51bc
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/rhn.py
@@ -0,0 +1,91 @@
+### 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.
+
+from sos.plugintools import PluginBase
+
+class rhn(PluginBase):
+ """RHN server related information
+ """
+ def checkenabled(self):
+ # XXX check for the presence of requisite packages
+ satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools")
+ proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools")
+ if not satellite and not proxy:
+ return False
+ return True
+
+ def setup(self):
+ #
+ # First, grab things needed from both Satellite and Proxy systems
+ #
+ # TODO: add chain load so we can use existing modules for httpd, &c.
+ #
+
+ # basic RHN logs and configs
+ self.addCopySpec("/var/log/rhn*")
+ self.addCopySpec("/etc/rhn")
+ self.collectExtOutput("/usr/share/rhn/up2date_client/hardware.py")
+
+ # httpd
+ self.addCopySpec("/etc/httpd/conf")
+ self.addCopySpec("/var/log/httpd")
+
+ # RPM manifests
+ self.collectExtOutput("/bin/rpm -qa --last | sort")
+
+ # monitoring scout logs
+ self.addCopySpec("/home/nocpulse/var/*.log*")
+ self.addCopySpec("/home/nocpulse/var/commands/*.log*")
+
+ satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools")
+ proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools")
+
+ #
+ # Now, go for product-specific data
+ #
+ if satellite:
+ self.setupSatellite(satellite)
+
+ if proxy:
+ self.setupProxy(proxy)
+
+ def setupSatellite(self, satellite):
+ self.collectExtOutput("/usr/bin/rhn-schema-version")
+ self.collectExtOutput("/usr/bin/rhn-charsets")
+
+ # oracle
+ self.addCopySpec("/etc/tnsnames.ora")
+
+ # tomcat (4.x and newer satellites only)
+ if not self.cInfo["policy"].pkgNVRA(satellite)[1].startswith("3."):
+ self.addCopySpec("/etc/tomcat5")
+ self.addCopySpec("/var/log/tomcat5")
+
+ # jabberd
+ # - logs to /var/log/messages
+ self.addCopySpec("/etc/jabberd")
+
+ # SSL build
+ self.addCopySpec("/root/ssl-build")
+
+ # monitoring logs
+ self.addCopySpec("/opt/notification/var/*.log*")
+ self.addCopySpec("/var/tmp/ack_handler.log*")
+ self.addCopySpec("/var/tmp/enqueue.log*")
+
+ def setupProxy(self, proxy):
+ # squid
+ self.addCopySpec("/etc/squid")
+ self.addCopySpec("/var/log/squid")
+
diff --git a/trunk/src/lib/sos/plugins/rpm.py b/trunk/src/lib/sos/plugins/rpm.py
new file mode 100644
index 00000000..b6fdb699
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/rpm.py
@@ -0,0 +1,33 @@
+### 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.
+
+import sos.plugintools
+
+class rpm(sos.plugintools.PluginBase):
+ """RPM information
+ """
+ optionList = [("rpmq", "Queries for package information via rpm -q", "fast", 1),
+ ("rpmva", "Runs a verify on all packages", "slow", 1)]
+
+ def setup(self):
+ self.addCopySpec("/var/log/rpmpkgs")
+
+ if self.isOptionEnabled("rpmq"):
+ self.collectExtOutput("/bin/rpm -qa --qf \"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}\n\"", root_symlink = "installed-rpms")
+
+ if self.isOptionEnabled("rpmva"):
+ self.eta_weight += 800 # this plugins takes 200x longer (for ETA)
+ self.collectExtOutput("/bin/rpm -Va", root_symlink = "rpm-Va")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/samba.py b/trunk/src/lib/sos/plugins/samba.py
new file mode 100644
index 00000000..ec65370e
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/samba.py
@@ -0,0 +1,26 @@
+### 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.
+
+import sos.plugintools
+
+class samba(sos.plugintools.PluginBase):
+ """Samba related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/samba")
+ self.addCopySpec("/var/log/samba/*")
+ self.collectExtOutput("/usr/bin/wbinfo -g")
+ self.collectExtOutput("/usr/bin/wbinfo -u")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/satellite.py b/trunk/src/lib/sos/plugins/satellite.py
new file mode 100644
index 00000000..930d11b0
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/satellite.py
@@ -0,0 +1,53 @@
+### 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.
+
+import sos.plugintools
+
+class satellite(sos.plugintools.PluginBase):
+ """RHN Satellite related information
+ """
+
+ def defaultenabled(self):
+ return False
+
+ def setup(self):
+ self.addCopySpec("/etc/httpd/conf")
+ self.addCopySpec("/etc/rhn")
+ self.addCopySpec("/etc/sysconfig/rhn")
+ self.addCopySpec("/etc/tnsnames.ora")
+ self.addCopySpec("/var/log/httpd") # httpd-logs
+ self.addCopySpec("/var/log/rhn*") # rhn-logs
+ self.addCopySpec("/var/log/rhn/rhn-database-installation.log")
+ self.addCopySpec("/etc/jabberd")
+
+ # tomcat for satellite 400+
+ self.addCopySpec("/etc/tomcat5")
+ self.addCopySpec("/var/log/tomcat5")
+
+ # all these used to go in $DIR/mon-logs
+ self.addCopySpec("/opt/notification/var/*.log*")
+ self.addCopySpec("/var/tmp/ack_handler.log*")
+ self.addCopySpec("/var/tmp/enqueue.log*")
+
+ self.addCopySpec("/home/nocpulse/var/*.log*")
+ self.addCopySpec("/home/nocpulse/var/commands/*.log*")
+ self.addCopySpec("/var/tmp/ack_handler.log*")
+ self.addCopySpec("/var/tmp/enqueue.log*")
+
+ self.addCopySpec("/root/ssl-build")
+ self.addCopySpec("rpm -qa --last") # $DIR/rpm-manifest
+ self.addCopySpec("/usr/bin/rhn-schema-version") # $DIR/database-schema-version
+ self.addCopySpec("/usr/bin/rhn-charsets") # $DIR/database-character-sets
+
+ return
diff --git a/trunk/src/lib/sos/plugins/selinux.py b/trunk/src/lib/sos/plugins/selinux.py
new file mode 100644
index 00000000..897c3991
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/selinux.py
@@ -0,0 +1,36 @@
+### 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.
+
+import sos.plugintools
+import commands
+
+class selinux(sos.plugintools.PluginBase):
+ """selinux related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/selinux/*")
+ self.collectExtOutput("/usr/bin/selinuxconfig")
+ self.collectExtOutput("/usr/sbin/sestatus", root_symlink = "sestatus")
+ self.collectExtOutput("/bin/rpm -q -V selinux-policy-targeted")
+ self.collectExtOutput("/bin/rpm -q -V selinux-policy-strict")
+ return
+
+ def checkenabled(self):
+ # is selinux enabled ?
+ try:
+ if commands.getoutput("/usr/sbin/sestatus").split(":")[1].strip() == "disabled":
+ return False
+ except:
+ pass
+ return True
diff --git a/trunk/src/lib/sos/plugins/sendmail.py b/trunk/src/lib/sos/plugins/sendmail.py
new file mode 100644
index 00000000..d95b5d7b
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/sendmail.py
@@ -0,0 +1,27 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class sendmail(sos.plugintools.PluginBase):
+ """sendmail information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/mail/*")
+ self.addCopySpec("/var/log/maillog")
+ self.collectExtOutput("/sbin/chkconfig --list sendmail")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/squid.py b/trunk/src/lib/sos/plugins/squid.py
new file mode 100644
index 00000000..4544ef9a
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/squid.py
@@ -0,0 +1,23 @@
+### 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.
+
+import sos.plugintools
+
+class squid(sos.plugintools.PluginBase):
+ """squid related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/squid/squid.conf")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/ssh.py b/trunk/src/lib/sos/plugins/ssh.py
new file mode 100644
index 00000000..92a4c9e5
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/ssh.py
@@ -0,0 +1,27 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class ssh(sos.plugintools.PluginBase):
+ """ssh-related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/ssh/ssh_config")
+ self.addCopySpec("/etc/ssh/sshd_config")
+ self.collectExtOutput("/sbin/chkconfig --list sshd")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/startup.py b/trunk/src/lib/sos/plugins/startup.py
new file mode 100644
index 00000000..02361393
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/startup.py
@@ -0,0 +1,25 @@
+### 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.
+
+import sos.plugintools
+
+class startup(sos.plugintools.PluginBase):
+ """startup information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/rc.d")
+
+ self.collectExtOutput("/sbin/chkconfig --list", root_symlink = "chkconfig")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/system.py b/trunk/src/lib/sos/plugins/system.py
new file mode 100644
index 00000000..19d7859e
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/system.py
@@ -0,0 +1,31 @@
+### 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.
+
+import sos.plugintools
+
+class system(sos.plugintools.PluginBase):
+ """core system related information
+ """
+ def setup(self):
+ self.addCopySpec("/proc/sys")
+ self.addCopySpec("/etc/sysctl.conf")
+ self.addCopySpec("/etc/cron*")
+ self.addCopySpec("/etc/syslog.conf")
+ self.addCopySpec("/etc/ntp.conf")
+ self.addCopySpec("/etc/ntp/step-tickers")
+ self.addCopySpec("/etc/ntp/ntpservers")
+ self.addCopySpec("/etc/auto.*")
+
+ return
+
diff --git a/trunk/src/lib/sos/plugins/systemtap.py b/trunk/src/lib/sos/plugins/systemtap.py
new file mode 100644
index 00000000..b99ce0cf
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/systemtap.py
@@ -0,0 +1,29 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class systemtap(sos.plugintools.PluginBase):
+ """SystemTap pre-requisites information
+ """
+ def setup(self):
+ # requires systemtap, systemtap-runtime, kernel-devel,
+ # kernel-debuginfo, kernel-debuginfo-common
+ self.collectExtOutput("/bin/rpm -qa | /bin/egrep -e kernel.*`uname -r` -e systemtap -e elfutils | sort")
+ self.collectExtOutput("/usr/bin/stap -V 2")
+ self.collectExtOutput("/bin/uname -r")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/x11.py b/trunk/src/lib/sos/plugins/x11.py
new file mode 100644
index 00000000..9b7b7ac9
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/x11.py
@@ -0,0 +1,26 @@
+### 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.
+
+import sos.plugintools
+
+class x11(sos.plugintools.PluginBase):
+ """X related information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/X11")
+ self.addCopySpec("/var/log/Xorg.*.log")
+ self.addCopySpec("/var/log/XFree86.*.log")
+ self.collectExtOutput("/bin/dmesg | grep -e 'agpgart.'")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/xen.py b/trunk/src/lib/sos/plugins/xen.py
new file mode 100644
index 00000000..d6daec63
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/xen.py
@@ -0,0 +1,82 @@
+### 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.
+
+import sos.plugintools
+import os, commands
+from stat import *
+
+class xen(sos.plugintools.PluginBase):
+ """Xen related information
+ """
+ def determineXenHost(self):
+ if os.access("/proc/acpi/dsdt", os.R_OK):
+ (status, output) = commands.getstatusoutput("/usr/bin/strings /proc/acpi/dsdt | grep -q int-xen")
+ if status == 0:
+ return "hvm"
+
+ if os.access("/proc/xen/capabilities", os.R_OK):
+ (status, output) = commands.getstatusoutput("grep -q control_d /proc/xen/capabilities")
+ if status == 0:
+ return "dom0"
+ else:
+ return "domU"
+ return "baremetal"
+
+ def checkenabled(self):
+ if self.determineXenHost() == "baremetal":
+ return False
+ return True
+
+ def domCollectProc(self):
+ self.addCopySpec("/proc/xen/balloon")
+ self.addCopySpec("/proc/xen/capabilities")
+ self.addCopySpec("/proc/xen/xsd_kva")
+ self.addCopySpec("/proc/xen/xsd_port")
+ # determine if CPU has PAE support
+ self.collectExtOutput("/bin/grep pae /proc/cpuinfo")
+ # determine if CPU has Intel-VT or AMD-V support
+ self.collectExtOutput("/bin/egrep -e 'vmx|svm' /proc/cpuinfo")
+
+ def setup(self):
+ host_type = self.determineXenHost()
+ if host_type == "domU":
+ # we should collect /proc/xen and /sys/hypervisor
+ self.domCollectProc()
+ # determine if hardware virtualization support is enabled
+ # in BIOS: /sys/hypervisor/properties/capabilities
+ self.addCopySpec("/sys/hypervisor")
+ elif host_type == "hvm":
+ # what do we collect here???
+ pass
+ elif host_type == "dom0":
+ # default of dom0, collect lots of system information
+ self.addCopySpec("/var/log/xen")
+ self.addCopySpec("/etc/xen")
+ self.collectExtOutput("/usr/bin/xenstore-ls")
+ self.collectExtOutput("/usr/sbin/xm dmesg")
+ self.collectExtOutput("/usr/sbin/xm info")
+ self.collectExtOutput("/usr/sbin/brctl show")
+ self.domCollectProc()
+ self.addCopySpec("/sys/hypervisor")
+ # FIXME: we *might* want to collect things in /sys/bus/xen*,
+ # /sys/class/xen*, /sys/devices/xen*, /sys/modules/blk*,
+ # /sys/modules/net*, but I've never heard of them actually being
+ # useful, so I'll leave it out for now
+ else:
+ # for bare-metal, we don't have to do anything special
+ return
+
+ self.addCustomText("Xen hostType: "+host_type)
+ return
+
diff --git a/trunk/src/lib/sos/plugins/xinetd.py b/trunk/src/lib/sos/plugins/xinetd.py
new file mode 100644
index 00000000..715c831f
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/xinetd.py
@@ -0,0 +1,27 @@
+## Copyright (C) 2007 Red Hat, Inc., Eugene Teo
+
+### 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.
+
+import sos.plugintools
+
+class xinetd(sos.plugintools.PluginBase):
+ """xinetd information
+ """
+ def setup(self):
+ self.addCopySpec("/etc/xinetd.conf")
+ self.addCopySpec("/etc/xinetd.d/*")
+ self.collectExtOutput("/sbin/chkconfig --list xinetd")
+ return
+
diff --git a/trunk/src/lib/sos/plugins/yum.py b/trunk/src/lib/sos/plugins/yum.py
new file mode 100644
index 00000000..89102c38
--- /dev/null
+++ b/trunk/src/lib/sos/plugins/yum.py
@@ -0,0 +1,37 @@
+### 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.
+
+import sos.plugintools
+
+class yum(sos.plugintools.PluginBase):
+ """yum information
+ """
+
+ def defaultenabled(self):
+ # enable with -e or -o
+ return False
+
+ def setup(self):
+ # Pull all yum related information
+ self.addCopySpec("/etc/yum")
+ self.addCopySpec("/etc/yum.repos.d")
+ self.addCopySpec("/etc/yum.conf")
+ self.addCopySpec("/var/log/yum.log")
+
+ # Get a list of channels the machine is subscribed to.
+ self.collectExtOutput("/bin/echo \"repo list\" | /usr/bin/yum shell")
+ # List various information about available packages
+ self.collectExtOutput("/usr/bin/yum list")
+
+ return
diff --git a/trunk/src/lib/sos/plugintools.py b/trunk/src/lib/sos/plugintools.py
new file mode 100644
index 00000000..238036ce
--- /dev/null
+++ b/trunk/src/lib/sos/plugintools.py
@@ -0,0 +1,568 @@
+## plugintools.py
+## This exports methods available for use by plugins for sos
+
+## Copyright (C) 2006 Steve Conklin
+
+### 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.
+
+# pylint: disable-msg = R0902
+# pylint: disable-msg = R0904
+# pylint: disable-msg = W0702
+# pylint: disable-msg = W0703
+# pylint: disable-msg = R0201
+# pylint: disable-msg = W0611
+# pylint: disable-msg = W0613
+
+"""
+This is the base class for sosreport plugins
+"""
+from sos.helpers import *
+from threading import Thread, activeCount
+import os, os.path, sys, string, itertools, glob, re, traceback
+import logging
+from stat import *
+from time import time
+
+class PluginBase:
+ """
+ Base class for plugins
+ """
+ def __init__(self, pluginname, commons):
+ # pylint: disable-msg = E0203
+ try:
+ len(self.optionList)
+ except:
+ self.optionList = []
+ # pylint: enable-msg = E0203
+ self.copiedFiles = []
+ self.copiedDirs = []
+ self.executedCommands = []
+ self.diagnose_msgs = []
+ self.alerts = []
+ self.customText = ""
+ self.optNames = []
+ self.optParms = []
+ self.piName = pluginname
+ self.cInfo = commons
+ self.forbiddenPaths = []
+ self.copyPaths = []
+ self.collectProgs = []
+ self.thread = None
+ self.pid = None
+ self.eta_weight = 1
+ self.time_start = None
+ self.time_stop = None
+
+ self.soslog = logging.getLogger('sos')
+
+ # get the option list into a dictionary
+ for opt in self.optionList:
+ self.optNames.append(opt[0])
+ self.optParms.append({'desc':opt[1], 'speed':opt[2], 'enabled':opt[3]})
+
+ # Method for applying regexp substitutions
+ def doRegexSub(self, srcpath, regexp, subst):
+ '''Apply a regexp substitution to a file archived by sosreport.
+ '''
+ if len(self.copiedFiles):
+ for afile in self.copiedFiles:
+ if afile['srcpath'] == srcpath:
+ abspath = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep))
+ try:
+ fp = open(abspath, 'r')
+ tmpout, occurs = re.subn( regexp, subst, fp.read() )
+ fp.close()
+ if occurs > 0:
+ fp = open(abspath,'w')
+ fp.write(tmpout)
+ fp.close()
+ return occurs
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except Exception, e:
+ self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (abspath,e))
+ break
+ return False
+
+ # Methods for copying files and shelling out
+ def doCopyFileOrDir(self, srcpath):
+ # pylint: disable-msg = R0912
+ # pylint: disable-msg = R0915
+ ''' Copy file or directory to the destination tree. If a directory, then everything
+ below it is recursively copied. A list of copied files are saved for use later
+ in preparing a report
+ '''
+ copyProhibited = 0
+ for path in self.forbiddenPaths:
+ if ( srcpath.count(path) > 0 ):
+ copyProhibited = 1
+
+ if copyProhibited:
+ return ''
+
+ if os.path.islink(srcpath):
+ # This is a symlink - We need to also copy the file that it points to
+ # file and dir symlinks ar ehandled the same
+ link = os.readlink(srcpath)
+ if os.path.isabs(link):
+ # the link was an absolute path, and will not point to the new
+ # tree. We must adjust it.
+
+ # What's the name of the symlink on the dest tree?
+ dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep))
+
+ # make sure the dst dir exists
+ if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))):
+ # create the directory
+ os.makedirs(os.path.dirname(dstslname))
+
+ dstsldir = os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep))
+ # Create the symlink on the dst tree
+ rpth = sosRelPath(os.path.dirname(dstslname), dstsldir)
+ os.symlink(rpth, dstslname)
+ else:
+ # no adjustment, symlink is the relative path
+ dstslname = link
+
+ if os.path.isdir(srcpath):
+ for afile in os.listdir(srcpath):
+ if afile == '.' or afile == '..':
+ pass
+ else:
+ try:
+ abspath = self.doCopyFileOrDir(srcpath+'/'+afile)
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except Exception, e:
+ self.soslog.warning(traceback.format_exc())
+
+ # if on forbidden list, abspath is null
+ if not abspath == '':
+ dstslname = sosRelPath(self.cInfo['rptdir'], abspath)
+ self.copiedDirs.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link})
+ else:
+ try:
+ dstslname, abspath = self.__copyFile(srcpath)
+ self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link})
+ self.cInfo['xmlreport'].add_file(srcpath,os.stat(srcpath))
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except Exception, e:
+ self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (srcpath, e))
+
+ return abspath
+
+ else:
+ if not os.path.exists(srcpath):
+ self.soslog.debug("File or directory %s does not exist\n" % srcpath)
+ elif os.path.isdir(srcpath):
+ for afile in os.listdir(srcpath):
+ if afile == '.' or afile == '..':
+ pass
+ else:
+ self.doCopyFileOrDir(srcpath+'/'+afile)
+ else:
+ # This is not a directory or a symlink
+ tdstpath, abspath = self.__copyFile(srcpath)
+ self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list
+ return abspath
+
+ def __copyFile(self, src):
+ """ call cp to copy a file, collect return status and output. Returns the
+ destination file name.
+ """
+ try:
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput("/bin/cp --parents -P --preserve=mode,ownership,timestamps,links " + src +" " + self.cInfo['dstroot'])
+ if status:
+ self.soslog.debug(shout)
+ abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep))
+ relpath = sosRelPath(self.cInfo['rptdir'], abspath)
+ return relpath, abspath
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except Exception,e:
+ self.soslog.warning("Problem copying file %s (%s)" % (src, e))
+
+ def addForbiddenPath(self, forbiddenPath):
+ """Specify a path to not copy, even if it's part of a copyPaths[] entry.
+ Note: do NOT use globs here.
+ """
+ self.forbiddenPaths.append(forbiddenPath)
+
+ def getAllOptions(self):
+ """
+ return a list of all options selected
+ """
+ return (self.optNames, self.optParms)
+
+ def setOption(self, optionname, enable):
+ ''' enable or disable the named option.
+ '''
+ for name, parms in zip(self.optNames, self.optParms):
+ if name == optionname:
+ parms['enabled'] = enable
+
+ def isOptionEnabled(self, optionname):
+ ''' see whether the named option is enabled.
+ '''
+ for name, parms in zip(self.optNames, self.optParms):
+ if name == optionname:
+ return parms['enabled']
+ # nonexistent options aren't enabled.
+ return 0
+
+ def addCopySpecLimit(self,fname,sizelimit = None):
+ """Add a file specification (with limits)
+ """
+ files = glob.glob(fname)
+ files.sort()
+ cursize = 0
+ for flog in files:
+ cursize += os.stat(flog)[ST_SIZE]
+ if sizelimit and (cursize / 1024 / 1024) > sizelimit:
+ break
+ self.addCopySpec(flog)
+
+ def addCopySpec(self, copyspec):
+ """ Add a file specification (can be file, dir,or shell glob) to be
+ copied into the sosreport by this module
+ """
+ # Glob case handling is such that a valid non-glob is a reduced glob
+ for filespec in glob.glob(copyspec):
+ self.copyPaths.append(filespec)
+
+ def copyFileGlob(self, srcglob):
+ """ Deprecated - please modify modules to use addCopySpec()
+ """
+ sys.stderr.write("Warning: thecopyFileGlob() function has been deprecated. Please")
+ sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.")
+ self.addCopySpec(srcglob)
+
+ def copyFileOrDir(self, srcpath):
+ """ Deprecated - please modify modules to use addCopySpec()
+ """
+ sys.stderr.write("Warning: the copyFileOrDir() function has been deprecated. Please\n")
+ sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.\n")
+ raise ValueError
+ #self.addCopySpec(srcpath)
+
+ def runExeInd(self, exe):
+ """ Deprecated - use callExtProg()
+ """
+ sys.stderr.write("Warning: the runExeInd() function has been deprecated. Please use\n")
+ sys.stderr.write("the callExtProg() function. This should only be called\n")
+ sys.stderr.write("if collect() is overridden.")
+ pass
+
+ def callExtProg(self, prog):
+ """ Execute a command independantly of the output gathering part of
+ sosreport
+ """
+ # Log if binary is not runnable or does not exist
+ if not os.access(prog.split()[0], os.X_OK):
+ self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable" % prog.split()[0])
+
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput(prog)
+ return status
+
+ def runExe(self, exe):
+ """ Deprecated - use collectExtOutput()
+ """
+ sys.stderr.write("Warning: the runExe() function has been deprecated. Please use\n")
+ sys.stderr.write("the collectExtOutput() function.\n")
+ pass
+
+ def collectExtOutput(self, exe, suggest_filename = None, root_symlink = None):
+ """
+ Run a program and collect the output
+ """
+ self.collectProgs.append( (exe,suggest_filename,root_symlink) )
+
+ def makeCommandFilename(self, exe):
+ """ The internal function to build up a filename based on a command """
+
+ mangledname = re.sub(r"[^\w\-\.\/]+", "_", exe)
+ mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64]
+
+ outfn = self.cInfo['cmddir'] + "/" + self.piName + "/" + mangledname
+
+ # check for collisions
+ inc = 0
+ if os.path.exists(outfn):
+ inc = 2
+ while True:
+ newfn = outfn + "_" + inc
+ if not os.path.exists(newfn):
+ break
+ inc +=1
+
+ return outfn
+
+ def collectOutputNow(self, exe, suggest_filename = None, root_symlink = False):
+ """ Execute a command and save the output to a file for inclusion in
+ the report
+ """
+ # First check to make sure the binary exists and is runnable.
+ if not os.access(exe.split()[0], os.X_OK):
+ self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable, trying anyways" % exe.split()[0])
+
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput(exe)
+
+ if suggest_filename:
+ outfn = self.makeCommandFilename(suggest_filename)
+ else:
+ outfn = self.makeCommandFilename(exe)
+
+ if not os.path.isdir(os.path.dirname(outfn)):
+ os.mkdir(os.path.dirname(outfn))
+
+ outfd = open(outfn, "w")
+ if status == 127: outfd.write("# the command returned exit status 127, this normally means that the command was not found.\n\n")
+ if len(shout): outfd.write(shout+"\n")
+ outfd.close()
+
+ if root_symlink:
+ curdir = os.getcwd()
+ os.chdir(self.cInfo['dstroot'])
+ os.symlink(outfn[len(self.cInfo['dstroot'])+1:], root_symlink.strip("/."))
+ os.chdir(curdir)
+
+ outfn = outfn[len(self.cInfo['cmddir'])+1:]
+
+ # sosStatus(status)
+ # save info for later
+ self.executedCommands.append({'exe': exe, 'file':outfn}) # save in our list
+ self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn,runtime=runtime)
+ return outfn
+
+ def writeTextToCommand(self, exe, text):
+ """ A function that allows you to write a random text string to the
+ command output location referenced by exe; this is useful if you want
+ to conditionally collect information, but still want the output file
+ to exist so as not to confuse readers """
+
+ outfn = self.makeCommandFilename(exe)
+
+ if not os.path.isdir(os.path.dirname(outfn)):
+ os.mkdir(os.path.dirname(outfn))
+
+ outfd = open(outfn, "w")
+ outfd.write(text)
+ outfd.close()
+
+ self.executedCommands.append({'exe': exe, 'file': outfn}) # save in our list
+ return outfn
+
+ # For adding warning messages regarding configuration sanity
+ def addDiagnose(self, alertstring):
+ """ Add a configuration sanity warning for this plugin. These
+ will be displayed on-screen before collection and in the report as well.
+ """
+ self.diagnose_msgs.append(alertstring)
+ return
+
+ # For adding output
+ def addAlert(self, alertstring):
+ """ Add an alert to the collection of alerts for this plugin. These
+ will be displayed in the report
+ """
+ self.alerts.append(alertstring)
+ return
+
+
+ def addCustomText(self, text):
+ """ Append text to the custom text that is included in the report. This
+ is freeform and can include html.
+ """
+ self.customText = self.customText + text
+ return
+
+ def doCollect(self):
+ """ This function has been replaced with copyStuff(threaded = True). Please change your
+ module calls. Calling setup() now.
+ """
+ return self.copyStuff(threaded = True)
+
+ def isRunning(self):
+ """
+ if threaded, is thread running ?
+ """
+ if self.thread: return self.thread.isAlive()
+ return None
+
+ def wait(self,timeout=None):
+ """
+ wait for a thread to complete - only called for threaded execution
+ """
+ self.thread.join(timeout)
+ return self.thread.isAlive()
+
+ def copyStuff(self, threaded = False, semaphore = None):
+ """
+ Collect the data for a plugin
+ """
+ if threaded and self.thread == None:
+ self.thread = Thread(target=self.copyStuff, name=self.piName+'-thread', args = [True, semaphore] )
+ self.thread.start()
+ return self.thread
+
+ if semaphore: semaphore.acquire()
+
+ self.soslog.log(logging.VERBOSE, "starting threaded plugin %s" % self.piName)
+
+ self.time_start = time()
+ self.time_stop = None
+
+ for path in self.copyPaths:
+ self.soslog.debug("copying pathspec %s" % path)
+ try:
+ self.doCopyFileOrDir(path)
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except Exception, e:
+ self.soslog.log(logging.VERBOSE, "error copying from pathspec %s (%s), traceback follows:" % (path,e))
+ self.soslog.log(logging.VERBOSE, traceback.format_exc())
+ for (prog,suggest_filename,root_symlink) in self.collectProgs:
+ self.soslog.debug("collecting output of '%s'" % prog)
+ try:
+ self.collectOutputNow(prog, suggest_filename, root_symlink)
+ except SystemExit:
+ raise SystemExit
+ except KeyboardInterrupt:
+ raise KeyboardInterrupt
+ except:
+ self.soslog.log(logging.VERBOSE, "error collection output of '%s', traceback follows:" % prog)
+ self.soslog.log(logging.VERBOSE, traceback.format_exc())
+
+ self.time_stop = time()
+
+ if semaphore: semaphore.release()
+ self.soslog.log(logging.VERBOSE, "plugin %s returning" % self.piName)
+
+ def get_description(self):
+ """ This function will return the description for the plugin"""
+ try:
+ return self.__doc__.strip()
+ except:
+ return ""
+
+ def checkenabled(self):
+ """ This function can be overidden to let the plugin decide whether
+ it should run or not.
+ """
+ return True
+
+ def defaultenabled(self):
+ """This devices whether a plugin should be automatically loaded or
+ only if manually specified in the command line."""
+ return True
+
+ def collect(self):
+ """ This function has been replaced with setup(). Please change your
+ module calls. Calling setup() now.
+ """
+ self.setup()
+
+ def diagnose(self):
+ """This class must be overridden to check the sanity of the system's
+ configuration before the collection begins.
+ """
+ pass
+
+ def setup(self):
+ """This class must be overridden to add the copyPaths, forbiddenPaths,
+ and external programs to be collected at a minimum.
+ """
+ pass
+
+ def analyze(self):
+ """
+ perform any analysis. To be replaced by a plugin if desired
+ """
+ pass
+
+ def postproc(self):
+ """
+ perform any postprocessing. To be replaced by a plugin if desired
+ """
+ pass
+
+ def report(self):
+ """ Present all information that was gathered in an html file that allows browsing
+ the results.
+ """
+ # make this prettier
+ html = '\n' % self.piName
+
+ # Intro
+ html = html + "
Plugin " + self.piName + "
\n"
+
+ # Files
+ if len(self.copiedFiles):
+ html = html + "
Files copied:
\n"
+ for afile in self.copiedFiles:
+ html = html + '
%s' % (afile['dstpath'], afile['srcpath'])
+ if (afile['symlink'] == "yes"):
+ html = html + " (symlink to %s)" % afile['pointsto']
+ html = html + '
\n'
+ html = html + "
\n"
+
+ # Dirs
+ if len(self.copiedDirs):
+ html = html + "
Directories Copied:
\n"
+ for adir in self.copiedDirs:
+ html = html + '
%s\n' % (adir['dstpath'], adir['srcpath'])
+ if (adir['symlink'] == "yes"):
+ html = html + " (symlink to %s)" % adir['pointsto']
+ html = html + '
\n'
+ html = html + "
\n"
+
+ # Command Output
+ if len(self.executedCommands):
+ html = html + "
Commands Executed:
\n"
+ # convert file name to relative path from our root
+ for cmd in self.executedCommands:
+ cmdOutRelPath = sosRelPath(self.cInfo['rptdir'], cmd['file'])
+ html = html + '
\n' % (cmdOutRelPath, cmd['exe'])
+ html = html + "
\n"
+
+ # Alerts
+ if len(self.alerts):
+ html = html + "
Alerts:
\n"
+ for alert in self.alerts:
+ html = html + '
%s
\n' % alert
+ html = html + "
\n"
+
+ # Custom Text
+ if (self.customText != ""):
+ html = html + "
Additional Information: \n"
+ html = html + self.customText + "
\n"
+
+ return html
+
+
diff --git a/trunk/src/lib/sos/policyredhat.py b/trunk/src/lib/sos/policyredhat.py
new file mode 100755
index 00000000..d4a4074d
--- /dev/null
+++ b/trunk/src/lib/sos/policyredhat.py
@@ -0,0 +1,151 @@
+## policy-redhat.py
+## Implement policies required for the sos system support tool
+
+## Copyright (C) Steve Conklin
+
+### 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.
+
+import os
+import commands
+import sys
+import string
+from tempfile import gettempdir
+from sos.helpers import *
+import random
+
+SOME_PATH = "/tmp/SomePath"
+
+#class SosError(Exception):
+# def __init__(self, code, message):
+# self.code = code
+# self.message = message
+#
+# def __str__(self):
+# return 'Sos Error %s: %s' % (self.code, self.message)
+
+
+class SosPolicy:
+ "This class implements various policies for sos"
+ def __init__(self):
+ #print "Policy init"
+ return
+
+ def setCommons(self, commons):
+ self.cInfo = commons
+ return
+
+ def validatePlugin(self, pluginpath):
+ "Validates the plugin as being acceptable to run"
+ # return value
+ # TODO implement this
+ #print "validating %s" % pluginpath
+ return True
+
+ def allPkgsByName(self, name):
+ # FIXME: we're relying on rpm to sort the output list
+ cmd = "/bin/rpm --qf '%%{N}-%%{V}-%%{R}-%%{ARCH}\n' -q %s" % (name,)
+ pkgs = os.popen(cmd).readlines()
+ return [pkg[:-1] for pkg in pkgs if pkg.startswith(name)]
+
+ def pkgByName(self, name):
+ # TODO: do a full NEVRA compare and return newest version, best arch
+ try:
+ # lame attempt at locating newest
+ pkg = self.allPkgsByName(name)[-1]
+ except IndexError:
+ pkg = None
+
+ return pkg
+
+ def pkgDictByName(self, name):
+ pkgName = self.pkgByName(name)
+ if pkgName and len(pkgName) > len(name):
+ return pkgName[len(name)+1:].split("-")
+ else:
+ return None
+
+ def runlevelByService(self, name):
+ ret = []
+ try:
+ for tabs in commands.getoutput("/sbin/chkconfig --list %s" % name).split():
+ (runlevel, onoff) = tabs.split(":")
+ if onoff == "on":
+ ret.append(int(runlevel))
+ except:
+ pass
+ return ret
+
+ def runlevelDefault(self):
+ # FIXME: get this from /etc/inittab
+ return 3
+
+ def pkgNVRA(self, pkg):
+ fields = pkg.split("-")
+ version, release, arch = fields[-3:]
+ name = "-".join(fields[:-3])
+ return (name, version, release, arch)
+
+ def packageResults(self):
+ localname = commands.getoutput("/bin/uname -n").split(".")[0]
+
+ try:
+ name = raw_input("Please enter your first initial and last name [%s]: " % localname)
+ if len(name) == 0: name = localname
+
+ ticketNumber = raw_input("Please enter the case number that you are generating this report for: ")
+ except KeyboardInterrupt:
+ print _("")
+ print _("Temporary files have been stored in ") % self.cInfo['dstroot']
+ return
+
+ if len(ticketNumber):
+ namestr = name + "." + ticketNumber
+ else:
+ namestr = name
+
+ ourtempdir = gettempdir()
+ tarballName = os.path.join(ourtempdir, "sosreport-" + namestr + ".tar.bz2")
+
+ namestr = namestr + "-" + str(random.randint(1, 999999))
+
+ aliasdir = os.path.join(ourtempdir, namestr)
+
+ tarcmd = "/bin/tar -jcf %s %s" % (tarballName, namestr)
+
+ print
+ print "Creating compressed tar archive..."
+ if not os.access(string.split(tarcmd)[0], os.X_OK):
+ print "Unable to create tarball"
+ return
+
+ # FIXME: gotta be a better way...
+ os.system("/bin/mv %s %s" % (self.cInfo['dstroot'], aliasdir))
+ curwd = os.getcwd()
+ os.chdir(ourtempdir)
+ oldmask = os.umask(077)
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput(tarcmd)
+ os.umask(oldmask)
+ os.chdir(curwd)
+ # FIXME: use python internal command
+ os.system("/bin/mv %s %s" % (aliasdir, self.cInfo['dstroot']))
+
+ sys.stdout.write("\n")
+ print "Your sosreport has been generated and saved in %s" % tarballName
+ print "Please send this file to your support representative."
+ sys.stdout.write("\n")
+
+ return
+
diff --git a/trunk/src/locale/en/LC_MESSAGES/sos.po b/trunk/src/locale/en/LC_MESSAGES/sos.po
new file mode 100644
index 00000000..c103a495
--- /dev/null
+++ b/trunk/src/locale/en/LC_MESSAGES/sos.po
@@ -0,0 +1,99 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2007-07-14 11:57\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: utf-8\n"
+"Generated-By: pygettext.py 1.5\n"
+
+
+#: sosreport:399
+msgid "sosreport (version %s)"
+msgstr ""
+
+#: sosreport:417
+msgid "plugin %s does not validate, skipping"
+msgstr ""
+
+#: sosreport:421
+msgid "plug %s skipped (noplugins)"
+msgstr ""
+
+#: sosreport:425
+msgid "plugin %s is inactive (use -e or -o to enable)."
+msgstr ""
+
+#: sosreport:433
+msgid "plugin %s not specified in --onlyplugin list"
+msgstr ""
+
+#: sosreport:438
+msgid "Plugin %s does not install, skipping"
+msgstr ""
+
+#: sosreport:441
+msgid "plugin load failed for %s"
+msgstr ""
+
+#: sosreport:447
+msgid "processing options from plugin: %s"
+msgstr ""
+
+#: sosreport:454
+msgid "no valid plugins found"
+msgstr ""
+
+#: sosreport:457
+msgid "The following plugins are currently enabled:"
+msgstr ""
+
+#: sosreport:463
+msgid "The following plugin options are available:"
+msgstr ""
+
+#: sosreport:469
+msgid "The following plugins are currently disabled:"
+msgstr ""
+
+#: sosreport:480
+msgid "sosreport requires root permissions to run."
+msgstr ""
+
+#: sosreport:487
+msgid "no valid plugins were enabled"
+msgstr ""
+
+#: sosreport:491
+msgid ""
+"This utility will collect some detailed information about the\n"
+"hardware and setup of your Red Hat Enterprise Linux system.\n"
+"This information will be used to diagnose problems with your \n"
+"system and will be considered confidential information.\n"
+"Red Hat will use this information for diagnostic purposes ONLY.\n"
+"\n"
+"This process may take a while to complete.\n"
+"No changes will be made to your system.\n"
+"\n"
+"Press ENTER to continue, or CTRL-C to quit.\n"
+msgstr ""
+
+#: sosreport:529
+msgid "Exiting."
+msgstr ""
+
+#: sosreport:676
+msgid "Collected information is in "
+msgstr ""
+
+#: sosreport:677
+msgid "Your html report is in "
+msgstr ""
+
diff --git a/trunk/src/locale/it/LC_MESSAGES/sos.po b/trunk/src/locale/it/LC_MESSAGES/sos.po
new file mode 100644
index 00000000..0b5e2b32
--- /dev/null
+++ b/trunk/src/locale/it/LC_MESSAGES/sos.po
@@ -0,0 +1,99 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2007-07-14 12:17\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: utf-8\n"
+"Generated-By: pygettext.py 1.5\n"
+
+
+#: sosreport:399
+msgid "sosreport (version %s)"
+msgstr "sosreport (versione %s)"
+
+#: sosreport:417
+msgid "plugin %s does not validate, skipping"
+msgstr ""
+
+#: sosreport:421
+msgid "plug %s skipped (noplugins)"
+msgstr ""
+
+#: sosreport:425
+msgid "plugin %s is inactive (use -e or -o to enable)."
+msgstr ""
+
+#: sosreport:433
+msgid "plugin %s not specified in --onlyplugin list"
+msgstr ""
+
+#: sosreport:438
+msgid "Plugin %s does not install, skipping"
+msgstr ""
+
+#: sosreport:441
+msgid "plugin load failed for %s"
+msgstr ""
+
+#: sosreport:447
+msgid "processing options from plugin: %s"
+msgstr ""
+
+#: sosreport:454
+msgid "no valid plugins found"
+msgstr ""
+
+#: sosreport:457
+msgid "The following plugins are currently enabled:"
+msgstr ""
+
+#: sosreport:463
+msgid "The following plugin options are available:"
+msgstr ""
+
+#: sosreport:469
+msgid "The following plugins are currently disabled:"
+msgstr ""
+
+#: sosreport:480
+msgid "sosreport requires root permissions to run."
+msgstr ""
+
+#: sosreport:487
+msgid "no valid plugins were enabled"
+msgstr ""
+
+#: sosreport:491
+msgid ""
+"This utility will collect some detailed information about the\n"
+"hardware and setup of your Red Hat Enterprise Linux system.\n"
+"This information will be used to diagnose problems with your \n"
+"system and will be considered confidential information.\n"
+"Red Hat will use this information for diagnostic purposes ONLY.\n"
+"\n"
+"This process may take a while to complete.\n"
+"No changes will be made to your system.\n"
+"\n"
+"Press ENTER to continue, or CTRL-C to quit.\n"
+msgstr ""
+
+#: sosreport:529
+msgid "Exiting."
+msgstr ""
+
+#: sosreport:676
+msgid "Collected information is in "
+msgstr ""
+
+#: sosreport:677
+msgid "Your html report is in "
+msgstr ""
+
diff --git a/trunk/src/pylintrc b/trunk/src/pylintrc
new file mode 100644
index 00000000..934f5255
--- /dev/null
+++ b/trunk/src/pylintrc
@@ -0,0 +1,354 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+# This checker also defines the following reports:
+# * R0001: Total errors / warnings
+# * R0002: % errors / warnings by module
+# * R0003: Messages
+# * R0004: Global evaluation
+[MASTER]
+
+# Profiled execution.
+profile=no
+
+# Add to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[REPORTS]
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# Use HTML as output format instead of text
+html=no
+
+# Use a parseable text output format, so your favorite text editor will be able
+# to jump to the line corresponding to a message.
+parseable=yes
+
+# Colorizes text output using ansi escape codes
+color=no
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Include message's id in output
+include-ids=yes
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Enable / disable this checker
+enable-variables=yes
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Enable / disable this checker
+enable-typecheck=yes
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered.
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+# This checker also defines the following reports:
+# * R0101: Statistics by type
+[BASIC]
+
+# Enable / disable this checker
+enable-basic=yes
+
+#disable-msg=C0121
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][A-Za-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][A-Za-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][A-Za-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][A-Za-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][A-Za-z0-9_]{0,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Enable / disable this checker
+enable-design=yes
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overriden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# Enable / disable this checker
+enable-classes=yes
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+# This checker also defines the following reports:
+# * R0401: External dependencies
+# * R0402: Modules dependencies graph
+[IMPORTS]
+
+# Enable / disable this checker
+enable-imports=no
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for usage of new style capabilities on old style classes and
+# other new/old styles conflicts problems
+# * use of property, __slots__, super
+# * "super" usage
+# * raising a new style class as exception
+#
+[NEWSTYLE]
+
+# Enable / disable this checker
+enable-newstyle=yes
+
+
+# checks for
+# * excepts without exception filter
+# * string exceptions
+#
+[EXCEPTIONS]
+
+# Enable / disable this checker
+enable-exceptions=yes
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Enable / disable this checker
+enable-format=yes
+
+# Maximum number of characters on a single line.
+max-line-length=132
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+# This checker also defines the following reports:
+# * R0801: Duplication
+[SIMILARITIES]
+
+# Enable / disable this checker
+enable-similarities=yes
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# Enable / disable this checker
+enable-miscellaneous=yes
+
+# List of note tags to take in consideration, separated by a comma. Default to
+# FIXME, XXX, TODO
+notes=FIXME,XXX,TODO
+
+
+# does not check anything but gives some raw metrics :
+# * total number of lines
+# * total number of code lines
+# * total number of docstring lines
+# * total number of comments lines
+# * total number of empty lines
+#
+# This checker also defines the following reports:
+# * R0701: Raw metrics
+[METRICS]
+
+# Enable / disable this checker
+enable-metrics=no
diff --git a/trunk/src/setup.py b/trunk/src/setup.py
new file mode 100644
index 00000000..26504147
--- /dev/null
+++ b/trunk/src/setup.py
@@ -0,0 +1,14 @@
+"""
+setup.py - Setup package with the help from Python's DistUtils
+"""
+
+from distutils.core import setup
+
+setup(
+ name = 'sos',
+ packages = ['sos', 'sos.plugins'],
+ scripts = [],
+ package_dir = {'': 'lib',},
+ data_files = [ ('/usr/sbin', ['sosreport']), ('/usr/share/man/man1', ['sosreport.1']), ('/usr/share/locale/en', []), ('/usr/share/locale/it', []), ('/usr/share/locale/en/LC_MESSAGES', ['locale/en/LC_MESSAGES/sos.mo']), ('/usr/share/locale/it/LC_MESSAGES', ['locale/it/LC_MESSAGES/sos.mo'])
+ ]
+)
diff --git a/trunk/src/sos.spec b/trunk/src/sos.spec
new file mode 100644
index 00000000..4b4237d7
--- /dev/null
+++ b/trunk/src/sos.spec
@@ -0,0 +1,145 @@
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+%define name sos
+%define version 1.7
+%define release 1
+
+%define _localedir %_datadir/locale
+
+Summary: A set of tools to gather troubleshooting information from a system
+Name: %{name}
+Version: %{version}
+Release: %{release}%{?dist}
+# The source for this package was pulled from upstream's svn. Use the
+# following commands to generate the tarball:
+# svn --username guest export https://sos.108.redhat.com/svn/sos/tags/r1-6 sos-1.6
+# tar -czvf sos-1.6.tar.gz sos-1.6
+Source0: %{name}-%{version}.tar.gz
+License: GPL
+Group: Development/Libraries
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+BuildArch: noarch
+Url: http://sos.108.redhat.com/
+BuildRequires: python-devel
+
+%description
+Sos is a set of tools that gathers information about system
+hardware and configuration. The information can then be used for
+diagnostic purposes and debugging. Sos is commonly used to help
+support technicians and developers.
+
+%prep
+%setup -q
+
+%build
+python setup.py build
+
+%install
+rm -rf ${RPM_BUILD_ROOT}
+python setup.py install --optimize 1 --root=$RPM_BUILD_ROOT
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%files
+%defattr(-,root,root,-)
+%{_sbindir}/sosreport
+%{python_sitelib}/sos/
+%{_mandir}/man1/sosreport.1*
+%{_localedir}/*/LC_MESSAGES/sos.mo
+%doc README TODO LICENSE ChangeLog
+
+%changelog
+* Mon Jul 5 2007 Navid Sheikhol-Eslami - 1.6-5
+- Yet more fixes to make package Fedora compliant.
+
+* Mon Jul 5 2007 Navid Sheikhol-Eslami - 1.6-4
+- More fixes to make package Fedora compliant.
+
+* Mon Jul 2 2007 Navid Sheikhol-Eslami - 1.6-3
+- Other fixes to make package Fedora compliant.
+
+* Mon Jul 2 2007 Navid Sheikhol-Eslami - 1.6-2
+- Minor fixes.
+
+* Mon Jul 2 2007 Navid Sheikhol-Eslami - 1.6-1
+- Beautified output of --list-plugins.
+- GPL licence is now included in the package.
+- added python-devel requirement for building package
+- fixed incompatibility with python from RHEL4
+
+* Fri May 25 2007 Steve Conklin - 1.5-1
+- Bumped version
+
+* Fri May 25 2007 Steve Conklin - 1.4-2
+- Fixed a backtrace on nonexistent file in kernel plugin (thanks, David Robinson)
+
+* Mon Apr 30 2007 Steve Conklin - 1.4-1
+- Fixed an error in option handling
+- Forced the file generated by traceroute to not end in .com
+- Fixed a problem with manpage
+- Added optional traceroute collection to networking plugin
+- Added clalance's patch to gather iptables info.
+- Fixes to the device-mapper plugin
+- Fixed a problem with installation of man page
+
+* Mon Apr 16 2007 Steve Conklin - 1.3-3
+- including patches to fix the following:
+- Resolves: bz219745 sosreport needs a man page
+- Resolves: bz219667 sosreport does not terminate cleanly on ^C
+- Resolves: bz233375 Make SOS flag the situation when running on a fully virtu...
+- Resolves: bz234873 rhel5 sos needs to include rpm-va by default
+- Resolves: bz219669 sosreport multi-threaded option sometimes fails
+- Resolves: bz219671 RFE for sosreport - allow specification of plugins to be run
+- Resolves: bz219672 RFE - show progress while sosreport is running
+- Resolves: bz219673 Add xen information gathering to sosreport
+- Resolves: bz219675 Collect information related to the new driver update model
+- Resolves: bz219877 'Cancel' button during option selection only cancels sele...
+
+* Tue Feb 20 2007 John Berninger - 1.3-2
+- Add man page
+
+* Fri Dec 15 2006 Steve Conklin - 1.3-1
+- really fixed bz_219654
+
+* Fri Dec 15 2006 Steve Conklin - 1.2-1
+- fixed a build problem
+
+* Fri Dec 15 2006 Steve Conklin - 1.1-1
+- Tighten permissions of tmp directory so only readable by creator bz_219657
+- Don't print message 'Problem at path ...' bz_219654
+- Removed useless message bz_219670
+- Preserve file modification times bz_219674
+- Removed unneeded message about files on copyProhibitedList bz_219712
+
+* Wed Aug 30 2006 Steve Conklin - 1.0-1
+- Seperated upstream and RPM versioning
+
+* Mon Aug 21 2006 Steve Conklin - 0.1-11
+- Code cleanup, fixed a regression in threading
+
+* Mon Aug 14 2006 Steve Conklin - 0.1-10
+- minor bugfixes, added miltithreading option, setup now quiet
+
+* Mon Jul 17 2006 Steve Conklin - 0.1-9
+- migrated to svn on 108.redhat.com, fixed a problem with command output linking in report
+
+* Mon Jun 19 2006 Steve Conklin - 0.1-6
+- Added LICENSE file containing GPL
+
+* Wed May 31 2006 Steve Conklin - 0.1-5
+- Added fixes to network plugin and prepped for Fedora submission
+
+* Wed May 31 2006 John Berninger - 0.1-4
+- Reconsolidated subpackages into one package per discussion with sconklin
+
+* Mon May 22 2006 John Berninger - 0.1-3
+- Added ftp, ldap, mail, named, samba, squid SOS plugins
+- Fixed various errors in kernel and hardware plugins
+
+* Mon May 22 2006 John Benringer - 0.1-2
+- split off cluster plugin into subpackage
+- correct file payload lists
+
+* Mon May 22 2006 John Berninger - 0.1-1
+- initial package build
diff --git a/trunk/src/sosreport b/trunk/src/sosreport
new file mode 100755
index 00000000..86d66b4d
--- /dev/null
+++ b/trunk/src/sosreport
@@ -0,0 +1,725 @@
+#!/usr/bin/env python
+"""
+Gather information about a system and report it using plugins
+supplied for application-specific information
+"""
+## sosreport.py
+## gather information about a system and report it
+
+## Copyright (C) 2006 Steve Conklin
+
+### 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.
+
+# pylint: disable-msg = W0611
+# pylint: disable-msg = W0702
+
+import sys
+import os
+#import curses
+from optparse import OptionParser, Option
+import sos.policyredhat
+from sos.helpers import *
+from snack import *
+from threading import Thread, activeCount, enumerate
+import signal
+import logging
+from stat import *
+from time import strftime, localtime, time
+from pwd import getpwuid
+import gettext
+from threading import Semaphore
+
+__version__ = 1.7
+
+__breakHits__ = 0 # Use this to track how many times we enter the exit routine
+
+## Set up routines to be linked to signals for termination handling
+def exittermhandler(signum, frame):
+ doExitCode()
+
+def doExitCode():
+ global __breakHits__
+ __breakHits__ += 1
+ if ( ( activeCount() > 1 ) and ( __breakHits__ == 1 ) ):
+ print "SIGTERM received, multiple threads detected, waiting for all threads to exit"
+ for thread in enumerate():
+ if thread.getName() != "MainThread":
+ thread.join()
+ print "All threads ended, cleaning up."
+ if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ):
+ print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately"
+ ## FIXME: Add thread-kill code (see FIXME below)
+ print "Threads dead, cleaning up."
+ if ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ):
+ print "Multiple SIGTERMs, single thread, exiting without cleaning up."
+ sys.exit(3)
+
+ # FIXME: Add code here to clean up /tmp
+ sys.exit("Abnormal exit")
+
+# Handle any sort of exit signal cleanly
+# Currently, we intercept only sig 15 (TERM)
+signal.signal(signal.SIGTERM, exittermhandler)
+
+## FIXME: Need to figure out how to IPC with child threads in case of
+## multiple SIGTERMs.
+## FIXME: Need to figure out how to handle SIGKILL - we can't intercept it.
+
+# for debugging
+__raisePlugins__ = 1
+
+class SosOption (Option):
+ """Allow to specify comma delimited list of plugins"""
+ ACTIONS = Option.ACTIONS + ("extend",)
+ STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
+ TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
+
+ def take_action(self, action, dest, opt, value, values, parser):
+ if action == "extend":
+ try: lvalue = value.split(",")
+ except: pass
+ else: values.ensure_value(dest, []).extend(lvalue)
+ else:
+ Option.take_action(self, action, dest, opt, value, values, parser)
+
+__cmdParser__ = OptionParser(option_class=SosOption)
+__cmdParser__.add_option("-a", "--alloptions", action="store_true", \
+ dest="usealloptions", default=False, \
+ help="Use all options for loaded plugins")
+__cmdParser__.add_option("-f", "--fastoptions", action="store_true", \
+ dest="fastoptions", default=False, \
+ help="Use only fast options for loaded plugins")
+__cmdParser__.add_option("-g", "--gatheronly", action="store_true", \
+ dest="gatheronly", default=False, \
+ help="Gather information locally but don't package or submit")
+__cmdParser__.add_option("-l", "--list-plugins", action="store_true", \
+ dest="listPlugins", default=False, \
+ help="list existing plugins")
+__cmdParser__.add_option("-n", "--noplugin", action="extend", \
+ dest="noplugins", type="string", \
+ help="skip these plugins", default = [])
+__cmdParser__.add_option("-o", "--onlyplugin", action="extend", \
+ dest="onlyplugins", type="string", \
+ help="enable these plugins only", default = [])
+__cmdParser__.add_option("-e", "--enableplugin", action="extend", \
+ dest="enableplugins", type="string", \
+ help="list of inactive plugins to be enabled", default = [])
+__cmdParser__.add_option("-k", "--pluginopts", action="extend", \
+ dest="plugopts", type="string", \
+ help="plugin options in plugin_name.option=value format")
+__cmdParser__.add_option("-v", "--verbose", action="count", \
+ dest="verbosity", \
+ help="How obnoxious we're being about telling the user what we're doing.")
+__cmdParser__.add_option("-c", "--curses", action="store_true", \
+ dest="use_curses", default=False, \
+ help="Display a text GUI menu to modify plugin options.")
+__cmdParser__.add_option("--no-progressbar", action="store_false", \
+ dest="progressbar", default=True, \
+ help="Do not display a progress bar.")
+__cmdParser__.add_option("--no-multithread", action="store_true", \
+ dest="nomultithread", \
+ help="Disable multi-threaded gathering mode (slower)", default=False)
+(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args()
+
+def textcolor(text, fg, bg=None, raw=0):
+ colors = { "black":"30", "red":"31", "green":"32", "brown":"33", "blue":"34",
+ "purple":"35", "cyan":"36", "lgray":"37", "gray":"1;30", "lred":"1;31",
+ "lgreen":"1;32", "yellow":"1;33", "lblue":"1;34", "pink":"1;35",
+ "lcyan":"1;36", "white":"1;37" }
+ opencol = "\033["
+ closecol = "m"
+ clear = opencol + "0" + closecol
+ f = opencol + colors[fg] + closecol
+ return "%s%s%s" % (f, text, clear)
+
+def get_curse_options(alloptions):
+ """
+ use curses to enable the user to select some options
+ """
+ # alloptions is an array of (plug, plugname, optname, parms(dictionary)) tuples
+ plugName = []
+ out = []
+
+ # get a sorted list of all plugin names
+ for rrr in alloptions:
+ if rrr[1] not in plugName:
+ plugName.append(rrr[1])
+
+ plugName.sort()
+ plugCbox = CheckboxTree(height=5, scroll=1)
+
+ countOpt = -1
+
+ optDic = {}
+ optDicCounter = 0
+
+ # iterate over all plugins with options
+ for curPlugName in plugName:
+ plugCbox.addItem(curPlugName, (snackArgs['append'],))
+ countOpt = countOpt+1
+
+ for opt in alloptions:
+ if opt[1] != curPlugName:
+ continue
+
+ snt = opt[2] + " ("+opt[3]['desc']+") is " + opt[3]['speed']
+ plugCbox.addItem(snt, (countOpt, snackArgs['append']), item = optDicCounter, selected = opt[3]['enabled'])
+ optDic[optDicCounter] = opt
+ optDicCounter += 1
+
+
+ screen = SnackScreen()
+ bb = ButtonBar(screen, (("Ok", "ok"), ("Cancel", "cancel")))
+ g = GridForm(screen, "Select Sosreport Options", 1, 10)
+ g.add(plugCbox, 0, 0)
+ g.add(bb, 0, 1, growx = 1)
+ result = g.runOnce()
+
+ screen.finish()
+
+ if bb.buttonPressed(result) == "cancel":
+ raise "Cancelled"
+
+ for rrr in range(0, optDicCounter):
+ optDic[rrr][3]['enabled'] = plugCbox.getEntryValue(rrr)[1]
+ out.append((optDic[rrr]))
+
+ return out
+
+class progressBar:
+ def __init__(self, minValue = 0, maxValue = 10, totalWidth=40):
+ self.progBar = "[]" # This holds the progress bar string
+ self.min = minValue
+ self.max = maxValue
+ self.width = totalWidth
+ self.amount = 0 # When amount == max, we are 100% done
+ self.time_start = time()
+ self.eta = 0
+ self.last_amount_update = time()
+ self.update()
+
+ def updateAmount(self, newAmount = 0):
+ if newAmount < self.min: newAmount = self.min
+ if newAmount > self.max: newAmount = self.max
+ if self.amount != newAmount:
+ self.last_amount_update = time()
+ self.amount = newAmount
+ last_update_relative = round(self.last_amount_update - self.time_start)
+ self.eta = round(last_update_relative * self.max / self.amount)
+
+ # generate ETA
+ timeElapsed = round(time() - self.time_start)
+ last_update_relative = round(self.last_amount_update - self.time_start)
+ if timeElapsed >= 10 and self.amount > 0:
+ percentDone = round(timeElapsed * 100 / self.eta)
+ if percentDone > 100:
+ percentDone = 100
+ ETA = timeElapsed
+ elif self.eta < timeElapsed:
+ ETA = timeElapsed
+ else:
+ ETA = self.eta
+ ETA = "[%02d:%02d/%02d:%02d]" % (round(timeElapsed/60), timeElapsed % 60, round(ETA/60), ETA % 60)
+ else:
+ ETA = "[%02d:%02d/--:--]" % (round(timeElapsed/60), timeElapsed % 60)
+ if self.amount < self.max:
+ percentDone = 0
+ else:
+ percentDone = 100
+
+ # Figure out how many hash bars the percentage should be
+ allFull = self.width - 2
+ numHashes = (percentDone / 100.0) * allFull
+ numHashes = int(round(numHashes))
+
+ # build a progress bar with hashes and spaces
+ self.progBar = " [" + '#'*numHashes + ' '*(allFull-numHashes) + "]"
+
+ # figure out where to put the percentage, roughly centered
+ percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
+ percentString = str(percentDone) + "%"
+
+ # slice the percentage into the bar
+ self.progBar = " Progress" + self.progBar[0:percentPlace] + percentString + self.progBar[percentPlace+len(percentString):] + ETA
+
+ def incAmount(self, toInc = 1):
+ self.updateAmount(self.amount+toInc)
+
+ def finished(self):
+ self.updateAmount(self.max)
+ sys.stdout.write(self.progBar + '\n')
+ sys.stdout.flush()
+
+ def update(self):
+ self.updateAmount(self.amount)
+ sys.stdout.write(self.progBar + '\r')
+ sys.stdout.flush()
+
+class XmlReport:
+ def __init__(self):
+ try:
+ import libxml2
+ except:
+ self.enabled = False
+ return
+ else:
+ self.enabled = True
+ self.doc = libxml2.newDoc("1.0")
+ self.root = self.doc.newChild(None, "sos", None)
+ self.commands = self.root.newChild(None, "commands", None)
+ self.files = self.root.newChild(None, "files", None)
+
+ def add_command(self,cmdline,exitcode,stdout = None,stderr = None,f_stdout=None,f_stderr=None, runtime=None):
+ if not self.enabled: return
+
+ cmd = self.commands.newChild(None, "cmd", None)
+
+ cmd.setNsProp(None, "cmdline", cmdline)
+
+ cmdchild = cmd.newChild(None, "exitcode", str(exitcode))
+
+ if runtime:
+ cmd.newChild(None, "runtime", str(runtime))
+
+ if stdout or f_stdout:
+ cmdchild = cmd.newChild(None, "stdout", stdout)
+ if f_stdout:
+ cmdchild.setNsProp(None, "file", f_stdout)
+
+ if stderr or f_stderr:
+ cmdchild = cmd.newChild(None, "stderr", stderr)
+ if f_stderr:
+ cmdchild.setNsProp(None, "file", f_stderr)
+
+ def add_file(self,fname,stats):
+ if not self.enabled: return
+
+ cfile = self.files.newChild(None,"file",None)
+
+ cfile.setNsProp(None, "fname", fname)
+
+ cchild = cfile.newChild(None, "uid", str(stats[ST_UID]))
+ cchild.setNsProp(None,"name", getpwuid(stats[ST_UID])[0])
+ cchild = cfile.newChild(None, "gid", str(stats[ST_GID]))
+ cchild.setNsProp(None,"name", getpwuid(stats[ST_GID])[0])
+ cfile.newChild(None, "mode", str(oct(S_IMODE(stats[ST_MODE]))))
+ cchild = cfile.newChild(None, "ctime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_CTIME])))
+ cchild.setNsProp(None,"tstamp", str(stats[ST_CTIME]))
+ cchild = cfile.newChild(None, "atime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_ATIME])))
+ cchild.setNsProp(None,"tstamp", str(stats[ST_ATIME]))
+ cchild = cfile.newChild(None, "mtime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_MTIME])))
+ cchild.setNsProp(None,"tstamp", str(stats[ST_MTIME]))
+
+ def serialize(self):
+ if not self.enabled: return
+
+ print self.doc.serialize(None, 1)
+
+ def serialize_to_file(self,fname):
+ if not self.enabled: return
+
+ outfn = open(fname,"w")
+ outfn.write(self.doc.serialize(None,1))
+ outfn.close()
+
+def sosreport():
+ # pylint: disable-msg = R0912
+ # pylint: disable-msg = R0914
+ # pylint: disable-msg = R0915
+ """
+ This is the top-level function that gathers and processes all sosreport information
+ """
+ loadedplugins = []
+ skippedplugins = []
+ alloptions = []
+
+ # perhaps we should automatically locate the policy module??
+ policy = sos.policyredhat.SosPolicy()
+
+ # find the plugins path
+ paths = sys.path
+ for path in paths:
+ if path.strip()[-len("site-packages"):] == "site-packages":
+ pluginpath = path + "/sos/plugins"
+ reporterpath = path + "/sos/reporters"
+
+ # Set up common info and create destinations
+
+ dstroot = sosFindTmpDir()
+ cmddir = os.path.join(dstroot, "sos_commands")
+ logdir = os.path.join(dstroot, "sos_logs")
+ rptdir = os.path.join(dstroot, "sos_reports")
+ os.mkdir(cmddir, 0755)
+ os.mkdir(logdir, 0755)
+ os.mkdir(rptdir, 0755)
+
+ # initialize i18n language localization
+ gettext.install('sos', '/usr/share/locale', unicode=False)
+
+ # initialize logging
+ soslog = logging.getLogger('sos')
+ soslog.setLevel(logging.DEBUG)
+
+ # log to a file
+ flog = logging.FileHandler(logdir + "/sos.log")
+ flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
+ flog.setLevel(logging.DEBUG)
+ soslog.addHandler(flog)
+
+ # define a Handler which writes INFO messages or higher to the sys.stderr
+ console = logging.StreamHandler(sys.stderr)
+ if __cmdLineOpts__.verbosity > 0:
+ console.setLevel(20 - __cmdLineOpts__.verbosity)
+ __cmdLineOpts__.progressbar = False
+ else:
+ console.setLevel(logging.INFO)
+ console.setFormatter(logging.Formatter('%(message)s'))
+ soslog.addHandler(console)
+
+ logging.VERBOSE = logging.INFO - 1
+ logging.VERBOSE2 = logging.INFO - 2
+ logging.VERBOSE3 = logging.INFO - 3
+ logging.addLevelName(logging.VERBOSE, "verbose")
+ logging.addLevelName(logging.VERBOSE2,"verbose2")
+ logging.addLevelName(logging.VERBOSE3,"verbose3")
+
+ xmlrep = XmlReport()
+
+ # set up dict so everyone can share the following
+ commons = {'dstroot': dstroot, 'cmddir': cmddir, 'logdir': logdir, 'rptdir': rptdir,
+ 'soslog': soslog, 'policy': policy, 'verbosity' : __cmdLineOpts__.verbosity,
+ 'xmlreport' : xmlrep }
+
+ # Make policy aware of the commons
+ policy.setCommons(commons)
+
+ print
+ soslog.info ( _("sosreport (version %s)") % __version__)
+ print
+
+ # generate list of available plugins
+ plugins = os.listdir(pluginpath)
+ plugins.sort()
+
+ # validate and load plugins
+ for plug in plugins:
+ plugbase = plug[:-3]
+ if not plug[-3:] == '.py' or plugbase == "__init__":
+ continue
+ try:
+ #print "importing plugin: %s" % plugbase
+ try:
+ if policy.validatePlugin(pluginpath + plug):
+ pluginClass = importPlugin("sos.plugins." + plugbase, plugbase)
+ else:
+ soslog.warning(_("plugin %s does not validate, skipping") % plug)
+ skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ continue
+ if plugbase in __cmdLineOpts__.noplugins:
+ soslog.log(logging.VERBOSE, _("plug %s skipped (noplugins)") % plugbase)
+ skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ continue
+ if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins:
+ soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug)
+ skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ continue
+ if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins:
+ soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug)
+ skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ continue
+ if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins:
+ soslog.log(logging.VERBOSE, _("plugin %s not specified in --onlyplugin list") % plug)
+ skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ continue
+ loadedplugins.append((plugbase, pluginClass(plugbase, commons)))
+ except:
+ soslog.warning(_("plugin %s does not install, skipping") % plug)
+ raise
+ except:
+ soslog.warning(_("could not load plugin %s") % plug)
+ if __raisePlugins__:
+ raise
+
+ # First, gather and process options
+ for plugname, plug in loadedplugins:
+ soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname)
+ names, parms = plug.getAllOptions()
+ for optname, optparm in zip(names, parms):
+ alloptions.append((plug, plugname, optname, optparm))
+
+ if __cmdLineOpts__.listPlugins:
+ if not len(loadedplugins) and not len(skippedplugins):
+ soslog.error(_("no valid plugins found"))
+ sys.exit(1)
+
+ if len(loadedplugins):
+ print _("The following plugins are currently enabled:")
+ print
+ for (plugname,plug) in loadedplugins:
+ print " %-25s %s" % (textcolor(plugname,"lblue"),plug.get_description())
+ else:
+ print _("No plugin enabled.")
+ print
+
+ if len(alloptions):
+ print _("The following plugin options are available:")
+ print
+ for (plug, plugname, optname, optparm) in alloptions:
+ print " %-25s %s [%d]" % (plugname + "." + optname, optparm["desc"], optparm["enabled"])
+ else:
+ print _("No plugin options available.")
+
+ if len(skippedplugins):
+ print
+ print _("The following plugins are currently disabled:")
+ print
+ for (plugname,plugclass) in skippedplugins:
+ print " %-25s %s" % (textcolor(plugname,"blue"),plugclass.get_description())
+
+ print
+ sys.exit()
+
+ # to go anywhere further than listing the plugins we will need root permissions.
+ #
+ if os.getuid() != 0:
+ print _('sosreport requires root permissions to run.')
+ sys.exit(1)
+
+ # we don't need to keep in memory plugins we are not going to use
+ del skippedplugins
+
+ if not len(loadedplugins):
+ soslog.error(_("no valid plugins were enabled"))
+ sys.exit(1)
+
+ try:
+ raw_input(_("""This utility will collect some detailed information about the
+hardware and setup of your Red Hat Enterprise Linux system.
+This information will be used to diagnose problems with your
+system and will be considered confidential information.
+Red Hat will use this information for diagnostic purposes ONLY.
+
+This process may take a while to complete.
+No changes will be made to your system.
+
+Press ENTER to continue, or CTRL-C to quit.
+"""))
+ except KeyboardInterrupt:
+ print
+ sys.exit(0)
+
+ # setup plugin options
+ if __cmdLineOpts__.plugopts:
+ opts = {}
+ for opt in __cmdLineOpts__.plugopts:
+ try: opt, val = opt.split("=")
+ except: val=1
+ plug, opt = opt.split(".")
+ try: val = int(val) # try to convert string "val" to int()
+ except: pass
+ try: opts[plug]
+ except KeyError: opts[plug] = []
+ opts[plug].append( (opt,val) )
+ for plugname, plug in loadedplugins:
+ if opts.has_key(plugname):
+ for opt,val in opts[plugname]:
+ soslog.log(logging.VERBOSE, "setting option %s for plugin %s to %s" % (plugname,opt,val))
+ plug.setOption(opt,val)
+ del opt,opts,val
+ elif not __cmdLineOpts__.fastoptions and not __cmdLineOpts__.usealloptions:
+ if len(alloptions) and __cmdLineOpts__.use_curses:
+ try:
+ get_curse_options(alloptions)
+ except "Cancelled":
+ sys.exit(_("Exiting."))
+ elif __cmdLineOpts__.fastoptions:
+ for i in range(len(alloptions)):
+ for plug, plugname, optname, optparm in alloptions:
+ if optparm['speed'] == 'fast':
+ plug.setOption(optname, 1)
+ else:
+ plug.setOption(optname, 0)
+ elif __cmdLineOpts__.usealloptions:
+ for i in range(len(alloptions)):
+ for plug, plugname, optname, optparm in alloptions:
+ plug.setOption(optname, 1)
+
+ # Call the diagnose() method for each plugin
+ tmpcount = 0
+ for plugname, plug in loadedplugins:
+ soslog.log(logging.VERBOSE2, "Performing sanity check for plugin %s" % plugname)
+ plug.diagnose()
+ tmpcount += len(plug.diagnose_msgs)
+ if tmpcount > 0:
+ print _("One or more plugin has detected a problem in your configuration.")
+ print _("Please review the following messages:")
+ print
+ for plugname, plug in loadedplugins:
+ for msg in plug.diagnose_msgs:
+ soslog.warning(" * %s: %s", plugname, msg)
+ print
+ try:
+ raw_input( _("Press ENTER to continue, or CTRL-C to quit.\n") )
+ except KeyboardInterrupt:
+ print
+ sys.exit(0)
+
+ # Call the setup() method for each plugin
+ for plugname, plug in loadedplugins:
+ soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname)
+ plug.setup()
+
+ # Setup the progress bar
+ if __cmdLineOpts__.progressbar:
+ # gather information useful for generating ETA
+ eta_weight = len(loadedplugins)
+ for plugname, plug in loadedplugins:
+ eta_weight += plug.eta_weight
+ pbar = progressBar(minValue = 0, maxValue = eta_weight)
+ # pbar.max = number_of_plugins + weight (default 1 per plugin)
+
+ if __cmdLineOpts__.nomultithread:
+ soslog.log(logging.VERBOSE, "using single-threading")
+ else:
+ soslog.log(logging.VERBOSE, "using multi-threading")
+
+ # Call the collect method for each plugin
+ plugrunning = Semaphore(2)
+ for plugname, plug in loadedplugins:
+ soslog.log(logging.VERBOSE, "executing plugin %s" % plugname)
+ if not __cmdLineOpts__.nomultithread:
+ plug.copyStuff(threaded = True, semaphore = plugrunning)
+ else:
+ plug.copyStuff()
+ if __cmdLineOpts__.progressbar:
+ pbar.incAmount(plug.eta_weight)
+ pbar.update()
+ del plugrunning
+
+ # Wait for all the collection threads to exit
+ if not __cmdLineOpts__.nomultithread:
+ finishedplugins = []
+ while len(loadedplugins) > 0:
+ plugname, plug = loadedplugins.pop(0)
+ if not plug.wait(0.5):
+ finishedplugins.append((plugname,plug))
+ soslog.log(logging.VERBOSE2, "plugin %s has returned" % plugname)
+ if __cmdLineOpts__.progressbar:
+ pbar.incAmount(plug.eta_weight)
+ else:
+ soslog.log(logging.VERBOSE3, "plugin %s still hasn't returned" % plugname)
+ loadedplugins.append((plugname,plug))
+ if __cmdLineOpts__.progressbar:
+ pbar.update()
+ loadedplugins = finishedplugins
+ del finishedplugins
+
+ xmlrep.serialize_to_file(rptdir + "/" + "sosreport.xml")
+
+ # Call the analyze method for each plugin
+ for plugname, plug in loadedplugins:
+ soslog.log(logging.VERBOSE2, "Analyzing results of plugin %s" % plugname,)
+ try:
+ plug.analyze()
+ except:
+ # catch exceptions in analyse() and keep working
+ pass
+ if __cmdLineOpts__.progressbar:
+ pbar.incAmount()
+ pbar.update()
+
+ if __cmdLineOpts__.progressbar:
+ pbar.finished()
+ sys.stdout.write("\n")
+
+ # Generate the header for the html output file
+ rfd = open(rptdir + "/" + "sosreport.html", "w")
+ rfd.write("""
+
+
+
+
+
+ Sos System Report
+
+
+
+ """)
+
+
+ # Make a pass to gather Alerts and a list of module names
+ allAlerts = []
+ plugNames = []
+ for plugname, plug in loadedplugins:
+ for alert in plug.alerts:
+ allAlerts.append('%s: %s' % (plugname, plugname, alert))
+ plugNames.append(plugname)
+
+
+
+ # Create a table of links to the module info
+ rfd.write("
Loaded Plugins:
")
+ rfd.write("
\n")
+ rr = 0
+ for i in range(len(plugNames)):
+ rfd.write('
')
+ if not (rr == 3):
+ rfd.write('')
+ rfd.write('
\n')
+
+ rfd.write('
Alerts:
')
+ rfd.write('
')
+ for alert in allAlerts:
+ rfd.write('
%s
' % alert)
+ rfd.write('
')
+
+
+ # Call the report method for each plugin
+ for plugname, plug in loadedplugins:
+ html = plug.report()
+ rfd.write(html)
+
+ rfd.write("")
+
+ rfd.close()
+
+ # Collect any needed user information (name, etc)
+
+ # Call the postproc method for each plugin
+ for plugname, plug in loadedplugins:
+ plug.postproc()
+
+ if __cmdLineOpts__.gatheronly:
+ soslog.info(_("Collected information is in ") + dstroot)
+ soslog.info(_("Your html report is in ") + rptdir + "/" + "sosreport.html")
+ else:
+ # package up the results for the support organization
+ policy.packageResults()
+ # delete gathered files
+ os.system("/bin/rm -rf %s" % dstroot)
+ # automated submission will go here
+
+ # Close all log files and perform any cleanup
+ logging.shutdown()
+
+
+if __name__ == '__main__':
+ try:
+ sosreport()
+ except KeyboardInterrupt:
+ doExitCode()
diff --git a/trunk/src/sosreport.1 b/trunk/src/sosreport.1
new file mode 100644
index 00000000..cf76afdb
--- /dev/null
+++ b/trunk/src/sosreport.1
@@ -0,0 +1,55 @@
+.TH SOSREPORT 1 "Tue Feb 20 2007"
+.SH NAME
+sosreport \- Generate debugging information for this system
+.SH SYNOPSIS
+.B sosreport
+[-a|--alloptions] [-f|--fastoptions] [-g|--gatheronly]
+ [-l|--list-plugins] [-n|--noplugin \fIplugin-name\fR]
+ [-o|--onlyplugin \fIplugin-name\fR]
+ [-v|--verbose [...]] [-m|--multithreaded]
+.SH DESCRIPTION
+\fBsosreport\fR generates a compressed tarball of debugging information
+for the system it is run on that can be sent to technical support
+reps that will give them a more complete view of the overall system
+status.
+.SH OPTIONS
+.TP
+.B \-a, \--alloptions
+Enable all options for all loaded plugins
+.TP
+.B \-f, \--fastoptions
+Enable all options marked as "fast" for loaded plugins. This will
+reduce running time while still gathering helpful information, but
+you may be asked to re-run later with some or all "slow" options
+enabled depending on the specific issue.
+.TP
+.B \-g, \--gatheronly
+Gather the diagnostic data and bundle it up, but do not attempt to
+send it to any support site. This option currently has no effect as
+sosreport currently does not support report transmission.
+.TP
+.B \-l, \--list-plugins
+List available plugins
+.TP
+.B \-n, \--noplugin
+Do not load specified plugin(s)
+.TP
+.B \-o, \--onlyplugin
+Load only the specified plugin(s), all otherplugins should be disabled
+.TP
+.B \-v, \--verbose
+Increase the verbosity of the output as sosreport is running. This option
+can be specified more than once.
+.TP
+.B \-m, \--multithreaded
+Enable a multithreaded collection and analysis of the sosreport data. Please
+note that this option is experimental and is known to have intermittent issues.
+.SH BUGS
+The multithreaded option can fail intermittently, please use it with care.
+.SH AUTHORS
+.nf
+Steve Conklin
+John Berninger
+Navid Sheikhol-Eslami
+Pierre Amadio
+.fi
diff --git a/trunk/src/tools/msgfmt.py b/trunk/src/tools/msgfmt.py
new file mode 100644
index 00000000..8a2d4e66
--- /dev/null
+++ b/trunk/src/tools/msgfmt.py
@@ -0,0 +1,203 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# Written by Martin v. Löwis
+
+"""Generate binary message catalog from textual translation description.
+
+This program converts a textual Uniforum-style message catalog (.po file) into
+a binary GNU catalog (.mo file). This is essentially the same function as the
+GNU msgfmt program, however, it is a simpler implementation.
+
+Usage: msgfmt.py [OPTIONS] filename.po
+
+Options:
+ -o file
+ --output-file=file
+ Specify the output file to write to. If omitted, output will go to a
+ file named filename.mo (based off the input file name).
+
+ -h
+ --help
+ Print this message and exit.
+
+ -V
+ --version
+ Display version information and exit.
+"""
+
+import sys
+import os
+import getopt
+import struct
+import array
+
+__version__ = "1.1"
+
+MESSAGES = {}
+
+
+
+def usage(code, msg=''):
+ print >> sys.stderr, __doc__
+ if msg:
+ print >> sys.stderr, msg
+ sys.exit(code)
+
+
+
+def add(id, str, fuzzy):
+ "Add a non-fuzzy translation to the dictionary."
+ global MESSAGES
+ if not fuzzy and str:
+ MESSAGES[id] = str
+
+
+
+def generate():
+ "Return the generated output."
+ global MESSAGES
+ keys = MESSAGES.keys()
+ # the keys are sorted in the .mo file
+ keys.sort()
+ offsets = []
+ ids = strs = ''
+ for id in keys:
+ # For each string, we need size and file offset. Each string is NUL
+ # terminated; the NUL does not count into the size.
+ offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id])))
+ ids += id + '\0'
+ strs += MESSAGES[id] + '\0'
+ output = ''
+ # The header is 7 32-bit unsigned integers. We don't use hash tables, so
+ # the keys start right after the index tables.
+ # translated string.
+ keystart = 7*4+16*len(keys)
+ # and the values start after the keys
+ valuestart = keystart + len(ids)
+ koffsets = []
+ voffsets = []
+ # The string table first has the list of keys, then the list of values.
+ # Each entry has first the size of the string, then the file offset.
+ for o1, l1, o2, l2 in offsets:
+ koffsets += [l1, o1+keystart]
+ voffsets += [l2, o2+valuestart]
+ offsets = koffsets + voffsets
+ output = struct.pack("Iiiiiii",
+ 0x950412deL, # Magic
+ 0, # Version
+ len(keys), # # of entries
+ 7*4, # start of key index
+ 7*4+len(keys)*8, # start of value index
+ 0, 0) # size and offset of hash table
+ output += array.array("i", offsets).tostring()
+ output += ids
+ output += strs
+ return output
+
+
+
+def make(filename, outfile):
+ ID = 1
+ STR = 2
+
+ # Compute .mo name from .po name and arguments
+ if filename.endswith('.po'):
+ infile = filename
+ else:
+ infile = filename + '.po'
+ if outfile is None:
+ outfile = os.path.splitext(infile)[0] + '.mo'
+
+ try:
+ lines = open(infile).readlines()
+ except IOError, msg:
+ print >> sys.stderr, msg
+ sys.exit(1)
+
+ section = None
+ fuzzy = 0
+
+ # Parse the catalog
+ lno = 0
+ for l in lines:
+ lno += 1
+ # If we get a comment line after a msgstr, this is a new entry
+ if l[0] == '#' and section == STR:
+ add(msgid, msgstr, fuzzy)
+ section = None
+ fuzzy = 0
+ # Record a fuzzy mark
+ if l[:2] == '#,' and l.find('fuzzy'):
+ fuzzy = 1
+ # Skip comments
+ if l[0] == '#':
+ continue
+ # Now we are in a msgid section, output previous section
+ if l.startswith('msgid'):
+ if section == STR:
+ add(msgid, msgstr, fuzzy)
+ section = ID
+ l = l[5:]
+ msgid = msgstr = ''
+ # Now we are in a msgstr section
+ elif l.startswith('msgstr'):
+ section = STR
+ l = l[6:]
+ # Skip empty lines
+ l = l.strip()
+ if not l:
+ continue
+ # XXX: Does this always follow Python escape semantics?
+ l = eval(l)
+ if section == ID:
+ msgid += l
+ elif section == STR:
+ msgstr += l
+ else:
+ print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
+ 'before:'
+ print >> sys.stderr, l
+ sys.exit(1)
+ # Add last entry
+ if section == STR:
+ add(msgid, msgstr, fuzzy)
+
+ # Compute output
+ output = generate()
+
+ try:
+ open(outfile,"wb").write(output)
+ except IOError,msg:
+ print >> sys.stderr, msg
+
+
+
+def main():
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'hVo:',
+ ['help', 'version', 'output-file='])
+ except getopt.error, msg:
+ usage(1, msg)
+
+ outfile = None
+ # parse options
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ usage(0)
+ elif opt in ('-V', '--version'):
+ print >> sys.stderr, "msgfmt.py", __version__
+ sys.exit(0)
+ elif opt in ('-o', '--output-file'):
+ outfile = arg
+ # do it
+ if not args:
+ print >> sys.stderr, 'No input file given'
+ print >> sys.stderr, "Try `msgfmt --help' for more information."
+ return
+
+ for filename in args:
+ make(filename, outfile)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/trunk/src/tools/pygettext.py b/trunk/src/tools/pygettext.py
new file mode 100644
index 00000000..040b5c7f
--- /dev/null
+++ b/trunk/src/tools/pygettext.py
@@ -0,0 +1,762 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# Originally written by Barry Warsaw
+#
+# Minimally patched to make it even more xgettext compatible
+# by Peter Funk
+#
+# 2002-11-22 Jürgen Hermann
+# Added checks that _() only contains string literals, and
+# command line args are resolved to module lists, i.e. you
+# can now pass a filename, a module or package name, or a
+# directory (including globbing chars, important for Win32).
+# Made docstring fit in 80 chars wide displays using pydoc.
+#
+
+import codecs
+
+# for selftesting
+import re
+try:
+ import fintl
+ _ = fintl.gettext
+except ImportError:
+ _ = lambda s: s
+
+__doc__ = _("""pygettext -- Python equivalent of xgettext(1)
+
+Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the
+internationalization of C programs. Most of these tools are independent of
+the programming language and can be used from within Python programs.
+Martin von Loewis' work[1] helps considerably in this regard.
+
+There's one problem though; xgettext is the program that scans source code
+looking for message strings, but it groks only C (or C++). Python
+introduces a few wrinkles, such as dual quoting characters, triple quoted
+strings, and raw strings. xgettext understands none of this.
+
+Enter pygettext, which uses Python's standard tokenize module to scan
+Python source code, generating .pot files identical to what GNU xgettext[2]
+generates for C and C++ code. From there, the standard GNU tools can be
+used.
+
+A word about marking Python strings as candidates for translation. GNU
+xgettext recognizes the following keywords: gettext, dgettext, dcgettext,
+and gettext_noop. But those can be a lot of text to include all over your
+code. C and C++ have a trick: they use the C preprocessor. Most
+internationalized C source includes a #define for gettext() to _() so that
+what has to be written in the source is much less. Thus these are both
+translatable strings:
+
+ gettext("Translatable String")
+ _("Translatable String")
+
+Python of course has no preprocessor so this doesn't work so well. Thus,
+pygettext searches only for _() by default, but see the -k/--keyword flag
+below for how to augment this.
+
+ [1] http://www.python.org/workshops/1997-10/proceedings/loewis.html
+ [2] http://www.gnu.org/software/gettext/gettext.html
+
+NOTE: pygettext attempts to be option and feature compatible with GNU
+xgettext where ever possible. However some options are still missing or are
+not fully implemented. Also, xgettext's use of command line switches with
+option arguments is broken, and in these cases, pygettext just defines
+additional switches.
+
+Usage: pygettext [options] inputfile ...
+
+Options:
+
+ -a
+ --extract-all
+ Extract all strings.
+
+ -d name
+ --default-domain=name
+ Rename the default output file from messages.pot to name.pot.
+
+ -E
+ --escape
+ Replace non-ASCII characters with octal escape sequences.
+
+ -D
+ --docstrings
+ Extract module, class, method, and function docstrings. These do
+ not need to be wrapped in _() markers, and in fact cannot be for
+ Python to consider them docstrings. (See also the -X option).
+
+ -h
+ --help
+ Print this help message and exit.
+
+ -k word
+ --keyword=word
+ Keywords to look for in addition to the default set, which are:
+ %(DEFAULTKEYWORDS)s
+
+ You can have multiple -k flags on the command line.
+
+ -K
+ --no-default-keywords
+ Disable the default set of keywords (see above). Any keywords
+ explicitly added with the -k/--keyword option are still recognized.
+
+ --no-location
+ Do not write filename/lineno location comments.
+
+ -n
+ --add-location
+ Write filename/lineno location comments indicating where each
+ extracted string is found in the source. These lines appear before
+ each msgid. The style of comments is controlled by the -S/--style
+ option. This is the default.
+
+ -o filename
+ --output=filename
+ Rename the default output file from messages.pot to filename. If
+ filename is `-' then the output is sent to standard out.
+
+ -p dir
+ --output-dir=dir
+ Output files will be placed in directory dir.
+
+ -S stylename
+ --style stylename
+ Specify which style to use for location comments. Two styles are
+ supported:
+
+ Solaris # File: filename, line: line-number
+ GNU #: filename:line
+
+ The style name is case insensitive. GNU style is the default.
+
+ -v
+ --verbose
+ Print the names of the files being processed.
+
+ -V
+ --version
+ Print the version of pygettext and exit.
+
+ -w columns
+ --width=columns
+ Set width of output to columns.
+
+ -x filename
+ --exclude-file=filename
+ Specify a file that contains a list of strings that are not be
+ extracted from the input files. Each string to be excluded must
+ appear on a line by itself in the file.
+
+ -X filename
+ --no-docstrings=filename
+ Specify a file that contains a list of files (one per line) that
+ should not have their docstrings extracted. This is only useful in
+ conjunction with the -D option above.
+
+If `inputfile' is -, standard input is read.
+""")
+
+import os
+import imp
+import sys
+import glob
+import time
+import getopt
+import token
+import tokenize
+import operator
+import codecs
+
+from elementtree.ElementTree import ElementTree, XML
+
+__version__ = '1.5'
+
+default_keywords = ['_']
+DEFAULTKEYWORDS = ', '.join(default_keywords)
+
+EMPTYSTRING = ''
+
+
+
+# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's
+# there.
+pot_header = _('''\
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\\n"
+"POT-Creation-Date: %(time)s\\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
+"Last-Translator: FULL NAME \\n"
+"Language-Team: LANGUAGE \\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=%(charset)s\\n"
+"Content-Transfer-Encoding: %(charset)s\\n"
+"Generated-By: pygettext.py %(version)s\\n"
+
+''')
+
+
+def usage(code, msg=''):
+ print >> sys.stderr, __doc__ % globals()
+ if msg:
+ print >> sys.stderr, msg
+ sys.exit(code)
+
+
+
+escapes = []
+
+def make_escapes(pass_iso8859):
+ global escapes
+ if pass_iso8859:
+ # Allow iso-8859 characters to pass through so that e.g. 'msgid
+ # "Höhe"' would result not result in 'msgid "H\366he"'. Otherwise we
+ # escape any character outside the 32..126 range.
+ mod = 128
+ else:
+ mod = 256
+ for i in range(256):
+ if 32 <= (i % mod) <= 126:
+ escapes.append(chr(i))
+ else:
+ escapes.append("\\%03o" % i)
+ escapes[ord('\\')] = '\\\\'
+ escapes[ord('\t')] = '\\t'
+ escapes[ord('\r')] = '\\r'
+ escapes[ord('\n')] = '\\n'
+ escapes[ord('\"')] = '\\"'
+
+
+def escape_ascii(s):
+ "Escapes all text outside of 7bit ASCII plus control characters and Python literals."
+ global escapes
+ s = list(s)
+ for i in range(len(s)):
+ s[i] = escapes[ord(s[i])]
+ return EMPTYSTRING.join(s)
+
+def escape_unicode(s):
+ "Escapes control characters and Python literals only leaving non-ascii text intact."
+ #for sp in ('\t', '\r', '\n', '\"', '\\'):
+ s = s.replace('\\', '\\\\')
+ s = s.replace('\t', '\\t')
+ s = s.replace('\r', '\\r')
+ s = s.replace('\n', '\\n')
+ s = s.replace('\"', '\\"')
+ # escape control chars
+ def repl(m): return "\\%03o" % ord(m.group(0))
+ s = re.sub('[\001-\037]', repl, s)
+ return s
+
+def safe_eval(s):
+ # unwrap quotes, safely
+ return eval(s, {'__builtins__':{}}, {})
+
+
+def normalize(s, escape=False):
+ # This converts the various Python string types into a format that is
+ # appropriate for .po files, namely much closer to C style.
+ lines = s.split('\n')
+ if len(lines) == 1:
+ s = '"' + escape_unicode(s) + '"'
+ else:
+ if not lines[-1]:
+ del lines[-1]
+ lines[-1] = lines[-1] + '\n'
+ for i in range(len(lines)):
+ lines[i] = escape_unicode(lines[i])
+ lineterm = '\\n"\n"'
+ s = '""\n"' + lineterm.join(lines) + '"'
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ if escape:
+ def repl(m): return "\\%03o" % ord(m.group(0))
+ s = re.sub('[\200-\377]', repl, s)
+ return s
+
+
+def containsAny(str, set):
+ """Check whether 'str' contains ANY of the chars in 'set'"""
+ return 1 in [c in str for c in set]
+
+
+def _visit_pyfiles(list, dirname, names):
+ """Helper for getFilesForName()."""
+ # get extension for python source files
+ if not globals().has_key('_py_ext'):
+ global _py_ext
+ _py_ext = [triple[0] for triple in imp.get_suffixes()
+ if triple[2] == imp.PY_SOURCE][0]
+
+ # don't recurse into CVS directories
+ if 'CVS' in names:
+ names.remove('CVS')
+ if '.svn' in names:
+ names.remove('.svn')
+
+ # add all *.py files to list
+ list.extend(
+ [os.path.join(dirname, file) for file in names
+ if os.path.splitext(file)[1] == _py_ext]
+ )
+
+
+def _get_modpkg_path(dotted_name, pathlist=None):
+ """Get the filesystem path for a module or a package.
+
+ Return the file system path to a file for a module, and to a directory for
+ a package. Return None if the name is not found, or is a builtin or
+ extension module.
+ """
+ # split off top-most name
+ parts = dotted_name.split('.', 1)
+
+ if len(parts) > 1:
+ # we have a dotted path, import top-level package
+ try:
+ file, pathname, description = imp.find_module(parts[0], pathlist)
+ if file: file.close()
+ except ImportError:
+ return None
+
+ # check if it's indeed a package
+ if description[2] == imp.PKG_DIRECTORY:
+ # recursively handle the remaining name parts
+ pathname = _get_modpkg_path(parts[1], [pathname])
+ else:
+ pathname = None
+ else:
+ # plain name
+ try:
+ file, pathname, description = imp.find_module(
+ dotted_name, pathlist)
+ if file:
+ file.close()
+ if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]:
+ pathname = None
+ except ImportError:
+ pathname = None
+
+ return pathname
+
+
+def getFilesForName(name):
+ """Get a list of module files for a filename, a module or package name,
+ or a directory.
+ """
+ if not os.path.exists(name):
+ # check for glob chars
+ if containsAny(name, "*?[]"):
+ files = glob.glob(name)
+ list = []
+ for file in files:
+ list.extend(getFilesForName(file))
+ return list
+
+ # try to find module or package
+ name = _get_modpkg_path(name)
+ if not name:
+ return []
+
+ if os.path.isdir(name):
+ # find all python files in directory
+ list = []
+ os.path.walk(name, _visit_pyfiles, list)
+ return list
+ elif os.path.exists(name):
+ # a single file
+ return [name]
+
+ return []
+
+
+class TokenEater:
+ def __init__(self, options):
+ self.__options = options
+ self.__messages = {}
+ self.__state = self.__waiting
+ self.__data = []
+ self.__lineno = -1
+ self.__freshmodule = 1
+ self.__curfile = None
+ self.__encoding = None
+
+ def __call__(self, ttype, tstring, stup, etup, line):
+ # dispatch
+## import token
+## print >> sys.stderr, 'ttype:', token.tok_name[ttype], \
+## 'tstring:', tstring
+ self.__state(ttype, tstring, stup[0])
+
+ def __waiting(self, ttype, tstring, lineno):
+ opts = self.__options
+ # Do docstring extractions, if enabled
+ if opts.docstrings and not opts.nodocstrings.get(self.__curfile):
+ # module docstring?
+ if self.__freshmodule:
+ if ttype == tokenize.STRING:
+ self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
+ self.__freshmodule = 0
+ elif ttype not in (tokenize.COMMENT, tokenize.NL):
+ self.__freshmodule = 0
+ return
+ # class docstring?
+ if ttype == tokenize.NAME and tstring in ('class', 'def'):
+ self.__state = self.__suiteseen
+ return
+ if ttype == tokenize.NAME and tstring in opts.keywords:
+ self.__state = self.__keywordseen
+
+ def __suiteseen(self, ttype, tstring, lineno):
+ # ignore anything until we see the colon
+ if ttype == tokenize.OP and tstring == ':':
+ self.__state = self.__suitedocstring
+
+ def __suitedocstring(self, ttype, tstring, lineno):
+ # ignore any intervening noise
+ if ttype == tokenize.STRING:
+ self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
+ self.__state = self.__waiting
+ elif ttype not in (tokenize.NEWLINE, tokenize.INDENT,
+ tokenize.COMMENT):
+ # there was no class docstring
+ self.__state = self.__waiting
+
+ def __keywordseen(self, ttype, tstring, lineno):
+ if ttype == tokenize.OP and tstring == '(':
+ self.__data = []
+ self.__lineno = lineno
+ self.__state = self.__openseen
+ else:
+ self.__state = self.__waiting
+
+ def __openseen(self, ttype, tstring, lineno):
+ if ttype == tokenize.OP and tstring == ')':
+ # We've seen the last of the translatable strings. Record the
+ # line number of the first line of the strings and update the list
+ # of messages seen. Reset state for the next batch. If there
+ # were no strings inside _(), then just ignore this entry.
+ if self.__data:
+ self.__addentry(EMPTYSTRING.join(self.__data))
+ self.__state = self.__waiting
+ elif ttype == tokenize.STRING:
+ self.__data.append(safe_eval(tstring))
+ elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT,
+ token.NEWLINE, tokenize.NL]:
+ # warn if we see anything else than STRING or whitespace
+ print >> sys.stderr, _(
+ '*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"'
+ ) % {
+ 'token': tstring,
+ 'file': self.__curfile,
+ 'lineno': self.__lineno
+ }
+ self.__state = self.__waiting
+
+ def __addentry(self, msg, lineno=None, isdocstring=0, iskidstring=0):
+ # tokenize module always return unicode strings
+ # even when they are in fact coded string instances
+ # to deal with this we use a hack:
+ # evaluate string's representation without leading "u"
+ # to force interpration as coded string
+ # then we decode it using already known file's encoding
+ if not iskidstring:
+ if type(msg) is str:
+ msg = eval(repr(msg))
+ else:
+ msg = eval(repr(msg)[1:])
+ msg = msg.decode(self.__encoding)
+ if lineno is None:
+ lineno = self.__lineno
+ if not msg in self.__options.toexclude:
+ entry = (self.__curfile, lineno)
+ self.__messages.setdefault(msg, {})[entry] = isdocstring
+
+ def set_filename(self, filename):
+ self.__curfile = filename
+ self.__freshmodule = 1
+
+ def set_file_encoding(self, fp):
+ """Searches for -*- coding: -*- magic comment to find out file encoding."""
+ self.__encoding = 'utf-8' # reset to default for each new file
+ for line in fp.readlines()[:5]:
+ m = re.match('#\s*-\*-\s+coding:\s+(\w+)\s+-\*-', line)
+ if m:
+ self.__encoding = m.group(1)
+ break
+ fp.seek(0)
+
+ def contains_inline_python(self,msg):
+ if '${' in msg and not '$${' in msg: return True
+ return False
+
+ def strip_namespace_uri(self,tag):
+ return tag.split('}')[-1]
+
+ def get_text_node(self,node):
+ tag = re.sub('({[^}]+})?(\w+)', '\\2', node.tag)
+
+ if node.text:
+ msg = node.text.strip()
+ if msg and not self.contains_inline_python(msg):
+ if tag not in ['script','style']:
+ self.__addentry(msg,self.strip_namespace_uri(node.tag), iskidstring=1)
+
+ if node.getchildren():
+ for child in node: self.get_text_node(child)
+
+ if node.tail:
+ msg = node.tail.strip()
+ if msg and not self.contains_inline_python(msg):
+ self.__addentry(msg,self.strip_namespace_uri(node.tag), iskidstring=1)
+
+ def extract_kid_strings(self):
+ if not self.__curfile: return
+ f = None
+ try:
+ file = open(self.__curfile)
+ f = ElementTree(XML( fixentities(file.read() )))
+ except Exception, e:
+ print 'Skip %s: %s' % (self.__curfile, e)
+ return
+
+ node = f.getroot()
+ self.get_text_node(node)
+
+ def write(self, fp):
+ options = self.__options
+ # format without tz information
+ # because %Z is timezone's name, not offset
+ # and, say, on localized Windows XP this is non-ascii string
+ timestamp = time.strftime('%Y-%m-%d %H:%M')
+ # The time stamp in the header doesn't have the same format as that
+ # generated by xgettext...
+ t = {'time': timestamp, 'version': __version__, 'charset':'utf-8'}
+ print >> fp, pot_header % t
+ # Sort the entries. First sort each particular entry's keys, then
+ # sort all the entries by their first item.
+ reverse = {}
+ for k, v in self.__messages.items():
+ keys = v.keys()
+ keys.sort()
+ reverse.setdefault(tuple(keys), []).append((k, v))
+ rkeys = reverse.keys()
+ rkeys.sort()
+ for rkey in rkeys:
+ rentries = reverse[rkey]
+ rentries.sort()
+ for k, v in rentries:
+ isdocstring = 0
+ # If the entry was gleaned out of a docstring, then add a
+ # comment stating so. This is to aid translators who may wish
+ # to skip translating some unimportant docstrings.
+ if reduce(operator.__add__, v.values()):
+ isdocstring = 1
+ # k is the message string, v is a dictionary-set of (filename,
+ # lineno) tuples. We want to sort the entries in v first by
+ # file name and then by line number.
+ v = v.keys()
+ v.sort()
+ if not options.writelocations:
+ pass
+ # location comments are different b/w Solaris and GNU:
+ elif options.locationstyle == options.SOLARIS:
+ for filename, lineno in v:
+ d = {'filename': filename, 'lineno': lineno}
+ print >>fp, _(
+ '# File: %(filename)s, line: %(lineno)s') % d
+ elif options.locationstyle == options.GNU:
+ # fit as many locations on one line, as long as the
+ # resulting line length doesn't exceeds 'options.width'
+ locline = '#:'
+ for filename, lineno in v:
+ d = {'filename': filename, 'lineno': lineno}
+ s = _(' %(filename)s:%(lineno)s') % d
+ if len(locline) + len(s) <= options.width:
+ locline = locline + s
+ else:
+ print >> fp, locline
+ locline = "#:" + s
+ if len(locline) > 2:
+ print >> fp, locline
+ if isdocstring:
+ print >> fp, '#, docstring'
+ if k: # do not output empty msgid
+ print >> fp, 'msgid', normalize(k, options.escape)
+ print >> fp, 'msgstr ""\n'
+
+
+def main():
+ global default_keywords
+ try:
+ opts, args = getopt.getopt(
+ sys.argv[1:],
+ 'ad:UDEhk:Kno:p:S:Vvw:x:X:',
+ ['extract-all', 'default-domain=', 'escape', 'help',
+ 'keyword=', 'no-default-keywords',
+ 'add-location', 'no-location', 'output=', 'output-dir=',
+ 'style=', 'verbose', 'version', 'width=', 'exclude-file=',
+ 'docstrings', 'no-docstrings', 'support-unicode',
+ ])
+ except getopt.error, msg:
+ usage(1, msg)
+
+ # for holding option values
+ class Options:
+ # constants
+ GNU = 1
+ SOLARIS = 2
+ # defaults
+ extractall = 0 # FIXME: currently this option has no effect at all.
+ escape = 0
+ keywords = []
+ outpath = ''
+ outfile = 'messages.pot'
+ writelocations = 1
+ locationstyle = GNU
+ verbose = 0
+ width = 78
+ excludefilename = ''
+ docstrings = 0
+ nodocstrings = {}
+
+ options = Options()
+ locations = {'gnu' : options.GNU,
+ 'solaris' : options.SOLARIS,
+ }
+
+ # parse options
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ usage(0)
+ elif opt in ('-a', '--extract-all'):
+ options.extractall = 1
+ elif opt in ('-d', '--default-domain'):
+ options.outfile = arg + '.pot'
+ elif opt in ('-E', '--escape'):
+ options.escape = 1
+ elif opt in ('-D', '--docstrings'):
+ options.docstrings = 1
+ elif opt in ('-k', '--keyword'):
+ options.keywords.append(arg)
+ elif opt in ('-K', '--no-default-keywords'):
+ default_keywords = []
+ elif opt in ('-n', '--add-location'):
+ options.writelocations = 1
+ elif opt in ('--no-location',):
+ options.writelocations = 0
+ elif opt in ('-S', '--style'):
+ options.locationstyle = locations.get(arg.lower())
+ if options.locationstyle is None:
+ usage(1, _('Invalid value for --style: %s') % arg)
+ elif opt in ('-o', '--output'):
+ options.outfile = arg
+ elif opt in ('-p', '--output-dir'):
+ options.outpath = arg
+ elif opt in ('-v', '--verbose'):
+ options.verbose = 1
+ elif opt in ('-V', '--version'):
+ print _('pygettext.py (xgettext for Python) %s') % __version__
+ sys.exit(0)
+ elif opt in ('-w', '--width'):
+ try:
+ options.width = int(arg)
+ except ValueError:
+ usage(1, _('--width argument must be an integer: %s') % arg)
+ elif opt in ('-x', '--exclude-file'):
+ options.excludefilename = arg
+ elif opt in ('-X', '--no-docstrings'):
+ fp = open(arg)
+ try:
+ while 1:
+ line = fp.readline()
+ if not line:
+ break
+ options.nodocstrings[line[:-1]] = 1
+ finally:
+ fp.close()
+
+ # calculate escapes
+ make_escapes(0)
+
+ # calculate all keywords
+ options.keywords.extend(default_keywords)
+
+ # initialize list of strings to exclude
+ if options.excludefilename:
+ try:
+ fp = open(options.excludefilename)
+ options.toexclude = fp.readlines()
+ fp.close()
+ except IOError:
+ print >> sys.stderr, _(
+ "Can't read --exclude-file: %s") % options.excludefilename
+ sys.exit(1)
+ else:
+ options.toexclude = []
+
+ # resolve args to module lists
+ expanded = []
+ for arg in args:
+ if arg == '-':
+ expanded.append(arg)
+ else:
+ expanded.extend(getFilesForName(arg))
+ args = expanded
+
+ # slurp through all the files
+ eater = TokenEater(options)
+ for filename in args:
+ if filename == '-':
+ if options.verbose:
+ print _('Reading standard input')
+ fp = sys.stdin
+ closep = 0
+ else:
+ if options.verbose:
+ print _('Working on %s') % filename
+ fp = open(filename)
+ eater.set_file_encoding(fp)
+ closep = 1
+ try:
+ eater.set_filename(filename)
+ try:
+ tokenize.tokenize(fp.readline, eater)
+ except tokenize.TokenError, e:
+ print >> sys.stderr, '%s: %s, line %d, column %d' % (
+ e[0], filename, e[1][0], e[1][1])
+ finally:
+ if closep:
+ fp.close()
+
+ if os.path.splitext(filename)[-1].lower() == '.kid': eater.extract_kid_strings()
+
+ # write the output
+ if options.outfile == '-':
+ fp = sys.stdout
+ closep = 0
+ else:
+ if options.outpath:
+ options.outfile = os.path.join(options.outpath, options.outfile)
+ fp = open(options.outfile, 'wt')
+ closep = 1
+ try:
+ eater.write(fp)
+ finally:
+ if closep:
+ fp.close()
+
+
+if __name__ == '__main__':
+ main()
+ # some more test strings
+ _(u'a unicode string')
+ # this one creates a warning
+ _('*** Seen unexpected token "%(token)s"') % {'token': 'test'}
+ _('more' 'than' 'one' 'string')
--
cgit