aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Jaggars <jhjaggars@gmail.com>2011-12-14 16:05:59 -0600
committerJesse Jaggars <jhjaggars@gmail.com>2011-12-14 16:05:59 -0600
commit6ea48cbbc85d007dfefd1f254db66ff2e0a9cec5 (patch)
tree35188a9f5f2be7f3ae6ea52beea0883dca47abda
parent1e83a26da25981f362d4acc35d61dfa3b2ab56a3 (diff)
downloadsos-6ea48cbbc85d007dfefd1f254db66ff2e0a9cec5.tar.gz
Major updates to most of SoSReport
Code reorganization Cross platform support for Windows, OS X and Linux Dynamically loaded policies Support for loading plugins from multiple locations via __path__ modification of sos.plugins Support for running via Jython Support for executing from a jarfile Support for json based reporting infrastructure - Previous reporting methods still exist Support for other checksum algorithms (determined by policy) Support for other compression algorithms (determined by policy) New plugin API for writing arbitrary information in a new file inside the report archive. New plugin API for modifying files that have been added to the archive. Added API for global plugin options - external interface is unavailable at this time Many small bugfixes
-rw-r--r--.gitignore9
-rw-r--r--AUTHORS2
-rw-r--r--ChangeLog.deprecated248
-rw-r--r--Makefile44
-rw-r--r--README36
-rwxr-xr-x__run__.py17
-rw-r--r--sos.spec2
-rw-r--r--sos/__init__.py (renamed from sos/__init__.py.in)25
-rwxr-xr-xsos/helpers.py79
-rw-r--r--sos/plugins/__init__.py615
-rw-r--r--sos/plugins/abrt.py6
-rw-r--r--sos/plugins/acpid.py4
-rw-r--r--sos/plugins/amd.py4
-rw-r--r--sos/plugins/anaconda.py4
-rw-r--r--sos/plugins/apache.py6
-rw-r--r--sos/plugins/auditd.py4
-rw-r--r--sos/plugins/autofs.py12
-rw-r--r--sos/plugins/bootloader.py4
-rw-r--r--sos/plugins/cluster.py12
-rw-r--r--sos/plugins/cobbler.py6
-rw-r--r--sos/plugins/corosync.py8
-rw-r--r--sos/plugins/crontab.py4
-rw-r--r--sos/plugins/cs.py4
-rw-r--r--sos/plugins/devicemapper.py5
-rw-r--r--sos/plugins/dhcp.py8
-rw-r--r--sos/plugins/dovecot.py4
-rw-r--r--sos/plugins/ds.py4
-rw-r--r--sos/plugins/eap6.py339
-rw-r--r--sos/plugins/emc.py8
-rw-r--r--sos/plugins/filesys.py10
-rw-r--r--sos/plugins/ftp.py4
-rw-r--r--sos/plugins/gdm.py4
-rw-r--r--sos/plugins/general.py4
-rw-r--r--sos/plugins/gluster.py4
-rw-r--r--sos/plugins/hardware.py4
-rw-r--r--sos/plugins/hts.py4
-rw-r--r--sos/plugins/i18n.py4
-rw-r--r--sos/plugins/initrd.py4
-rw-r--r--sos/plugins/ipa.py4
-rw-r--r--sos/plugins/ipsec.py4
-rw-r--r--sos/plugins/iscsi.py4
-rw-r--r--sos/plugins/iscsitarget.py6
-rw-r--r--sos/plugins/jboss.py708
-rw-r--r--sos/plugins/kdump.py4
-rw-r--r--sos/plugins/kernel.py6
-rw-r--r--sos/plugins/kvm.py4
-rw-r--r--sos/plugins/ldap.py6
-rw-r--r--sos/plugins/libraries.py6
-rw-r--r--sos/plugins/libvirt.py4
-rw-r--r--sos/plugins/logrotate.py6
-rw-r--r--sos/plugins/lsbrelease.py4
-rw-r--r--sos/plugins/memory.py6
-rw-r--r--sos/plugins/mrggrid.py4
-rw-r--r--sos/plugins/mrgmessg.py4
-rw-r--r--sos/plugins/mysql.py6
-rw-r--r--sos/plugins/named.py8
-rw-r--r--sos/plugins/netdump.py4
-rw-r--r--sos/plugins/networking.py4
-rw-r--r--sos/plugins/nfsserver.py4
-rw-r--r--sos/plugins/nscd.py4
-rw-r--r--sos/plugins/ntp.py4
-rw-r--r--sos/plugins/oddjob.py4
-rw-r--r--sos/plugins/openssl.py4
-rw-r--r--sos/plugins/openswan.py4
-rw-r--r--sos/plugins/pam.py4
-rw-r--r--sos/plugins/postfix.py6
-rw-r--r--sos/plugins/postgresql.py56
-rw-r--r--sos/plugins/ppp.py4
-rw-r--r--sos/plugins/printing.py4
-rw-r--r--sos/plugins/process.py4
-rw-r--r--sos/plugins/psacct.py4
-rw-r--r--sos/plugins/pxe.py6
-rw-r--r--sos/plugins/qpidd.py4
-rw-r--r--sos/plugins/quagga.py4
-rw-r--r--sos/plugins/radius.py4
-rw-r--r--sos/plugins/rhevm.py23
-rw-r--r--sos/plugins/rhn.py4
-rw-r--r--sos/plugins/rpm.py6
-rw-r--r--sos/plugins/s390.py4
-rw-r--r--sos/plugins/samba.py4
-rw-r--r--sos/plugins/sar.py6
-rw-r--r--sos/plugins/selinux.py4
-rw-r--r--sos/plugins/sendmail.py4
-rw-r--r--sos/plugins/smartcard.py4
-rw-r--r--sos/plugins/snmp.py4
-rw-r--r--sos/plugins/soundcard.py4
-rw-r--r--sos/plugins/squid.py6
-rw-r--r--sos/plugins/ssh.py4
-rw-r--r--sos/plugins/sssd.py4
-rw-r--r--sos/plugins/startup.py6
-rw-r--r--sos/plugins/system.py4
-rw-r--r--sos/plugins/systemtap.py6
-rw-r--r--sos/plugins/tftpserver.py4
-rw-r--r--sos/plugins/tomcat.py6
-rw-r--r--sos/plugins/udev.py4
-rw-r--r--sos/plugins/veritas.py6
-rw-r--r--sos/plugins/vmware.py6
-rw-r--r--sos/plugins/x11.py6
-rw-r--r--sos/plugins/xen.py6
-rw-r--r--sos/plugins/xinetd.py4
-rw-r--r--sos/plugins/yum.py6
-rw-r--r--sos/plugintools.py560
-rw-r--r--sos/policies/__init__.py276
-rw-r--r--sos/policies/osx.py13
-rw-r--r--sos/policies/redhat.py229
-rw-r--r--sos/policies/windows.py41
-rwxr-xr-xsos/policyredhat.py411
-rw-r--r--sos/reporting.py131
-rw-r--r--[-rwxr-xr-x]sos/sosreport.py1262
-rw-r--r--sos/utilities.py445
-rwxr-xr-xsosreport8
-rwxr-xr-xtests/archive_tests.py130
-rw-r--r--tests/report_tests.py118
l---------tests/worker_link1
-rw-r--r--tools/osdetect.py87
115 files changed, 4204 insertions, 2144 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..790380d2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*~
+*.class
+*swp*
+*.pyc
+*.sw*
+tags
+buildjar/
+gpgkeys/rhsupport.*
+rpm-build/*
diff --git a/AUTHORS b/AUTHORS
index 267d996e..36e08803 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,9 +7,11 @@ Keith Kearnan <kearnan_keith@emc.com>
Kent Lamb <klamb@redhat.com>
Marc Sauton <msauton@redhat.com>
Navid Sheikhol-Eslami <navid@redhat.com>
+Pierre Amadio <pamadio@redhat.com>
Pierre Carrier <pcarrier@redhat.com>
Ranjith Rajaram <rrajaram@redhat.com>
Sadique Puthen <sputhenp@redhat.com>
Shijoe George <spanjikk@redhat.com>
Steve Conklin <sconklin@redhat.com>
Tomas Smetana <tsmetana@redhat.com>
+John Berninger <jwb@redhat.com>
diff --git a/ChangeLog.deprecated b/ChangeLog.deprecated
deleted file mode 100644
index 92947fd9..00000000
--- a/ChangeLog.deprecated
+++ /dev/null
@@ -1,248 +0,0 @@
-2007-06-15 Navid Sheikhol-Eslami <navid@redhat.com>
-
- * [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 <eteo@redhat.com>
-
- * 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 <eteo@redhat.com>
-
- * lib/sos/plugins/systemtap.py:
- - Added systemtap.py to gather SystemTap pre-requisites information
-
-2007-05-28 Eugene Teo <eteo@redhat.com>
-
- * lib/sos/plugins/amd.py:
- - Added amd.py to gather Amd automounter information
-
-2007-05-25 Eugene Teo <eteo@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * Replaced xen plugin with (better) version from Chris Lalancette <clalance@redhat.com>
-
-2007-03-29 Navid Sheikhol-Eslami <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * fixed BZ#219877 (ncurses "Cancel" button makes sosreport exit)
-
-2007-03-07 Navid Sheikhol-Eslami <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <navid@redhat.com>
-
- * 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 <sconklin@tintin>
-
- * ChangeLog, LICENSE, setup.py, sos.spec:
- Added License file and bumped release
-
-2006-06-08 dlehman <dlehman@tintin>
-
- * 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 <dlehman@tintin>
-
- * 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 <jwhiter@tintin>
-
- * lib/sos/plugins/system.py:
- adding the abilit to capture the autofs maps to system.py
-
-2006-05-31 Steve Conklin <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <jwhiter@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <jwhiter@tintin>
-
- * Changelog, lib/sos/plugins/kernel.py, setup.py:
- Adding jwb's patch to have sosreport grab sysrq data.
-
-2006-05-22 Steve Conklin <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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 <sconklin@tintin>
-
- * 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/Makefile b/Makefile
index 2e677354..a4b8513f 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,10 @@ REPO = http://svn.fedorahosted.org/svn/sos
SUBDIRS = po sos sos/plugins
PYFILES = $(wildcard *.py)
+# OS X via brew
+# MSGCAT = /usr/local/Cellar/gettext/0.18.1.1/bin/msgcat
+MSGCAT = msgcat
+
RPM_BUILD_DIR = rpm-build
RPM_DEFINES = --define "_topdir %(pwd)/$(RPM_BUILD_DIR)" \
@@ -19,6 +23,12 @@ RPM_DEFINES = --define "_topdir %(pwd)/$(RPM_BUILD_DIR)" \
--define "_sourcedir %{_topdir}"
RPM = rpmbuild
RPM_WITH_DIRS = $(RPM) $(RPM_DEFINES)
+ARCHIVE_DIR = $(RPM_BUILD_DIR)/$(NAME)-$(VERSION)
+
+ARCHIVE_NAME = sosreport.zip
+SRC_BUILD = $(RPM_BUILD_DIR)/sdist
+PO_DIR = $(SRC_BUILD)/sos/po
+ZIP_DEST = $(SRC_BUILD)/$(ARCHIVE_NAME)
build:
for d in $(SUBDIRS); do make -C $$d; [ $$? = 0 ] || exit 1 ; done
@@ -37,14 +47,14 @@ install:
install -m644 LICENSE README TODO $(DESTDIR)/usr/share/$(NAME)/.
install -m644 $(NAME).conf $(DESTDIR)/etc/$(NAME).conf
install -m644 gpgkeys/rhsupport.pub $(DESTDIR)/usr/share/$(NAME)/.
- sed 's/@SOSVERSION@/$(VERSION)/g'<sos/__init__.py.in >sos/__init__.py
+ sed 's/@SOSVERSION@/$(VERSION)/g' < sos/__init__.py > sos/__init__.py
for d in $(SUBDIRS); do make DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
$(NAME)-$(VERSION).tar.gz: clean gpgkey
- @mkdir -p $(RPM_BUILD_DIR)
- @svn export --force $(PWD) $(RPM_BUILD_DIR)/$(NAME)-$(VERSION)
- @mkdir -p $(RPM_BUILD_DIR)/$(NAME)-$(VERSION)/gpgkeys
- @cp gpgkeys/rhsupport.pub $(RPM_BUILD_DIR)/$(NAME)-$(VERSION)/gpgkeys/.
+ @mkdir -p $(ARCHIVE_DIR)
+ @tar -cv sosreport sos doc man po sos.conf TODO LICENSE README sos.spec Makefile | tar -x -C $(ARCHIVE_DIR)
+ @mkdir -p $(ARCHIVE_DIR)/gpgkeys
+ @cp gpgkeys/rhsupport.pub $(ARCHIVE_DIR)/gpgkeys/.
@tar Ccvzf $(RPM_BUILD_DIR) $(RPM_BUILD_DIR)/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
clean:
@@ -65,3 +75,27 @@ gpgkey:
@echo "Building gpg key"
@test -f gpgkeys/rhsupport.pub && echo "GPG key already exists." || \
gpg --batch --gen-key gpgkeys/gpg.template
+
+po: clean
+ mkdir -p $(PO_DIR)
+ for po in `ls po/*.po`; do \
+ $(MSGCAT) -p -o $(PO_DIR)/$$(basename $$po | awk -F. '{print $$1}').properties $$po; \
+ done; \
+
+ cp $(PO_DIR)/en.properties $(PO_DIR)/en_US.properties
+
+eap6: po
+ cp -r sos/* $(SRC_BUILD)/sos/
+ find $(SRC_BUILD)/sos/plugins/ -not -name "*eap6.py" -not -name "*__init__.py" -type f -delete
+
+zip: po
+ zip -r $(ZIP_DEST) sos
+ zip -r $(ZIP_DEST) __run__.py
+ cd $(SRC_BUILD) && zip -r $(ARCHIVE_NAME) sos
+ cd $(SRC_BUILD) && rm -rf sos
+
+test:
+ @for test in `ls tests/*test*.py`; do \
+ echo $$test; \
+ PYTHONPATH=`pwd` python $$test; \
+ done; \
diff --git a/README b/README
index d46c33fd..e878d057 100644
--- a/README
+++ b/README
@@ -1,35 +1,15 @@
-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 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://fedorahosted.org/sos
-For the latest version, to contribute, and for more information, please visit there.
+This project is hosted at http://github.com/sosreport/sosreport 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 export http://svn.fedorahosted.org/svn/sos/trunk sos --username guest
+ git clone git://github.com/sosreport/sosreport.git
to install locally (as root) ==> make install
to build an rpm ==> make rpm
-
-See the Makefile.
-
-Maintainer:
-
- Adam Stokes <ajs@redhat.com>
-
-Developers and Contributors:
-
- Steve Conklin <sconklin@redhat.com>
- Pierre Amadio <pamadio@redhat.com>
- John Berninger <jwb@redhat.com>
- Navid Sheikhol-Eslami <navid at redhat dot com>
-
-Thanks to:
-
- Eva Schaller <eschaller@redhat.com> for providing an Italian translation
- Marco Ceci <mceci@redhat.com> for helping me out with the cluster plugin
- Leonardo Macchia <lmacchia@redhat.com> for being my personal regexp generator
- Imed Chihi <ichihi@redhat.com> for providing Arabic and French translations
+to build a zipfile for use with jython ==> make zip
diff --git a/__run__.py b/__run__.py
new file mode 100755
index 00000000..663ee144
--- /dev/null
+++ b/__run__.py
@@ -0,0 +1,17 @@
+# 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.sosreport import main
+import sys
+
+main(sys.argv[1:])
diff --git a/sos.spec b/sos.spec
index 81671390..077eeaf2 100644
--- a/sos.spec
+++ b/sos.spec
@@ -46,7 +46,7 @@ rm -rf ${RPM_BUILD_ROOT}
%{python_sitelib}/*
%{_mandir}/man1/*
%{_mandir}/man5/*
-%doc README TODO LICENSE ChangeLog doc/*
+%doc README TODO LICENSE doc/*
%config(noreplace) %{_sysconfdir}/sos.conf
%changelog
diff --git a/sos/__init__.py.in b/sos/__init__.py
index 64b87506..8d45216a 100644
--- a/sos/__init__.py.in
+++ b/sos/__init__.py
@@ -15,13 +15,24 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import gettext
+__version__ = "@SOSVERSION@"
-gettext_dir = "/usr/share/locale"
-gettext_app = "sos"
+try:
+ from java.util import ResourceBundle
-gettext.bindtextdomain(gettext_app, gettext_dir)
+ rb = ResourceBundle.getBundle("sos.po.sos")
-__version__="@SOSVERSION@"
-def _sos(msg):
- return gettext.dgettext(gettext_app, msg)
+ def _sos(msg):
+ try:
+ return rb.getString(msg).encode('utf-8')
+ except:
+ return msg
+except:
+ import gettext
+ gettext_dir = "/usr/share/locale"
+ gettext_app = "sos"
+
+ gettext.bindtextdomain(gettext_app, gettext_dir)
+
+ def _sos(msg):
+ return gettext.dgettext(gettext_app, msg)
diff --git a/sos/helpers.py b/sos/helpers.py
deleted file mode 100755
index b6b099a5..00000000
--- a/sos/helpers.py
+++ /dev/null
@@ -1,79 +0,0 @@
-## helpers.py
-## Implement policies required for the sos system support tool
-
-## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com>
-
-### 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, sys
-import logging
-from subprocess import Popen, PIPE
-
-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 sosGetCommandOutput(command, timeout = 300):
- """ Execute a command and gather stdin, stdout, and return status.
- """
- # soslog = logging.getLogger('sos')
- # Log if binary is not runnable or does not exist
- for path in os.environ["PATH"].split(":"):
- cmdfile = command.strip("(").split()[0]
- # handle both absolute or relative paths
- if ( ( not os.path.isabs(cmdfile) and os.access(os.path.join(path,cmdfile), os.X_OK) ) or \
- ( os.path.isabs(cmdfile) and os.access(cmdfile, os.X_OK) ) ):
- break
- else:
- # soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable" % cmdfile)
- return (127, "", 0)
-
- p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, bufsize=-1)
- stdout, stderr = p.communicate()
- return (p.returncode, stdout.strip(), 0)
-
-def commonPrefix(l1, l2, common = []):
- ''' 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 len(l1) < 1 or len(l2) < 1 or l1[0] != l2[0]: return common, [l1, l2]
- return commonPrefix(l1[1:], l2[1:], common+[l1[0]])
-
-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.
- '''
- try:
- common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep))
- except AttributeError:
- return path2
-
- if not common:
- return path2 # leave path absolute if nothing at all in common
- return sep.join( [pardir]*len(u1) + u2 )
-
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index e69de29b..ebb76ea3 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -0,0 +1,615 @@
+## This exports methods available for use by plugins for sos
+
+## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com>
+
+### 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
+
+from sos.utilities import sosGetCommandOutput, import_module
+from sos import _sos as _
+import inspect
+import os
+import sys
+import string
+import glob
+import re
+import traceback
+import shutil
+from stat import *
+from time import time
+from itertools import *
+from collections import deque
+import logging
+
+
+def commonPrefix(l1, l2, common = None):
+ """
+ Returns a tuple like the following:
+ ([common, elements, from l1, and l2], [[tails, from, l1], [tails, from, l2]])
+
+ >>> commonPrefix(['usr','share','foo'], ['usr','share','bar'])
+ (['usr','share'], [['foo'], ['bar']])
+ """
+ if common is None:
+ common = []
+ if len(l1) < 1 or len(l2) < 1 or l1[0] != l2[0]:
+ return (common, [l1, l2])
+ return commonPrefix(l1[1:], l2[1:], common+[l1[0]])
+
+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.
+ '''
+ try:
+ common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep))
+ except AttributeError:
+ return path2
+
+ if not common:
+ return path2 # leave path absolute if nothing at all in common
+ return sep.join( [pardir]*len(u1) + u2 )
+
+
+class PluginException(Exception):
+ pass
+
+
+class Plugin(object):
+ """
+ This is the base class for sosreport plugins. This class should
+ be subclassed by platform specific superclasses. Actual plugins
+ should not subclass this class directly.
+ """
+
+ requires_root = True
+ version = 'unversioned'
+
+ def __init__(self, commons):
+ if not getattr(self, "optionList", False):
+ self.optionList = deque()
+
+ self.copiedFiles = deque()
+ self.executedCommands = deque()
+ self.diagnose_msgs = deque()
+ self.alerts = deque()
+ self.customText = ""
+ self.optNames = deque()
+ self.optParms = deque()
+ self.cInfo = commons
+ self.forbiddenPaths = deque()
+ self.copyPaths = deque()
+ self.copyStrings = deque()
+ self.collectProgs = deque()
+
+ self.packages = deque()
+ self.files = deque()
+
+ self.must_exit = False
+
+ self.soslog = logging.getLogger('sos')
+ self.proflog = logging.getLogger('sosprofile')
+
+ # 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]})
+
+ @classmethod
+ def name(class_):
+ "Returns the plugin's name as a string"
+ return class_.__name__.lower()
+
+ def setArchive(self, archive):
+ self.archive = archive
+
+ def policy(self):
+ return self.cInfo["policy"]
+
+ def isInstalled(self, package_name):
+ '''Is the package $package_name installed?
+ '''
+ return (self.policy().pkgByName(package_name) is not None)
+
+ def doRegexSub(self, srcpath, regexp, subst):
+ '''Apply a regexp substitution to a file archived by sosreport.
+ srcpath is the path in the archive where the file can be found.
+ regexp can be a regexp string or a compiled re object.
+ subst is a string to replace each occurance of regexp in the content
+ of srcpath.
+
+ This function returns the number of replacements made.
+ '''
+ try:
+ path = self._get_dest_for_srcpath(srcpath)
+ if not path:
+ return 0
+ readable = self.archive.open_file(path)
+ result, replacements = re.subn(regexp, subst, readable.read())
+ if replacements:
+ self.archive.add_string(result, srcpath)
+ return replacements
+ else:
+ return 0
+ except Exception:
+ return 0
+
+ def doRegexFindAll(self, regex, fname):
+ ''' Return a list of all non overlapping matches in the string(s)
+ '''
+ try:
+ return re.findall(regex, open(fname, 'r').read(), re.MULTILINE)
+ except: # IOError, AttributeError, etc.
+ return []
+
+ def _path_in_path_list(self, path, path_list):
+ for p in path_list:
+ if p in path:
+ return True
+ return False
+
+ def copy_symlink(self, srcpath, sub=None):
+ link = os.readlink(srcpath)
+ if not os.path.isabs(link):
+ link = os.path.normpath(
+ os.path.join(
+ os.path.dirname(srcpath),
+ link)
+ )
+
+ if os.path.isdir(link):
+ self.soslog.debug("link %s is a directory, skipping..." % link)
+ return
+
+ dest = link
+
+ if sub:
+ old, new = sub
+ dest = srcpath.replace(old, new)
+
+ self.archive.add_file(link, dest=dest)
+
+ self.copiedFiles.append({
+ 'srcpath':srcpath,
+ 'dstpath':dest,
+ 'symlink':"yes",
+ 'pointsto':link})
+
+ def copy_dir(self, srcpath, sub=None):
+ for afile in os.listdir(srcpath):
+ self.doCopyFileOrDir(os.path.join(srcpath, afile), dest=None, sub=sub)
+
+ def _get_dest_for_srcpath(self, srcpath):
+ for copied in self.copiedFiles:
+ if srcpath == copied["srcpath"]:
+ return copied["dstpath"]
+ return None
+
+ # Methods for copying files and shelling out
+ def doCopyFileOrDir(self, srcpath, dest=None, sub=None):
+ # 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. sub can be used to
+ rename the destination of the file, sub should be a two-tuple of
+ (old,new). For example if you passed in ("etc","configurations") for
+ use against /etc/my_file.conf the file would end up at
+ /configurations/my_file.conf.
+ '''
+
+ if self.cInfo['cmdlineopts'].profiler:
+ start_time = time()
+
+ if self._path_in_path_list(srcpath, self.forbiddenPaths):
+ self.soslog.debug("%s is in the forbidden path list" % srcpath)
+ return ''
+
+ if not os.path.exists(srcpath):
+ self.soslog.debug("file or directory %s does not exist" % srcpath)
+ return
+
+ if not dest:
+ dest = srcpath
+
+ if sub:
+ old, new = sub
+ dest = srcpath.replace(old, new)
+
+ if os.path.islink(srcpath):
+ self.copy_symlink(srcpath, sub=sub)
+ return
+ else:
+ if os.path.isdir(srcpath):
+ self.copy_dir(srcpath, sub=sub)
+ return
+
+ # if we get here, it's definitely a regular file (not a symlink or dir)
+ self.soslog.debug("copying file %s to %s" % (srcpath,dest))
+
+ try:
+ self.archive.add_file(srcpath, dest)
+
+ self.copiedFiles.append({
+ 'srcpath':srcpath,
+ 'dstpath':dest,
+ 'symlink':"no"})
+
+ if self.cInfo['cmdlineopts'].profiler:
+ time_passed = time() - start_time
+ self.proflog.debug("copied: %-75s time: %f" % (srcpath, time_passed))
+ except Exception, e:
+ self.soslog.debug(traceback.format_exc())
+
+
+ def addForbiddenPath(self, forbiddenPath):
+ """Specify a path to not copy, even if it's part of a copyPaths[] entry.
+ """
+ # Glob case handling is such that a valid non-glob is a reduced glob
+ for filespec in glob.glob(forbiddenPath):
+ self.forbiddenPaths.append(filespec)
+
+ def getAllOptions(self):
+ """
+ return a list of all options selected
+ """
+ return (self.optNames, self.optParms)
+
+ def setOption(self, optionname, value):
+ ''' set the named option to value.
+ '''
+ for name, parms in izip(self.optNames, self.optParms):
+ if name == optionname:
+ parms['enabled'] = value
+ return True
+ else:
+ return False
+
+ def isOptionEnabled(self, optionname):
+ ''' Deprecated, use getOption() instead
+ '''
+ return self.getOption(optionname)
+
+ def getOption(self, optionname, default=0):
+ """Returns the first value that matches 'optionname' in parameters
+ passed in via the command line or set via set_option or via the
+ global_plugin_options dictionary, in that order.
+
+ optionaname may be iterable, in which case the first option that matches
+ any of the option names is returned."""
+
+ def _check(key):
+ if hasattr(key, "__iter__"):
+ return key in optionname
+ else:
+ return key == optionname
+
+ for name, parms in izip(self.optNames, self.optParms):
+ if _check(name):
+ return parms['enabled']
+
+ for key, value in self.cInfo.get('global_plugin_options', {}).iteritems():
+ if _check(key):
+ return value
+
+ return default
+
+ def getOptionAsList(self, optionname, delimiter=",", default=None):
+ '''Will try to return the option as a list separated by the delimiter'''
+ option = self.getOption(optionname)
+ try:
+ opt_list = [opt.strip() for opt in option.split(delimiter)]
+ return filter(None, opt_list)
+ except Exception:
+ return default
+
+ def addCopySpecLimit(self, fname, sizelimit=None, sub=None):
+ """Add a file specification (with limits)
+ """
+ if not ( fname and len(fname) ):
+ # self.soslog.warning("invalid file path")
+ return False
+ files = glob.glob(fname)
+ files.sort()
+ cursize = 0
+ limit_reached = False
+ sizelimit *= 1024 * 1024 # in MB
+ for flog in files:
+ cursize += os.stat(flog)[ST_SIZE]
+ if sizelimit and cursize > sizelimit:
+ limit_reached = True
+ break
+ self.addCopySpec(flog, sub)
+ # Truncate the first file (others would likely be compressed),
+ # ensuring we get at least some logs
+ # FIXME: figure this out for jython
+ if flog == files[0] and limit_reached:
+ self.collectExtOutput("tail -c%d %s" % (sizelimit, flog),
+ "tail_" + os.path.basename(flog), flog[1:] + ".tailed")
+
+ def addCopySpecs(self, copyspecs, sub=None):
+ for copyspec in copyspecs:
+ self.addCopySpec(copyspec, sub)
+
+ def addCopySpec(self, copyspec, sub=None):
+ """ Add a file specification (can be file, dir,or shell glob) to be
+ copied into the sosreport by this module
+ """
+ if not (copyspec and len(copyspec)):
+ # self.soslog.warning("invalid file path")
+ return False
+ # Glob case handling is such that a valid non-glob is a reduced glob
+ for filespec in glob.glob(copyspec):
+ if filespec not in self.copyPaths:
+ self.copyPaths.append((filespec, sub))
+
+ def callExtProg(self, prog):
+ """ Execute a command independantly of the output gathering part of
+ sosreport
+ """
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput(prog)
+ return (status, shout, runtime)
+
+ def checkExtprog(self, prog):
+ """ Execute a command independently of the output gathering part of
+ sosreport and check the return code. Return True for a return code of 0
+ and False otherwise."""
+ (status, output, runtime) = self.callExtProg(prog)
+ return (status == 0)
+
+
+ def collectExtOutput(self, exe, suggest_filename = None, root_symlink = None, timeout = 300):
+ """
+ Run a program and collect the output
+ """
+ self.collectProgs.append( (exe, suggest_filename, root_symlink, timeout) )
+
+ def fileGrep(self, regexp, fname):
+ try:
+ return [l for l in open(fname).readlines() if re.match(regexp, l)]
+ except: # IOError, AttributeError, etc.
+ return []
+
+ def mangleCommand(self, exe):
+ # FIXME: this can be improved
+ mangledname = re.sub(r"^/(usr/|)(bin|sbin)/", "", exe)
+ mangledname = re.sub(r"[^\w\-\.\/]+", "_", mangledname)
+ mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64]
+ return mangledname
+
+ def makeCommandFilename(self, exe):
+ """ The internal function to build up a filename based on a command """
+
+ outfn = os.path.join(self.cInfo['cmddir'], self.name(), self.mangleCommand(exe))
+
+ # check for collisions
+ if os.path.exists(outfn):
+ inc = 2
+ while True:
+ newfn = "%s_%d" % (outfn, inc)
+ if not os.path.exists(newfn):
+ outfn = newfn
+ break
+ inc +=1
+
+ return outfn
+
+ def addStringAsFile(self, content, filename):
+ """Add a string to the archive as a file named `filename`"""
+ self.copyStrings.append((content, filename))
+
+ def collectOutputNow(self, exe, suggest_filename=None, root_symlink=False, timeout=300):
+ """ Execute a command and save the output to a file for inclusion in
+ the report
+ """
+ if self.cInfo['cmdlineopts'].profiler:
+ start_time = time()
+
+ # pylint: disable-msg = W0612
+ status, shout, runtime = sosGetCommandOutput(exe, timeout=timeout)
+
+ if suggest_filename:
+ outfn = self.makeCommandFilename(suggest_filename)
+ else:
+ outfn = self.makeCommandFilename(exe)
+
+ if not (status == 127 or status == 32512): # if not command_not_found
+ outfn_strip = outfn[len(self.cInfo['cmddir'])+1:]
+ self.archive.add_string(shout, outfn)
+ else:
+ self.soslog.debug("could not run command: %s" % exe)
+ outfn = None
+ outfn_strip = None
+
+ # save info for later
+ self.executedCommands.append({'exe': exe, 'file':outfn_strip}) # save in our list
+ self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn_strip,runtime=runtime)
+
+ if self.cInfo['cmdlineopts'].profiler:
+ time_passed = time() - start_time
+ self.proflog.debug("output: %-75s time: %f" % (exe, time_passed))
+
+ 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)
+
+ # 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)
+
+ 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 += text
+
+ def copyStuff(self):
+ """
+ Collect the data for a plugin
+ """
+ for path, sub in self.copyPaths:
+ self.doCopyFileOrDir(path, sub=sub)
+
+ for string, file_name in self.copyStrings:
+ try:
+ self.archive.add_string(string,
+ os.path.join('sos_strings', self.name(), file_name))
+ except Exception, e:
+ self.soslog.debug("could not create %s, traceback follows: %s" % (file_name, e))
+
+ for progs in izip(self.collectProgs):
+ prog, suggest_filename, root_symlink, timeout = progs[0]
+ # self.soslog.debug("collecting output of '%s'" % prog)
+ try:
+ self.collectOutputNow(prog, suggest_filename, root_symlink, timeout)
+ except Exception, e:
+ self.soslog.debug("error collection output of '%s', traceback follows: %s" % (prog, e))
+
+ def exit_please(self):
+ """ This function tells the plugin that it should exit ASAP"""
+ self.must_exit = True
+
+ def get_description(self):
+ """ This function will return the description for the plugin"""
+ try:
+ return self.__doc__.strip()
+ except:
+ return "<no description available>"
+
+ def checkenabled(self):
+ """ This function can be overidden to let the plugin decide whether
+ it should run or not.
+ """
+ # some files or packages have been specified for this package
+ if len(self.files) or len(self.packages):
+ for fname in self.files:
+ if os.path.exists(fname):
+ return True
+ for pkgname in self.packages:
+ if self.isInstalled(pkgname):
+ return True
+ return False
+
+ 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 diagnose(self):
+ """This method must be overridden to check the sanity of the system's
+ configuration before the collection begins.
+ """
+ pass
+
+ def setup(self):
+ """This method 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 = '<hr/><a name="%s"></a>\n' % self.name()
+
+ # Intro
+ html = html + "<h2> Plugin <em>" + self.name() + "</em></h2>\n"
+
+ # Files
+ if len(self.copiedFiles):
+ html = html + "<p>Files copied:<br><ul>\n"
+ for afile in self.copiedFiles:
+ html = html + '<li><a href="%s">%s</a>' % (afile['dstpath'], afile['srcpath'])
+ if (afile['symlink'] == "yes"):
+ html = html + " (symlink to %s)" % afile['pointsto']
+ html = html + '</li>\n'
+ html = html + "</ul></p>\n"
+
+ # Command Output
+ if len(self.executedCommands):
+ html = html + "<p>Commands Executed:<br><ul>\n"
+ # convert file name to relative path from our root
+ for cmd in self.executedCommands:
+ if cmd["file"] and len(cmd["file"]):
+ cmdOutRelPath = sosRelPath(self.cInfo['rptdir'], self.cInfo['cmddir'] + "/" + cmd['file'])
+ html = html + '<li><a href="%s">%s</a></li>\n' % (cmdOutRelPath, cmd['exe'])
+ else:
+ html = html + '<li>%s</li>\n' % (cmd['exe'])
+ html = html + "</ul></p>\n"
+
+ # Alerts
+ if len(self.alerts):
+ html = html + "<p>Alerts:<br><ul>\n"
+ for alert in self.alerts:
+ html = html + '<li>%s</li>\n' % alert
+ html = html + "</ul></p>\n"
+
+ # Custom Text
+ if (self.customText != ""):
+ html = html + "<p>Additional Information:<br>\n"
+ html = html + self.customText + "</p>\n"
+
+ return html
+
+
+class RedHatPlugin(object):
+ """Tagging class to indicate that this plugin works with Red Hat Linux"""
+ pass
+
+class IndependentPlugin(object):
+ """Tagging class that indicates this plugin can run on any platform"""
+ pass
+
+def import_plugin(name):
+ """Import name as a module and return a list of all classes defined in that
+ module"""
+ try:
+ plugin_fqname = "sos.plugins.%s" % name
+ return import_module(plugin_fqname, superclass=Plugin)
+ except ImportError, e:
+ return None
diff --git a/sos/plugins/abrt.py b/sos/plugins/abrt.py
index 22637a87..3ab7584c 100644
--- a/sos/plugins/abrt.py
+++ b/sos/plugins/abrt.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class abrt(sos.plugintools.PluginBase):
+class abrt(Plugin, RedHatPlugin):
"""ABRT log dump
"""
@@ -26,7 +26,7 @@ class abrt(sos.plugintools.PluginBase):
def checkenabled(self):
return self.isInstalled("abrt-cli") or \
exists("/var/spool/abrt")
-
+
def do_backtraces(self):
ret, output, rtime = self.callExtProg('/usr/bin/sqlite3 /var/spool/abrt/abrt-db \'select UUID from abrt_v4\'')
try:
diff --git a/sos/plugins/acpid.py b/sos/plugins/acpid.py
index 24ec432c..bc335eea 100644
--- a/sos/plugins/acpid.py
+++ b/sos/plugins/acpid.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class acpid(sos.plugintools.PluginBase):
+class acpid(Plugin, RedHatPlugin):
"""acpid related information
"""
def setup(self):
diff --git a/sos/plugins/amd.py b/sos/plugins/amd.py
index c0fd930b..2a6776c6 100644
--- a/sos/plugins/amd.py
+++ b/sos/plugins/amd.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class amd(sos.plugintools.PluginBase):
+class amd(Plugin, RedHatPlugin):
"""Amd automounter information
"""
diff --git a/sos/plugins/anaconda.py b/sos/plugins/anaconda.py
index aa7d4d1f..de7787bd 100644
--- a/sos/plugins/anaconda.py
+++ b/sos/plugins/anaconda.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class anaconda(sos.plugintools.PluginBase):
+class anaconda(Plugin, RedHatPlugin):
"""Anaconda / Installation information
"""
def checkenabled(self):
diff --git a/sos/plugins/apache.py b/sos/plugins/apache.py
index 4a41de11..a85f56ac 100644
--- a/sos/plugins/apache.py
+++ b/sos/plugins/apache.py
@@ -12,13 +12,13 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class apache(sos.plugintools.PluginBase):
+class apache(Plugin, RedHatPlugin):
"""Apache related information
"""
optionList = [("log", "gathers all apache logs", "slow", False)]
-
+
def setup(self):
self.addCopySpecs([
"/etc/httpd/conf/httpd.conf",
diff --git a/sos/plugins/auditd.py b/sos/plugins/auditd.py
index c76bf5f8..8ccae7f7 100644
--- a/sos/plugins/auditd.py
+++ b/sos/plugins/auditd.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class auditd(sos.plugintools.PluginBase):
+class auditd(Plugin, RedHatPlugin):
"""Auditd related information
"""
diff --git a/sos/plugins/autofs.py b/sos/plugins/autofs.py
index 84de4497..a295b23e 100644
--- a/sos/plugins/autofs.py
+++ b/sos/plugins/autofs.py
@@ -14,17 +14,17 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os, re
-class autofs(sos.plugintools.PluginBase):
+class autofs(Plugin, RedHatPlugin):
"""autofs server-related information
"""
def checkenabled(self):
self.packages = [ "autofs" ]
self.files = [ "/etc/sysconfig/autofs" ]
- return sos.plugintools.PluginBase.checkenabled(self)
-
+ return Plugin.checkenabled(self)
+
def checkdebug(self):
""" testing if autofs debug has been enabled anywhere
"""
@@ -35,14 +35,14 @@ class autofs(sos.plugintools.PluginBase):
if opt2 in ("--debug", "debug"):
return True
return False
-
+
def getdaemondebug(self):
""" capture daemon debug output
"""
debugout = self.fileGrep(r"^(daemon.*)\s+(\/var\/log\/.*)", "/etc/sysconfig/autofs")
for i in debugout:
return i[1]
-
+
def setup(self):
self.addCopySpec("/etc/auto*")
self.collectExtOutput("/bin/rpm -qV autofs")
diff --git a/sos/plugins/bootloader.py b/sos/plugins/bootloader.py
index 49f9bcf3..b0df9a9f 100644
--- a/sos/plugins/bootloader.py
+++ b/sos/plugins/bootloader.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class bootloader(sos.plugintools.PluginBase):
+class bootloader(Plugin, RedHatPlugin):
"""Bootloader information
"""
def setup(self):
diff --git a/sos/plugins/cluster.py b/sos/plugins/cluster.py
index 45bb23e6..2c61380c 100644
--- a/sos/plugins/cluster.py
+++ b/sos/plugins/cluster.py
@@ -12,11 +12,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os, re
from glob import glob
-class cluster(sos.plugintools.PluginBase):
+class cluster(Plugin, RedHatPlugin):
"""cluster suite and GFS related information
"""
@@ -26,7 +26,7 @@ class cluster(sos.plugintools.PluginBase):
def checkenabled(self):
rhelver = self.policy().rhelVersion()
if rhelver == 4:
- self.packages = [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins",
+ self.packages = [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins",
"rgmanager", "fence", "dlm", "dlm-kernel", "gulm",
"GFS", "GFS-kernel", "lvm2-cluster" ]
elif rhelver == 5:
@@ -38,7 +38,7 @@ class cluster(sos.plugintools.PluginBase):
"cman", "clusterlib", "fence-agents" ]
self.files = [ "/etc/cluster/cluster.conf" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
rhelver = self.policy().rhelVersion()
@@ -76,10 +76,10 @@ class cluster(sos.plugintools.PluginBase):
if rhelver is 4:
self.addCopySpec("/proc/cluster/*")
self.collectExtOutput("cman_tool nodes")
-
+
if rhelver is not 4: # 5+
self.collectExtOutput("cman_tool -a nodes")
-
+
if rhelver is 5:
self.collectExtOutput("group_tool -v")
self.collectExtOutput("group_tool dump fence")
diff --git a/sos/plugins/cobbler.py b/sos/plugins/cobbler.py
index c1a05147..d17b8020 100644
--- a/sos/plugins/cobbler.py
+++ b/sos/plugins/cobbler.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class cobbler(sos.plugintools.PluginBase):
+class cobbler(Plugin, RedHatPlugin):
"""cobbler related information
"""
def checkenabled(self):
@@ -23,5 +23,5 @@ class cobbler(sos.plugintools.PluginBase):
def setup(self):
self.addCopySpec("/etc/cobbler")
self.addCopySpec("/var/log/cobbler")
- self.addCopySpec("/var/lib/rhn/kickstarts")
+ self.addCopySpec("/var/lib/rhn/kickstarts")
self.addCopySpec("/var/lib/cobbler")
diff --git a/sos/plugins/corosync.py b/sos/plugins/corosync.py
index 9c1592f7..862659fe 100644
--- a/sos/plugins/corosync.py
+++ b/sos/plugins/corosync.py
@@ -12,18 +12,16 @@
## 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
-import time, libxml2
+from sos.plugins import Plugin, RedHatPlugin
-class corosync(sos.plugintools.PluginBase):
+class corosync(Plugin, RedHatPlugin):
""" corosync information
"""
def checkenabled(self):
self.files = ['/usr/sbin/corosync']
self.packages = ['corosync']
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
self.addCopySpecs([
diff --git a/sos/plugins/crontab.py b/sos/plugins/crontab.py
index 191b13f5..ae0a55d3 100644
--- a/sos/plugins/crontab.py
+++ b/sos/plugins/crontab.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class crontab(sos.plugintools.PluginBase):
+class crontab(Plugin, RedHatPlugin):
"""Crontab information
"""
def setup(self):
diff --git a/sos/plugins/cs.py b/sos/plugins/cs.py
index 3142892e..1d518117 100644
--- a/sos/plugins/cs.py
+++ b/sos/plugins/cs.py
@@ -16,11 +16,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
from glob import glob
-class cs(sos.plugintools.PluginBase):
+class cs(Plugin, RedHatPlugin):
"""Red Hat Certificate System 7.1, 7.3, 8.0 and dogtag related information
"""
diff --git a/sos/plugins/devicemapper.py b/sos/plugins/devicemapper.py
index 03cb54fe..a345e5bc 100644
--- a/sos/plugins/devicemapper.py
+++ b/sos/plugins/devicemapper.py
@@ -12,11 +12,10 @@
## 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
-from sos.helpers import sosGetCommandOutput
+from sos.plugins import Plugin, RedHatPlugin
-class devicemapper(sos.plugintools.PluginBase):
+class devicemapper(Plugin, RedHatPlugin):
"""device-mapper related information (dm, lvm, multipath)
"""
diff --git a/sos/plugins/dhcp.py b/sos/plugins/dhcp.py
index 4eb185d7..ee6719a6 100644
--- a/sos/plugins/dhcp.py
+++ b/sos/plugins/dhcp.py
@@ -12,16 +12,16 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class dhcp(sos.plugintools.PluginBase):
+class dhcp(Plugin, RedHatPlugin):
"""DHCP related information
"""
def checkenabled(self):
self.files = ['/etc/rc.d/init.d/dhcpd']
self.packages = ['dhcp']
- return sos.plugintools.PluginBase.checkenabled(self)
-
+ return Plugin.checkenabled(self)
+
def setup(self):
self.addCopySpecs([
"/etc/dhcpd.conf",
diff --git a/sos/plugins/dovecot.py b/sos/plugins/dovecot.py
index 9c201cea..705088e2 100644
--- a/sos/plugins/dovecot.py
+++ b/sos/plugins/dovecot.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class dovecot(sos.plugintools.PluginBase):
+class dovecot(Plugin, RedHatPlugin):
"""dovecot server related information
"""
def setup(self):
diff --git a/sos/plugins/ds.py b/sos/plugins/ds.py
index ca8a4b2a..107274c8 100644
--- a/sos/plugins/ds.py
+++ b/sos/plugins/ds.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class ds(sos.plugintools.PluginBase):
+class ds(Plugin, RedHatPlugin):
"""Directory Server information
"""
diff --git a/sos/plugins/eap6.py b/sos/plugins/eap6.py
new file mode 100644
index 00000000..2ac942f4
--- /dev/null
+++ b/sos/plugins/eap6.py
@@ -0,0 +1,339 @@
+import os
+import re
+import zipfile
+import urllib2
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+from sos.plugins import Plugin, IndependentPlugin
+from sos.utilities import DirTree, find, checksum
+
+class Request(object):
+
+ def __init__(self, resource, operation="read-resource", parameters=None):
+ self.resource = resource
+ self.operation = operation
+ if parameters:
+ self.parameters = parameters
+ else:
+ self.parameters = {}
+
+ def url_parts(self):
+ """Generator function to split a url into (key, value) tuples. The url
+ should contain an even number of pairs. In the case of / the generator
+ will immediately stop iteration."""
+ parts = self.resource.strip("/").split("/")
+
+ if parts == ['']:
+ raise StopIteration
+
+ while parts:
+ yield (parts.pop(0), parts.pop(0))
+
+
+class EAP6(Plugin, IndependentPlugin):
+ """JBoss related information
+ """
+
+ requires_root = False
+
+ version = "1.0"
+
+ optionList = [
+ ("home", "JBoss's installation dir (i.e. JBOSS_HOME)", '', False),
+ ("logsize", 'max size (MiB) to collect per log file', '', 15),
+ ("stdjar", 'Collect jar statistics for standard jars.', '', True),
+ ("host", 'hostname of the management api for jboss', '', 'localhost'),
+ ("port", 'port of the management api for jboss', '', '9990'),
+ ("user", 'username for management console', '', None),
+ ("pass", 'password for management console', '', None),
+ ("appxml", "comma separated list of application's whose XML descriptors you want. The keyword 'all' will collect all descriptors in the designated profile(s).", '', False),
+ ]
+
+ __MD5_CHUNK_SIZE=128
+ __jbossHome=None
+ __haveJava=False
+ __twiddleCmd=None
+ __jbossServerConfigDirs = ["standalone", "domain"]
+ __jbossHTMLBody=None
+
+ def __alert(self, msg):
+ self.soslog.error(msg)
+ self.addAlert(msg)
+
+ def __getJbossHome(self):
+ """
+ Will attempt to locate the JBoss installation dir in either jboss.home or
+ scrape it from the environment variable JBOSS_HOME.
+ Returns:
+ True JBOSS_HOME is set and the path exists. False otherwise.
+ """
+
+ if self.getOption("home"):
+ ## Prefer this value first over the ENV
+ self.__jbossHome=self.getOption(("home", "as7_home"))
+ self.addAlert("INFO: The JBoss installation directory supplied to SOS is " +
+ self.__jbossHome)
+ elif os.environ.get("JBOSS_HOME"):
+ self.__jbossHome=os.environ.get("JBOSS_HOME")
+ self.addAlert("INFO: The JBoss installation directory (i.e. JBOSS_HOME) from the environment is " +
+ self.__jbossHome)
+ else:
+ self.addAlert("ERROR: The JBoss installation directory was not supplied.\
+ The JBoss SOS plug-in cannot continue.")
+ return False
+
+ return True
+
+ def __getMd5(self, file):
+ """Returns the MD5 sum of the specified file."""
+
+ retVal = "?" * 32
+
+ try:
+ retVal = checksum(file, self.__MD5_CHUNK_SIZE)
+ except IOError, ioe:
+ self.__alert("ERROR: Unable to open %s for reading. Error: %s" % (file,ioe))
+
+ return retVal
+
+ def __getManifest(self, jarFile):
+ """
+ Given a jar file, this function will extract the Manifest and return it's contents
+ as a string.
+ """
+ manifest = None
+ try:
+ zf = zipfile.ZipFile(jarFile)
+ try:
+ manifest = zf.read("META-INF/MANIFEST.MF")
+ except Exception, e:
+ self.__alert("ERROR: reading manifest from %s. Error: %s" % (jarFile, e))
+ zf.close()
+ except Exception, e:
+ self.__alert("ERROR: reading contents of %s. Error: %s" % (jarFile, e))
+ return manifest
+
+ def __getStdJarInfo(self):
+ found = False
+ jar_info_list = []
+ for jarFile in find("*.jar", self.__jbossHome):
+ checksum = self.__getMd5(jarFile)
+ manifest = self.__getManifest(jarFile)
+ path = jarFile.replace(self.__jbossHome, 'JBOSSHOME')
+ if manifest:
+ manifest = manifest.strip()
+ jar_info_list.append((path, checksum, manifest))
+ found = True
+ if found:
+ jar_info_list.sort()
+ self.addStringAsFile("\n".join([
+ "%s\n%s\n%s\n" % (name, checksum, manifest)
+ for (name, checksum, manifest) in jar_info_list]),
+ 'jarinfo.txt')
+ else:
+ self.addAlert("WARN: No jars found in JBoss system path (" + self.__jbossHome + ").")
+
+
+ def query(self, request_obj):
+ try:
+ return self.query_java(request_obj)
+ except Exception, e:
+ self.addAlert("JBOSS API call failed, falling back to HTTP: %s" % e)
+ return self.query_http(request_obj)
+
+ def query_java(self, request_obj):
+ from org.jboss.dmr import ModelNode
+ controller_client = self.getOption('controller_client_proxy')
+ if not controller_client:
+ raise AttributeError("Controller Client is not available")
+
+ request = ModelNode()
+ request.get("operation").set(request_obj.operation)
+
+ for key, val in request_obj.url_parts():
+ request.get('address').add(key,val)
+
+ if request_obj.parameters:
+ for key, value in request_obj.parameters.iteritems():
+ request.get(key).set(value)
+
+ return controller_client.execute(request).toJSONString(True)
+
+ def query_http(self, request_obj, postdata=None):
+ host = self.getOption(('host', 'as7_host'))
+ port = self.getOption(('port', 'as7_port'))
+
+ username = self.getOption(('user', 'as7_user'), None)
+ password = self.getOption(('pass', 'as7_pass'), None)
+
+ uri = "http://%s:%s/management" % (host,port)
+
+ json_data = {'operation': request_obj.operation,
+ 'address': []}
+
+ for key, val in request_obj.url_parts():
+ json_data['address'].append({key:val})
+
+ for key, val in request_obj.parameters.iteritems():
+ json_data[key] = val
+
+ postdata = json.dumps(json_data)
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+
+ opener = urllib2.build_opener()
+
+ if username and password:
+ passwd_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ passwd_manager.add_password(realm="ManagementRealm",
+ uri=uri,
+ user=username,
+ passwd=password)
+ digest_auth_handler = urllib2.HTTPDigestAuthHandler(passwd_manager)
+ basic_auth_handler = urllib2.HTTPBasicAuthHandler(passwd_manager)
+
+ opener.add_handler(digest_auth_handler)
+ opener.add_handler(basic_auth_handler)
+
+ req = urllib2.Request(uri, data=postdata, headers=headers)
+
+ try:
+ resp = opener.open(req)
+ return resp.read()
+ except Exception, e:
+ err_msg = "Could not query url: %s; error: %s" % (uri, e)
+ self.addAlert(err_msg)
+ return err_msg
+
+ def _set_domain_info(self, parameters=None):
+ """This function will add host controller and server instance
+ name data if it is present to the desired resource. This is to support
+ domain-mode operation in AS7"""
+ host_controller_name = self.getOption("as7_host_controller_name")
+ server_name = self.getOption("as7_server_name")
+
+ if host_controller_name and server_name:
+ if not parameters:
+ parameters = {}
+
+ parameters['host'] = host_controller_name
+ parameters['server'] = server_name
+
+ return parameters
+
+
+ def _resource_to_file(self, resource=None, parameters=None, operation='read-resource', outfile=None):
+ parameters = self._set_domain_info(parameters)
+
+ r = Request(resource=resource,
+ parameters=parameters,
+ operation=operation)
+ self.addStringAsFile(self.query(r), filename=outfile)
+
+
+ def get_online_data(self):
+ """
+ This function co-locates calls to the management api that gather
+ information from a running system.
+ """
+ self._resource_to_file(resource="/",
+ parameters={"recursive": "true"},
+ outfile="configuration.json")
+ self._resource_to_file(resource="/core-service/service-container",
+ operation="dump-services",
+ outfile="dump-services.json")
+ self._resource_to_file(resource="/subsystem/modcluster",
+ operation="read-proxies-configuration",
+ outfile="cluster-proxies-configuration.json")
+ self._resource_to_file(resource="/core-service/platform-mbean/type/threading",
+ operation="dump-all-threads",
+ parameters={"locked-synchronizers": "true",
+ "locked-monitors": "true"},
+ outfile="threaddump.json")
+
+ def __getFiles(self, configDirAry):
+ """
+ This function will collect files from JBOSS_HOME for analysis. The scope of files to
+ be collected are determined by options to this SOS plug-in.
+ """
+
+ for dir_ in configDirAry:
+ path = os.path.join(self.__jbossHome, dir_)
+ ## First add forbidden files
+ self.addForbiddenPath(os.path.join(path, "tmp"))
+ self.addForbiddenPath(os.path.join(path, "work"))
+ self.addForbiddenPath(os.path.join(path, "data"))
+
+ if os.path.exists(path):
+ ## First get everything in the conf dir
+ confDir = os.path.join(path, "configuration")
+ self.addForbiddenPath(os.path.join(confDir, 'mgmt-users.properties'))
+
+ self.doCopyFileOrDir(confDir, sub=(self.__jbossHome, 'JBOSSHOME'))
+ ## Log dir next
+ logDir = os.path.join(path, "log")
+
+ for logFile in find("*", logDir):
+ self.addCopySpecLimit(logFile,
+ self.getOption("logsize"),
+ sub=(self.__jbossHome, 'JBOSSHOME'))
+
+ ## Deploy dir
+ deployDir = os.path.join(path, "deployments")
+
+ for deployFile in find("*", deployDir, max_depth=1):
+ self.addCopySpec(deployFile, sub=(self.__jbossHome, 'JBOSSHOME'))
+
+ def setup(self):
+
+ ## We need to know where JBoss is installed and if we can't find it we
+ ## must exit immediately.
+ if not self.__getJbossHome():
+ self.exit_please()
+
+ try:
+ self.get_online_data()
+ except urllib2.URLError:
+ pass
+
+ ## Generate hashes of the stock Jar files for the report.
+ if self.getOption("stdjar"):
+ self.__getStdJarInfo()
+
+ ## Generate a Tree for JBOSS_HOME
+ tree = DirTree(self.__jbossHome).as_string()
+ self.addStringAsFile(tree, "jboss_home_tree.txt")
+
+ self.__getFiles(self.__jbossServerConfigDirs)
+
+ # FIXME: this is still not right, tweak the search paths to pick up the right files
+ def postproc(self):
+ """
+ Obfuscate passwords.
+ """
+
+ password_xml_regex = re.compile(r'<password>.*</password>', re.IGNORECASE)
+
+ for dir_ in self.__jbossServerConfigDirs:
+ path = os.path.join(self.__jbossHome, dir_)
+
+ self.doRegexSub(os.path.join(path,"configuration","*.xml"),
+ password_xml_regex,
+ r'<password>********</password>')
+
+ tmp = os.path.join(path,"configuration")
+ for propFile in find("*-users.properties", tmp):
+ self.doRegexSub(propFile,
+ r"=(.*)",
+ r'=********')
+
+# Remove PW from -ds.xml files
+ tmp = os.path.join(path, "deployments")
+ for dsFile in find("*-ds.xml", tmp):
+ self.doRegexSub(dsFile,
+ password_xml_regex,
+ r"<password>********</password>")
diff --git a/sos/plugins/emc.py b/sos/plugins/emc.py
index 843e7ef1..4d88e151 100644
--- a/sos/plugins/emc.py
+++ b/sos/plugins/emc.py
@@ -16,9 +16,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools, os
+from sos.plugins import Plugin, RedHatPlugin, os
-class emc(sos.plugintools.PluginBase):
+class emc(Plugin, RedHatPlugin):
"""EMC related information (PowerPath, Solutions Enabler CLI and Navisphere CLI)
"""
@@ -146,7 +146,7 @@ class emc(sos.plugintools.PluginBase):
def checkenabled(self):
self.packages = [ "EMCpower" ]
self.files = [ "/opt/Navisphere/bin", "/proc/emcp" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
from subprocess import Popen, PIPE
@@ -168,7 +168,7 @@ class emc(sos.plugintools.PluginBase):
self.get_pp_config()
## If Solutions Enabler is installed collect Symmetrix/DMX specific information
- if len(self.allPkgsByNameRegex('[Ss][Yy][Mm][Cc][Ll][Ii]-[Ss][Yy][Mm][Cc][Ll][Ii]')) > 0:
+ if len(self.policy().package_manager.allPkgsByNameRegex('[Ss][Yy][Mm][Cc][Ll][Ii]-[Ss][Yy][Mm][Cc][Ll][Ii]')) > 0:
print "EMC Solutions Enabler SYMCLI is installed."
print " Gathering EMC Solutions Enabler SYMCLI information..."
self.addCustomText("EMC Solutions Enabler is installed.<br>")
diff --git a/sos/plugins/filesys.py b/sos/plugins/filesys.py
index baefaa32..25e46c50 100644
--- a/sos/plugins/filesys.py
+++ b/sos/plugins/filesys.py
@@ -12,12 +12,12 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
import re
from itertools import *
-class filesys(sos.plugintools.PluginBase):
+class filesys(Plugin, RedHatPlugin):
"""information on filesystems
"""
optionList = [("lsof", 'gathers information on all open files', 'slow', False)]
@@ -33,14 +33,14 @@ class filesys(sos.plugintools.PluginBase):
"/etc/raidtab",
"/etc/mdadm.conf"])
mounts = self.collectOutputNow("/bin/mount -l", root_symlink = "mount")
-
+
self.collectExtOutput("/bin/findmnt")
self.collectExtOutput("/bin/df -al", root_symlink = "df")
self.collectExtOutput("/bin/df -ali")
if self.getOption('lsof'):
self.collectExtOutput("/usr/sbin/lsof -b +M -n -l -P", root_symlink = "lsof")
self.collectExtOutput("/sbin/blkid -c /dev/null")
-
+
part_titlep = re.compile("^major")
blankp = re.compile("^$")
partlist = []
@@ -68,7 +68,7 @@ class filesys(sos.plugintools.PluginBase):
if bool(part_in_disk.match(dev)):
devlist.append(dev)
- for i in devlist:
+ for i in devlist:
self.collectExtOutput("/sbin/parted -s %s print" % (i))
if self.getOption('dumpe2fs'):
diff --git a/sos/plugins/ftp.py b/sos/plugins/ftp.py
index e5914e78..3a7d2647 100644
--- a/sos/plugins/ftp.py
+++ b/sos/plugins/ftp.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class ftp(sos.plugintools.PluginBase):
+class ftp(Plugin, RedHatPlugin):
"""FTP server related information
"""
diff --git a/sos/plugins/gdm.py b/sos/plugins/gdm.py
index 62207c0e..63cae615 100644
--- a/sos/plugins/gdm.py
+++ b/sos/plugins/gdm.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class gdm(sos.plugintools.PluginBase):
+class gdm(Plugin, RedHatPlugin):
"""gdm related information
"""
def setup(self):
diff --git a/sos/plugins/general.py b/sos/plugins/general.py
index 52c671d7..92244ceb 100644
--- a/sos/plugins/general.py
+++ b/sos/plugins/general.py
@@ -13,10 +13,10 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import commands
-class general(sos.plugintools.PluginBase):
+class general(Plugin, RedHatPlugin):
"""basic system information
"""
diff --git a/sos/plugins/gluster.py b/sos/plugins/gluster.py
index bf5b25c7..24199512 100644
--- a/sos/plugins/gluster.py
+++ b/sos/plugins/gluster.py
@@ -13,9 +13,9 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os.path
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class gluster(sos.plugintools.PluginBase):
+class gluster(Plugin, RedHatPlugin):
'''gluster related information'''
def checkenabled(self):
diff --git a/sos/plugins/hardware.py b/sos/plugins/hardware.py
index 230100ca..631701f4 100644
--- a/sos/plugins/hardware.py
+++ b/sos/plugins/hardware.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from glob import glob
-class hardware(sos.plugintools.PluginBase):
+class hardware(Plugin, RedHatPlugin):
"""hardware related information
"""
def setup(self):
diff --git a/sos/plugins/hts.py b/sos/plugins/hts.py
index 10af675d..7ab9cac7 100644
--- a/sos/plugins/hts.py
+++ b/sos/plugins/hts.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class hts(sos.plugintools.PluginBase):
+class hts(Plugin, RedHatPlugin):
"""Red Hat Hardware Test Suite related information
"""
def setup(self):
diff --git a/sos/plugins/i18n.py b/sos/plugins/i18n.py
index 8d4a26f7..2640f32d 100644
--- a/sos/plugins/i18n.py
+++ b/sos/plugins/i18n.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class i18n(sos.plugintools.PluginBase):
+class i18n(Plugin, RedHatPlugin):
"""i18n related information
"""
def setup(self):
diff --git a/sos/plugins/initrd.py b/sos/plugins/initrd.py
index 98d604a1..351e532d 100644
--- a/sos/plugins/initrd.py
+++ b/sos/plugins/initrd.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from glob import glob
-class initrd(sos.plugintools.PluginBase):
+class initrd(Plugin, RedHatPlugin):
"""initrd related information
"""
def setup(self):
diff --git a/sos/plugins/ipa.py b/sos/plugins/ipa.py
index f315dcba..824526ad 100644
--- a/sos/plugins/ipa.py
+++ b/sos/plugins/ipa.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class ipa(sos.plugintools.PluginBase):
+class ipa(Plugin, RedHatPlugin):
"""IPA diagnostic information
"""
# ntp and dirserver stuff are covered in existing sos plugins, so we really only
diff --git a/sos/plugins/ipsec.py b/sos/plugins/ipsec.py
index 82c8a431..8fc09628 100644
--- a/sos/plugins/ipsec.py
+++ b/sos/plugins/ipsec.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class ipsec(sos.plugintools.PluginBase):
+class ipsec(Plugin, RedHatPlugin):
"""ipsec related information
"""
def checkenabled(self):
diff --git a/sos/plugins/iscsi.py b/sos/plugins/iscsi.py
index 26139c3c..56c5149b 100644
--- a/sos/plugins/iscsi.py
+++ b/sos/plugins/iscsi.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class iscsi(sos.plugintools.PluginBase):
+class iscsi(Plugin, RedHatPlugin):
"""iscsi-initiator related information
"""
def setup(self):
diff --git a/sos/plugins/iscsitarget.py b/sos/plugins/iscsitarget.py
index fddea77c..b12e7712 100644
--- a/sos/plugins/iscsitarget.py
+++ b/sos/plugins/iscsitarget.py
@@ -14,15 +14,15 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class iscsitarget(sos.plugintools.PluginBase):
+class iscsitarget(Plugin, RedHatPlugin):
"""iscsi-target related information
"""
def checkenabled(self):
self.packages = [ "scsi-target-utils" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
self.addCopySpec("/etc/tgt/targets.conf")
diff --git a/sos/plugins/jboss.py b/sos/plugins/jboss.py
new file mode 100644
index 00000000..cffaf043
--- /dev/null
+++ b/sos/plugins/jboss.py
@@ -0,0 +1,708 @@
+import os
+import zipfile
+import platform
+import fnmatch
+import shlex
+import subprocess
+import string
+import grp, pwd
+
+from sos.plugins import Plugin, RedHatPlugin
+from sos.utilities import DirTree, find
+
+class jboss(Plugin, RedHatPlugin):
+ """JBoss related information
+ """
+
+ optionList = [("home", 'JBoss\'s installation dir (i.e. JBOSS_HOME)', '', False),
+ ("javahome", 'Java\'s installation dir (i.e. JAVA_HOME)', '', False),
+ ("profile", 'Quoted and space separated list of server profiles to limit collection. \
+Default=\'all default minimal production standard web\'.', '', False),
+ ("user", 'JBoss JMX invoker user to be used with twiddle.', '', False),
+ ("pass", 'JBoss JMX invoker user\'s password to be used with twiddle.', '', False),
+ ("logsize", 'max size (MiB) to collect per log file', '', 15),
+ ("stdjar", 'Collect jar statistics for standard jars.', '', True),
+ ("servjar", 'Collect jar statistics from any server configuration dirs.', '', True),
+ ("twiddle", 'Collect twiddle data.', '', True),
+ ("appxml", 'Quoted and space separated list of application\'s whose XML descriptors you want. The keyword \"all\" will collect all descriptors in the designated profile(s).', '', False)]
+
+ __MD5_CHUNK_SIZE=128
+ __jbossHome=None
+ __haveJava=False
+ __twiddleCmd=None
+ __jbossSystemJarDirs = [ "client", "lib" , "common/lib" ]
+ __jbossServerConfigDirs = ["all", "default", "minimal", "production", "standard", "web"]
+ __jbossHTMLBody=None
+
+ def __getJbossHome(self):
+ """
+ Will attempt to locate the JBoss installation dir in either jboss.home or
+ scrape it from the environment variable JBOSS_HOME.
+ Returns:
+ True JBOSS_HOME is set and the path exists. False otherwise.
+ """
+ if self.getOption("home"):
+ ## Prefer this value first over the ENV
+ self.__jbossHome=self.getOption("home")
+ self.addAlert("INFO: The JBoss installation directory supplied to SOS is " +
+ self.__jbossHome)
+ elif os.environ.get("JBOSS_HOME"):
+ self.__jbossHome=os.environ.get("JBOSS_HOME")
+ self.addAlert("INFO: The JBoss installation directory (i.e. JBOSS_HOME) from the environment is " +
+ self.__jbossHome)
+ else:
+ self.addAlert("ERROR: The JBoss installation directory was not supplied.\
+ The JBoss SOS plug-in cannot continue.")
+ return False
+
+ if os.path.exists(self.__jbossHome):
+ ## We need to set JBOSS_CLASSPATH otherwise some twiddle commands will not work.
+ jbossClasspath=None
+ tmp=os.path.join(self.__jbossHome, "lib")
+ if os.path.exists(tmp):
+ jbossClasspath=tmp + os.sep + "*" + os.pathsep
+ else:
+ self.addAlert("WARN: The JBoss lib directory does not exist. Dir(%s) " % tmp)
+
+ tmp=os.path.join(self.__jbossHome, "common" , "lib")
+ if os.path.exists(tmp):
+ jbossClasspath+=tmp + os.sep + "*"
+ else:
+ self.addAlert("WARN: The JBoss lib directory does not exist. Dir(%s) " % tmp)
+
+ os.environ['JBOSS_CLASSPATH']=jbossClasspath
+
+ return True
+ else:
+ msg = "ERROR: The path to the JBoss installation directory does not exist. Path is: " + self.__jbossHome
+ print msg
+ self.addAlert(msg)
+ return False
+
+ def __getJavaHome(self):
+ """
+ This SOS plug-in makes extensive use of JBoss' twiddle program and twiddle uses Java. As such, we
+ need to ensure that java and JAVA_HOME is known to the plug-in so that it can use Java.
+ This function will put JAVA_HOME and JAVA_HOME/bin into the environment if they're not already
+ there.
+ """
+ javaHome=None
+ java="bin/java"
+
+ if self.getOption("javahome"):
+ ## Prefer this value first over the ENV
+ javaHome=self.getOption("javahome")
+ self.addAlert("INFO: The Java installation directory supplied to SOS is " +
+ javaHome)
+ elif os.environ.get("JAVA_HOME"):
+ javaHome=os.environ.get("JAVA_HOME")
+ self.addAlert("INFO: The Java installation directory (i.e. JAVA_HOME) from the environment is " +
+ javaHome)
+ else:
+ ## Test to see if Java is already in the PATH
+ (status, output, rtime) = self.callExtProg("java -version")
+ if (status == 0):
+ self.addAlert("INFO: The Java installation directory is in the system path.")
+ return True
+ else:
+ self.addAlert("ERROR: The Java installation directory was not supplied.\
+ The JBoss SOS plug-in will not collect twiddle data.")
+ return False
+
+
+ java=os.path.join(javaHome, java)
+ if os.path.exists(java) and os.access(java, os.X_OK):
+ os.environ['JAVA_HOME']=javaHome
+ ## Place the supplied Java at the *head* of the path.
+ os.environ['PATH'] = os.path.join(javaHome, "bin") + os.pathsep + os.environ['PATH']
+ return True
+ else:
+ msg = "ERROR: The path to the Java installation directory does not exist. Path is: %s" % (javaHome)
+ print msg
+ self.addAlert(msg)
+ return False
+
+
+ def __getJMXCredentials(self):
+ """
+ Read the JMX credentials from the option list.
+ Returns:
+ A formatted credential string for twiddle consumption if both user and pass
+ are supplied. None otherwise.
+ """
+ credential = None
+ ## Let's make a best effort not to pass expansions or escapes to the shell
+ ## by strong quoting the user's input
+ if self.getOption("user"):
+ credential=" -u '" + self.getOption("user") + "' "
+ if self.getOption("pass"):
+ credential+=" -p '" + self.getOption("pass") + "' "
+ else:
+ credential=None
+ return credential
+
+ def __updateServerConfigDirs(self):
+ """
+ By default this plug-in will attempt to collect logs from every
+ JBoss server configuration directory (i.e. profile). The
+ user may have supplied a limited list, as such, we must respect
+ that wish.
+ Returns:
+ Nothing. Will update __jbossServerConfigDirs if the user
+ supplied a limited list.
+ """
+ if self.getOption("profile"):
+ profiles=self.getOption("profile")
+ ## I'd rather use comma as the delimiter but getOption doesn't seem to be passing it through.
+ ## Since we are using spaces as the delimiter, we need to filter out empty list elements
+ ## if the user did something like ' all default web '.
+ profiles=profiles.split(' ')
+ ## Flter(None doesn't work. Allows 0.
+ self.__jbossServerConfigDirs=filter(lambda x: len(x), profiles)
+ return
+
+ def __buildTwiddleCmd(self):
+ """
+ Utility function to build the twiddle command with/without credentials
+ so that it can be used by later fcns. If twiddle is found
+ """
+ ## In the off-chance that SOS is ever ported to cygwin or this plugin
+ ## is ported to win...
+ if platform.system() == "Windows":
+ self.__twiddleCmd=os.path.join(self.__jbossHome, "bin", "twiddle.bat")
+ else:
+ self.__twiddleCmd=os.path.join(self.__jbossHome, "bin", "twiddle.sh")
+
+ if os.path.exists(self.__twiddleCmd) and os.access(self.__twiddleCmd, os.X_OK):
+ credential = self.__getJMXCredentials()
+ if credential:
+ self.__twiddleCmd += credential
+ else:
+ ## Reset twiddlecmd to None
+ self.addAlert("ERROR: The twiddle program could not be found. Program=%s" % (self.__twiddleCmd))
+ self.__twiddleCmd = None
+
+ return
+
+ def __createHTMLBodyStart(self):
+ """
+ The free-form HTML that can be inserted into the SOS report with addCustomText is within
+ a <p> block. We need to add a few pieces of HTML so that all of our subsequent data will
+ be rendered properly.
+ """
+ self.__jbossHTMLBody = """
+ <br/>
+ <br/>
+ <script type="text/javascript">
+ <!--
+ function show(h) {
+ var tbl = document.getElementById(h);
+ tbl.style.display = 'block';
+ }
+ function hide(h) {
+ var tbl = document.getElementById(h);
+ tbl.style.display = 'none';
+ }
+ // -->
+ </script>
+ <b>JBoss SOS Report Table of Contents</b>
+ <ul style="list-style-type: square">
+ <li><a href="#system-jar-info">JBoss System Jar Information</a>
+ </li>
+ <li><a href="#profile-jar-info">JBoss Server Configurations Jar Information</a>
+ </li>
+ <li><a href="#jboss-home-directory-tree">JBOSS_HOME Directory Tree</a>
+ </li>
+ <li><a href="#jboss-system-mbean-data">JBoss JMX MBean Data from <tt>jboss.system:*</tt></a>
+ </li>
+ <li><a href="#jboss-mbean-data">JBoss JMX MBean Data from <tt>jboss:*</tt></a>
+ </li>
+ <li><a href="#jboss-mbean-summary">JBoss MBean Summary</a>
+ </li>
+ <li><a href="#jboss-messaging">JBoss JMX Messaging MBean Data from <tt>jboss.messaging:*</tt></a>
+ </li>
+ <li><a href="#jboss-j2ee">JBoss JMX J2EE MBean Data from <tt>jboss.j2ee:*</tt></a>
+ </li>
+ <li><a href="#jboss-vfs">JBoss JMX VFS MBean Data from <tt>jboss.vfs:*</tt></a>
+ </li>
+ <li><a href="#jboss-jsr77-data">JBoss JSR77 Data</a>
+ </li>
+ </ul>
+ <br/>
+ <br/>
+ """
+
+ def __getMd5(self, file):
+ """
+ Will perform an MD5 sum on a given file and return the file's message digest. This function
+ will not read the entire file into memory, instead, it will consume the file in 128 byte
+ chunks. This might be slightly slower but, the intent of a SOS report is to collect data from
+ a system that could be under stress and we shouldn't stress it more by loading entire Jars into
+ real memory.
+
+ Note: This fcn expects hashlib; however, this isn't always available. If it isn't then
+ we will use md5sum
+ """
+
+ retVal="????????????????????????????????"
+
+ try:
+ import hashlib
+ try:
+ fd = open(file,"rb")
+ except IOError, ioe:
+ msg = "ERROR: Unable to open %s for reading. Error: " % (file,ioe)
+ print msg
+ self.addAlert(msg)
+
+ md5 = hashlib.md5()
+ data = fd.read(self.__MD5_CHUNK_SIZE)
+ while data:
+ md5.update(data)
+ data = fd.read(self.__MD5_CHUNK_SIZE)
+ retVal = md5.hexdigest()
+ except ImportError, e:
+ process = subprocess.Popen(['md5sum', file],
+ shell=False,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ result = process.communicate()
+ if (process.returncode == 0):
+ retVal = result[0].partition(' ')[0]
+ else:
+ msg = "ERROR: Unable to compute md5sum of %s. Msg (%s)" % (file, result[1])
+ print msg
+ self.addAlert(msg)
+
+ return retVal
+
+
+ def __getManifest(self, jarFile):
+ """
+ Given a jar file, this function will extract the Manifest and return it's contents
+ as a string.
+ """
+ manifest=None
+ try:
+ zf = zipfile.ZipFile(jarFile)
+ try:
+ manifest=zf.read("META-INF/MANIFEST.MF")
+ except Exception, e:
+ msg="ERROR: reading manifest from %s. Error: %s" % (jarFile, e)
+ print msg
+ self.addAlert(msg)
+ zf.close()
+ except Exception, e:
+ msg="ERROR: reading contents of %s. Error: %s" % (jarFile, e)
+ print msg
+ self.addAlert(msg)
+ return manifest
+
+ def __getStdJarInfo(self):
+
+ self.__jbossHTMLBody += """
+ <div id="system-jar-info" style="font-weight: bold;">&ndash; JBoss System Jar Information</div>
+ """
+
+ for dir in self.__jbossSystemJarDirs:
+ path=os.path.join(self.__jbossHome, dir)
+ if os.path.exists(path):
+ nicePath=path.replace(os.sep, "-")
+ self.__jbossHTMLBody += """
+ <div>
+ &mdash; Summary of Jar Files in JBoss System Directory
+ <tt>%s</tt>
+ ( <a href="javascript:show('%s')">Show</a> / <a
+ href="javascript:hide('%s')">Hide</a> ):
+ </div>
+ <div id="%s" style="overflow: hidden; display: none">
+ <ul style="list-style-type: square">
+ """ % (path,nicePath,nicePath,nicePath)
+
+ found= False
+ for jarFile in find("*.jar", path):
+ found= True
+ nicePath=jarFile.replace(os.sep, "-")
+ self.__jbossHTMLBody += """
+ <li>Jar File: <tt>%s</tt><br/>
+ MD5: <tt>%s</tt>
+ <br /> Manifest File (
+ <a href="javascript:show('%s')">Show</a> /
+ <a href="javascript:hide('%s')">Hide</a> ):<br />
+ <div id="%s" style="overflow: hidden; display: none">
+ <pre>
+ %s
+ </pre>
+ </div>
+ </li>
+ """ % (jarFile,
+ self.__getMd5(jarFile),
+ nicePath,
+ nicePath,
+ nicePath,
+ self.__getManifest(jarFile))
+
+ if not found:
+ self.addAlert("WARN: No jars found in JBoss system path (" + path + ").")
+ self.__jbossHTMLBody += """
+ </ul>
+ </div>
+ """
+ else:
+ self.addAlert("ERROR: JBoss system path (" + path + ") does not exist.")
+ return
+
+ def __getServerConfigJarInfo(self, configDirAry):
+
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="profile-jar-info" style="font-weight: bold;">&ndash; JBoss Server Configurations Jar Information</div>
+ """
+ for dir in configDirAry:
+ serverDir = os.path.join("server", dir)
+ path=os.path.join(self.__jbossHome, serverDir)
+ if os.path.exists(path):
+ nicePath=path.replace(os.sep, "-")
+ self.__jbossHTMLBody += """
+ <div>
+ &mdash; Summary of Jar Files in the <tt>%s</tt> JBoss Server Configuration
+ ( <a href="javascript:show('%s')">Show</a> / <a
+ href="javascript:hide('%s')">Hide</a> ):
+ </div>
+ <div id="%s" style="overflow: hidden; display: none">
+ <ul style="list-style-type: square">
+ """ % (dir, nicePath,nicePath,nicePath)
+
+ found = False
+ for jarFile in find("*.jar", path):
+ found = True
+ nicePath=jarFile.replace(os.sep, "-")
+ self.__jbossHTMLBody += """
+ <li id="system-jar-info">Jar File: <tt>%s</tt><br/>
+ MD5: <tt>%s</tt>
+ <br /> Manifest File (
+ <a href="javascript:show('%s')">Show</a> /
+ <a href="javascript:hide('%s')">Hide</a> ):<br />
+ <div id="%s" style="overflow: hidden; display: none">
+ <pre>
+ %s
+ </pre>
+ </div>
+ </li>
+ """ % (jarFile,
+ self.__getMd5(jarFile),
+ nicePath,
+ nicePath,
+ nicePath,
+ self.__getManifest(jarFile))
+
+ if not found:
+ self.addAlert("WARN: No jars found in the JBoss server configuration (%s)." % (path))
+
+ self.__jbossHTMLBody += """
+ </ul>
+</div>
+ """
+ else:
+ self.addAlert("ERROR: JBoss server configuration path (" + path + ") does not exist.")
+
+ return
+
+ def __getJBossHomeTree(self):
+ """
+ This function will execute the "tree" command on JBOSS_HOME.
+ """
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-home-directory-tree" style="font-weight: bold;">&ndash; JBOSS_HOME Directory Tree</div>
+
+ <div>
+ &mdash; JBOSS_HOME Tree
+ ( <a href="javascript:show('jboss-home-tree')">Show</a> / <a
+ href="javascript:hide('jboss-home-tree')">Hide</a> ):
+ </div>
+ <div id="jboss-home-tree" style="overflow: hidden; display: none">
+ <pre>
+ """
+ try:
+ output = DirTree(self.__jbossHome).as_string()
+ self.__jbossHTMLBody += """
+%s
+ </pre>
+ </div>
+ """ % (output)
+ except Exception, e:
+ self.__jbossHTMLBody += """
+ ERROR: Unable to generate <tt>tree</tt> on JBOSS_HOME.
+ Exception: %s
+ </pre>
+ </div>
+ """ % e
+ return
+
+ def __getMbeanData(self, dataTitle, divId, twiddleOpts):
+ credentials = ""
+ if self.__haveJava and self.__twiddleCmd:
+ self.__jbossHTMLBody += """
+ <div>
+ &mdash; %s
+ ( <a href="javascript:show('%s')">Show</a> / <a
+ href="javascript:hide('%s')">Hide</a> ):
+ </div>
+ <div id="%s" style="overflow: hidden; display: none">
+ <table style="margin-left: 30px;font-size:14px">
+ <tr>
+ <td align="left">
+ Twiddle Options:
+ </td>
+ <td align="left"><tt>%s</tt></td>
+ </tr>
+ </table>
+ <pre>
+
+ """ % (dataTitle, divId, divId, divId,twiddleOpts)
+ cmd = "%s %s" % (self.__twiddleCmd, twiddleOpts)
+
+ proc = subprocess.Popen(shlex.split(cmd), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+ output = proc.communicate()[0]
+ status = proc.returncode
+ if status == 0 and output:
+ self.__jbossHTMLBody += output.strip()
+ else:
+ self.__jbossHTMLBody += """
+ ERROR: Unable to collect %s data.
+ Output: %s
+ Status: %d
+ """ % (twiddleOpts, output, status)
+ else:
+ self.__jbossHTMLBody += "ERROR: Unable to collect data twiddle or Java is missing."
+
+ self.__jbossHTMLBody += """
+ </pre>
+ </div>
+ """
+ return
+
+ def __getTwiddleData(self):
+ """
+ This function co-locates all of the calls to twiddle so that they can be easily disabled.
+ """
+
+ ## Get jboss.system.* Data
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-system-mbean-data" style="font-weight: bold;">&ndash; JBoss JMX MBean Data from <tt>jboss.system:*</tt></div>
+ """
+ self.__getMbeanData("JBoss Server Info",
+ "jboss-server-info",
+ " get 'jboss.system:type=ServerInfo' ")
+ self.__getMbeanData("JBoss Server Config Info",
+ "jboss-server-config-info",
+ " get 'jboss.system:type=ServerConfig' ")
+ self.__getMbeanData("JBoss CXF Server Config Info",
+ "jboss-cxfserver-config-info",
+ " get 'jboss.ws:service=ServerConfig' ")
+ self.__getMbeanData("JBoss Memory Pool Info",
+ "jboss-memory-pool-info",
+ " invoke 'jboss.system:type=ServerInfo' listMemoryPools true ")
+ self.__getMbeanData("JBoss Thread CPU Utilization",
+ "jboss-thread-cpu-info",
+ " invoke 'jboss.system:type=ServerInfo' listThreadCpuUtilization ")
+ self.__getMbeanData("JBoss Thread Dump",
+ "jboss-thread-dump",
+ " invoke 'jboss.system:type=ServerInfo' listThreadDump ")
+ self.__getMbeanData("JBoss Logging Config Info",
+ "jboss-logging-config-info",
+ " get 'jboss.system:service=Logging,type=Log4jService' ")
+
+ ## Get jboss.* Data
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-mbean-data" style="font-weight: bold;">&ndash; JBoss JMX MBean Data from <tt>jboss:*</tt></div>
+ """
+ self.__getMbeanData("JBoss System Properties",
+ "jboss-system-properties-info",
+ " invoke 'jboss:name=SystemProperties,type=Service' showAll ")
+
+ self.__getMbeanData("JBoss JNDI List View",
+ "jboss-jndi-list-info",
+ " invoke 'jboss:service=JNDIView' list true ")
+
+ ## MBean Summary
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-mbean-summary" style="font-weight: bold;">&ndash; JBoss MBean Summary</div>
+ """
+ self.__getMbeanData("JBoss MBean Vendor/Version Info",
+ "jboss-vendor-version",
+ " get 'JMImplementation:type=MBeanServerDelegate' ")
+ self.__getMbeanData("JBoss MBean Count",
+ "jboss-mbean-count",
+ " serverinfo -c ")
+ self.__getMbeanData("JBoss MBean List",
+ "jboss-mbean-list",
+ " serverinfo -l ")
+
+ ##JBoss Messaging Data
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-messaging" style="font-weight: bold;">&ndash; JBoss JMX Messaging MBean Data from <tt>jboss.messaging:*</tt></div>
+ """
+ self.__getMbeanData("JBoss Message Counters",
+ "jboss-message-counters",
+ " invoke 'jboss.messaging:service=ServerPeer' listMessageCountersAsHTML ")
+
+ self.__getMbeanData("JBoss Prepared Transactions Table",
+ "jboss-prepared-transactions",
+ " invoke 'jboss.messaging:service=ServerPeer' listAllPreparedTransactions ")
+
+ self.__getMbeanData("JBoss Active Clients Table",
+ "jboss-active-clients",
+ " invoke 'jboss.messaging:service=ServerPeer' showActiveClientsAsHTML ")
+
+ ## Get j2ee Data query 'jboss.j2ee:*'
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-j2ee" style="font-weight: bold;">&ndash; JBoss JMX J2EE MBean Data from <tt>jboss.j2ee:*</tt></div>
+ """
+ self.__getMbeanData("JBoss J2EE MBeans",
+ "jboss-j2ee-mbeans",
+ " query 'jboss.j2ee:*' ")
+
+ ## VFS
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-vfs" style="font-weight: bold;">&ndash; JBoss JMX VFS MBean Data from <tt>jboss.vfs:*</tt></div>
+ """
+ self.__getMbeanData("JBoss VFS Cached Contexts",
+ "jboss-vfs-contexts",
+ " invoke 'jboss.vfs:service=VFSCacheStatistics' listCachedContexts ")
+
+ ## Get jsr77 Data
+ self.__jbossHTMLBody += """
+ <br/>
+ <br/>
+ <div id="jboss-jsr77-data" style="font-weight: bold;">&ndash; JBoss JSR77 Data</div>
+ """
+ self.__getMbeanData("JBoss JSR77 Data",
+ "jboss-jsr77",
+ " jsr77 ")
+ return
+
+
+ def __getFiles(self, configDirAry):
+ """
+ This function will collect files from JBOSS_HOME for analysis. The scope of files to
+ be collected are determined by options to this SOS plug-in.
+ """
+
+ for dir in configDirAry:
+ path=os.path.join(self.__jbossHome, "server", dir)
+ ## First add forbidden files
+ self.addForbiddenPath(os.path.join(path, "tmp"))
+ self.addForbiddenPath(os.path.join(path, "work"))
+ self.addForbiddenPath(os.path.join(path, "data"))
+
+ if os.path.exists(path):
+ ## First get everything in the conf dir
+ confDir=os.path.join(path, "conf")
+ self.doCopyFileOrDir(confDir)
+ ## Log dir next
+ logDir=os.path.join(path, "log")
+
+ for logFile in find("*", logDir):
+ self.addCopySpecLimit(logFile, self.getOption("logsize"))
+ ## Deploy dir
+ deployDir=os.path.join(path, "deploy")
+
+ for deployFile in find("*", deployDir, max_depth=1):
+ self.addCopySpec(deployFile)
+
+ ## Get application deployment descriptors if designated.
+ if self.isOptionEnabled("appxml"):
+ appxml=self.getOption("appxml")
+ ## I'd rather use comma as the delimiter but getOption doesn't seem to be passing it through.
+ ## Since we are using spaces as the delimiter, we need to filter out empty list elements
+ ## if the user did something like ' all default web '.
+ appxml=appxml.split(' ')
+ ## Flter(None doesn't work. Allows 0.
+ appxml=filter(lambda x: len(x), appxml)
+ for app in appxml:
+ pat = os.path.join("*%s*" % (app,), "WEB-INF")
+ for file in find("*.xml", deployDir, path_pattern=pat):
+ self.addCopySpec(file)
+ return
+
+ def setup(self):
+
+ ## We need to know where JBoss is installed and if we can't find it we
+ ## must exit immediately.
+ if not self.__getJbossHome():
+ self.exit_please()
+ return
+
+ ## Check to see if the user passed in a limited list of server config jars.
+ self.__updateServerConfigDirs()
+
+ ## Generate HTML Body for report
+ self.__createHTMLBodyStart()
+
+ ## Generate hashes of the stock Jar files for the report.
+ if self.getOption("stdjar"):
+ self.__getStdJarInfo()
+
+ ## Generate hashes for the Jars in the various profiles
+ if self.getOption("servjar"):
+ self.__getServerConfigJarInfo(self.__jbossServerConfigDirs)
+
+ ## Generate a Tree for JBOSS_HOME
+ self.__getJBossHomeTree()
+
+ if self.getOption("twiddle"):
+ ## We need to know where Java is installed or at least ensure that it
+ ## is available to the plug-in so that we can run twiddle.
+ self.__haveJava = self.__getJavaHome()
+ self.__buildTwiddleCmd()
+ self.__getTwiddleData()
+
+
+ self.addCustomText(self.__jbossHTMLBody)
+
+ self.__getFiles(self.__jbossServerConfigDirs)
+
+ return
+
+ def postproc(self):
+ """
+ Obfuscate passwords.
+ """
+
+ for dir in self.__jbossServerConfigDirs:
+ path=os.path.join(self.__jbossHome, "server", dir)
+ ## Really annoying that there appears to be no vehicle to
+ ## say I want ignore case...argh!
+ self.doRegexSub(os.path.join(path,"conf","login-config.xml"),
+ r"\"[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]\".*>.*</[Mm][Oo][Dd][Uu][Ll][Ee]-[Oo][Pp][Tt][Ii][Oo][Nn].*>",
+ r'"password">********</module-option>')
+
+ tmp = os.path.join(path,"conf", "props")
+ for propFile in find("*-users.properties", tmp):
+ self.doRegexSub(propFile,
+ r"=(.*)",
+ r'=********')
+
+ ## Remove PW from -ds.xml files
+ tmp=os.path.join(path, "deploy")
+ for dsFile in find("*-ds.xml", tmp):
+ self.doRegexSub(dsFile,
+ r"<[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd].*>.*</[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd].*>",
+ r"<password>********</password>")
+ return
diff --git a/sos/plugins/kdump.py b/sos/plugins/kdump.py
index bea5d0b9..3a138693 100644
--- a/sos/plugins/kdump.py
+++ b/sos/plugins/kdump.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class kdump(sos.plugintools.PluginBase):
+class kdump(Plugin, RedHatPlugin):
"""Kdump related information
"""
def checkenabled(self):
diff --git a/sos/plugins/kernel.py b/sos/plugins/kernel.py
index c43dbdf4..8a0e860c 100644
--- a/sos/plugins/kernel.py
+++ b/sos/plugins/kernel.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os, re
-class kernel(sos.plugintools.PluginBase):
+class kernel(Plugin, RedHatPlugin):
"""kernel related information
"""
optionList = [("modinfo", 'gathers information on all kernel modules', 'fast', True)]
@@ -47,7 +47,7 @@ class kernel(sos.plugintools.PluginBase):
if self.getOption('modinfo'):
runcmd = ""
ret, mods, rtime = self.callExtProg('/sbin/lsmod | /bin/cut -f1 -d" " 2>/dev/null | /bin/grep -v Module 2>/dev/null')
- for kmod in mods.split('\n'):
+ for kmod in mods.split('\n'):
if '' != kmod.strip():
runcmd = runcmd + " " + kmod
if len(runcmd):
diff --git a/sos/plugins/kvm.py b/sos/plugins/kvm.py
index 111bcf97..46624282 100644
--- a/sos/plugins/kvm.py
+++ b/sos/plugins/kvm.py
@@ -15,10 +15,10 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class kvm(sos.plugintools.PluginBase):
+class kvm(Plugin, RedHatPlugin):
"""KVM related information
"""
diff --git a/sos/plugins/ldap.py b/sos/plugins/ldap.py
index 81ec53aa..b331c26e 100644
--- a/sos/plugins/ldap.py
+++ b/sos/plugins/ldap.py
@@ -12,16 +12,16 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class ldap(sos.plugintools.PluginBase):
+class ldap(Plugin, RedHatPlugin):
"""LDAP related information
"""
def checkenabled(self):
self.packages = [ "openldap" ]
self.files = [ "/etc/openldap/ldap.conf" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def get_ldap_opts(self):
# capture /etc/openldap/ldap.conf options in dict
diff --git a/sos/plugins/libraries.py b/sos/plugins/libraries.py
index 39c21467..bf0223a2 100644
--- a/sos/plugins/libraries.py
+++ b/sos/plugins/libraries.py
@@ -12,13 +12,13 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class libraries(sos.plugintools.PluginBase):
+class libraries(Plugin, RedHatPlugin):
"""information on shared libraries
"""
- optionList = [('ldconfigv', 'the name of each directory as it is scanned, and any links that are created.',
+ optionList = [('ldconfigv', 'the name of each directory as it is scanned, and any links that are created.',
"slow", False)]
def setup(self):
diff --git a/sos/plugins/libvirt.py b/sos/plugins/libvirt.py
index 63d62bea..9f2a18d5 100644
--- a/sos/plugins/libvirt.py
+++ b/sos/plugins/libvirt.py
@@ -12,8 +12,8 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
-class libvirt(sos.plugintools.PluginBase):
+from sos.plugins import Plugin, RedHatPlugin
+class libvirt(Plugin, RedHatPlugin):
"""libvirt-related information
"""
def setup(self):
diff --git a/sos/plugins/logrotate.py b/sos/plugins/logrotate.py
index adc65aae..660623ae 100644
--- a/sos/plugins/logrotate.py
+++ b/sos/plugins/logrotate.py
@@ -12,12 +12,12 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class logrotate(sos.plugintools.PluginBase):
+class logrotate(Plugin, RedHatPlugin):
"""logrotate configuration files and debug info
"""
-
+
def setup(self):
self.collectExtOutput("/usr/sbin/logrotate --debug /etc/logrotate.conf",
suggest_filename = "logrotate_debug")
diff --git a/sos/plugins/lsbrelease.py b/sos/plugins/lsbrelease.py
index 7a58a024..782038e1 100644
--- a/sos/plugins/lsbrelease.py
+++ b/sos/plugins/lsbrelease.py
@@ -13,10 +13,10 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class lsbrelease(sos.plugintools.PluginBase):
+class lsbrelease(Plugin, RedHatPlugin):
"""Linux Standard Base information
"""
def diagnose(self):
diff --git a/sos/plugins/memory.py b/sos/plugins/memory.py
index 8d4cf202..3ee8ef7e 100644
--- a/sos/plugins/memory.py
+++ b/sos/plugins/memory.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class memory(sos.plugintools.PluginBase):
+class memory(Plugin, RedHatPlugin):
"""memory usage information
"""
def setup(self):
@@ -23,7 +23,7 @@ class memory(sos.plugintools.PluginBase):
"/proc/meminfo",
"/proc/vmstat",
"/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")
diff --git a/sos/plugins/mrggrid.py b/sos/plugins/mrggrid.py
index 54e1d6a4..b47fd635 100644
--- a/sos/plugins/mrggrid.py
+++ b/sos/plugins/mrggrid.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class mrggrid(sos.plugintools.PluginBase):
+class mrggrid(Plugin, RedHatPlugin):
"""MRG GRID related information
"""
def setup(self):
diff --git a/sos/plugins/mrgmessg.py b/sos/plugins/mrgmessg.py
index 2c01fa87..7668b6b9 100644
--- a/sos/plugins/mrgmessg.py
+++ b/sos/plugins/mrgmessg.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class mrgmessg(sos.plugintools.PluginBase):
+class mrgmessg(Plugin, RedHatPlugin):
"""MRG Messaging related information
"""
def setup(self):
diff --git a/sos/plugins/mysql.py b/sos/plugins/mysql.py
index 59b6bb28..5c7049db 100644
--- a/sos/plugins/mysql.py
+++ b/sos/plugins/mysql.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class mysql(sos.plugintools.PluginBase):
+class mysql(Plugin, RedHatPlugin):
"""MySQL related information
"""
@@ -23,7 +23,7 @@ class mysql(sos.plugintools.PluginBase):
return self.isInstalled("mysql-server") or \
exists("/etc/my.cnf") or \
self.isInstalled("mysql")
-
+
def setup(self):
self.addCopySpecs([
"/etc/my.cnf",
diff --git a/sos/plugins/named.py b/sos/plugins/named.py
index 6710a165..8e6af6dd 100644
--- a/sos/plugins/named.py
+++ b/sos/plugins/named.py
@@ -12,19 +12,19 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import commands
from os.path import normpath, join, exists
-class named(sos.plugintools.PluginBase):
+class named(Plugin, RedHatPlugin):
"""named related information
"""
def checkenabled(self):
self.files = [ "/etc/named.conf",
"/etc/sysconfig/named" ]
self.packages = [ "bind" ]
- return sos.plugintools.PluginBase.checkenabled(self)
-
+ return Plugin.checkenabled(self)
+
def getDnsDir(self, configFile):
""" grab directory path from named{conf,boot}
"""
diff --git a/sos/plugins/netdump.py b/sos/plugins/netdump.py
index 1dcbc0f0..815c5955 100644
--- a/sos/plugins/netdump.py
+++ b/sos/plugins/netdump.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class netdump(sos.plugintools.PluginBase):
+class netdump(Plugin, RedHatPlugin):
"""Netdump Configuration Information
"""
def checkenabled(self):
diff --git a/sos/plugins/networking.py b/sos/plugins/networking.py
index fbc8d67d..b0b3010c 100644
--- a/sos/plugins/networking.py
+++ b/sos/plugins/networking.py
@@ -12,11 +12,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
import re
-class networking(sos.plugintools.PluginBase):
+class networking(Plugin, RedHatPlugin):
"""network related information
"""
optionList = [("traceroute", "collects a traceroute to rhn.redhat.com", "slow", False)]
diff --git a/sos/plugins/nfsserver.py b/sos/plugins/nfsserver.py
index 5dad1676..876192ab 100644
--- a/sos/plugins/nfsserver.py
+++ b/sos/plugins/nfsserver.py
@@ -14,11 +14,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
from stat import ST_SIZE
-class nfsserver(sos.plugintools.PluginBase):
+class nfsserver(Plugin, RedHatPlugin):
"""NFS server-related information
"""
def checkenabled(self):
diff --git a/sos/plugins/nscd.py b/sos/plugins/nscd.py
index 8ae28878..6fcc3ad2 100644
--- a/sos/plugins/nscd.py
+++ b/sos/plugins/nscd.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class nscd(sos.plugintools.PluginBase):
+class nscd(Plugin, RedHatPlugin):
"""NSCD related information
"""
diff --git a/sos/plugins/ntp.py b/sos/plugins/ntp.py
index f3010ae4..bd9b1a76 100644
--- a/sos/plugins/ntp.py
+++ b/sos/plugins/ntp.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class ntp(sos.plugintools.PluginBase):
+class ntp(Plugin, RedHatPlugin):
"""NTP related information
"""
def setup(self):
diff --git a/sos/plugins/oddjob.py b/sos/plugins/oddjob.py
index 64d40784..19f666a7 100644
--- a/sos/plugins/oddjob.py
+++ b/sos/plugins/oddjob.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class oddjob(sos.plugintools.PluginBase):
+class oddjob(Plugin, RedHatPlugin):
"""oddjob related information
"""
def checkenabled(self):
diff --git a/sos/plugins/openssl.py b/sos/plugins/openssl.py
index 629f3d7b..35911593 100644
--- a/sos/plugins/openssl.py
+++ b/sos/plugins/openssl.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class openssl(sos.plugintools.PluginBase):
+class openssl(Plugin, RedHatPlugin):
"""openssl related information
"""
diff --git a/sos/plugins/openswan.py b/sos/plugins/openswan.py
index 442531f2..bd9de349 100644
--- a/sos/plugins/openswan.py
+++ b/sos/plugins/openswan.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class openswan(sos.plugintools.PluginBase):
+class openswan(Plugin, RedHatPlugin):
"""ipsec related information
"""
def checkenabled(self):
diff --git a/sos/plugins/pam.py b/sos/plugins/pam.py
index 5143f7b2..08b076c7 100644
--- a/sos/plugins/pam.py
+++ b/sos/plugins/pam.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class pam(sos.plugintools.PluginBase):
+class pam(Plugin, RedHatPlugin):
"""PAM related information
"""
def setup(self):
diff --git a/sos/plugins/postfix.py b/sos/plugins/postfix.py
index 32932468..1c604898 100644
--- a/sos/plugins/postfix.py
+++ b/sos/plugins/postfix.py
@@ -12,15 +12,15 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class postfix(sos.plugintools.PluginBase):
+class postfix(Plugin, RedHatPlugin):
"""mail server related information
"""
def checkenabled(self):
return self.isInstalled("postfix") or exists("/etc/rc.d/init.d/postfix")
-
+
def setup(self):
self.addCopySpecs([
"/etc/mail",
diff --git a/sos/plugins/postgresql.py b/sos/plugins/postgresql.py
new file mode 100644
index 00000000..f3a44eb3
--- /dev/null
+++ b/sos/plugins/postgresql.py
@@ -0,0 +1,56 @@
+import os
+import fnmatch
+import shlex
+import subprocess
+import tempfile
+
+from sos.plugins import Plugin, RedHatPlugin
+from sos.utilities import find
+
+class postgresql(Plugin, RedHatPlugin):
+ """PostgreSQL related information"""
+
+ optionList = [
+ ("pghome", 'PostgreSQL server home directory.', '', '/var/lib/pgsql'),
+ ("username", 'username for pg_dump', '', 'postgres'),
+ ("password", 'password for pg_dump', '', ''),
+ ("dbname", 'database name to dump for pg_dump', '', ''),
+ ]
+
+ def pg_dump(self):
+ dest_file = os.path.join(self.tmp_dir, "sos_pgdump.tar")
+ old_env_pgpassword = os.environ.get("PGPASSWORD")
+ os.environ["PGPASSWORD"] = self.getOption("password")
+ (status, output, rtime) = self.callExtProg("pg_dump %s -U %s -w -f %s -F t" %
+ (self.getOption("dbname"),
+ self.getOption("username"),
+ dest_file))
+ if old_env_pgpassword is not None:
+ os.environ["PGPASSWORD"] = old_env_pgpassword
+ if (status == 0):
+ self.addCopySpec(dest_file)
+ else:
+ self.addAlert("ERROR: Unable to execute pg_dump. Error(%s)" % (output))
+
+ def setup(self):
+ if self.getOption("dbname"):
+ if self.getOption("password"):
+ self.tmp_dir = tempfile.mkdtemp()
+ self.pg_dump()
+ else:
+ self.addAlert("WARN: password must be supplied to dump a database.")
+
+ # Copy PostgreSQL log files.
+ for file in find("*.log", self.getOption("pghome")):
+ self.addCopySpec(file)
+ # Copy PostgreSQL config files.
+ for file in find("*.conf", self.getOption("pghome")):
+ self.addCopySpec(file)
+
+ self.addCopySpec(os.path.join(self.getOption("pghome"), "data" , "PG_VERSION"))
+ self.addCopySpec(os.path.join(self.getOption("pghome"), "data" , "postmaster.opts"))
+
+
+ def postproc(self):
+ import shutil
+ shutil.rmtree(self.tmp_dir)
diff --git a/sos/plugins/ppp.py b/sos/plugins/ppp.py
index 08f45585..98c68a84 100644
--- a/sos/plugins/ppp.py
+++ b/sos/plugins/ppp.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class ppp(sos.plugintools.PluginBase):
+class ppp(Plugin, RedHatPlugin):
"""ppp, wvdial and rp-pppoe related information
"""
def checkenabled(self):
diff --git a/sos/plugins/printing.py b/sos/plugins/printing.py
index 15353fe1..48c25901 100644
--- a/sos/plugins/printing.py
+++ b/sos/plugins/printing.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class printing(sos.plugintools.PluginBase):
+class printing(Plugin, RedHatPlugin):
"""printing related information (cups)
"""
optionList = [("cups", "max size (MiB) to collect per cups log file",
diff --git a/sos/plugins/process.py b/sos/plugins/process.py
index ea19be55..48ec4bd6 100644
--- a/sos/plugins/process.py
+++ b/sos/plugins/process.py
@@ -12,11 +12,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import time
import os
-class process(sos.plugintools.PluginBase):
+class process(Plugin, RedHatPlugin):
"""process information
"""
def setup(self):
diff --git a/sos/plugins/psacct.py b/sos/plugins/psacct.py
index 3862f902..3c8765a0 100644
--- a/sos/plugins/psacct.py
+++ b/sos/plugins/psacct.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class psacct(sos.plugintools.PluginBase):
+class psacct(Plugin, RedHatPlugin):
"""Process accounting related information
"""
def setup(self):
diff --git a/sos/plugins/pxe.py b/sos/plugins/pxe.py
index 018718fb..3a00d478 100644
--- a/sos/plugins/pxe.py
+++ b/sos/plugins/pxe.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class pxe(sos.plugintools.PluginBase):
+class pxe(Plugin, RedHatPlugin):
"""PXE related information
"""
@@ -23,7 +23,7 @@ class pxe(sos.plugintools.PluginBase):
def checkenabled(self):
return self.isInstalled("system-config-netboot-cmd") or exists("/usr/sbin/pxeos")
-
+
def setup(self):
self.collectExtOutput("/usr/sbin/pxeos -l")
self.addCopySpec("/etc/dhcpd.conf")
diff --git a/sos/plugins/qpidd.py b/sos/plugins/qpidd.py
index 5684806b..564da7d2 100644
--- a/sos/plugins/qpidd.py
+++ b/sos/plugins/qpidd.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class qpidd(sos.plugintools.PluginBase):
+class qpidd(Plugin, RedHatPlugin):
"""Messaging related information
"""
def checkenabled(self):
diff --git a/sos/plugins/quagga.py b/sos/plugins/quagga.py
index 22459676..013d41ab 100644
--- a/sos/plugins/quagga.py
+++ b/sos/plugins/quagga.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class quagga(sos.plugintools.PluginBase):
+class quagga(Plugin, RedHatPlugin):
"""quagga related information
"""
diff --git a/sos/plugins/radius.py b/sos/plugins/radius.py
index 8b75bf65..f9621aeb 100644
--- a/sos/plugins/radius.py
+++ b/sos/plugins/radius.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class radius(sos.plugintools.PluginBase):
+class radius(Plugin, RedHatPlugin):
"""radius related information
"""
def checkenabled(self):
diff --git a/sos/plugins/rhevm.py b/sos/plugins/rhevm.py
new file mode 100644
index 00000000..236ead49
--- /dev/null
+++ b/sos/plugins/rhevm.py
@@ -0,0 +1,23 @@
+from sos.plugins import Plugin, RedHatPlugin
+
+# Class name must be the same as file name and method names must not change
+class rhevm(Plugin, RedHatPlugin):
+ """Nogah related information"""
+
+ optionList = [("vdsmlogs", 'Directory containing all of the SOS logs from the RHEV hypervisor(s)', '', False)]
+
+ def setup(self):
+ # Copy rhevm config files.
+ self.addCopySpec("/etc/rhevm")
+ self.addCopySpec("/var/log/rhevm")
+ if self.getOption("vdsmlogs"):
+ self.addCopySpec(self.getOption("vdsmlogs"))
+
+ def postproc(self):
+ """
+ Obfuscate passwords.
+ """
+
+ self.doRegexSub("/etc/rhevm/rhevm-config/rhevm-config.properties",
+ r"Password.type=(.*)",
+ r'Password.type=********')
diff --git a/sos/plugins/rhn.py b/sos/plugins/rhn.py
index 73bb7e8a..c94d17b1 100644
--- a/sos/plugins/rhn.py
+++ b/sos/plugins/rhn.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class rhn(sos.plugintools.PluginBase):
+class rhn(Plugin, RedHatPlugin):
"""RHN Satellite related information
"""
satellite = False
diff --git a/sos/plugins/rpm.py b/sos/plugins/rpm.py
index 33529cee..511f64ca 100644
--- a/sos/plugins/rpm.py
+++ b/sos/plugins/rpm.py
@@ -12,14 +12,14 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class rpm(sos.plugintools.PluginBase):
+class rpm(Plugin, RedHatPlugin):
"""RPM information
"""
optionList = [("rpmq", "queries for package information via rpm -q", "fast", True),
("rpmva", "runs a verify on all packages", "slow", False)]
-
+
def setup(self):
self.addCopySpec("/var/log/rpmpkgs")
diff --git a/sos/plugins/s390.py b/sos/plugins/s390.py
index a5445ee7..b0b3472a 100644
--- a/sos/plugins/s390.py
+++ b/sos/plugins/s390.py
@@ -15,9 +15,9 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class s390(sos.plugintools.PluginBase):
+class s390(Plugin, RedHatPlugin):
"""s390 related information
"""
diff --git a/sos/plugins/samba.py b/sos/plugins/samba.py
index c88bb371..70273057 100644
--- a/sos/plugins/samba.py
+++ b/sos/plugins/samba.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class samba(sos.plugintools.PluginBase):
+class samba(Plugin, RedHatPlugin):
"""Samba related information
"""
def setup(self):
diff --git a/sos/plugins/sar.py b/sos/plugins/sar.py
index b017ffa9..50e6077b 100644
--- a/sos/plugins/sar.py
+++ b/sos/plugins/sar.py
@@ -12,11 +12,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os import listdir
from os.path import exists
-class sar(sos.plugintools.PluginBase):
+class sar(Plugin, RedHatPlugin):
"""Generate the sar file from /var/log/sa/saXX files
"""
def setup(self):
@@ -31,4 +31,4 @@ class sar(sos.plugintools.PluginBase):
self.collectOutputNow(sar_command, sar_filename, root_symlink=sar_filename)
def checkenabled(self):
- return exists("/var/log/sa") and os.path.exists("/usr/bin/sar")
+ return exists("/var/log/sa") and exists("/usr/bin/sar")
diff --git a/sos/plugins/selinux.py b/sos/plugins/selinux.py
index 92bfc630..0f80ed9a 100644
--- a/sos/plugins/selinux.py
+++ b/sos/plugins/selinux.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class selinux(sos.plugintools.PluginBase):
+class selinux(Plugin, RedHatPlugin):
"""selinux related information
"""
optionList = [("fixfiles", 'Print incorrect file context labels', 'slow', False)]
diff --git a/sos/plugins/sendmail.py b/sos/plugins/sendmail.py
index 0b73451b..d7672da4 100644
--- a/sos/plugins/sendmail.py
+++ b/sos/plugins/sendmail.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class sendmail(sos.plugintools.PluginBase):
+class sendmail(Plugin, RedHatPlugin):
"""sendmail information
"""
def checkenabled(self):
diff --git a/sos/plugins/smartcard.py b/sos/plugins/smartcard.py
index ce628501..2f9ee3c2 100644
--- a/sos/plugins/smartcard.py
+++ b/sos/plugins/smartcard.py
@@ -14,11 +14,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
from time import time
-class smartcard(sos.plugintools.PluginBase):
+class smartcard(Plugin, RedHatPlugin):
"""Smart Card related information
"""
diff --git a/sos/plugins/snmp.py b/sos/plugins/snmp.py
index 896c8d7b..81536f57 100644
--- a/sos/plugins/snmp.py
+++ b/sos/plugins/snmp.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class snmp(sos.plugintools.PluginBase):
+class snmp(Plugin, RedHatPlugin):
"""snmp related information
"""
def checkenabled(self):
diff --git a/sos/plugins/soundcard.py b/sos/plugins/soundcard.py
index 33a31c1f..c70729ba 100644
--- a/sos/plugins/soundcard.py
+++ b/sos/plugins/soundcard.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class soundcard(sos.plugintools.PluginBase):
+class soundcard(Plugin, RedHatPlugin):
""" Sound card information
"""
diff --git a/sos/plugins/squid.py b/sos/plugins/squid.py
index a6a8ed44..b7f58371 100644
--- a/sos/plugins/squid.py
+++ b/sos/plugins/squid.py
@@ -12,16 +12,16 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class squid(sos.plugintools.PluginBase):
+class squid(Plugin, RedHatPlugin):
"""squid related information
"""
def checkenabled(self):
self.files = [ "/etc/squid/squid.conf" ]
self.packages = [ "squid" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
self.addCopySpec("/etc/squid/squid.conf")
diff --git a/sos/plugins/ssh.py b/sos/plugins/ssh.py
index ebfb2772..9cc02345 100644
--- a/sos/plugins/ssh.py
+++ b/sos/plugins/ssh.py
@@ -14,9 +14,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class ssh(sos.plugintools.PluginBase):
+class ssh(Plugin, RedHatPlugin):
"""ssh-related information
"""
def setup(self):
diff --git a/sos/plugins/sssd.py b/sos/plugins/sssd.py
index 45e962bc..641ae916 100644
--- a/sos/plugins/sssd.py
+++ b/sos/plugins/sssd.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class sssd(sos.plugintools.PluginBase):
+class sssd(Plugin, RedHatPlugin):
"""sssd-related Diagnostic Information
"""
diff --git a/sos/plugins/startup.py b/sos/plugins/startup.py
index a8b70088..eafca6d9 100644
--- a/sos/plugins/startup.py
+++ b/sos/plugins/startup.py
@@ -12,16 +12,16 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class startup(sos.plugintools.PluginBase):
+class startup(Plugin, RedHatPlugin):
"""startup information
"""
optionList = [("servicestatus", "get a status of all running services", "slow", False)]
def setup(self):
self.addCopySpec("/etc/rc.d")
-
+
self.collectExtOutput("LC_ALL=C /sbin/chkconfig --list", root_symlink = "chkconfig")
if self.getOption('servicestatus'):
self.collectExtOutput("/sbin/service --status-all")
diff --git a/sos/plugins/system.py b/sos/plugins/system.py
index 68033475..73becd5e 100644
--- a/sos/plugins/system.py
+++ b/sos/plugins/system.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class system(sos.plugintools.PluginBase):
+class system(Plugin, RedHatPlugin):
"""core system related information
"""
def setup(self):
diff --git a/sos/plugins/systemtap.py b/sos/plugins/systemtap.py
index ff44d983..0f227a36 100644
--- a/sos/plugins/systemtap.py
+++ b/sos/plugins/systemtap.py
@@ -14,15 +14,15 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class systemtap(sos.plugintools.PluginBase):
+class systemtap(Plugin, RedHatPlugin):
"""SystemTap information
"""
def checkenabled(self):
self.files = [ "/usr/bin/stap" ]
self.packages = [ "systemtap", "systemtap-runtime" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def setup(self):
self.collectExtOutput("/usr/bin/stap -V 2")
diff --git a/sos/plugins/tftpserver.py b/sos/plugins/tftpserver.py
index 500dc535..5b563d84 100644
--- a/sos/plugins/tftpserver.py
+++ b/sos/plugins/tftpserver.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class tftpserver(sos.plugintools.PluginBase):
+class tftpserver(Plugin, RedHatPlugin):
"""tftpserver related information
"""
def checkenabled(self):
diff --git a/sos/plugins/tomcat.py b/sos/plugins/tomcat.py
index 64e0146c..382e8531 100644
--- a/sos/plugins/tomcat.py
+++ b/sos/plugins/tomcat.py
@@ -12,13 +12,13 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class tomcat(sos.plugintools.PluginBase):
+class tomcat(Plugin, RedHatPlugin):
"""Tomcat related information
"""
def checkenabled(self):
return self.isInstalled("tomcat5")
-
+
def setup(self):
self.addCopySpecs(["/etc/tomcat5", "/var/log/tomcat5"])
diff --git a/sos/plugins/udev.py b/sos/plugins/udev.py
index 7a0c4896..4a71d02d 100644
--- a/sos/plugins/udev.py
+++ b/sos/plugins/udev.py
@@ -12,9 +12,9 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
-class udev(sos.plugintools.PluginBase):
+class udev(Plugin, RedHatPlugin):
"""udev related information
"""
def setup(self):
diff --git a/sos/plugins/veritas.py b/sos/plugins/veritas.py
index 7359bbfb..95564579 100644
--- a/sos/plugins/veritas.py
+++ b/sos/plugins/veritas.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class veritas(sos.plugintools.PluginBase):
+class veritas(Plugin, RedHatPlugin):
"""Veritas related information
"""
# Information about VRTSexplorer obtained from
@@ -24,7 +24,7 @@ class veritas(sos.plugintools.PluginBase):
def checkenabled(self):
return os.path.isfile(self.getOption("script"))
-
+
def setup(self):
""" interface with vrtsexplorer to capture veritas related data """
stat, out, runtime = self.callExtProg(self.getOption("script"))
diff --git a/sos/plugins/vmware.py b/sos/plugins/vmware.py
index 074b6524..a833df2e 100644
--- a/sos/plugins/vmware.py
+++ b/sos/plugins/vmware.py
@@ -12,15 +12,15 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class vmware(sos.plugintools.PluginBase):
+class vmware(Plugin, RedHatPlugin):
"""VMWare related information
"""
def checkenabled(self):
return exists("/usr/bin/vmware")
-
+
def setup(self):
self.collectExtOutput("/usr/bin/vmware -v")
self.addCopySpecs(["/etc/vmware/locations", "/etc/vmware/config"])
diff --git a/sos/plugins/x11.py b/sos/plugins/x11.py
index 7b8d08e7..16b29d74 100644
--- a/sos/plugins/x11.py
+++ b/sos/plugins/x11.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
-class x11(sos.plugintools.PluginBase):
+class x11(Plugin, RedHatPlugin):
"""X related information
"""
def checkenabled(self):
@@ -26,6 +26,6 @@ class x11(sos.plugintools.PluginBase):
"/etc/X11",
"/var/log/Xorg.*.log",
"/var/log/XFree86.*.log",
- )
+ ])
self.addForbiddenPath("/etc/X11/X")
self.addForbiddenPath("/etc/X11/fontpath.d")
diff --git a/sos/plugins/xen.py b/sos/plugins/xen.py
index 9eeae3a7..be0f6b9c 100644
--- a/sos/plugins/xen.py
+++ b/sos/plugins/xen.py
@@ -12,12 +12,12 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
import re
from stat import *
-class xen(sos.plugintools.PluginBase):
+class xen(Plugin, RedHatPlugin):
"""Xen related information
"""
def determineXenHost(self):
@@ -79,7 +79,7 @@ class xen(sos.plugintools.PluginBase):
self.collectExtOutput("/usr/sbin/xm list --long")
self.collectExtOutput("/usr/sbin/brctl show")
self.domCollectProc()
- if self.is_running_xenstored():
+ if self.is_running_xenstored():
self.addCopySpec("/sys/hypervisor/uuid")
self.collectExtOutput("/usr/bin/xenstore-ls")
else:
diff --git a/sos/plugins/xinetd.py b/sos/plugins/xinetd.py
index 323e3814..6458cd5c 100644
--- a/sos/plugins/xinetd.py
+++ b/sos/plugins/xinetd.py
@@ -14,10 +14,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class xinetd(sos.plugintools.PluginBase):
+class xinetd(Plugin, RedHatPlugin):
"""xinetd information
"""
def checkenabled(self):
diff --git a/sos/plugins/yum.py b/sos/plugins/yum.py
index 41dc268d..0c2ad2f0 100644
--- a/sos/plugins/yum.py
+++ b/sos/plugins/yum.py
@@ -12,10 +12,10 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import sos.plugintools
+from sos.plugins import Plugin, RedHatPlugin
import os
-class yum(sos.plugintools.PluginBase):
+class yum(Plugin, RedHatPlugin):
"""yum information
"""
@@ -25,7 +25,7 @@ class yum(sos.plugintools.PluginBase):
def checkenabled(self):
self.files = [ "/etc/yum.conf" ]
self.packages = [ "yum" ]
- return sos.plugintools.PluginBase.checkenabled(self)
+ return Plugin.checkenabled(self)
def analyze(self):
# repo sanity checking
diff --git a/sos/plugintools.py b/sos/plugintools.py
deleted file mode 100644
index 8aa99600..00000000
--- a/sos/plugintools.py
+++ /dev/null
@@ -1,560 +0,0 @@
-## plugintools.py
-## This exports methods available for use by plugins for sos
-
-## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com>
-
-### 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 sos import _sos as _
-import os, os.path, sys, string, glob, re, traceback
-import shutil
-from stat import *
-from time import time
-from itertools import *
-from collections import deque
-
-class PluginException(Exception): pass
-
-class PluginBase:
- """
- Base class for plugins
- """
- def __init__(self, pluginname, commons):
- if not getattr(self, "optionList", False):
- self.optionList = deque()
-
- self.copiedFiles = deque()
- self.copiedDirs = deque()
- self.executedCommands = deque()
- self.diagnose_msgs = deque()
- self.alerts = deque()
- self.customText = ""
- self.optNames = deque()
- self.optParms = deque()
- self.piName = pluginname
- self.cInfo = commons
- self.forbiddenPaths = deque()
- self.copyPaths = deque()
- self.collectProgs = deque()
-
- self.packages = deque()
- self.files = deque()
-
- self.must_exit = False
-
- self.soslog = logging.getLogger('sos')
- self.proflog = logging.getLogger('sosprofile')
-
- # 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]})
-
- def policy(self):
- return self.cInfo["policy"]
-
- def isInstalled(self, package_name):
- '''Is the package $package_name installed?
- '''
- return (self.policy().pkgByName(package_name) != {})
-
- # 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.debug("problem at path %s (%s)" % (abspath,e))
- break
- return False
-
- def doRegexFindAll(self, regex, fname):
- ''' Return a list of all non overlapping matches in the string(s)
- '''
- try:
- return re.findall(regex, open(fname, 'r').read(), re.MULTILINE)
- except: # IOError, AttributeError, etc.
- return []
-
- # 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
- '''
- if self.cInfo['cmdlineopts'].profiler:
- start_time = time()
-
- copyProhibited = 0
- for path in self.forbiddenPaths:
- if ( srcpath.count(path) > 0 ):
- copyProhibited = 1
-
- if copyProhibited:
- return ''
-
- if not os.path.exists(srcpath):
- # self.soslog.debug("file or directory %s does not exist" % srcpath)
- return
-
- if os.path.islink(srcpath):
- # This is a symlink - We need to also copy the file that it points to
-
- # FIXME: ignore directories for now
- if os.path.isdir(srcpath):
- return
-
- link = os.readlink(srcpath)
-
- # What's the name of the symlink on the dest tree?
- dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep))
-
- if os.path.isabs(link):
- # the link was an absolute path, and will not point to the new
- # tree. We must adjust it.
- rpth = sosRelPath(os.path.dirname(dstslname), os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep)))
- else:
- # no adjustment, symlink is the relative path
- rpth = link
-
- # make sure the link doesn't already exists
- if os.path.exists(dstslname):
- # self.soslog.debug("skipping symlink creation: already exists (%s)" % dstslname)
- return
-
- # make sure the dst dir exists
- if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))):
- os.makedirs(os.path.dirname(dstslname))
-
- # self.soslog.debug("creating symlink %s -> %s" % (dstslname, rpth))
-
- try:
- os.symlink(rpth, dstslname)
- except OSError:
- # self.soslog.debug("skipping symlink creation: already exists (%s)" % dstslname)
- return
-
- self.copiedFiles.append({'srcpath':srcpath, 'dstpath':rpth, 'symlink':"yes", 'pointsto':link})
- return
-
- else: # not a symlink
- if os.path.isdir(srcpath):
- for afile in os.listdir(srcpath):
- if afile == '.' or afile == '..':
- pass
- else:
- self.doCopyFileOrDir(srcpath+'/'+afile)
- return
-
- # if we get here, it's definitely a regular file (not a symlink or dir)
-
- # self.soslog.debug("copying file %s" % srcpath)
- try:
- tdstpath, abspath = self.__copyFile(srcpath)
- except EnvironmentError:
- # self.soslog.debug("error copying file %s (already exists)" % (srcpath))
- return
- except IOError:
- # self.soslog.debug("error copying file %s (IOError)" % (srcpath))
- return
- except:
- # self.soslog.debug("error copying file %s (SOMETHING HAPPENED)" % (srcpath))
- return
-
- self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list
-
- if self.cInfo['cmdlineopts'].profiler:
- time_passed = time() - start_time
- self.proflog.debug("copied: %-75s time: %f" % (srcpath, time_passed))
-
- return abspath
-
- def __copyFile(self, src):
- """ call cp to copy a file, collect return status and output. Returns the
- destination file name.
- """
- rel_dir = os.path.dirname(src).lstrip(os.path.sep)
- new_dir = os.path.join(self.cInfo['dstroot'], rel_dir)
- new_fname = os.path.join(new_dir, os.path.basename(src))
-
- if not os.path.exists(new_fname):
- if not os.path.isdir(new_dir):
- os.makedirs(new_dir)
-
- if os.path.islink(src):
- linkto = os.readlink(src)
- os.symlink(linkto, new_fname)
- else:
- fsrc = open(src,'r')
- fdst = open(new_fname, 'w')
- shutil.copyfileobj(fsrc, fdst, -1)
- fsrc.close()
- fdst.close()
- else:
- raise PluginException('Error copying file: already exists')
-
- abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep))
- relpath = sosRelPath(self.cInfo['rptdir'], abspath)
- return (relpath, abspath)
-
- def addForbiddenPath(self, forbiddenPath):
- """Specify a path to not copy, even if it's part of a copyPaths[] entry.
- """
- # Glob case handling is such that a valid non-glob is a reduced glob
- for filespec in glob.glob(forbiddenPath):
- self.forbiddenPaths.append(filespec)
-
- def getAllOptions(self):
- """
- return a list of all options selected
- """
- return (self.optNames, self.optParms)
-
- def setOption(self, optionname, value):
- ''' set the named option to value.
- '''
- for name, parms in izip(self.optNames, self.optParms):
- if name == optionname:
- parms['enabled'] = value
- return True
- else:
- return False
-
- def isOptionEnabled(self, optionname):
- ''' Deprecated, use getOption() instead
- '''
- return self.getOption(optionname)
-
- def getOption(self, optionname):
- ''' see whether the named option is enabled.
- '''
- for name, parms in izip(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)
- """
- if not ( fname and len(fname) ):
- # self.soslog.warning("invalid file path")
- return False
- files = glob.glob(fname)
- files.sort()
- cursize = 0
- limit_reached = False
- sizelimit *= 1024 * 1024 # in MB
- for flog in files:
- cursize += os.stat(flog)[ST_SIZE]
- if sizelimit and cursize > sizelimit:
- limit_reached = True
- break
- self.addCopySpec(flog)
- # Truncate the first file (others would likely be compressed),
- # ensuring we get at least some logs
- if flog == files[0] and limit_reached:
- self.collectExtOutput("tail -c%d %s" % (sizelimit, flog),
- "tail_" + os.path.basename(flog), flog[1:] + ".tailed")
-
- def addCopySpecs(self, copyspecs):
- for copyspec in copyspecs:
- self.addCopySpec(copyspec)
-
- def addCopySpec(self, copyspec):
- """ Add a file specification (can be file, dir,or shell glob) to be
- copied into the sosreport by this module
- """
- if not (copyspec and len(copyspec)):
- # self.soslog.warning("invalid file path")
- return False
- # Glob case handling is such that a valid non-glob is a reduced glob
- for filespec in glob.glob(copyspec):
- if filespec not in self.copyPaths:
- self.copyPaths.append(filespec)
-
- def callExtProg(self, prog):
- """ Execute a command independantly of the output gathering part of
- sosreport
- """
- # pylint: disable-msg = W0612
- status, shout, runtime = sosGetCommandOutput(prog)
- return (status, shout, runtime)
-
- def collectExtOutput(self, exe, suggest_filename = None, root_symlink = None, timeout = 300):
- """
- Run a program and collect the output
- """
- self.collectProgs.append( (exe, suggest_filename, root_symlink, timeout) )
-
- def fileGrep(self, regexp, fname):
- try:
- return [l for l in open(fname).readlines() if re.match(regexp, l)]
- except: # IOError, AttributeError, etc.
- return []
-
- def mangleCommand(self, exe):
- # FIXME: this can be improved
- mangledname = re.sub(r"^/(usr/|)(bin|sbin)/", "", exe)
- mangledname = re.sub(r"[^\w\-\.\/]+", "_", mangledname)
- mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64]
- return mangledname
-
- def makeCommandFilename(self, exe):
- """ The internal function to build up a filename based on a command """
-
- outfn = self.cInfo['cmddir'] + "/" + self.piName + "/" + self.mangleCommand(exe)
-
- # check for collisions
- if os.path.exists(outfn):
- inc = 2
- while True:
- newfn = "%s_%d" % (outfn, inc)
- if not os.path.exists(newfn):
- outfn = newfn
- break
- inc +=1
-
- return outfn
-
- def collectOutputNow(self, exe, suggest_filename = None, root_symlink = False, timeout = 300):
- """ Execute a command and save the output to a file for inclusion in
- the report
- """
-
- if self.cInfo['cmdlineopts'].profiler:
- start_time = time()
-
- # pylint: disable-msg = W0612
- status, shout, runtime = sosGetCommandOutput(exe, timeout = timeout)
-
- 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))
-
- if not (status == 127 or status == 32512): # if not command_not_found
- outfd = open(outfn, "w")
- if len(shout):
- outfd.write(shout+"\n")
- outfd.close()
-
- if root_symlink:
- curdir = os.getcwd()
- os.chdir(self.cInfo['dstroot'])
- try:
- dst_from_root = outfn[len(self.cInfo['dstroot'])+1:]
- target = ("../" * string.count(dst_from_root, "/")) + dst_from_root
- os.symlink(target, root_symlink.strip("/."))
- except:
- pass
- os.chdir(curdir)
-
- outfn_strip = outfn[len(self.cInfo['cmddir'])+1:]
-
- else:
- # self.soslog.debug("could not run command: %s" % exe)
- outfn = None
- outfn_strip = None
-
- # save info for later
- self.executedCommands.append({'exe': exe, 'file':outfn_strip}) # save in our list
- self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn_strip,runtime=runtime)
-
- if self.cInfo['cmdlineopts'].profiler:
- time_passed = time() - start_time
- self.proflog.debug("output: %-75s time: %f" % (exe, time_passed))
-
- 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)
-
- # 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)
-
- 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 += text
-
- def copyStuff(self):
- """
- Collect the data for a plugin
- """
- copyMap = map(self.doCopyFileOrDir, self.copyPaths)
-
- for progs in izip(self.collectProgs):
- prog, suggest_filename, root_symlink, timeout = progs[0]
- # self.soslog.debug("collecting output of '%s'" % prog)
- try:
- self.collectOutputNow(prog, suggest_filename, root_symlink, timeout)
- except Exception, e:
- self.soslog.debug("error collection output of '%s', traceback follows:" % prog)
-
- def exit_please(self):
- """ This function tells the plugin that it should exit ASAP"""
- self.must_exit = True
-
- def get_description(self):
- """ This function will return the description for the plugin"""
- try:
- return self.__doc__.strip()
- except:
- return "<no description available>"
-
- def checkenabled(self):
- """ This function can be overidden to let the plugin decide whether
- it should run or not.
- """
- # some files or packages have been specified for this package
- if len(self.files) or len(self.packages):
- for fname in self.files:
- if os.path.exists(fname):
- return True
- for pkgname in self.packages:
- if self.isInstalled(pkgname):
- return True
- return False
-
- 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 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 = '<hr/><a name="%s"></a>\n' % self.piName
-
- # Intro
- html = html + "<h2> Plugin <em>" + self.piName + "</em></h2>\n"
-
- # Files
- if len(self.copiedFiles):
- html = html + "<p>Files copied:<br><ul>\n"
- for afile in self.copiedFiles:
- html = html + '<li><a href="%s">%s</a>' % (afile['dstpath'], afile['srcpath'])
- if (afile['symlink'] == "yes"):
- html = html + " (symlink to %s)" % afile['pointsto']
- html = html + '</li>\n'
- html = html + "</ul></p>\n"
-
- # Dirs
- if len(self.copiedDirs):
- html = html + "<p>Directories Copied:<br><ul>\n"
- for adir in self.copiedDirs:
- html = html + '<li><a href="%s">%s</a>\n' % (adir['dstpath'], adir['srcpath'])
- if (adir['symlink'] == "yes"):
- html = html + " (symlink to %s)" % adir['pointsto']
- html = html + '</li>\n'
- html = html + "</ul></p>\n"
-
- # Command Output
- if len(self.executedCommands):
- html = html + "<p>Commands Executed:<br><ul>\n"
- # convert file name to relative path from our root
- for cmd in self.executedCommands:
- if cmd["file"] and len(cmd["file"]):
- cmdOutRelPath = sosRelPath(self.cInfo['rptdir'], self.cInfo['cmddir'] + "/" + cmd['file'])
- html = html + '<li><a href="%s">%s</a></li>\n' % (cmdOutRelPath, cmd['exe'])
- else:
- html = html + '<li>%s</li>\n' % (cmd['exe'])
- html = html + "</ul></p>\n"
-
- # Alerts
- if len(self.alerts):
- html = html + "<p>Alerts:<br><ul>\n"
- for alert in self.alerts:
- html = html + '<li>%s</li>\n' % alert
- html = html + "</ul></p>\n"
-
- # Custom Text
- if (self.customText != ""):
- html = html + "<p>Additional Information:<br>\n"
- html = html + self.customText + "</p>\n"
-
- return html
-# vim:ts=4 sw=4 et
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
new file mode 100644
index 00000000..07c75f64
--- /dev/null
+++ b/sos/policies/__init__.py
@@ -0,0 +1,276 @@
+import os
+import platform
+import time
+
+from sos.utilities import ImporterHelper, import_module, get_hash_name
+from sos.plugins import IndependentPlugin
+from sos import _sos as _
+import hashlib
+
+def import_policy(name):
+ policy_fqname = "sos.policies.%s" % name
+ try:
+ return import_module(policy_fqname, Policy)
+ except ImportError:
+ return None
+
+def load(cache={}):
+ if 'policy' in cache:
+ return cache.get('policy')
+
+ import sos.policies
+ helper = ImporterHelper(sos.policies)
+ for module in helper.get_modules():
+ for policy in import_policy(module):
+ if policy.check():
+ cache['policy'] = policy()
+ return policy()
+ raise Exception("No policy could be loaded.")
+
+
+class PackageManager(object):
+
+ def allPkgsByName(self, name):
+ """
+ Return a list of packages that match name.
+ """
+ return []
+
+ def allPkgsByNameRegex(self, regex_name, flags=None):
+ """
+ Return a list of packages that match regex_name.
+ """
+ return []
+
+ def pkgByName(self, name):
+ """
+ Return a single package that matches name.
+ """
+ return None
+
+ def allPkgs(self):
+ """
+ Return a list of all packages.
+ """
+ return []
+
+
+class Policy(object):
+
+ msg = _("""This utility will collect some detailed information about the
+hardware and setup of your %(distro)s system.
+The information is collected and an archive is packaged under
+/tmp, which you can send to a support representative.
+%(distro)s will use this information for diagnostic purposes ONLY
+and it will be considered confidential information.
+
+This process may take a while to complete.
+No changes will be made to your system.
+
+""")
+
+ distro = ""
+
+ def __init__(self):
+ """Subclasses that choose to override this initializer should call
+ super() to ensure that they get the required platform bits attached.
+ super(SubClass, self).__init__()"""
+ self._parse_uname()
+ self.reportName = self.hostname
+ self.ticketNumber = None
+ self.package_manager = PackageManager()
+
+ def check(self):
+ """
+ This function is responsible for determining if the underlying system
+ is supported by this policy.
+ """
+ return False
+
+ def preferedArchive(self):
+ """
+ Return the class object of the prefered archive format for this platform
+ """
+ from sos.utilities import TarFileArchive
+ return TarFileArchive
+
+ def getArchiveName(self):
+ """
+ This function should return the filename of the archive without the
+ extension.
+ """
+ if self.ticketNumber:
+ self.reportName += "." + self.ticketNumber
+ return "sosreport-%s-%s" % (self.reportName, time.strftime("%Y%m%d%H%M%S"))
+
+ def validatePlugin(self, plugin_class):
+ """
+ Verifies that the plugin_class should execute under this policy
+ """
+ return issubclass(plugin_class, IndependentPlugin)
+
+ def preWork(self):
+ """
+ This function is called prior to collection.
+ """
+ pass
+
+ def packageResults(self, package_name):
+ """
+ This function is called prior to packaging.
+ """
+ pass
+
+ def postWork(self):
+ """
+ This function is called after the sosreport has been generated.
+ """
+ pass
+
+ def pkgByName(self, pkg):
+ return None
+
+ def _parse_uname(self):
+ (system, node, release,
+ version, machine, processor) = platform.uname()
+ self.hostname = node
+ self.release = release
+ self.smp = version.split()[1] == "SMP"
+ self.machine = machine
+
+ def setCommons(self, commons):
+ self.commons = commons
+
+ def is_root(self):
+ """This method should return true if the user calling the script is
+ considered to be a superuser"""
+ return (os.getuid() == 0)
+
+ def _create_checksum(self, final_filename=None):
+ if not final_filename:
+ return False
+
+ archive_fp = open(final_filename, 'r')
+ digest = hashlib.new(get_hash_name())
+ digest.update(archive_fp.read())
+ archive_fp.close()
+ return digest.hexdigest()
+
+
+ def getPreferredHashAlgorithm(self):
+ """Returns the string name of the hashlib-supported checksum algorithm
+ to use"""
+ return "md5"
+
+ def displayResults(self, final_filename=None):
+
+ # make sure a report exists
+ if not final_filename:
+ return False
+
+ # store checksum into file
+ fp = open(final_filename + "." + get_hash_name(), "w")
+ checksum = self._create_checksum(final_filename)
+ if checksum:
+ fp.write(checksum + "\n")
+ fp.close()
+
+ self._print()
+ self._print(_("Your sosreport has been generated and saved in:\n %s") % final_filename)
+ self._print()
+ if checksum:
+ self._print(_("The checksum is: ") + checksum)
+ self._print()
+ self._print(_("Please send this file to your support representative."))
+ self._print()
+
+ def uploadResults(self, final_filename):
+
+ # make sure a report exists
+ if not final_filename:
+ return False
+
+ self._print()
+ # make sure it's readable
+ try:
+ fp = open(final_filename, "r")
+ except:
+ return False
+
+ # read ftp URL from configuration
+ if self.commons['cmdlineopts'].upload:
+ upload_url = self.commons['cmdlineopts'].upload
+ else:
+ try:
+ upload_url = self.commons['config'].get("general", "ftp_upload_url")
+ except:
+ self._print(_("No URL defined in config file."))
+ return
+
+ from urlparse import urlparse
+ url = urlparse(upload_url)
+
+ if url[0] != "ftp":
+ self._print(_("Cannot upload to specified URL."))
+ return
+
+ # extract username and password from URL, if present
+ if url[1].find("@") > 0:
+ username, host = url[1].split("@", 1)
+ if username.find(":") > 0:
+ username, passwd = username.split(":", 1)
+ else:
+ passwd = None
+ else:
+ username, passwd, host = None, None, url[1]
+
+ # extract port, if present
+ if host.find(":") > 0:
+ host, port = host.split(":", 1)
+ port = int(port)
+ else:
+ port = 21
+
+ path = url[2]
+
+ try:
+ from ftplib import FTP
+ upload_name = os.path.basename(final_filename)
+
+ ftp = FTP()
+ ftp.connect(host, port)
+ if username and passwd:
+ ftp.login(username, passwd)
+ else:
+ ftp.login()
+ ftp.cwd(path)
+ ftp.set_pasv(True)
+ ftp.storbinary('STOR %s' % upload_name, fp)
+ ftp.quit()
+ except Exception, e:
+ self._print(_("There was a problem uploading your report to Red Hat support. " + str(e)))
+ else:
+ self._print(_("Your report was successfully uploaded to %s with name:" % (upload_url,)))
+ self._print(" " + upload_name)
+ self._print()
+ self._print(_("Please communicate this name to your support representative."))
+ self._print()
+
+ fp.close()
+
+ def _print(self, msg=None):
+ """A wrapper around print that only prints if we are not running in
+ silent mode"""
+ if not self.commons['cmdlineopts'].silent:
+ if msg:
+ print msg
+ else:
+ print
+
+
+ def get_msg(self):
+ """This method is used to prepare the preamble text to display to
+ the user in non-batch mode. If your policy sets self.distro that
+ text will be substituted accordingly. You can also override this
+ method to do something more complicated."""
+ return self.msg % {'distro': self.distro}
diff --git a/sos/policies/osx.py b/sos/policies/osx.py
new file mode 100644
index 00000000..60b7f6a9
--- /dev/null
+++ b/sos/policies/osx.py
@@ -0,0 +1,13 @@
+from sos.policies import PackageManager, Policy
+from sos.utilities import shell_out
+
+class OSXPolicy(Policy):
+
+ distro = "Mac OS X"
+
+ @classmethod
+ def check(class_):
+ try:
+ return "Mac OS X" in shell_out("sw_vers")
+ except Exception, e:
+ return False
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
new file mode 100644
index 00000000..cfd31ef2
--- /dev/null
+++ b/sos/policies/redhat.py
@@ -0,0 +1,229 @@
+## Implement policies required for the sos system support tool
+
+## Copyright (C) Steve Conklin <sconklin@redhat.com>
+
+### 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.
+
+# This enables the use of with syntax in python 2.5 (e.g. jython)
+from __future__ import with_statement
+
+import os
+import sys
+from tempfile import gettempdir
+import random
+import re
+import platform
+import time
+from collections import deque
+
+from sos import _sos as _
+from sos.plugins import RedHatPlugin, IndependentPlugin
+from sos.policies import Policy, PackageManager
+from sos.utilities import shell_out
+
+sys.path.insert(0, "/usr/share/rhn/")
+try:
+ from up2date_client import up2dateAuth
+ from up2date_client import config
+ from rhn import rpclib
+except:
+ # might fail if non-RHEL
+ pass
+
+
+class RHELPackageManager(PackageManager):
+
+ def _get_rpm_list(self):
+ pkg_list = shell_out(["rpm",
+ "-qa",
+ "--queryformat",
+ "%{NAME}|%{VERSION}\\n"]).splitlines()
+ self._rpms = {}
+ for pkg in pkg_list:
+ name, version = pkg.split("|")
+ self._rpms[name] = {
+ 'name': name,
+ 'version': version
+ }
+
+ def allPkgsByName(self, name):
+ return fnmatch.filter(self.allPkgs().keys(), name)
+
+ def allPkgsByNameRegex(self, regex_name, flags=None):
+ reg = re.compile(regex_name, flags)
+ return [pkg for pkg in self.allPkgs().keys() if reg.match(pkg)]
+
+ def pkgByName(self, name):
+ try:
+ self.AllPkgsByName(name)[-1]
+ except Exception:
+ return None
+
+ def allPkgs(self):
+ if not self._rpms:
+ self._rpms = self._get_rpm_list()
+ return self._rpms
+
+ def pkgNVRA(self, pkg):
+ fields = pkg.split("-")
+ version, release, arch = fields[-3:]
+ name = "-".join(fields[:-3])
+ return (name, version, release, arch)
+
+
+class RHELPolicy(Policy):
+
+ def __init__(self):
+ super(RHELPolicy, self).__init__()
+ self.reportName = ""
+ self.ticketNumber = ""
+ self.package_manager = RHELPackageManager()
+
+ def validatePlugin(self, plugin_class):
+ "Checks that the plugin will execute given the environment"
+ return issubclass(plugin_class, RedHatPlugin) or issubclass(plugin_class, IndependentPlugin)
+
+ @classmethod
+ def check(self):
+ "This method checks to see if we are running on RHEL. It returns True or False."
+ return os.path.isfile('/etc/redhat-release')
+
+ def preferedArchive(self):
+ from sos.utilities import TarFileArchive
+ return TarFileArchive
+
+ def getPreferredHashAlgorithm(self):
+ checksum = "md5"
+ try:
+ fp = open("/proc/sys/crypto/fips_enabled", "r")
+ except:
+ return checksum
+
+ fips_enabled = fp.read()
+ if fips_enabled.find("1") >= 0:
+ checksum = "sha256"
+ fp.close()
+ return checksum
+
+ def pkgByName(self, name):
+ return self.package_manager.pkgByName(name)
+
+ def runlevelByService(self, name):
+ from subprocess import Popen, PIPE
+ ret = []
+ p = Popen("LC_ALL=C /sbin/chkconfig --list %s" % name,
+ shell=True,
+ stdout=PIPE,
+ stderr=PIPE,
+ bufsize=-1)
+ out, err = p.communicate()
+ if err:
+ return ret
+ for tabs in out.split()[1:]:
+ try:
+ (runlevel, onoff) = tabs.split(":", 1)
+ except:
+ pass
+ else:
+ if onoff == "on":
+ ret.append(int(runlevel))
+ return ret
+
+ def runlevelDefault(self):
+ try:
+ with open("/etc/inittab") as fp:
+ pattern = r"id:(\d{1}):initdefault:"
+ text = fp.read()
+ return int(re.findall(pattern, text)[0])
+ except:
+ return 3
+
+ def kernelVersion(self):
+ return self.release
+
+ def hostName(self):
+ return self.hostname
+
+ def rhelVersion(self):
+ try:
+ pkg = self.pkgByName("redhat-release") or \
+ self.allPkgsByNameRegex("redhat-release-.*")[-1]
+ pkgname = pkg["version"]
+ if pkgname[0] == "4":
+ return 4
+ elif pkgname in [ "5Server", "5Client" ]:
+ return 5
+ elif pkgname[0] == "6":
+ return 6
+ except:
+ pass
+ return False
+
+ def rhnUsername(self):
+ try:
+ cfg = config.initUp2dateConfig()
+
+ return rpclib.xmlrpclib.loads(up2dateAuth.getSystemId())[0][0]['username']
+ except:
+ # ignore any exception and return an empty username
+ return ""
+
+ def isKernelSMP(self):
+ return self.smp
+
+ def getArch(self):
+ return self.machine
+
+ def preWork(self):
+ # this method will be called before the gathering begins
+
+ localname = self.rhnUsername()
+ if len(localname) == 0: localname = self.hostName()
+
+ if not self.commons['cmdlineopts'].batch and not self.commons['cmdlineopts'].silent:
+ try:
+ self.reportName = raw_input(_("Please enter your first initial and last name [%s]: ") % localname)
+ self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
+
+ self.ticketNumber = raw_input(_("Please enter the case number that you are generating this report for: "))
+ self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
+ self._print()
+ except:
+ self._print()
+ sys.exit(0)
+
+ if len(self.reportName) == 0:
+ self.reportName = localname
+
+ if self.commons['cmdlineopts'].customerName:
+ self.reportName = self.commons['cmdlineopts'].customerName
+ self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
+
+ if self.commons['cmdlineopts'].ticketNumber:
+ self.ticketNumber = self.commons['cmdlineopts'].ticketNumber
+ self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
+
+ return
+
+ def packageResults(self, archive_filename):
+ self._print(_("Creating compressed archive..."))
+
+ def get_msg(self):
+ msg_dict = {"distro": "Red Hat Enterprise Linux"}
+ if os.path.isfile('/etc/fedora-release'):
+ msg_dict['distro'] = 'Fedora'
+ return self.msg % msg_dict
+
+# vim: ts=4 sw=4 et
diff --git a/sos/policies/windows.py b/sos/policies/windows.py
new file mode 100644
index 00000000..64e780bf
--- /dev/null
+++ b/sos/policies/windows.py
@@ -0,0 +1,41 @@
+### 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 time
+
+from sos.policies import PackageManager, Policy
+from sos.utilities import shell_out
+
+class WindowsPolicy(Policy):
+
+ distro = "Microsoft Windows"
+
+ @classmethod
+ def check(class_):
+ try:
+ return "Windows" in shell_out("ver")
+ except Exception, e:
+ return False
+
+ def is_root(self):
+ if "S-1-16-12288" in shell_out("whoami /groups"):
+ return True
+ else:
+ admins = shell_out("net localgroup administrators")
+ username = shell_out("echo %USERNAME%")
+ return username.strip() in admins
+
+ def preferedArchive(self):
+ from sos.utilities import ZipFileArchive
+ return ZipFileArchive
diff --git a/sos/policyredhat.py b/sos/policyredhat.py
deleted file mode 100755
index 863445ae..00000000
--- a/sos/policyredhat.py
+++ /dev/null
@@ -1,411 +0,0 @@
-## policy-redhat.py
-## Implement policies required for the sos system support tool
-
-## Copyright (C) Steve Conklin <sconklin@redhat.com>
-
-### 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 sys
-import string
-from tempfile import gettempdir
-from sos.helpers import *
-import random
-import re
-try:
- from hashlib import md5
-except ImportError:
- from md5 import md5
-import rpm
-import time
-from subprocess import Popen, PIPE
-from collections import deque
-from sos import _sos as _
-
-sys.path.insert(0, "/usr/share/rhn/")
-try:
- from up2date_client import up2dateAuth
- from up2date_client import config
- from rhn import rpclib
-except:
- # might fail if non-RHEL
- pass
-
-#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)
-
-def memoized(function):
- ''' function decorator to allow caching of return values
- '''
- function.cache={}
- def f(*args):
- try:
- return function.cache[args]
- except KeyError:
- result = function.cache[args] = function(*args)
- return result
- return f
-
-class SosPolicy:
- "This class implements various policies for sos"
- def __init__(self):
- self.report_file = ""
- self.report_file_ext = ""
- self.report_md5 = ""
- self.reportName = ""
- self.ticketNumber = ""
-
- 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 pkgProvides(self, name):
- return self.pkgByName(name).get('providename')
-
- def pkgRequires(self, name):
- return self.pkgByName(name).get('requirename')
-
- def allPkgsByName(self, name):
- return self.allPkgs("name", name)
-
- def allPkgsByNameRegex(self, regex_name):
- reg = re.compile(regex_name)
- return [pkg for pkg in self.allPkgs() if reg.match(pkg['name'])]
-
- def pkgByName(self, name):
- # TODO: do a full NEVRA compare and return newest version, best arch
- try:
- # lame attempt at locating newest
- return self.allPkgsByName(name)[-1]
- except:
- pass
- return {}
-
- def allPkgs(self, ds = None, value = None):
- # if possible return the cached values
- try: return self._cache_rpm[ "%s-%s" % (ds,value) ]
- except AttributeError: self._cache_rpm = {}
- except KeyError: pass
-
- ts = rpm.TransactionSet()
- if ds and value:
- mi = ts.dbMatch(ds, value)
- else:
- mi = ts.dbMatch()
-
- self._cache_rpm[ "%s-%s" % (ds,value) ] = [pkg for pkg in mi]
- del mi, ts
- return self._cache_rpm[ "%s-%s" % (ds,value) ]
-
- def runlevelByService(self, name):
- ret = []
- p = Popen("LC_ALL=C /sbin/chkconfig --list %s" % name, shell=True, stdout=PIPE, stderr=PIPE, bufsize=-1)
- out, err = p.communicate()
- if err:
- return ret
- for tabs in out.split()[1:]:
- try:
- (runlevel, onoff) = tabs.split(":", 1)
- except:
- pass
- else:
- if onoff == "on":
- ret.append(int(runlevel))
- return ret
-
- def runlevelDefault(self):
- try:
- reg=self.doRegexFindAll(r"^id:(\d{1}):initdefault:", "/etc/inittab")
- for initlevel in reg:
- return initlevel
- except:
- return 3
-
- def kernelVersion(self):
- return Popen("/bin/uname -r", shell=True, stdout=PIPE, bufsize=-1).stdout.read().strip("\n")
-
- def hostName(self):
- return Popen("/bin/hostname", shell=True, stdout=PIPE, bufsize=-1).stdout.read().strip("\n").split(".")[0]
-
- def rhelVersion(self):
- try:
- pkg = self.pkgByName("redhat-release") or \
- self.allPkgsByNameRegex("redhat-release-.*")[-1]
- pkgname = pkg["version"]
- if pkgname[0] == "4":
- return 4
- elif pkgname in [ "5Server", "5Client" ]:
- return 5
- elif pkgname[0] == "6":
- return 6
- except: pass
- return False
-
- def rhnUsername(self):
- try:
- cfg = config.initUp2dateConfig()
-
- return rpclib.xmlrpclib.loads(up2dateAuth.getSystemId())[0][0]['username']
- except:
- # ignore any exception and return an empty username
- return ""
-
- def isKernelSMP(self):
- pipe = Popen("/bin/hostname", shell=True, stdout=PIPE, bufsize=-1).read().stdout
- if pipe.split()[1] == "SMP":
- return True
- else:
- return False
-
- def getArch(self):
- return Popen("/bin/uname -m", shell=True, stdout=PIPE, bufsize=-1).stdout.read().strip()
-
- def pkgNVRA(self, pkg):
- fields = pkg.split("-")
- version, release, arch = fields[-3:]
- name = "-".join(fields[:-3])
- return (name, version, release, arch)
-
- def getDstroot(self, tmpdir='/tmp'):
- """Find a temp directory to form the root for our gathered information
- and reports.
- """
- uniqname = "%s-%s" % (self.hostName(), time.strftime("%Y%m%d%H%M%s"))
- dstroot = os.path.join(os.path.abspath(tmpdir),uniqname)
- try:
- os.makedirs(dstroot, 0700)
- except:
- return False
- return dstroot
-
- def preWork(self):
- # this method will be called before the gathering begins
-
- localname = self.rhnUsername()
- if len(localname) == 0: localname = self.hostName()
-
- if not self.cInfo['cmdlineopts'].batch:
- try:
- self.reportName = raw_input(_("Please enter your first initial and last name [%s]: ") % localname)
- self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
-
- self.ticketNumber = raw_input(_("Please enter the case number that you are generating this report for: "))
- self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
- print
- except:
- print
- sys.exit(0)
-
- if len(self.reportName) == 0:
- self.reportName = localname
-
- if self.cInfo['cmdlineopts'].customerName:
- self.reportName = self.cInfo['cmdlineopts'].customerName
- self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
-
- if self.cInfo['cmdlineopts'].ticketNumber:
- self.ticketNumber = self.cInfo['cmdlineopts'].ticketNumber
- self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
-
- return
-
- def renameResults(self, newName):
- newName = os.path.join(os.path.dirname(self.cInfo['dstroot']), newName)
- if len(self.report_file) and os.path.isfile(self.report_file):
- try:
- os.rename(self.report_file, newName)
- except:
- return False
- self.report_file = newName
-
- def packageResults(self):
-
- if len(self.ticketNumber):
- self.reportName = self.reportName + "." + self.ticketNumber
- else:
- self.reportName = self.reportName
-
- curwd = os.getcwd()
- os.chdir(os.path.dirname(self.cInfo['dstroot']))
- oldmask = os.umask(077)
-
- print _("Creating compressed archive...")
-
- if os.path.isfile("/usr/bin/xz"):
- self.report_file_ext = "tar.xz"
- self.renameResults("sosreport-%s-%s.%s" % (self.reportName, time.strftime("%Y%m%d%H%M%S"), self.report_file_ext))
- cmd = "/bin/tar -c %s | /usr/bin/xz -1 > %s" % (os.path.basename(self.cInfo['dstroot']),self.report_file)
- p = Popen(cmd, shell=True, bufsize=-1)
- sts = os.waitpid(p.pid, 0)[1]
- else:
- self.report_file_ext = "tar.bz2"
- self.renameResults("sosreport-%s-%s.%s" % (self.reportName, time.strftime("%Y%m%d%H%M%S"), self.report_file_ext))
- tarcmd = "/bin/tar -jcf %s %s" % (self.report_file, os.path.basename(self.cInfo['dstroot']))
- p = Popen(tarcmd, shell=True, stdout=PIPE, stderr=PIPE, bufsize=-1)
- output = p.communicate()[0]
-
- os.umask(oldmask)
- os.chdir(curwd)
- return
-
- def cleanDstroot(self):
- if not os.path.isdir(os.path.join(self.cInfo['dstroot'],"sos_commands")):
- # doesn't look like a dstroot, refusing to clean
- return False
- os.system("/bin/rm -rf %s" % self.cInfo['dstroot'])
-
- def encryptResults(self):
- # make sure a report exists
- if not self.report_file:
- return False
-
- print _("Encrypting archive...")
- gpgname = self.report_file + ".gpg"
-
- try:
- keyring = self.cInfo['config'].get("general", "gpg_keyring")
- except:
- keyring = "/usr/share/sos/rhsupport.pub"
-
- try:
- recipient = self.cInfo['config'].get("general", "gpg_recipient")
- except:
- recipient = "support@redhat.com"
-
- p = Popen("""/usr/bin/gpg --trust-model always --batch --keyring "%s" --no-default-keyring --compress-level 0 --encrypt --recipient "%s" --output "%s" "%s" """ % (keyring, recipient, gpgname, self.report_file),
- shell=True, stdout=PIPE, stderr=PIPE, bufsize=-1)
- stdout, stderr = p.communicate()
- if p.returncode == 0:
- os.unlink(self.report_file)
- self.report_file = gpgname
- else:
- print _("There was a problem encrypting your report.")
- sys.exit(1)
-
- def displayResults(self):
- # make sure a report exists
- if not self.report_file:
- return False
-
- # calculate md5
- fp = open(self.report_file, "r")
- self.report_md5 = md5(fp.read()).hexdigest()
- fp.close()
-
- self.renameResults("sosreport-%s-%s-%s.%s" % (self.reportName,
- time.strftime("%Y%m%d%H%M%S"),
- self.report_md5[-4:],
- self.report_file_ext))
-
- # store md5 into file
- fp = open(self.report_file + ".md5", "w")
- fp.write(self.report_md5 + "\n")
- fp.close()
-
- print
- print _("Your sosreport has been generated and saved in:\n %s") % self.report_file
- print
- if len(self.report_md5):
- print _("The md5sum is: ") + self.report_md5
- print
- print _("Please send this file to your support representative.")
- print
-
- def uploadResults(self):
- # make sure a report exists
- if not self.report_file:
- return False
-
- print
- # make sure it's readable
- try:
- fp = open(self.report_file, "r")
- except:
- return False
-
- # read ftp URL from configuration
- if self.cInfo['cmdlineopts'].upload:
- upload_url = self.cInfo['cmdlineopts'].upload
- else:
- try:
- upload_url = self.cInfo['config'].get("general", "ftp_upload_url")
- except:
- print _("No URL defined in config file.")
- return
-
- from urlparse import urlparse
- url = urlparse(upload_url)
-
- if url[0] != "ftp":
- print _("Cannot upload to specified URL.")
- return
-
- # extract username and password from URL, if present
- if url[1].find("@") > 0:
- username, host = url[1].split("@", 1)
- if username.find(":") > 0:
- username, passwd = username.split(":", 1)
- else:
- passwd = None
- else:
- username, passwd, host = None, None, url[1]
-
- # extract port, if present
- if host.find(":") > 0:
- host, port = host.split(":", 1)
- port = int(port)
- else:
- port = 21
-
- path = url[2]
-
- try:
- from ftplib import FTP
- upload_name = os.path.basename(self.report_file)
-
- ftp = FTP()
- ftp.connect(host, port)
- if username and passwd:
- ftp.login(username, passwd)
- else:
- ftp.login()
- ftp.cwd(path)
- ftp.set_pasv(True)
- ftp.storbinary('STOR %s' % upload_name, fp)
- ftp.quit()
- except:
- print _("There was a problem uploading your report to Red Hat support.")
- else:
- print _("Your report was successfully uploaded to %s with name:" % (upload_url,))
- print " " + upload_name
- print
- print _("Please communicate this name to your support representative.")
- print
-
- fp.close()
-
-# vim: ts=4 sw=4 et
diff --git a/sos/reporting.py b/sos/reporting.py
new file mode 100644
index 00000000..bf5addfa
--- /dev/null
+++ b/sos/reporting.py
@@ -0,0 +1,131 @@
+"""This provides a restricted tag language to define the sosreport index/report"""
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+
+class Node(object):
+
+ def __str__(self):
+ return json.dumps(self.data)
+
+ def can_add(self, node):
+ return False
+
+class Leaf(Node):
+ """Marker class that can be added to a Section node"""
+ pass
+
+
+class Report(Node):
+ """The root element of a report. This is a container for sections."""
+
+ def __init__(self):
+ self.data = {}
+
+ def can_add(self, node):
+ return isinstance(node, Section)
+
+ def add(self, *nodes):
+ for node in nodes:
+ if self.can_add(node):
+ self.data[node.name] = node.data
+
+
+class Section(Node):
+ """A section is a container for leaf elements. Sections may be nested
+ inside of Report objects only."""
+
+ def __init__(self, name):
+ self.name = name
+ self.data = {}
+
+ def can_add(self, node):
+ return isinstance(node, Leaf)
+
+ def add(self, *nodes):
+ for node in nodes:
+ if self.can_add(node):
+ self.data.setdefault(node.ADDS_TO, []).append(node.data)
+
+
+class Command(Leaf):
+
+ ADDS_TO = "commands"
+
+ def __init__(self, name, return_code, href):
+ self.data = {"name": name,
+ "return_code": return_code,
+ "href": href}
+
+
+class CopiedFile(Leaf):
+
+ ADDS_TO = "copied_files"
+
+ def __init__(self, name, href):
+ self.data = {"name": name,
+ "href": href}
+
+
+class CreatedFile(Leaf):
+
+ ADDS_TO = "created_files"
+
+ def __init__(self, name):
+ self.data = {"name": name}
+
+
+class Alert(Leaf):
+
+ ADDS_TO = "alerts"
+
+ def __init__(self, content):
+ self.data = content
+
+
+class Note(Leaf):
+
+ ADDS_TO = "notes"
+
+ def __init__(self, content):
+ self.data = content
+
+
+class PlainTextReport(object):
+ """Will generate a plain text report from a top_level Report object"""
+
+ LEAF = " * %(name)s"
+ ALERT = " ! %s"
+ NOTE = " * %s"
+ DIVIDER = "=" * 72
+
+ subsections = (
+ (Command, LEAF, "- commands executed:"),
+ (CopiedFile, LEAF, "- files copied:"),
+ (CreatedFile, LEAF, "- files created:"),
+ (Alert, ALERT, "- alerts:"),
+ (Note, NOTE, "- notes:"),
+ )
+
+ buf = []
+
+ def __init__(self, report_node):
+ self.report_node = report_node
+
+ def __str__(self):
+ self.buf = buf = []
+ for section_name, section_contents in sorted(self.report_node.data.iteritems()):
+ buf.append(section_name + "\n" + self.DIVIDER)
+ for type_, format_, header in self.subsections:
+ self.process_subsection(section_contents, type_.ADDS_TO, header, format_)
+
+ return "\n".join(buf)
+
+ def process_subsection(self, section, key, header, format_):
+ if key in section:
+ self.buf.append(header)
+ for item in section.get(key):
+ self.buf.append(format_ % item)
diff --git a/sos/sosreport.py b/sos/sosreport.py
index 45103ec9..08f11fd6 100755..100644
--- a/sos/sosreport.py
+++ b/sos/sosreport.py
@@ -32,76 +32,52 @@ supplied for application-specific information
# pylint: disable-msg = R0904
# pylint: disable-msg = R0903
-import sys, traceback
+import sys
+import traceback
import os
import logging
from optparse import OptionParser, Option
import ConfigParser
-import sos.policyredhat
-from sos.helpers import importPlugin
-import signal
+from sos.plugins import import_plugin
+from sos.utilities import ImporterHelper
from stat import ST_UID, ST_GID, ST_MODE, ST_CTIME, ST_ATIME, ST_MTIME, S_IMODE
from time import strftime, localtime
from collections import deque
from itertools import izip
+import textwrap
+import tempfile
from sos import _sos as _
from sos import __version__
+import sos.policies
+from sos.utilities import TarFileArchive, ZipFileArchive, compress
+from sos.reporting import Report, Section, Command, CopiedFile, CreatedFile, Alert, Note, PlainTextReport
-if os.path.isfile('/etc/fedora-release'):
- __distro__ = 'Fedora'
-else:
- __distro__ = 'Red Hat Enterprise Linux'
+class TempFileUtil(object):
-class GlobalVars:
- """ Generic container for shared vars """
- def __init__(self):
- pass
-
-## Set up routines to be linked to signals for termination handling
-def exittermhandler(signum, frame):
- """ Handle signals cleanly """
- del frame, signum
- doExitCode()
-
-def doExitCode():
- """ Exit with return """
-
- for plugname, plug in GlobalVars.loadedplugins:
- plug.exit_please()
- del plugname
-
- print "All processes ended, cleaning up."
- doExit(1)
-
-def doExit(error=0):
- """ Exit with return """
- # We will attempt to clean dstroot; there is only
- # one instance where the policy is not set and that is
- # during the actual creation of dstroot
- try:
- GlobalVars.policy.cleanDstroot()
- except AttributeError:
- sys.exit(error)
- sys.exit(error)
-
-def doException(etype, eval, etrace):
- """ Wrap exception in debugger if not in tty """
- if hasattr(sys, 'ps1') or not sys.stderr.isatty():
- # we are in interactive mode or we don't have a tty-like
- # device, so we call the default hook
- sys.__excepthook__(etype, eval, etrace)
- else:
- import traceback, pdb
- # we are NOT in interactive mode, print the exception...
- traceback.print_exception(etype, eval, etrace, limit=2, file=sys.stdout)
- print
- # ...then start the debugger in post-mortem mode.
- pdb.pm()
+ def __init__(self, tmp_dir):
+ self.tmp_dir = tmp_dir
+ self.files = []
+
+ def new(self):
+ fd, fname = tempfile.mkstemp(dir=self.tmp_dir)
+ fobj = open(fname, 'w')
+ self.files.append((fname, fobj))
+ return fobj
+
+ def clean(self):
+ for fname, f in self.files:
+ try:
+ f.flush()
+ f.close()
+ except Exception, e:
+ pass
+ try:
+ os.unlink(fname)
+ except Exception, e:
+ pass
+ self.files = []
-# Handle any sort of exit signal cleanly
-# Currently, we intercept only sig 15 (TERM)
-signal.signal(signal.SIGTERM, exittermhandler)
class OptionParserExtended(OptionParser):
""" Show examples """
@@ -117,9 +93,8 @@ class OptionParserExtended(OptionParser):
print " disable memory and samba plugins, turn off rpm -Va collection:"
print " # sosreport -n memory,samba -k rpm.rpmva=off"
print
- del out
-class SosOption (Option):
+class SosOption(Option):
"""Allow to specify comma delimited list of plugins"""
ACTIONS = Option.ACTIONS + ("extend",)
STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
@@ -137,92 +112,9 @@ class SosOption (Option):
else:
Option.take_action(self, action, dest, opt, value, values, parser)
-def parse_options(opts):
- """ Parse command line options """
-
- __cmdParser__ = OptionParserExtended(option_class=SosOption)
- __cmdParser__.add_option("-l", "--list-plugins", action="store_true", \
- dest="listPlugins", default=False, \
- help="list plugins and available plugin options")
- __cmdParser__.add_option("-n", "--skip-plugins", action="extend", \
- dest="noplugins", type="string", \
- help="skip these plugins", default = deque())
- __cmdParser__.add_option("-e", "--enable-plugins", action="extend", \
- dest="enableplugins", type="string", \
- help="enable these plugins", default = deque())
- __cmdParser__.add_option("-o", "--only-plugins", action="extend", \
- dest="onlyplugins", type="string", \
- help="enable these plugins only", default = deque())
- __cmdParser__.add_option("-k", action="extend", \
- dest="plugopts", type="string", \
- help="plugin options in plugname.option=value format (see -l)")
- __cmdParser__.add_option("-a", "--alloptions", action="store_true", \
- dest="usealloptions", default=False, \
- help="enable all options for loaded plugins")
- __cmdParser__.add_option("-u", "--upload", action="store", \
- dest="upload", default=False, \
- help="upload the report to an ftp server")
- #__cmdParser__.add_option("--encrypt", action="store_true", \
- # dest="encrypt", default=False, \
- # help="encrypt with GPG using Red Hat support's public key")
- __cmdParser__.add_option("--batch", action="store_true", \
- dest="batch", default=False, \
- help="do not ask any question (batch mode)")
- __cmdParser__.add_option("--build", action="store_true", \
- dest="build", default=False, \
- help="keep sos tree available and dont package results")
- __cmdParser__.add_option("--no-colors", action="store_true", \
- dest="nocolors", default=False, \
- help="do not use terminal colors for text")
- __cmdParser__.add_option("-v", "--verbose", action="count", \
- dest="verbosity", \
- help="increase verbosity")
- __cmdParser__.add_option("--debug", action="count", \
- dest="debug", \
- help="enabling debugging through python debugger")
- __cmdParser__.add_option("--ticket-number", action="store", \
- dest="ticketNumber", \
- help="set ticket number")
- __cmdParser__.add_option("--name", action="store", \
- dest="customerName", \
- help="define customer name")
- __cmdParser__.add_option("--config-file", action="store", \
- dest="config_file", \
- help="specify alternate configuration file")
- __cmdParser__.add_option("--tmp-dir", action="store", \
- dest="tmp_dir", \
- help="specify alternate temporary directory", default="/tmp")
- __cmdParser__.add_option("--diagnose", action="store_true", \
- dest="diagnose", \
- help="enable diagnostics", default=False)
- __cmdParser__.add_option("--analyze", action="store_true", \
- dest="analyze", \
- help="enable analyzations", default=False)
- __cmdParser__.add_option("--report", action="store_true", \
- dest="report", \
- help="Enable html/xml reporting", default=False)
- __cmdParser__.add_option("--profile", action="store_true", \
- dest="profiler", \
- help="turn on profiling", default=False)
-
- (GlobalVars.__cmdLineOpts__, GlobalVars.__cmdLineArgs__) = __cmdParser__.parse_args(opts)
-
-def textcolor(text, color, raw=0):
- """ Terminal text coloring function """
- if GlobalVars.__cmdLineOpts__.nocolors or not sys.stdout.isatty():
- return text
- 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[color] + closecol
- del raw
- return "%s%s%s" % (f, text, clear)
-
-class XmlReport:
+
+
+class XmlReport(object):
""" Report build class """
def __init__(self):
try:
@@ -290,474 +182,550 @@ class XmlReport:
if not self.enabled:
return
- print self.doc.serialize(None, 1)
+ self.ui_log.info(self.doc.serialize(None, 1))
def serialize_to_file(self, fname):
""" Serializes to file """
if not self.enabled:
return
- outfn = open(fname,"w")
- outfn.write(self.doc.serialize(None, 1))
- outfn.close()
-
-# if debugging is enabled, allow plugins to raise exceptions
-def isDebug():
- """ Enable plugin to raise exception """
- if GlobalVars.__cmdLineOpts__.debug:
- sys.excepthook = doException
- GlobalVars.__raisePlugins__ = 1
- else:
- GlobalVars.__raisePlugins__ = 0
-
-def sosreport(opts):
- """
- This is the top-level function that gathers
- and processes all sosreport information
- """
- parse_options(opts)
- # check debug
- isDebug()
-
- config = ConfigParser.ConfigParser()
- if GlobalVars.__cmdLineOpts__.config_file:
- config_file = GlobalVars.__cmdLineOpts__.config_file
- else:
- config_file = '/etc/sos.conf'
- try:
- config.readfp(open(config_file))
- except IOError:
- pass
-
- GlobalVars.loadedplugins = deque()
- skippedplugins = deque()
- alloptions = deque()
-
- # perhaps we should automatically locate the policy module??
- GlobalVars.policy = sos.policyredhat.SosPolicy()
-
- # find the plugins path
- paths = sys.path
- for path in paths:
- if path.strip()[-len("site-packages"):] == "site-packages" \
- and os.path.isdir(path + "/sos/plugins"):
- pluginpath = path + "/sos/plugins"
-
- # Set up common info and create destinations
-
- GlobalVars.dstroot = GlobalVars.policy.getDstroot(GlobalVars.__cmdLineOpts__.tmp_dir)
- if not GlobalVars.dstroot:
- print _("Could not create temporary directory.")
- doExit()
-
- cmddir = os.path.join(GlobalVars.dstroot, "sos_commands")
- logdir = os.path.join(GlobalVars.dstroot, "sos_logs")
- rptdir = os.path.join(GlobalVars.dstroot, "sos_reports")
- os.mkdir(cmddir, 0755)
- os.mkdir(logdir, 0755)
- os.mkdir(rptdir, 0755)
-
- # initialize logging
- soslog = logging.getLogger('sos')
- soslog.setLevel(logging.DEBUG)
-
- 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")
-
- if GlobalVars.__cmdLineOpts__.profiler:
- proflog = logging.getLogger('sosprofile')
- proflog.setLevel(logging.DEBUG)
-
- # if stdin is not a tty, disable colors and don't ask questions
- if not sys.stdin.isatty():
- GlobalVars.__cmdLineOpts__.nocolors = True
- GlobalVars.__cmdLineOpts__.batch = True
-
- # log to a file
- flog = logging.FileHandler(logdir + "/sos.log")
- flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
- flog.setLevel(logging.VERBOSE3)
- soslog.addHandler(flog)
-
- if GlobalVars.__cmdLineOpts__.profiler:
- # setup profile log
- plog = logging.FileHandler(logdir + "/sosprofile.log")
- plog.setFormatter(logging.Formatter('%(message)s'))
- plog.setLevel(logging.DEBUG)
- proflog.addHandler(plog)
-
- # define a Handler which writes INFO messages or higher to the sys.stderr
- console = logging.StreamHandler(sys.stderr)
- if GlobalVars.__cmdLineOpts__.verbosity > 0:
- console.setLevel(20 - GlobalVars.__cmdLineOpts__.verbosity)
- else:
- console.setLevel(logging.INFO)
- console.setFormatter(logging.Formatter('%(message)s'))
- soslog.addHandler(console)
-
- xmlrep = XmlReport()
-
- # set up dict so everyone can share the following
- commons = {'dstroot': GlobalVars.dstroot, 'cmddir': cmddir, 'logdir': logdir, 'rptdir': rptdir,
- 'soslog': soslog, 'policy': GlobalVars.policy, 'verbosity' : GlobalVars.__cmdLineOpts__.verbosity,
- 'xmlreport' : xmlrep, 'cmdlineopts':GlobalVars.__cmdLineOpts__, 'config':config }
-
- # Make policy aware of the commons
- GlobalVars.policy.setCommons(commons)
-
- print
- print _("sosreport (version %s)" % (__version__,))
- print
-
- # disable plugins that we read from conf files
- conf_disable_plugins_list = deque()
- conf_disable_plugins = None
- if config.has_option("plugins", "disable"):
- conf_disable_plugins = config.get("plugins", "disable").split(',')
- for item in conf_disable_plugins:
- conf_disable_plugins_list.append(item.strip())
-
- # generate list of available plugins
- plugins = os.listdir(pluginpath)
- plugins.sort()
- plugin_names = deque()
-
- # validate and load plugins
- for plug in plugins:
- plugbase = plug[:-3]
- if not plug[-3:] == '.py' or plugbase == "__init__":
- continue
- try:
- if GlobalVars.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
- # plug-in is valid, let's decide whether run it or not
- plugin_names.append(plugbase)
- if plugbase in GlobalVars.__cmdLineOpts__.noplugins or \
- plugbase in conf_disable_plugins_list:
- # skipped
- skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
- continue
- if not pluginClass(plugbase, commons).checkenabled() and \
- not plugbase in GlobalVars.__cmdLineOpts__.enableplugins and \
- not plugbase in GlobalVars.__cmdLineOpts__.onlyplugins:
- # inactive
- skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
- continue
- if not pluginClass(plugbase, commons).defaultenabled() and \
- not plugbase in GlobalVars.__cmdLineOpts__.enableplugins and \
- not plugbase in GlobalVars.__cmdLineOpts__.onlyplugins:
- # not loaded by default
- skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
- continue
- if GlobalVars.__cmdLineOpts__.onlyplugins and \
- not plugbase in GlobalVars.__cmdLineOpts__.onlyplugins:
- # not specified
- skippedplugins.append((plugbase, pluginClass(plugbase, commons)))
- continue
- GlobalVars.loadedplugins.append((plugbase, pluginClass(plugbase, commons)))
- except:
- soslog.warning(_("plugin %s does not install, skipping") % plug)
- if GlobalVars.__raisePlugins__:
- raise
+ outf = tempfile.NamedTemporaryFile()
+ outf.write(self.doc.serialize(None, 1))
+ outf.flush()
+ self.archive.add_file(outf.name, dest=fname)
+ outf.close()
- # First, gather and process options
- # using the options specified in the command line (if any)
- if GlobalVars.__cmdLineOpts__.usealloptions:
- for plugname, plug in GlobalVars.loadedplugins:
- for name, parms in zip(plug.optNames, plug.optParms):
- if type(parms["enabled"])==bool:
- parms["enabled"] = True
- del name
-
- # read plugin tunables from configuration file
- if config.has_section("tunables"):
- if not GlobalVars.__cmdLineOpts__.plugopts:
- GlobalVars.__cmdLineOpts__.plugopts = deque()
-
- for opt, val in config.items("tunables"):
- if not opt.split('.')[0] in conf_disable_plugins_list:
- GlobalVars.__cmdLineOpts__.plugopts.append(opt + "=" + val)
-
- if GlobalVars.__cmdLineOpts__.plugopts:
- opts = {}
- for opt in GlobalVars.__cmdLineOpts__.plugopts:
- # split up "general.syslogsize=5"
- try:
- opt, val = opt.split("=")
- except:
- val = True
- else:
- if val.lower() in ["off", "disable", "disabled", "false"]:
- val = False
- else:
- # try to convert string "val" to int()
- try:
- val = int(val)
- except:
- pass
- # split up "general.syslogsize"
- try:
- plug, opt = opt.split(".")
- except:
- plug = opt
- opt = True
+class SoSReport(object):
- try:
- opts[plug]
- except KeyError:
- opts[plug] = deque()
- opts[plug].append( (opt, val) )
-
- for plugname, plug in GlobalVars.loadedplugins:
- if plugname in opts:
- for opt, val in opts[plugname]:
- if not plug.setOption(opt, val):
- soslog.error('no such option "%s" for plugin ' \
- '(%s)' % (opt,plugname))
- doExit(1)
- del opts[plugname]
- for plugname in opts.keys():
- soslog.error('unable to set option for disabled or non-existing ' \
- 'plugin (%s)' % (plugname))
- # Do not want to exit on invalid opts due to a misconfiguration in sos.conf
- # doExit(1)
- del opt, opts, val
-
- # error if the user references a plugin which does not exist
- unk_plugs = [plugname.split(".")[0] for plugname in \
- GlobalVars.__cmdLineOpts__.onlyplugins \
- if not plugname.split(".")[0] in plugin_names]
- unk_plugs += [plugname.split(".")[0] for plugname in \
- GlobalVars.__cmdLineOpts__.noplugins \
- if not plugname.split(".")[0] in plugin_names]
- unk_plugs += [plugname.split(".")[0] for plugname in \
- GlobalVars.__cmdLineOpts__.enableplugins \
- if not plugname.split(".")[0] in plugin_names]
- if len(unk_plugs):
- for plugname in unk_plugs:
- soslog.error('a non-existing plugin (%s) was specified in the ' \
- 'command line' % (plugname))
- doExit(1)
- del unk_plugs
-
- for plugname, plug in GlobalVars.loadedplugins:
- names, parms = plug.getAllOptions()
- for optname, optparm in zip(names, parms):
- alloptions.append((plug, plugname, optname, optparm))
-
- # when --listplugins is specified we do a dry-run
- # which tells the user which plugins are going to be enabled
- # and with what options.
-
- if GlobalVars.__cmdLineOpts__.listPlugins:
- if not len(GlobalVars.loadedplugins) and not len(skippedplugins):
- soslog.error(_("no valid plugins found"))
- doExit(1)
-
- if len(GlobalVars.loadedplugins):
- print _("The following plugins are currently enabled:")
- print
- for (plugname, plug) in GlobalVars.loadedplugins:
- print " %-25s %s" % (textcolor(plugname,"lblue"),
- plug.get_description())
+ def __init__(self, opts):
+ self.loaded_plugins = deque()
+ self.skipped_plugins = deque()
+ self.all_options = deque()
+ self.xml_report = XmlReport()
+ self.global_plugin_options = {}
+
+ try:
+ import signal
+ signal.signal(signal.SIGTERM, self.get_exit_handler())
+ except Exception:
+ pass # not available in java, but we don't care
+
+
+ self.opts, self.args = self.parse_options(opts)
+ self.tempfile_util = TempFileUtil(tmp_dir=self.opts.tmp_dir)
+ self._set_debug()
+ self._read_config()
+ self.policy = sos.policies.load()
+ self._is_root = self.policy.is_root()
+ self._set_directories()
+
+ def print_header(self):
+ self.ui_log.info("\n%s\n" % _("sosreport (version %s)" % (__version__,)))
+
+ def get_commons(self):
+ return {
+ 'cmddir': self.cmddir,
+ 'logdir': self.logdir,
+ 'rptdir': self.rptdir,
+ 'soslog': self.soslog,
+ 'policy': self.policy,
+ 'verbosity': self.opts.verbosity,
+ 'xmlreport': self.xml_report,
+ 'cmdlineopts': self.opts,
+ 'config': self.config,
+ 'global_plugin_options': self.global_plugin_options,
+ }
+
+ def get_temp_file(self):
+ return self.tempfile_util.new()
+
+ def _set_archive(self):
+ if self.opts.compression_type not in ('auto', 'zip', 'bzip2', 'gzip', 'xz'):
+ raise Exception("Invalid compression type specified. Options are: auto, zip, bzip2, gzip and xz")
+ archive_name = os.path.join(self.opts.tmp_dir,self.policy.getArchiveName())
+ if self.opts.compression_type == 'auto':
+ auto_archive = self.policy.preferedArchive()
+ self.archive = auto_archive(archive_name)
+ elif self.opts.compression_type == 'zip':
+ self.archive = ZipFileArchive(archive_name)
else:
- print _("No plugin enabled.")
- print
+ self.archive = TarFileArchive(archive_name)
- if len(skippedplugins):
- print _("The following plugins are currently disabled:")
- print
- for (plugname, plugclass) in skippedplugins:
- print " %-25s %s" % (textcolor(plugname,"cyan"),
- plugclass.get_description())
- print
+ def _set_directories(self):
+ self.cmddir = 'sos_commands'
+ self.logdir = 'sos_logs'
+ self.rptdir = 'sos_reports'
- if len(alloptions):
- print _("The following plugin options are available:")
+ def _set_debug(self):
+ if self.opts.debug:
+ sys.excepthook = self._exception
+ self.raise_plugins = True
+ else:
+ self.raise_plugins = False
+
+ @staticmethod
+ def _exception(etype, eval_, etrace):
+ """ Wrap exception in debugger if not in tty """
+ if hasattr(sys, 'ps1') or not sys.stderr.isatty():
+ # we are in interactive mode or we don't have a tty-like
+ # device, so we call the default hook
+ sys.__excepthook__(etype, eval_, etrace)
+ else:
+ import traceback, pdb
+ # we are NOT in interactive mode, print the exception...
+ traceback.print_exception(etype, eval_, etrace, limit=2, file=sys.stdout)
print
- for (plug, plugname, optname, optparm) in alloptions:
+ # ...then start the debugger in post-mortem mode.
+ pdb.pm()
+
+ def _exit(self, error=0):
+ raise SystemExit()
+# sys.exit(error)
+
+ def _exit_nice(self):
+ for plugname, plugin in self.loaded_plugins:
+ plugin.exit_please()
+ self.ui_log.info("All processes ended, cleaning up.")
+ self._exit(1)
+
+ def get_exit_handler(self):
+ def exit_handler(signum, frame):
+ self._exit_nice()
+ return exit_handler
+
+ def _read_config(self):
+ self.config = ConfigParser.ConfigParser()
+ if self.opts.config_file:
+ config_file = self.opts.config_file
+ else:
+ config_file = '/etc/sos.conf'
+ try:
+ self.config.readfp(open(config_file))
+ except IOError:
+ pass
+
+ def _setup_logging(self):
+
+ if not sys.stdin.isatty():
+ self.opts.nocolors = True
+ self.opts.batch = True
+
+ # main soslog
+ self.soslog = logging.getLogger('sos')
+ self.soslog.setLevel(logging.DEBUG)
+ self.sos_log_file = self.get_temp_file()
+ self.sos_log_file.close()
+ flog = logging.FileHandler(self.sos_log_file.name)
+ flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
+ flog.setLevel(logging.INFO)
+ self.soslog.addHandler(flog)
+
+ if not self.opts.silent:
+ console = logging.StreamHandler(sys.stderr)
+ console.setFormatter(logging.Formatter('%(message)s'))
+ if self.opts.verbosity > 1:
+ console.setLevel(logging.DEBUG)
+ elif self.opts.verbosity > 0:
+ console.setLevel(logging.INFO)
+ else:
+ console.setLevel(logging.FATAL)
+ self.soslog.addHandler(console)
+
+ # ui log
+ self.ui_log = logging.getLogger('sos_ui')
+ self.ui_log.setLevel(logging.INFO)
+ self.sos_ui_log_file = self.get_temp_file()
+ self.sos_ui_log_file.close()
+ ui_fhandler = logging.FileHandler(self.sos_ui_log_file.name)
+ ui_fhandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
+
+ self.ui_log.addHandler(ui_fhandler)
+
+ if not self.opts.silent:
+ ui_console = logging.StreamHandler(sys.stdout)
+ ui_console.setFormatter(logging.Formatter('%(message)s'))
+ ui_console.setLevel(logging.INFO)
+ self.ui_log.addHandler(ui_console)
+
+ # profile logging
+ if self.opts.profiler:
+ self.proflog = logging.getLogger('sosprofile')
+ self.proflog.setLevel(logging.DEBUG)
+ self.sos_profile_log_file = self.get_temp_file()
+ plog = logging.FileHandler(self.sos_profile_log_file.name)
+ plog.setFormatter(logging.Formatter('%(message)s'))
+ plog.setLevel(logging.DEBUG)
+ self.proflog.addHandler(plog)
+
+ def _finish_logging(self):
+ logging.shutdown()
+
+ # the logging module seems to persist in the jython/jboss/eap world
+ # so the handlers need to be removed
+ for logger in [logging.getLogger(x) for x in ('sos', 'sosprofile', 'sos_ui')]:
+ for h in logger.handlers:
+ logger.removeHandler(h)
+
+ if getattr(self, "sos_log_file", None):
+ self.archive.add_file(self.sos_log_file.name, dest=os.path.join('sos_logs', 'sos.log'))
+ if getattr(self, "sos_profile_log_file", None):
+ self.archive.add_file(self.sos_profile_log_file.name, dest=os.path.join('sos_logs', 'profile.log'))
+ if getattr(self, "sos_ui_log_file", None):
+ self.archive.add_file(self.sos_ui_log_file.name, dest=os.path.join('sos_logs', 'ui.log'))
+
+ def _get_disabled_plugins(self):
+ disabled = []
+ if self.config.has_option("plugins", "disable"):
+ disabled = [plugin.strip() for plugin in
+ self.config.get("plugins", "disable").split(',')]
+ return disabled
+
+ def _is_skipped(self, plugin_name):
+ return (plugin_name in self.opts.noplugins or
+ plugin_name in self._get_disabled_plugins())
+
+ def _is_inactive(self, plugin_name, pluginClass):
+ return (not pluginClass(self.get_commons()).checkenabled() and
+ not plugin_name in self.opts.enableplugins and
+ not plugin_name in self.opts.onlyplugins)
+
+ def _is_not_default(self, plugin_name, pluginClass):
+ return (not pluginClass(self.get_commons()).defaultenabled() and
+ not plugin_name in self.opts.enableplugins and
+ not plugin_name in self.opts.onlyplugins)
+
+ def _is_not_specified(self, plugin_name):
+ return (self.opts.onlyplugins and
+ not plugin_name in self.opts.onlyplugins)
+
+ def _skip(self, plugin_class, reason="unknown"):
+ self.skipped_plugins.append((
+ plugin_class.name(),
+ plugin_class(self.get_commons()),
+ reason
+ ))
+
+ def _load(self, plugin_class):
+ self.loaded_plugins.append((
+ plugin_class.name(),
+ plugin_class(self.get_commons())
+ ))
+
+
+ def load_plugins(self):
+
+ import sos.plugins
+ helper = ImporterHelper(sos.plugins)
+ plugins = helper.get_modules()
+ self.plugin_names = deque()
+
+ # validate and load plugins
+ for plug in plugins:
+ plugbase, ext = os.path.splitext(plug)
+ try:
+ plugin_classes = import_plugin(plugbase)
+
+ for plugin_class in plugin_classes:
+ if not self.policy.validatePlugin(plugin_class):
+ self.soslog.debug(_("plugin %s does not validate, skipping") % plug)
+ self._skip(plugin_class, _("does not validate"))
+ continue
+
+ if plugin_class.requires_root and not self._is_root:
+ self.soslog.debug(_("plugin %s requires root permissions to execute, skipping") % plug)
+ self._skip(plugin_class, _("requires root"))
+ continue
+
+ # plug-in is valid, let's decide whether run it or not
+ self.plugin_names.append(plugbase)
+
+ if any((self._is_skipped(plugbase),
+ self._is_inactive(plugbase, plugin_class),
+ self._is_not_default(plugbase, plugin_class),
+ self._is_not_specified(plugbase),
+ )):
+ self._skip(plugin_class, _("inactive"))
+ continue
+
+ self._load(plugin_class)
+ except Exception, e:
+ self.soslog.warning(_("plugin %s does not install, skipping: %s") % (plug, e))
+ if self.raise_plugins:
+ raise
+
+ def _set_all_options(self):
+ if self.opts.usealloptions:
+ for plugname, plug in self.loaded_plugins:
+ for name, parms in zip(plug.optNames, plug.optParms):
+ if type(parms["enabled"])==bool:
+ parms["enabled"] = True
+
+ def _set_tunables(self):
+ if self.config.has_section("tunables"):
+ if not self.opts.plugopts:
+ self.opts.plugopts = deque()
+
+ for opt, val in self.config.items("tunables"):
+ if not opt.split('.')[0] in self.disabled:
+ self.opts.plugopts.append(opt + "=" + val)
+ if self.opts.plugopts:
+ opts = {}
+ for opt in self.opts.plugopts:
+ # split up "general.syslogsize=5"
+ try:
+ opt, val = opt.split("=")
+ except:
+ val = True
+ else:
+ if val.lower() in ["off", "disable", "disabled", "false"]:
+ val = False
+ else:
+ # try to convert string "val" to int()
+ try:
+ val = int(val)
+ except:
+ pass
+
+ # split up "general.syslogsize"
+ try:
+ plug, opt = opt.split(".")
+ except:
+ plug = opt
+ opt = True
+
+ try:
+ opts[plug]
+ except KeyError:
+ opts[plug] = deque()
+ opts[plug].append( (opt, val) )
+
+ for plugname, plug in self.loaded_plugins:
+ if plugname in opts:
+ for opt, val in opts[plugname]:
+ if not plug.setOption(opt, val):
+ self.soslog.error('no such option "%s" for plugin '
+ '(%s)' % (opt,plugname))
+ self._exit(1)
+ del opts[plugname]
+ for plugname in opts.keys():
+ self.soslog.error('unable to set option for disabled or non-existing '
+ 'plugin (%s)' % (plugname))
+
+ def _check_for_unknown_plugins(self):
+ import itertools
+ for plugin in itertools.chain(self.opts.onlyplugins,
+ self.opts.noplugins,
+ self.opts.enableplugins):
+ plugin_name = plugin.split(".")[0]
+ if not plugin_name in self.plugin_names:
+ self.soslog.fatal('a non-existing plugin (%s) was specified in the '
+ 'command line' % (plugin_name))
+ self._exit(1)
+
+ def _set_plugin_options(self):
+ for plugin_name, plugin in self.loaded_plugins:
+ names, parms = plugin.getAllOptions()
+ for optname, optparm in zip(names, parms):
+ self.all_options.append((plugin, plugin_name, optname, optparm))
+
+ def list_plugins(self):
+ if not self.loaded_plugins and not self.skipped_plugins:
+ self.soslog.fatal(_("no valid plugins found"))
+ return
+
+ if self.loaded_plugins:
+ self.ui_log.info(_("The following plugins are currently enabled:"))
+ self.ui_log.info("")
+ for (plugname, plug) in self.loaded_plugins:
+ self.ui_log.info(" %-15s %s" % (plugname, plug.get_description()))
+ else:
+ self.ui_log.info(_("No plugin enabled."))
+ self.ui_log.info("")
+
+ if self.skipped_plugins:
+ self.ui_log.info(_("The following plugins are currently disabled:"))
+ self.ui_log.info("")
+ for (plugname, plugclass, reason) in self.skipped_plugins:
+ self.ui_log.info(" %-15s %-14s %s" % (plugname,
+ reason,
+ plugclass.get_description()))
+ self.ui_log.info("")
+
+ if self.all_options:
+ self.ui_log.info(_("The following plugin options are available:"))
+ self.ui_log.info("")
+ for (plug, plugname, optname, optparm) in self.all_options:
# format and colorize option value based on its type (int or bool)
if type(optparm["enabled"]) == bool:
if optparm["enabled"] == True:
- tmpopt = textcolor("on","lred")
- else:
- tmpopt = textcolor("off","red")
- elif type(optparm["enabled"]) == int:
- if optparm["enabled"] > 0:
- tmpopt = textcolor(optparm["enabled"],"lred")
+ tmpopt = "on"
else:
- tmpopt = textcolor(optparm["enabled"],"red")
+ tmpopt = "off"
else:
tmpopt = optparm["enabled"]
- print " %-21s %-5s %s" % (plugname + "." + optname,
- tmpopt, optparm["desc"])
- del tmpopt
+ self.ui_log.info(" %-25s %-15s %s" % (
+ plugname + "." + optname, tmpopt, optparm["desc"]))
else:
- print _("No plugin options available.")
-
- print
- doExit()
-
- # to go anywhere further than listing the
- # plugins we will need root permissions.
- if os.getuid() != 0:
- print _('sosreport requires root permissions to run.')
- doExit(1)
-
- # we don't need to keep in memory plugins we are not going to use
- del skippedplugins
-
- if not len(GlobalVars.loadedplugins):
- soslog.error(_("no valid plugins were enabled"))
- doExit(1)
-
- msg = _("""This utility will collect some detailed information about the
-hardware and setup of your %(distroa)s system.
-The information is collected and an archive is packaged under
-/tmp, which you can send to a support representative.
-%(distrob)s will use this information for diagnostic purposes ONLY
-and it will be considered confidential information.
+ self.ui_log.info(_("No plugin options available."))
-This process may take a while to complete.
-No changes will be made to your system.
+ self.ui_log.info("")
-""" % {'distroa':__distro__, 'distrob':__distro__})
+ def batch(self):
+ if self.opts.batch:
+ self.ui_log.info(self.policy.get_msg())
+ else:
+ msg = self.policy.get_msg()
+ msg += _("Press ENTER to continue, or CTRL-C to quit.\n")
+ try:
+ raw_input(msg)
+ except:
+ self.ui_log.info("")
+ self._exit()
- if GlobalVars.__cmdLineOpts__.batch:
- print msg
- else:
- msg += _("""Press ENTER to continue, or CTRL-C to quit.\n""")
- try:
- raw_input(msg)
- except:
- print
- doExit()
- del msg
+ def _log_plugin_exception(self, plugin_name):
+ self.soslog.error("%s\n%s" % (plugin_name, traceback.format_exc()))
- if GlobalVars.__cmdLineOpts__.diagnose:
- # Call the diagnose() method for each plugin
+ def diagnose(self):
tmpcount = 0
- for plugname, plug in GlobalVars.loadedplugins:
+ for plugname, plug in self.loaded_plugins:
try:
plug.diagnose()
except:
- if GlobalVars.__raisePlugins__:
+ if self.raise_plugins:
raise
else:
- error_log = open(logdir + "/sosreport-plugin-errors.txt", "a")
- etype, eval, etrace = sys.exc_info()
- traceback.print_exception(etype, eval, etrace, limit=2, file=sys.stdout)
- error_log.write(traceback.format_exc())
- error_log.close()
+ self._log_plugin_exception(plugname)
tmpcount += len(plug.diagnose_msgs)
if tmpcount > 0:
- print _("One or more plugins have detected a problem in your " \
- "configuration.")
- print _("Please review the following messages:")
- print
+ self.ui_log.info(_("One or more plugins have detected a problem in your "
+ "configuration."))
+ self.ui_log.info(_("Please review the following messages:"))
+ self.ui_log.info("")
- fp = open(rptdir + "/diagnose.txt", "w")
- for plugname, plug in GlobalVars.loadedplugins:
+ fp = self.get_temp_file()
+ for plugname, plug in self.loaded_plugins:
for tmpcount2 in range(0, len(plug.diagnose_msgs)):
if tmpcount2 == 0:
- soslog.warning( textcolor("%s:" % plugname, "red") )
+ soslog.warning("%s:" % plugname)
soslog.warning(" * %s" % plug.diagnose_msgs[tmpcount2])
fp.write("%s: %s\n" % (plugname, plug.diagnose_msgs[tmpcount2]))
- fp.close()
+ self.archive.add_file(fp.name, dest=os.path.join(self.rptdir, 'diagnose.txt'))
- print
- if not GlobalVars.__cmdLineOpts__.batch:
+ self.ui_log.info("")
+ if not self.opts.batch:
try:
while True:
- yorno = raw_input( _("Are you sure you would like to " \
+ yorno = raw_input( _("Are you sure you would like to "
"continue (y/n) ? ") )
if yorno == _("y") or yorno == _("Y"):
- print
+ self.ui_log.info("")
break
elif yorno == _("n") or yorno == _("N"):
- doExit(0)
+ self._exit(0)
del yorno
except KeyboardInterrupt:
- print
- doExit(0)
+ self.ui_log.info("")
+ self._exit(0)
- GlobalVars.policy.preWork()
-
- # Call the setup() method for each plugin
- for plugname, plug in GlobalVars.loadedplugins:
+ def prework(self):
try:
- plug.setup()
- except KeyboardInterrupt:
- raise
- except:
- if GlobalVars.__raisePlugins__:
+ self.policy.preWork()
+ self._set_archive()
+ except Exception, e:
+ import traceback
+ traceback.print_exc(e)
+ self.ui_log.info(e)
+ self._exit(0)
+
+ def setup(self):
+ for plugname, plug in self.loaded_plugins:
+ try:
+ plug.setArchive(self.archive)
+ plug.setup()
+ except KeyboardInterrupt:
raise
- else:
- error_log = open(logdir + "/sosreport-plugin-errors.txt", "a")
- etype, eval, etrace = sys.exc_info()
- traceback.print_exception(etype, eval, etrace, limit=2, file=sys.stdout)
- error_log.write(traceback.format_exc())
- error_log.close()
-
- print _(" Running plugins. Please wait ...")
- print
- plugruncount = 0
- for i in izip(GlobalVars.loadedplugins):
- plugruncount += 1
- plugname, plug = i[0]
- sys.stdout.write("\r Running %d/%d: %s... " % (plugruncount,
- len(GlobalVars.loadedplugins),
- plugname))
- sys.stdout.flush()
- try:
- plug.copyStuff()
- except KeyboardInterrupt:
- raise
- except:
- if GlobalVars.__raisePlugins__:
+ except:
+ if self.raise_plugins:
+ raise
+ else:
+ self._log_plugin_exception(plugname)
+
+ def version(self):
+ """Fetch version information from all plugins and store in the report
+ version file"""
+
+ versions = []
+ versions.append("sosreport: %s" % __version__)
+ for plugname, plug in self.loaded_plugins:
+ versions.append("%s: %s" % (plugname, plug.version))
+ self.archive.add_string(content="\n".join(versions), dest='version.txt')
+
+
+ def copy_stuff(self):
+ plugruncount = 0
+ for i in izip(self.loaded_plugins):
+ plugruncount += 1
+ plugname, plug = i[0]
+ if not self.opts.silent:
+ sys.stdout.write("\r Running %d/%d: %s... " % (plugruncount, len(self.loaded_plugins), plugname))
+ sys.stdout.flush()
+ try:
+ plug.copyStuff()
+ except KeyboardInterrupt:
raise
- else:
- error_log = open(logdir + "/sosreport-plugin-errors.txt", "a")
- etype, eval, etrace = sys.exc_info()
- traceback.print_exception(etype, eval, etrace, limit=2, file=sys.stdout)
- error_log.write(traceback.format_exc())
- error_log.close()
-
- print
+ except:
+ if self.raise_plugins:
+ raise
+ else:
+ self._log_plugin_exception(plugname)
- if GlobalVars.__cmdLineOpts__.report:
- for plugname, plug in GlobalVars.loadedplugins:
+ def report(self):
+ for plugname, plug in self.loaded_plugins:
for oneFile in plug.copiedFiles:
try:
- xmlrep.add_file(oneFile["srcpath"], os.stat(oneFile["srcpath"]))
+ self.xml_report.add_file(oneFile["srcpath"], os.stat(oneFile["srcpath"]))
except:
pass
- xmlrep.serialize_to_file(rptdir + "/sosreport.xml")
+ self.xml_report.serialize_to_file(
+ os.path.join(self.rptdir, "sosreport.xml"))
- if GlobalVars.__cmdLineOpts__.analyze:
- # Call the analyze method for each plugin
- for plugname, plug in GlobalVars.loadedplugins:
- try:
- plug.analyze()
- except:
- # catch exceptions in analyze() and keep working
- pass
- if GlobalVars.__cmdLineOpts__.report:
+ def plain_report(self):
+ report = Report()
+
+ for plugname, plug in self.loaded_plugins:
+ section = Section(name=plugname)
+
+ for alert in plug.alerts:
+ section.add(Alert(alert))
+
+ if plug.customText:
+ section.add(Note(plug.customText))
+
+ for f in plug.copiedFiles:
+ section.add(CopiedFile(name=f["srcpath"], href=f["dstpath"]))
+
+ for cmd in plug.executedCommands:
+ section.add(Command(name=cmd['exe'], return_code=0, href=cmd['file']))
+
+ for content, f in plug.copyStrings:
+ section.add(CreatedFile(name=f))
+
+ report.add(section)
+
+ fd = self.get_temp_file()
+ fd.write(str(PlainTextReport(report)))
+ fd.flush()
+ self.archive.add_file(fd.name, dest=os.path.join('sos_reports', 'sos.txt'))
+
+
+ def html_report(self):
# Generate the header for the html output file
- rfd = open(rptdir + "/" + "sosreport.html", "w")
+ rfd = self.get_temp_file()
rfd.write("""
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
@@ -772,9 +740,9 @@ No changes will be made to your system.
# Make a pass to gather Alerts and a list of module names
- allAlerts = deque()
- plugNames = deque()
- for plugname, plug in GlobalVars.loadedplugins:
+ allAlerts = deque()
+ plugNames = deque()
+ for plugname, plug in self.loaded_plugins:
for alert in plug.alerts:
allAlerts.append('<a href="#%s">%s</a>: %s' % (plugname, plugname,
alert))
@@ -802,49 +770,179 @@ No changes will be made to your system.
# Call the report method for each plugin
- for plugname, plug in GlobalVars.loadedplugins:
+ for plugname, plug in self.loaded_plugins:
try:
html = plug.report()
except:
- if GlobalVars.__raisePlugins__:
+ if self.raise_plugins:
raise
else:
rfd.write(html)
rfd.write("</body></html>")
- rfd.close()
+ rfd.flush()
+
+ self.archive.add_file(rfd.name, dest=os.path.join('sos_reports', 'sos.html'))
+
+ def postproc(self):
+ for plugname, plug in self.loaded_plugins:
+ try:
+ plug.postproc()
+ except:
+ if self.raise_plugins:
+ raise
+
+ def final_work(self):
+
+ # package up the results for the support organization
+ self.policy.packageResults(self.archive.name())
+
+ self._finish_logging()
+
+ self.archive.close()
- # Call the postproc method for each plugin
- for plugname, plug in GlobalVars.loadedplugins:
+ final_filename = compress(self.archive, self.opts.compression_type)
+
+ # automated submission will go here
+ if not self.opts.upload:
+ self.policy.displayResults(final_filename)
+ else:
+ self.policy.uploadResults(final_filename)
+
+ self.tempfile_util.clean()
+
+ return final_filename
+
+ def ensure_plugins(self):
+ if not self.loaded_plugins:
+ self.soslog.error(_("no valid plugins were enabled"))
+ self._exit(1)
+
+ def parse_options(self, opts):
+ """ Parse command line options """
+
+ self.parser = parser = OptionParserExtended(option_class=SosOption)
+ parser.add_option("-l", "--list-plugins", action="store_true",
+ dest="listPlugins", default=False,
+ help="list plugins and available plugin options")
+ parser.add_option("-n", "--skip-plugins", action="extend",
+ dest="noplugins", type="string",
+ help="skip these plugins", default = deque())
+ parser.add_option("-e", "--enable-plugins", action="extend",
+ dest="enableplugins", type="string",
+ help="enable these plugins", default = deque())
+ parser.add_option("-o", "--only-plugins", action="extend",
+ dest="onlyplugins", type="string",
+ help="enable these plugins only", default = deque())
+ parser.add_option("-k", action="append",
+ dest="plugopts", type="string",
+ help="plugin options in plugname.option=value format (see -l)")
+ parser.add_option("-a", "--alloptions", action="store_true",
+ dest="usealloptions", default=False,
+ help="enable all options for loaded plugins")
+ parser.add_option("-u", "--upload", action="store",
+ dest="upload", default=False,
+ help="upload the report to an ftp server")
+ parser.add_option("--batch", action="store_true",
+ dest="batch", default=False,
+ help="do not ask any question (batch mode)")
+ parser.add_option("--no-colors", action="store_true",
+ dest="nocolors", default=False,
+ help="do not use terminal colors for text")
+ parser.add_option("-v", "--verbose", action="count",
+ dest="verbosity",
+ help="increase verbosity")
+ parser.add_option("", "--silent", action="store_true",
+ dest="silent", default=False,
+ help="Only display FATAL errors on stdout")
+ parser.add_option("--debug", action="count",
+ dest="debug",
+ help="enabling debugging through python debugger")
+ parser.add_option("--ticket-number", action="store",
+ dest="ticketNumber",
+ help="set ticket number")
+ parser.add_option("--name", action="store",
+ dest="customerName",
+ help="define customer name")
+ parser.add_option("--config-file", action="store",
+ dest="config_file",
+ help="specify alternate configuration file")
+ parser.add_option("--tmp-dir", action="store",
+ dest="tmp_dir",
+ help="specify alternate temporary directory", default=tempfile.gettempdir())
+ parser.add_option("--diagnose", action="store_true",
+ dest="diagnose",
+ help="enable diagnostics", default=False)
+ parser.add_option("--analyze", action="store_true",
+ dest="analyze",
+ help="enable analyzations", default=False)
+ parser.add_option("--report", action="store_true",
+ dest="report",
+ help="Enable html/xml reporting", default=False)
+ parser.add_option("--profile", action="store_true",
+ dest="profiler",
+ help="turn on profiling", default=False)
+ parser.add_option("-z", "--compression-type", dest="compression_type",
+ help="compression technology to use [auto, zip, gzip, bzip2, xz] (default=auto)",
+ default="auto")
+
+ return parser.parse_args(opts)
+
+ def set_option(self, name, value=None):
+ """Allows setting of 'command line options' without passing in a
+ command line. Name should be the command line option name of the
+ option to set."""
+ if self.parser.has_option(name):
+ option = self.parser.get_option(name)
+ option.process(name, value, self.opts, self.parser)
+
+ def set_global_plugin_option(self, key, value):
+ self.global_plugin_options[key] = value;
+
+ def execute(self):
try:
- plug.postproc()
- except:
- if GlobalVars.__raisePlugins__:
- raise
+ self._setup_logging()
+ self.policy.setCommons(self.get_commons())
+ self.print_header()
+ self.load_plugins()
+ self._set_tunables()
+ self._check_for_unknown_plugins()
+ self._set_plugin_options()
- if GlobalVars.__cmdLineOpts__.build:
- print
- print _(" sosreport build tree is located at : %s" % (GlobalVars.dstroot,))
- print
- return GlobalVars.dstroot
+ if self.opts.listPlugins:
+ self.list_plugins()
+ return
+
+ self.ensure_plugins()
+ self.batch()
+
+ if self.opts.diagnose:
+ self.diagnose()
+
+ self.prework()
+ self.setup()
+
+ self.ui_log.info(_(" Running plugins. Please wait ..."))
+ self.ui_log.info("")
- # package up the results for the support organization
- GlobalVars.policy.packageResults()
+ self.copy_stuff()
- # delete gathered files
- GlobalVars.policy.cleanDstroot()
+ self.ui_log.info("")
- # let's encrypt the tar-ball
- #if GlobalVars.__cmdLineOpts__.encrypt:
- # policy.encryptResults()
+ if self.opts.report:
+ self.report()
+ self.html_report()
+ self.plain_report()
- # automated submission will go here
- if not GlobalVars.__cmdLineOpts__.upload:
- GlobalVars.policy.displayResults()
- else:
- GlobalVars.policy.uploadResults()
+ self.postproc()
+ self.version()
- # Close all log files and perform any cleanup
- logging.shutdown()
+ return self.final_work()
+ except SystemExit:
+ return None
+def main(args):
+ """The main entry point"""
+ sos = SoSReport(args)
+ sos.execute()
diff --git a/sos/utilities.py b/sos/utilities.py
new file mode 100644
index 00000000..e91586de
--- /dev/null
+++ b/sos/utilities.py
@@ -0,0 +1,445 @@
+### 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
+
+import os
+import re
+import sys
+import string
+import fnmatch
+import inspect
+from stat import *
+from itertools import *
+from subprocess import Popen, PIPE
+import shlex
+import logging
+import zipfile
+import tarfile
+import hashlib
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import time
+
+def checksum(filename, chunk_size=128):
+ """Returns the checksum of the supplied filename. The file is read in
+ chunk_size blocks"""
+ name = get_hash_name()
+ digest = hashlib.new(name)
+ fd = open(filename, 'rb')
+ data = fd.read(chunk_size)
+ while data:
+ digest.update(data)
+ data = fd.read(chunk_size)
+ return digest.hexdigest()
+
+def get_hash_name():
+ """Returns the algorithm used when computing a hash"""
+ import sos.policies
+ policy = sos.policies.load()
+ try:
+ name = policy.getPreferredHashAlgorithm()
+ hashlib.new(name)
+ return name
+ except:
+ return 'sha256'
+
+class DirTree(object):
+ """Builds an ascii representation of a directory structure"""
+
+ def __init__(self, top_directory):
+ self.directory_count = 0
+ self.file_count = 0
+ self.buffer = []
+ self.top_directory = top_directory
+ self._build_tree()
+
+ def buf(self, s):
+ self.buffer.append(s)
+
+ def printtree(self):
+ print self.as_string()
+
+ def as_string(self):
+ return "\n".join(self.buffer)
+
+ def _build_tree(self):
+ self.buf(os.path.abspath(self.top_directory))
+ self.tree_i(self.top_directory, first=True)
+
+ def _convert_bytes(self, n):
+ K, M, G, T = 1 << 10, 1 << 20, 1 << 30, 1 << 40
+ if n >= T:
+ return '%.1fT' % (float(n) / T)
+ elif n >= G:
+ return '%.1fG' % (float(n) / G)
+ elif n >= M:
+ return '%.1fM' % (float(n) / M)
+ elif n >= K:
+ return '%.1fK' % (float(n) / K)
+ else:
+ return '%d' % n
+
+ def _get_user(self, stats):
+ try:
+ import pwd
+ return pwd.getpwuid(stats.st_uid)[0]
+ except ImportError:
+ return str(stats.st_uid)
+
+ def _get_group(self, stats):
+ try:
+ import grp
+ return grp.getgrgid(stats.st_gid)[0]
+ except ImportError:
+ return str(stats.st_uid)
+
+ def _format(self, path):
+ """Conditionally adds detail to paths"""
+ stats = os.stat(path)
+ details = {
+ "filename": os.path.basename(path),
+ "user": self._get_user(stats),
+ "group": self._get_group(stats),
+ "filesize": self._convert_bytes(stats.st_size),
+ }
+ return ("[%(user)s %(group)s %(filesize)s] " % details, "%(filename)s" % details)
+
+ def tree_i(self, dir_, padding='', first=False, fmt="%-30s %s%s%s"):
+ if not first:
+ details, filename = self._format(os.path.abspath(dir_))
+ line = fmt % (details, padding[:-1], "+-- ", filename)
+ self.buf(line)
+ padding += ' '
+
+ count = 0
+ files = os.listdir(dir_)
+ files.sort(key=string.lower)
+ for f in files:
+ count += 1
+ path = os.path.join(dir_, f)
+
+ if f.startswith("."):
+ pass
+ elif os.path.isfile(path):
+ self.file_count += 1
+ details, filename = self._format(path)
+ line = fmt % (details, padding, "+-- ", filename)
+ self.buf(line)
+ elif os.path.islink(path):
+ self.buf(padding +
+ '+-- ' +
+ f +
+ ' -> ' + os.path.basename(os.path.realpath(path)))
+ if os.path.isdir(path):
+ self.directory_count += 1
+ else:
+ self.file_count += 1
+ elif os.path.isdir(path):
+ self.directory_count += 1
+ if count == len(files):
+ self.tree_i(path, padding + ' ')
+ else:
+ self.tree_i(path, padding + '|')
+
+
+class ImporterHelper(object):
+ """Provides a list of modules that can be imported in a package.
+ Importable modules are located along the module __path__ list and modules
+ are files that end in .py. This class will read from PKZip archives as well
+ for listing out jar and egg contents."""
+
+ def __init__(self, package):
+ """package is a package module
+ import my.package.module
+ helper = ImporterHelper(my.package.module)"""
+ self.package = package
+
+ def _plugin_name(self, path):
+ "Returns the plugin module name given the path"
+ base = os.path.basename(path)
+ name, ext = os.path.splitext(base)
+ return name
+
+ def _get_plugins_from_list(self, list_):
+ plugins = [self._plugin_name(plugin)
+ for plugin in list_
+ if "__init__" not in plugin
+ and plugin.endswith(".py")]
+ plugins.sort()
+ return plugins
+
+ def _find_plugins_in_dir(self, path):
+ if os.path.exists(path):
+ py_files = list(find("*.py", path))
+ pnames = self._get_plugins_from_list(py_files)
+ if pnames:
+ return pnames
+ else:
+ return []
+
+ def _get_path_to_zip(self, path, tail_list=None):
+ if not tail_list:
+ tail_list = ['']
+
+ if path.endswith(('.jar', '.zip', '.egg')):
+ return path, os.path.join(*tail_list)
+
+ head, tail = os.path.split(path)
+ tail_list.insert(0, tail)
+
+ if head == path:
+ raise Exception("not a zip file")
+ else:
+ return self._get_path_to_zip(head, tail_list)
+
+
+ def _find_plugins_in_zipfile(self, path):
+ try:
+ path_to_zip, tail = self._get_path_to_zip(path)
+ zf = zipfile.ZipFile(path_to_zip)
+ root_names = [name for name in zf.namelist() if tail in name]
+ candidates = self._get_plugins_from_list(root_names)
+ zf.close()
+ if candidates:
+ return candidates
+ else:
+ return []
+ except (IOError, Exception):
+ return []
+
+ def get_modules(self):
+ "Returns the list of importable modules in the configured python package."
+ plugins = []
+ for path in self.package.__path__:
+ if os.path.isdir(path) or path == '':
+ plugins.extend(self._find_plugins_in_dir(path))
+ else:
+ plugins.extend(self._find_plugins_in_zipfile(path))
+
+ return plugins
+
+
+def find(file_pattern, top_dir, max_depth=None, path_pattern=None):
+ """generator function to find files recursively. Usage:
+
+ for filename in find("*.properties", "/var/log/foobar"):
+ print filename
+ """
+ if max_depth:
+ base_depth = os.path.dirname(top_dir).count(os.path.sep)
+ max_depth += base_depth
+
+ for path, dirlist, filelist in os.walk(top_dir):
+ if max_depth and path.count(os.path.sep) >= max_depth:
+ del dirlist[:]
+
+ if path_pattern and not fnmatch.fnmatch(path, path_pattern):
+ continue
+
+ for name in fnmatch.filter(filelist, file_pattern):
+ yield os.path.join(path, name)
+
+
+def sosGetCommandOutput(command, timeout=300):
+ """Execute a command through the system shell. First checks to see if the
+ requested command is executable. Returns (returncode, stdout, 0)"""
+ # XXX: what is this doing this for?
+ cmdfile = command.strip("(").split()[0]
+
+ possibles = [cmdfile] + [os.path.join(path, cmdfile) for path in os.environ.get("PATH", "").split(":")]
+
+ if any(os.access(path, os.X_OK) for path in possibles):
+ p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, bufsize=-1)
+ stdout, stderr = p.communicate()
+ return (p.returncode, stdout.strip(), 0)
+ else:
+ return (127, "", 0)
+
+
+def import_module(module_fqname, superclass=None):
+ module_name = module_fqname.rpartition(".")[-1]
+ module = __import__(module_fqname, globals(), locals(), [module_name])
+ modules = [class_ for cname, class_ in
+ inspect.getmembers(module, inspect.isclass)
+ if class_.__module__ == module_fqname]
+ if superclass:
+ modules = [m for m in modules if issubclass(m, superclass)]
+
+ return modules
+
+class Archive(object):
+
+ _name = "unset"
+
+ def prepend(self, src):
+ if src:
+ name = os.path.split(self._name)[-1]
+ return os.path.join(name, src.lstrip(os.sep))
+
+
+class TarFileArchive(Archive):
+
+ def __init__(self, name):
+ self._name = name
+ self.tarfile = tarfile.open(self.name(), mode="w")
+
+ def name(self):
+ return "%s.tar" % self._name
+
+ def add_file(self, src, dest=None):
+ if dest:
+ dest = self.prepend(dest)
+ else:
+ dest = self.prepend(src)
+
+ fp = open(src, 'rb')
+ content = fp.read()
+ fp.close()
+
+ tar_info = tarfile.TarInfo(name=dest)
+ tar_info.size = len(content)
+ tar_info.mtime = os.stat(src).st_mtime
+
+ self.tarfile.addfile(tar_info, StringIO(content))
+
+ def add_string(self, content, dest):
+ dest = self.prepend(dest)
+ tar_info = tarfile.TarInfo(name=dest)
+ tar_info.size = len(content)
+ tar_info.mtime = time.time()
+ self.tarfile.addfile(tar_info, StringIO(content))
+
+ def open_file(self, name):
+ try:
+ self.tarfile.close()
+ self.tarfile = tarfile.open(self.name(), mode="r")
+ name = self.prepend(name)
+ file_obj = self.tarfile.extractfile(name)
+ file_obj = StringIO(file_obj.read())
+ return file_obj
+ finally:
+ self.tarfile.close()
+ self.tarfile = tarfile.open(self.name(), mode="a")
+
+ def close(self):
+ self.tarfile.close()
+
+
+class ZipFileArchive(Archive):
+
+ def __init__(self, name):
+ self._name = name
+ try:
+ import zlib
+ self.compression = zipfile.ZIP_DEFLATED
+ except:
+ self.compression = zipfile.ZIP_STORED
+
+ self.zipfile = zipfile.ZipFile(self.name(), mode="w", compression=self.compression)
+
+ def name(self):
+ return "%s.zip" % self._name
+
+ def add_file(self, src, dest=None):
+ if os.path.isdir(src):
+ # We may not need, this, but if we do I only want to do it
+ # one time
+ regex = re.compile(r"^" + src)
+ for path, dirnames, filenames in os.walk(src):
+ for filename in filenames:
+ filename = path + filename
+ if dest:
+ self.zipfile.write(filename,
+ self.prepend(re.sub(regex, dest, filename)))
+ else:
+ self.zipfile.write(filename, self.prepend(filename))
+ else:
+ if dest:
+ self.zipfile.write(src, self.prepend(dest))
+ else:
+ self.zipfile.write(src, self.prepend(src))
+
+ def add_string(self, content, dest):
+ info = zipfile.ZipInfo(self.prepend(dest),
+ date_time=time.localtime(time.time()))
+ info.compress_type = self.compression
+ info.external_attr = 0400 << 16L
+ self.zipfile.writestr(info, content)
+
+ def open_file(self, name):
+ try:
+ self.zipfile.close()
+ self.zipfile = zipfile.ZipFile(self.name(), mode="r")
+ name = self.prepend(name)
+ file_obj = self.zipfile.open(name)
+ return file_obj
+ finally:
+ self.zipfile.close()
+ self.zipfile = zipfile.ZipFile(self.name(), mode="a")
+
+ def close(self):
+ self.zipfile.close()
+
+
+def compress(archive, method):
+ """Compress an archive object via method. ZIP archives are ignored. If
+ method is automatic then the following technologies are tried in order: xz,
+ bz2 and gzip"""
+
+ if method == "zip":
+ return archive.name()
+
+ methods = ['xz', 'bzip2', 'gzip']
+
+ if method in ('xz', 'bzip2', 'gzip'):
+ methods = [method]
+
+ compressed = False
+ last_error = None
+ for cmd in ('xz', 'bzip2', 'gzip'):
+ if compressed:
+ break
+ try:
+ command = shlex.split("%s %s" % (cmd,archive.name()))
+ p = Popen(command, stdout=PIPE, stderr=PIPE, bufsize=-1)
+ stdout, stderr = p.communicate()
+ log = logging.getLogger('sos')
+ if stdout:
+ log.info(stdout)
+ if stderr:
+ log.error(stderr)
+ compressed = True
+ return archive.name() + "." + cmd.replace('ip','')
+ except Exception, e:
+ last_error = e
+
+ if not compressed:
+ raise last_error
+
+
+def shell_out(cmd):
+ """Uses subprocess.Popen to make a system call and returns stdout.
+ Does not handle exceptions."""
+ p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+ return p.communicate()[0]
+# vim:ts=4 sw=4 et
diff --git a/sosreport b/sosreport
index 0908a1b4..5d3cc749 100755
--- a/sosreport
+++ b/sosreport
@@ -15,13 +15,11 @@
""" sos entry point. """
-from sos.sosreport import sosreport, doExitCode
+#from sos.sosreport import sosreport, doExitCode
+from sos.sosreport import main
import sys
if __name__ == '__main__':
- try:
- sosreport(sys.argv[1:])
- except KeyboardInterrupt:
- doExitCode()
+ main(sys.argv[1:])
# vim:ts=4 et sw=4
diff --git a/tests/archive_tests.py b/tests/archive_tests.py
new file mode 100755
index 00000000..01c46395
--- /dev/null
+++ b/tests/archive_tests.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+import unittest
+import os
+import tarfile
+import zipfile
+
+from sos.utilities import TarFileArchive, ZipFileArchive
+
+class ZipFileArchiveTest(unittest.TestCase):
+
+ def setUp(self):
+ self.zf = ZipFileArchive('test')
+
+ def tearDown(self):
+ os.unlink('test.zip')
+
+ def check_for_file(self, filename):
+ zf = zipfile.ZipFile('test.zip', 'r')
+ zf.getinfo(filename)
+ zf.close()
+
+ def test_create(self):
+ self.zf.close()
+
+ def test_add_file(self):
+ self.zf.add_file('tests/worker.py')
+ self.zf.close()
+
+ self.check_for_file('test/tests/worker.py')
+
+ def test_add_dir(self):
+ self.zf.add_file('tests/')
+ self.zf.close()
+
+ self.check_for_file('test/tests/worker.py')
+
+ def test_add_renamed(self):
+ self.zf.add_file('tests/worker.py', dest='tests/worker_renamed.py')
+ self.zf.close()
+
+ self.check_for_file('test/tests/worker_renamed.py')
+
+ def test_add_renamed_dir(self):
+ self.zf.add_file('tests/', 'tests_renamed/')
+ self.zf.close()
+
+ self.check_for_file('test/tests_renamed/worker.py')
+
+ def test_add_string(self):
+ self.zf.add_string('this is content', 'tests/string_test.txt')
+ self.zf.close()
+
+ self.check_for_file('test/tests/string_test.txt')
+
+ def test_get_file(self):
+ self.zf.add_string('this is my content', 'tests/string_test.txt')
+
+ afp = self.zf.open_file('tests/string_test.txt')
+ self.assertEquals('this is my content', afp.read())
+
+ def test_overwrite_file(self):
+ self.zf.add_string('this is my content', 'tests/string_test.txt')
+ self.zf.add_string('this is my new content', 'tests/string_test.txt')
+
+ afp = self.zf.open_file('tests/string_test.txt')
+ self.assertEquals('this is my new content', afp.read())
+
+class TarFileArchiveTest(unittest.TestCase):
+
+ def setUp(self):
+ self.tf = TarFileArchive('test')
+
+ def tearDown(self):
+ os.unlink('test.tar')
+
+ def check_for_file(self, filename):
+ rtf = tarfile.open('test.tar')
+ rtf.getmember(filename)
+ rtf.close()
+
+ def test_create(self):
+ self.tf.close()
+ self.assertTrue(os.path.exists('test.tar'))
+
+ def test_add_file(self):
+ self.tf.add_file('tests/worker.py')
+ self.tf.close()
+
+ self.check_for_file('test/tests/worker.py')
+
+ def test_add_dir(self):
+ self.tf.add_file('tests/')
+ self.tf.close()
+
+ self.check_for_file('test/tests/worker.py')
+
+ def test_add_renamed(self):
+ self.tf.add_file('tests/worker.py', dest='tests/worker_renamed.py')
+ self.tf.close()
+
+ self.check_for_file('test/tests/worker_renamed.py')
+
+ def test_add_renamed_dir(self):
+ self.tf.add_file('tests/', 'tests_renamed/')
+ self.tf.close()
+
+ self.check_for_file('test/tests_renamed/worker.py')
+
+ def test_add_string(self):
+ self.tf.add_string('this is content', 'tests/string_test.txt')
+ self.tf.close()
+
+ self.check_for_file('test/tests/string_test.txt')
+
+ def test_get_file(self):
+ self.tf.add_string('this is my content', 'tests/string_test.txt')
+
+ afp = self.tf.open_file('tests/string_test.txt')
+ self.assertEquals('this is my content', afp.read())
+
+ def test_overwrite_file(self):
+ self.tf.add_string('this is my content', 'tests/string_test.txt')
+ self.tf.add_string('this is my new content', 'tests/string_test.txt')
+
+ afp = self.tf.open_file('tests/string_test.txt')
+ self.assertEquals('this is my new content', afp.read())
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/report_tests.py b/tests/report_tests.py
new file mode 100644
index 00000000..a08f0aec
--- /dev/null
+++ b/tests/report_tests.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+
+import unittest
+import os
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+from sos.reporting import Report, Section, Command, CopiedFile, CreatedFile, Alert
+from sos.reporting import PlainTextReport
+
+class ReportTest(unittest.TestCase):
+
+ def test_empty(self):
+ report = Report()
+
+ expected = json.dumps({})
+
+ self.assertEquals(expected, str(report))
+
+ def test_nested_section(self):
+ report = Report()
+ section = Section(name="section")
+ report.add(section)
+
+ expected = json.dumps({"section": {}})
+
+ self.assertEquals(expected, str(report))
+
+ def test_multiple_sections(self):
+ report = Report()
+ section = Section(name="section")
+ report.add(section)
+
+ section2 = Section(name="section2")
+ report.add(section2)
+
+ expected = json.dumps({"section": {},
+ "section2": {},})
+
+ self.assertEquals(expected, str(report))
+
+
+ def test_deeply_nested(self):
+ report = Report()
+ section = Section(name="section")
+ command = Command(name="a command", return_code=0, href="does/not/matter")
+
+ section.add(command)
+ report.add(section)
+
+ expected = json.dumps({"section": {"commands": [{"name": "a command",
+ "return_code": 0,
+ "href": "does/not/matter"}]}})
+
+ self.assertEquals(expected, str(report))
+
+
+class TestPlainReport(unittest.TestCase):
+
+ def setUp(self):
+ self.report = Report()
+ self.section = Section(name="plugin")
+ self.div = PlainTextReport.DIVIDER
+
+ def test_basic(self):
+ self.assertEquals("", str(PlainTextReport(self.report)))
+
+ def test_one_section(self):
+ self.report.add(self.section)
+
+ self.assertEquals("plugin\n" + self.div, str(PlainTextReport(self.report)))
+
+ def test_two_sections(self):
+ section1 = Section(name="first")
+ section2 = Section(name="second")
+ self.report.add(section1, section2)
+
+ self.assertEquals("first\n" + self.div + "\nsecond\n" + self.div, str(PlainTextReport(self.report)))
+
+ def test_command(self):
+ cmd = Command(name="ls -al /foo/bar/baz",
+ return_code=0,
+ href="sos_commands/plugin/ls_-al_foo.bar.baz")
+ self.section.add(cmd)
+ self.report.add(self.section)
+
+ self.assertEquals("plugin\n" + self.div + "\n- commands executed:\n * ls -al /foo/bar/baz",
+ str(PlainTextReport(self.report)))
+
+ def test_copied_file(self):
+ cf = CopiedFile(name="/etc/hosts", href="etc/hosts")
+ self.section.add(cf)
+ self.report.add(self.section)
+
+ self.assertEquals("plugin\n" + self.div + "\n- files copied:\n * /etc/hosts",
+ str(PlainTextReport(self.report)))
+
+ def test_created_file(self):
+ crf = CreatedFile(name="sample.txt")
+ self.section.add(crf)
+ self.report.add(self.section)
+
+ self.assertEquals("plugin\n" + self.div + "\n- files created:\n * sample.txt",
+ str(PlainTextReport(self.report)))
+
+ def test_alert(self):
+ alrt = Alert("this is an alert")
+ self.section.add(alrt)
+ self.report.add(self.section)
+
+ self.assertEquals("plugin\n" + self.div + "\n- alerts:\n ! this is an alert",
+ str(PlainTextReport(self.report)))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/worker_link b/tests/worker_link
new file mode 120000
index 00000000..20be81a3
--- /dev/null
+++ b/tests/worker_link
@@ -0,0 +1 @@
+worker.py \ No newline at end of file
diff --git a/tools/osdetect.py b/tools/osdetect.py
new file mode 100644
index 00000000..fea78cc5
--- /dev/null
+++ b/tools/osdetect.py
@@ -0,0 +1,87 @@
+'''
+Created on Aug 2, 2011
+@author: Keith Robertson
+'''
+
+import pprint
+import os
+import re
+
+class OSTypes:
+ """
+ Utility class with enumerations for the various OSes to facilitate
+ OS detection.
+ """
+ JAVA_OS_NAME_KEY='os.name'
+
+ OS_TYPE_LINUX='Linux'
+ OS_TYPE_LINUX_PAT=re.compile('^%s$' % OS_TYPE_LINUX, re.I)
+ OS_TYPE_WIN='Windows'
+ OS_TYPE_WIN_PAT=re.compile('^%s$' % OS_TYPE_WIN, re.I)
+ OS_TYPE_AIX='AIX'
+ OS_TYPE_AIX_PAT=re.compile('^%s$' % OS_TYPE_AIX, re.I)
+ OS_TYPE_MAC='Mac OS'
+ OS_TYPE_MAC_PAT=re.compile('^%s$' % OS_TYPE_MAC, re.I)
+ OS_TYPE_390='OS/390'
+ OS_TYPE_390_PAT=re.compile('^%s$' % OS_TYPE_390, re.I)
+ OS_TYPE_HPUX='HP-UX'
+ OS_TYPE_HPUX_PAT=re.compile('^%s$' % OS_TYPE_HPUX, re.I)
+
+def printProps():
+ try:
+ from java.lang import System
+ from java.util import Set
+ from java.util import Iterator
+
+ set = System.getProperties().entrySet()
+ it = set.iterator()
+ while it.hasNext():
+ me = it.next();
+ print "Key (%s) Value(%s)" % (me.getKey(), me.getValue())
+ except Exception, e:
+ print "ERROR: unable to print Java properties %s" % e
+
+
+
+def java_detect_os():
+ """
+ Try to load Java packages. If successful then we know we are running
+ in JYthon. Use the JRE to determine what type of OS and return the proper
+ policy.
+ """
+ try:
+ from java.lang import System
+
+ ostype = System.getProperty(OSTypes.JAVA_OS_NAME_KEY)
+ if ostype:
+ if OSTypes.OS_TYPE_LINUX_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_LINUX
+ # Lots of checks here to determine linux version.
+ #return proper policy here
+ elif OSTypes.OS_TYPE_WIN_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_WIN
+ elif OS_TYPE_AIX_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_AIX
+ elif OS_TYPE_MAC_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_MAC
+ elif OS_TYPE_390_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_390
+ elif OS_TYPE_HPUX_PAT.match(ostype):
+ print "Matched %s" % OSTypes.OS_TYPE_HPUX
+ else:
+ raise Exception("Unsupported OS type of %s." % ostype)
+ else:
+ raise Exception("Unable to get %s from JRE's system properties." % OSTypes.JAVA_OS_NAME_KEY)
+ except Exception, e:
+ print "WARN: unable to print Java properties %s" % e
+
+def native_detect_os():
+ print "here"
+
+
+
+if __name__ == '__main__':
+ #printProps()
+ detectOS()
+
+ pass \ No newline at end of file