diff options
Diffstat (limited to 'trunk/src/lib')
42 files changed, 2290 insertions, 0 deletions
diff --git a/trunk/src/lib/sos/__init__.py b/trunk/src/lib/sos/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/trunk/src/lib/sos/__init__.py diff --git a/trunk/src/lib/sos/helpers.py b/trunk/src/lib/sos/helpers.py new file mode 100755 index 00000000..54a86dba --- /dev/null +++ b/trunk/src/lib/sos/helpers.py @@ -0,0 +1,120 @@ +## helpers.py +## Implement policies required for the sos system support tool + +## Copyright (C) 2006 Steve Conklin <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, popen2, fcntl, select, itertools, sys, commands +from time import time +from tempfile import mkdtemp + +def importPlugin(pluginname, name): + """ Import a plugin to extend capabilities of sosreport + """ + try: + plugin = __import__(pluginname, globals(), locals(), [name]) + except ImportError: + return None + return getattr(plugin, name) + + +def sosFindTmpDir(): + """Find a temp directory to form the root for our gathered information + and reports. + """ + workingBase = mkdtemp("","sos_") + return workingBase + + +def makeNonBlocking(afd): + """ Make the file descriptor non-blocking. This prevents deadlocks. + """ + fl = fcntl.fcntl(afd, fcntl.F_GETFL) + try: + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.O_NDELAY) + except AttributeError: + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY) + + +def sosGetCommandOutput(command): + """ Execute a command and gather stdin, stdout, and return status. + Adapted from Python Cookbook - O'Reilly + """ + stime = time() + errdata = '' + status,outdata=commands.getstatusoutput(command) + return (status, outdata, time()-stime) + + +# FIXME: this needs to be made clean and moved to the plugin tools, so +# that it prints nice color output like sysreport if the progress bar +# is not enabled. +def sosStatus(stat): + """ Complete a status line that has been output to the console, + providing pass/fail indication. + """ + if not stat: + print " [ OK ]" + else: + print " [ FAILED ]" + sys.stdout.flush() + return + + +def allEqual(elements): + ''' return True if all the elements are equal, otherwise False. ''' + first_element = elements[0] + for other_element in elements[1:]: + if other_element != first_element: + return False + return True + + +def commonPrefix(*sequences): + ''' return a list of common elements at the start of all sequences, + then a list of lists that are the unique tails of each sequence. ''' + # if there are no sequences at all, we're done + if not sequences: + return [], [] + # loop in parallel on the sequences + common = [] + for elements in itertools.izip(*sequences): + # unless all elements are equal, bail out of the loop + if not allEqual(elements): + break + # got one more common element, append it and keep looping + common.append(elements[0]) + # return the common prefix and unique tails + return common, [ sequence[len(common):] for sequence in sequences ] + +def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir): + ''' return a relative path from path1 equivalent to path path2. + In particular: the empty string, if path1 == path2; + path2, if path1 and path2 have no common prefix. + ''' + common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep)) + if not common: + return path2 # leave path absolute if nothing at all in common + return sep.join( [pardir]*len(u1) + u2 ) + diff --git a/trunk/src/lib/sos/plugins/__init__.py b/trunk/src/lib/sos/plugins/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/trunk/src/lib/sos/plugins/__init__.py diff --git a/trunk/src/lib/sos/plugins/amd.py b/trunk/src/lib/sos/plugins/amd.py new file mode 100644 index 00000000..1b1375cc --- /dev/null +++ b/trunk/src/lib/sos/plugins/amd.py @@ -0,0 +1,31 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class amd(sos.plugintools.PluginBase): + """Amd automounter information + """ + def setup(self): + self.addCopySpec("/etc/amd.*") + self.addCopySpec("/etc/rc.d/init.d/amd") + self.addCopySpec("/etc/sysconfig/amd") + self.collectExtOutput("/bin/rpm -qV am-utils") + self.collectExtOutput("/bin/egrep -e 'automount|pid.*nfs' /proc/mounts") + self.collectExtOutput("/bin/mount | egrep -e 'automount|pid.*nfs'") + self.collectExtOutput("/sbin/chkconfig --list amd") + return + diff --git a/trunk/src/lib/sos/plugins/apache.py b/trunk/src/lib/sos/plugins/apache.py new file mode 100644 index 00000000..c5250fd9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/apache.py @@ -0,0 +1,26 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +from threading import Thread + +class apache(sos.plugintools.PluginBase): + """Apache related information + """ + def setup(self): + self.addCopySpec("/etc/httpd/conf/httpd.conf") + self.addCopySpec("/etc/httpd/conf.d/*.conf") + self.addCopySpec("/var/log/httpd/*") + return + diff --git a/trunk/src/lib/sos/plugins/bootloader.py b/trunk/src/lib/sos/plugins/bootloader.py new file mode 100644 index 00000000..a820069e --- /dev/null +++ b/trunk/src/lib/sos/plugins/bootloader.py @@ -0,0 +1,31 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class bootloader(sos.plugintools.PluginBase): + """Bootloader information + """ + def setup(self): + self.addCopySpec("/etc/lilo.conf") + self.addCopySpec("/etc/milo.conf") + self.addCopySpec("/etc/silo.conf") + self.addCopySpec("/boot/grub/grub.conf") + self.addCopySpec("/boot/grub/device.map") + self.addCopySpec("/boot/efi/elilo.conf") + self.addCopySpec("/boot/yaboot.conf") + + self.collectExtOutput("/sbin/lilo -q") + return + diff --git a/trunk/src/lib/sos/plugins/cluster.py b/trunk/src/lib/sos/plugins/cluster.py new file mode 100644 index 00000000..595d940a --- /dev/null +++ b/trunk/src/lib/sos/plugins/cluster.py @@ -0,0 +1,78 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands, os + +class cluster(sos.plugintools.PluginBase): + """cluster suite and GFS related information + """ + def checkenabled(self): + # enable if any related package is installed + for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", + "rgmanager", "fence", "dlm", "dlm-kernel", "gulm", + "GFS", "GFS-kernel", "lvm2-cluster" ]: + if self.cInfo["policy"].pkgByName(pkg) != None: + return True + + # enable if any related file is present + for fname in [ "/etc/cluster/cluster.conf" ]: + try: os.stat(fname) + except:pass + else: return True + + # no data related to RHCS/GFS exists + return False + + def diagnose(self): + rhelver = self.cInfo["policy"].pkgDictByName("fedora-release")[0] + if rhelver == "6": + # check if the minimum set of packages is installed + # for RHEL4 RHCS(ccs, cman, cman-kernel, magma, magma-plugins, (dlm, dlm-kernel) || gulm, perl-Net-Telnet, rgmanager, fence) + # RHEL4 GFS (GFS, GFS-kernel, ccs, lvm2-cluster, fence) + for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", "perl-Net-Telnet", "rgmanager", "fence" ]: + if self.cInfo["policy"].pkgByName(pkg) == None: + self.addDiagnose("required package is missing: %s" % pkg) + + # check if all the needed daemons are active at sosreport time + # check if they are started at boot time in RHEL4 RHCS (cman, ccsd, rgmanager, fenced) + # and GFS (gfs, ccsd, clvmd, fenced) + for service in [ "cman", "ccsd", "rgmanager", "fence" ]: + if commands.getstatus("/sbin/service %s status" % service): + self.addDiagnose("service %s is not running" % service) + + if not self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService(service): + self.addDiagnose("service %s is not started in default runlevel" % service) + + # FIXME: what locking are we using ? check if packages exist +# if self.cInfo["policy"].pkgByName(pkg) and self.cInfo["policy"].pkgByName(pkg) and not self.cInfo["policy"].pkgByName(pkg) + + def setup(self): + self.collectExtOutput("/sbin/fdisk -l") + self.addCopySpec("/etc/cluster.conf") + self.addCopySpec("/etc/cluster.xml") + self.addCopySpec("/etc/cluster") + self.collectExtOutput("/usr/sbin/rg_test test /etc/cluster/cluster.conf") + self.addCopySpec("/proc/cluster") + self.collectExtOutput("/usr/bin/cman_tool status") + self.collectExtOutput("/usr/bin/cman_tool services") + self.collectExtOutput("/usr/bin/cman_tool -af nodes") + self.collectExtOutput("/usr/bin/ccs_tool lsnode") + self.collectExtOutput("/usr/bin/openais-cfgtool -s") + self.collectExtOutput("/usr/bin/clustat") + return + + def postproc(self): + self.doRegexSub("/etc/cluster/cluster.conf", r"(\s*\<fencedevice\s*.*\s*passwd\s*=\s*)\S+(\")", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/devicemapper.py b/trunk/src/lib/sos/plugins/devicemapper.py new file mode 100644 index 00000000..e517aa3d --- /dev/null +++ b/trunk/src/lib/sos/plugins/devicemapper.py @@ -0,0 +1,38 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class devicemapper(sos.plugintools.PluginBase): + """device-mapper related information (dm, lvm, multipath) + """ + def setup(self): + self.collectExtOutput("/sbin/dmsetup info -c") + self.collectExtOutput("/sbin/dmsetup table") + self.collectExtOutput("/sbin/dmsetup status") + + self.collectExtOutput("/usr/sbin/vgscan -vvv") + self.collectExtOutput("/usr/sbin/vgdisplay -vv", root_symlink = "vgdisplay") + self.collectExtOutput("/usr/sbin/pvscan -v") + self.collectExtOutput("/usr/sbin/lvs -a -o +devices") + self.collectExtOutput("/usr/sbin/pvs -a -v") + self.collectExtOutput("/usr/sbin/vgs -v") + + self.addCopySpec("/etc/lvm/lvm.conf") + + self.addCopySpec("/etc/multipath.conf") + self.addCopySpec("/var/lib/multipath/bindings") + self.collectExtOutput("/sbin/multipath -v4 -ll") + + return diff --git a/trunk/src/lib/sos/plugins/filesys.py b/trunk/src/lib/sos/plugins/filesys.py new file mode 100644 index 00000000..02bde2eb --- /dev/null +++ b/trunk/src/lib/sos/plugins/filesys.py @@ -0,0 +1,47 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class filesys(sos.plugintools.PluginBase): + """information on filesystems + """ + def setup(self): + self.addCopySpec("/proc/filesystems") + self.addCopySpec("/etc/fstab") + self.addCopySpec("/proc/self/mounts") + self.addCopySpec("/proc/mounts") + self.addCopySpec("/proc/mdstat") + self.addCopySpec("/etc/raidtab") + self.addCopySpec("/etc/mdadm.conf") + self.addCopySpec("/etc/auto.master") + self.addCopySpec("/etc/auto.misc") + self.addCopySpec("/etc/auto.net") + + self.collectExtOutput("/bin/df -al", root_symlink = "df") + self.collectExtOutput("/usr/sbin/lsof -b +M -n -l", root_symlink = "lsof") + self.collectExtOutput("/bin/mount -l", root_symlink = "mount") + self.collectExtOutput("/sbin/blkid") + + raiddevs = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep \/ | /bin/egrep -v \"p[0123456789]$\"") + disks = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep -v / | /bin/egrep -v \"[0123456789]$\"") + for disk in raiddevs.split('\n'): + if '' != disk.strip(): + self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) + for disk in disks.split('\n'): + if '' != disk.strip(): + self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) + return + diff --git a/trunk/src/lib/sos/plugins/ftp.py b/trunk/src/lib/sos/plugins/ftp.py new file mode 100644 index 00000000..026954c0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ftp.py @@ -0,0 +1,24 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class ftp(sos.plugintools.PluginBase): + """FTP server related information + """ + def setup(self): + self.addCopySpec("/etc/ftp*") + self.addCopySpec("/etc/vsftpd") + return + diff --git a/trunk/src/lib/sos/plugins/general.py b/trunk/src/lib/sos/plugins/general.py new file mode 100644 index 00000000..06f55228 --- /dev/null +++ b/trunk/src/lib/sos/plugins/general.py @@ -0,0 +1,43 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import glob + +class general(sos.plugintools.PluginBase): + """very basic system information + """ + + optionList = [("syslogsize", "maximum size (in MiB) of logs to collect per syslog file", "", 15)] + + def setup(self): + self.addCopySpec("/etc/redhat-release") + self.addCopySpec("/etc/sysconfig") + self.addCopySpec("/proc/stat") + self.addCopySpec("/var/log/dmesg") + self.addCopySpec("/var/log/messages") + self.addCopySpecLimit("/var/log/messages.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpec("/var/log/secure") + self.addCopySpecLimit("/var/log/secure.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpec("/var/log/sa") + self.addCopySpec("/var/log/up2date") + self.addCopySpec("/etc/exports") + self.collectExtOutput("/bin/hostname", root_symlink = "hostname") + self.collectExtOutput("/bin/date", root_symlink = "date") + self.collectExtOutput("/usr/bin/uptime", root_symlink = "uptime") + return + + def postproc(self): + self.doRegexSub("/etc/sysconfig/rhn/up2date", r"(\s*proxyPassword\s*=\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/hardware.py b/trunk/src/lib/sos/plugins/hardware.py new file mode 100644 index 00000000..f8eeda88 --- /dev/null +++ b/trunk/src/lib/sos/plugins/hardware.py @@ -0,0 +1,55 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class hardware(sos.plugintools.PluginBase): + """hardware related information + """ + def setup(self): + self.addCopySpec("/proc/partitions") + self.addCopySpec("/proc/cpuinfo") + self.addCopySpec("/proc/meminfo") + self.addCopySpec("/proc/ioports") + self.addCopySpec("/proc/interrupts") + self.addCopySpec("/proc/scsi") + self.addCopySpec("/proc/dma") + self.addCopySpec("/proc/devices") + self.addCopySpec("/proc/rtc") + self.addCopySpec("/proc/ide") + self.addCopySpec("/proc/bus") + self.addCopySpec("/etc/stinit.def") + self.addCopySpec("/etc/sysconfig/hwconf") + self.addCopySpec("/proc/chandev") + self.addCopySpec("/proc/dasd") + self.addCopySpec("/proc/s390dbf/tape") + self.collectExtOutput("/usr/share/rhn/up2dateclient/hardware.py") + self.collectExtOutput("""/bin/echo "lspci" ; /bin/echo ; /sbin/lspci ; /bin/echo ; /bin/echo ; /bin/echo "lspci -nvv" ; /bin/echo ; /sbin/lspci -nvv""", suggest_filename = "lspci", root_symlink = "lspci") + + # FIXME: what is this for? + self.collectExtOutput("/bin/dmesg | /bin/grep -e 'e820.' -e 'agp.'") + + # FIXME: what is this for? + tmpreg = "" + for hwmodule in commands.getoutput('cat /lib/modules/$(uname -r)/modules.pcimap | cut -d " " -f 1 | grep "[:alpha:]" | sort -u').split("\n"): + hwmodule = hwmodule.strip() + if len(hwmodule): + tmpreg = tmpreg + "|" + hwmodule + self.collectExtOutput("/bin/dmesg | /bin/egrep '(%s)'" % tmpreg[1:]) + + self.collectExtOutput("/sbin/lsusb") + self.collectExtOutput("/usr/bin/lshal") + return + diff --git a/trunk/src/lib/sos/plugins/initrd.py b/trunk/src/lib/sos/plugins/initrd.py new file mode 100644 index 00000000..83356548 --- /dev/null +++ b/trunk/src/lib/sos/plugins/initrd.py @@ -0,0 +1,28 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import glob + +class initrd(sos.plugintools.PluginBase): + """initrd related information + """ + def setup(self): + for initrd in glob.glob('/boot/initrd-*.img'): + self.collectExtOutput("/bin/zcat "+initrd+" | /bin/cpio "+ + "--extract --to-stdout init" ) + return + + def defaultenabled(self): + return False diff --git a/trunk/src/lib/sos/plugins/kernel.py b/trunk/src/lib/sos/plugins/kernel.py new file mode 100644 index 00000000..09381b38 --- /dev/null +++ b/trunk/src/lib/sos/plugins/kernel.py @@ -0,0 +1,131 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands, os, re + +class kernel(sos.plugintools.PluginBase): + """kernel related information + """ + optionList = [("modinfo", 'Gathers module information on all modules', 'fast', 1), + ('sysrq', 'Trigger SysRq dumps', 'fast', 0)] + moduleFile = "" + taintList = [ + {'regex':'mvfs*', 'description':'Clearcase module'}, + {'regex':'vnode*', 'description':'Clearcase module'}, + {'regex':'vxfs*', 'description':'Veritas file system module'}, + {'regex':'vxportal*', 'description':'Veritas module'}, + {'regex':'vxdmp*', 'description':'Veritas dynamic multipathing module'}, + {'regex':'vxio*', 'description':'Veritas module'}, + {'regex':'vxspec*"', 'description':'Veritas module'}, + {'regex':'dcd*', 'description':'Dell OpenManage Server Administrator module'}, + {'regex':'ocfs', 'description':'Oracle cluster filesystem module'}, + {'regex':'oracle*', 'description':'Oracle module'}, + {'regex':'vmnet*', 'description':'VMware module'}, + {'regex':'vmmon*', 'description':'VMware module'}, + {'regex':'egenera*', 'description':'Egenera module'}, + {'regex':'emcp*', 'description':'EMC module'}, + {'regex':'ocfs*', 'description':'OCFS module'}, + {'regex':'nvidia', 'description':'NVidia module'}, + {'regex':'ati-', 'description':'ATI module'} + ] + + # HP + # + # + + + def setup(self): + self.collectExtOutput("/bin/uname -a", root_symlink = "uname") + self.moduleFile = self.collectOutputNow("/sbin/lsmod", root_symlink = "lsmod") + if self.isOptionEnabled('modinfo'): + runcmd = "" + for kmod in commands.getoutput('/sbin/lsmod | /bin/cut -f1 -d" " 2>/dev/null | /bin/grep -v Module 2>/dev/null').split('\n'): + if '' != kmod.strip(): + runcmd = runcmd + " " + kmod + if len(runcmd): + self.collectExtOutput("/sbin/modinfo " + runcmd) + self.collectExtOutput("/sbin/ksyms") + self.addCopySpec("/proc/filesystems") + self.addCopySpec("/proc/ksyms") + self.addCopySpec("/proc/slabinfo") + kver = commands.getoutput('/bin/uname -r') + depfile = "/lib/modules/%s/modules.dep" % (kver,) + self.addCopySpec(depfile) + self.addCopySpec("/etc/conf.modules") + self.addCopySpec("/etc/modules.conf") + self.addCopySpec("/etc/modprobe.conf") + self.collectExtOutput("/usr/sbin/dmidecode", root_symlink = "dmidecode") + self.collectExtOutput("/usr/sbin/dkms status") + self.addCopySpec("/proc/cmdline") + self.addCopySpec("/proc/driver") + self.addCopySpec("/proc/sys/kernel/tainted") + # trigger some sysrq's. I'm not sure I like doing it this way, but + # since we end up with the sysrq dumps in syslog whether we run the + # syslog report before or after this, I suppose I can live with it. + if self.isOptionEnabled('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK) and os.access("/proc/sys/kernel/sysrq", os.R_OK): + sysrq_state = commands.getoutput("/bin/cat /proc/sys/kernel/sysrq") + commands.getoutput("/bin/echo 1 > /proc/sys/kernel/sysrq") + for key in ['m', 'p', 't']: + commands.getoutput("/bin/echo %s > /proc/sysrq-trigger" % (key,)) + commands.getoutput("/bin/echo %s > /proc/sys/kernel/sysrq" % (sysrq_state,)) + # No need to grab syslog here if we can't trigger sysrq, so keep this + # inside the if + self.addCopySpec("/var/log/messages") + + return + + def analyze(self): + infd = open("/proc/modules", "r") + modules = infd.readlines() + infd.close() + + for modname in modules: + modname=modname.split(" ")[0] + modinfo_srcver = commands.getoutput("/sbin/modinfo -F srcversion %s" % modname) + if not os.access("/sys/module/%s/srcversion" % modname, os.R_OK): + continue + infd = open("/sys/module/%s/srcversion" % modname, "r") + sys_srcver = infd.read().strip("\n") + infd.close() + if modinfo_srcver != sys_srcver: + self.addAlert("Loaded module %s differs from the one present on the file-system") + + # this would be a good moment to check the module's signature + # but at the moment there's no easy way to do that outside of + # the kernel. i will probably need to write a C lib (derived from + # the kernel sources to do this verification. + + savedtaint = os.path.join(self.cInfo['dstroot'], "/proc/sys/kernel/tainted") + infd = open(savedtaint, "r") + line = infd.read() + infd.close() + line = line.strip() + if (line != "0"): + self.addAlert("Kernel taint flag is <%s>\n" % line) + + + infd = open(self.moduleFile, "r") + modules = infd.readlines() + infd.close() + + #print(modules) + for tainter in self.taintList: + p = re.compile(tainter['regex']) + for line in modules: + if p.match(line) != None: + # found a taint match, create an alert + moduleName = line.split()[0] + self.addAlert("Check for tainted kernel by module %s, which is %s" % (moduleName, tainter['description'])) + return diff --git a/trunk/src/lib/sos/plugins/ldap.py b/trunk/src/lib/sos/plugins/ldap.py new file mode 100644 index 00000000..318a3ba9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ldap.py @@ -0,0 +1,27 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class ldap(sos.plugintools.PluginBase): + """LDAP related information + """ + def setup(self): + self.addCopySpec("/etc/ldap.conf") + self.addCopySpec("/etc/openldap") + return + + def postproc(self): + self.doRegexSub("/etc/ldap.conf", r"(\s*bindpw\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/libraries.py b/trunk/src/lib/sos/plugins/libraries.py new file mode 100644 index 00000000..70b63dba --- /dev/null +++ b/trunk/src/lib/sos/plugins/libraries.py @@ -0,0 +1,24 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class libraries(sos.plugintools.PluginBase): + """information on shared libraries + """ + def setup(self): + self.addCopySpec("/etc/ld.so.conf") + self.addCopySpec("/etc/ld.so.conf.d") + return + diff --git a/trunk/src/lib/sos/plugins/mail.py b/trunk/src/lib/sos/plugins/mail.py new file mode 100644 index 00000000..0d4dda29 --- /dev/null +++ b/trunk/src/lib/sos/plugins/mail.py @@ -0,0 +1,25 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class mail(sos.plugintools.PluginBase): + """mail server related information + """ + def setup(self): + self.addCopySpec("/etc/mail") + self.addCopySpec("/etc/postfix/main.cf") + self.addCopySpec("/etc/postfix/master.cf") + return + diff --git a/trunk/src/lib/sos/plugins/memory.py b/trunk/src/lib/sos/plugins/memory.py new file mode 100644 index 00000000..7fbe39c3 --- /dev/null +++ b/trunk/src/lib/sos/plugins/memory.py @@ -0,0 +1,30 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class memory(sos.plugintools.PluginBase): + """memory usage information + """ + def setup(self): + self.addCopySpec("/proc/pci") + self.addCopySpec("/proc/meminfo") + self.addCopySpec("/proc/vmstat") + self.addCopySpec("/proc/slabinfo") + + self.collectExtOutput("/bin/dmesg | grep -e 'e820.' -e 'aperature.'") + self.collectExtOutput("/usr/bin/free", root_symlink = "free") + self.collectExtOutput("/usr/bin/free -m") + return + diff --git a/trunk/src/lib/sos/plugins/named.py b/trunk/src/lib/sos/plugins/named.py new file mode 100644 index 00000000..68d56ca0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/named.py @@ -0,0 +1,36 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands +import os + +class named(sos.plugintools.PluginBase): + """named related information + """ + def setup(self): + dnsdir = "" + self.addCopySpec("/etc/named.boot") + self.addCopySpec("/etc/named.conf") + self.addCopySpec("/etc/sysconfig/named") + if os.access("/etc/named.conf", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.conf | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if os.access("/etc/named.boot", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.boot | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if '' != dnsdir.strip(): + self.addCopySpec(dnsdir) + self.addForbiddenPath('/var/named/chroot/proc') + self.addForbiddenPath('/var/named/chroot/dev') + return + diff --git a/trunk/src/lib/sos/plugins/networking.py b/trunk/src/lib/sos/plugins/networking.py new file mode 100644 index 00000000..54723663 --- /dev/null +++ b/trunk/src/lib/sos/plugins/networking.py @@ -0,0 +1,75 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import os,re,commands + +class networking(sos.plugintools.PluginBase): + """network related information + """ + optionList = [("traceroute", "collects a traceroute to rhn.redhat.com", "slow", 0)] + + def get_interface_name(self,ifconfigFile): + """Return a dictionary for which key are interface name according to the + output of ifconifg-a stored in ifconfigFile. + """ + out={} + if(os.path.isfile(ifconfigFile)): + f=open(ifconfigFile,'r') + content=f.read() + f.close() + reg=re.compile(r"^(eth\d+)\D",re.MULTILINE) + for name in reg.findall(content): + out[name]=1 + return out + + def collectIPTable(self,tablename): + """ When running the iptables command, it unfortunately auto-loads + the modules before trying to get output. Some people explicitly + don't want this, so check if the modules are loaded before running + the command. If they aren't loaded, there can't possibly be any + relevant rules in that table """ + + cmd = "/sbin/iptables -t "+tablename+" -nvL" + + (status, output) = commands.getstatusoutput("/sbin/lsmod | grep -q "+tablename) + if status == 0: + self.collectExtOutput(cmd) + else: + self.writeTextToCommand(cmd,"IPTables module "+tablename+" not loaded\n") + + def setup(self): + self.addCopySpec("/etc/nsswitch.conf") + self.addCopySpec("/etc/yp.conf") + self.addCopySpec("/etc/inetd.conf") + self.addCopySpec("/etc/xinetd.conf") + self.addCopySpec("/etc/xinetd.d") + self.addCopySpec("/etc/host*") + self.addCopySpec("/etc/resolv.conf") + ifconfigFile=self.collectExtOutput("/sbin/ifconfig -a", root_symlink = "ifconfig") + self.collectExtOutput("/sbin/route -n", root_symlink = "route") + self.collectExtOutput("/sbin/ipchains -nvL") + self.collectIPTable("filter") + self.collectIPTable("nat") + self.collectIPTable("mangle") + self.collectExtOutput("/bin/netstat -nap") + if ifconfigFile: + for eth in self.get_interface_name(ifconfigFile): + self.collectExtOutput("/sbin/ethtool "+eth) + if self.isOptionEnabled("traceroute"): + # The semicolon prevents the browser from thinking this is a link when viewing the report + self.collectExtOutput("/bin/traceroute rhn.redhat.com;") + + return + diff --git a/trunk/src/lib/sos/plugins/nfsserver.py b/trunk/src/lib/sos/plugins/nfsserver.py new file mode 100644 index 00000000..6c894177 --- /dev/null +++ b/trunk/src/lib/sos/plugins/nfsserver.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class nfsserver(sos.plugintools.PluginBase): + """NFS server-related information + """ + def setup(self): + self.addCopySpec("/etc/exports") + self.collectExtOutput("/usr/sbin/rpcinfo -p localhost") + self.collectExtOutput("/usr/sbin/nfsstat") + return + diff --git a/trunk/src/lib/sos/plugins/pam.py b/trunk/src/lib/sos/plugins/pam.py new file mode 100644 index 00000000..8164bba3 --- /dev/null +++ b/trunk/src/lib/sos/plugins/pam.py @@ -0,0 +1,25 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class pam(sos.plugintools.PluginBase): + """PAM related information + """ + def setup(self): + self.addCopySpec("/etc/pam.d") + self.addCopySpec("/etc/security") + self.collectExtOutput("/bin/ls -laF /lib/security/pam_*so") + return + diff --git a/trunk/src/lib/sos/plugins/printing.py b/trunk/src/lib/sos/plugins/printing.py new file mode 100644 index 00000000..76a476eb --- /dev/null +++ b/trunk/src/lib/sos/plugins/printing.py @@ -0,0 +1,24 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class printing(sos.plugintools.PluginBase): + """printing related information (cups) + """ + def setup(self): + self.addCopySpec("/etc/cups/*.conf") + self.addCopySpec("/var/log/cups") + return + diff --git a/trunk/src/lib/sos/plugins/process.py b/trunk/src/lib/sos/plugins/process.py new file mode 100644 index 00000000..7ed13a5e --- /dev/null +++ b/trunk/src/lib/sos/plugins/process.py @@ -0,0 +1,24 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class process(sos.plugintools.PluginBase): + """process information + """ + def setup(self): + self.collectExtOutput("/bin/ps auxww", root_symlink = "ps") + self.collectExtOutput("/usr/bin/pstree", root_symlink = "pstree") + return + diff --git a/trunk/src/lib/sos/plugins/radius.py b/trunk/src/lib/sos/plugins/radius.py new file mode 100644 index 00000000..7ab53810 --- /dev/null +++ b/trunk/src/lib/sos/plugins/radius.py @@ -0,0 +1,29 @@ +## Copyright (C) 2007 Navid Sheikhol-Eslami <navid@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 sos.plugintools + +class radius(sos.plugintools.PluginBase): + """radius related information + """ + def setup(self): + self.addCopySpec("/etc/raddb") + self.addCopySpec("/etc/pam.d/radiusd") + return + + def postproc(self): + self.doRegexSub("/etc/raddb/sql.conf", r"(\s*password\s*=\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/rhn.py b/trunk/src/lib/sos/plugins/rhn.py new file mode 100644 index 00000000..049e51bc --- /dev/null +++ b/trunk/src/lib/sos/plugins/rhn.py @@ -0,0 +1,91 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +from sos.plugintools import PluginBase + +class rhn(PluginBase): + """RHN server related information + """ + def checkenabled(self): + # XXX check for the presence of requisite packages + satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools") + proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools") + if not satellite and not proxy: + return False + return True + + def setup(self): + # + # First, grab things needed from both Satellite and Proxy systems + # + # TODO: add chain load so we can use existing modules for httpd, &c. + # + + # basic RHN logs and configs + self.addCopySpec("/var/log/rhn*") + self.addCopySpec("/etc/rhn") + self.collectExtOutput("/usr/share/rhn/up2date_client/hardware.py") + + # httpd + self.addCopySpec("/etc/httpd/conf") + self.addCopySpec("/var/log/httpd") + + # RPM manifests + self.collectExtOutput("/bin/rpm -qa --last | sort") + + # monitoring scout logs + self.addCopySpec("/home/nocpulse/var/*.log*") + self.addCopySpec("/home/nocpulse/var/commands/*.log*") + + satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools") + proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools") + + # + # Now, go for product-specific data + # + if satellite: + self.setupSatellite(satellite) + + if proxy: + self.setupProxy(proxy) + + def setupSatellite(self, satellite): + self.collectExtOutput("/usr/bin/rhn-schema-version") + self.collectExtOutput("/usr/bin/rhn-charsets") + + # oracle + self.addCopySpec("/etc/tnsnames.ora") + + # tomcat (4.x and newer satellites only) + if not self.cInfo["policy"].pkgNVRA(satellite)[1].startswith("3."): + self.addCopySpec("/etc/tomcat5") + self.addCopySpec("/var/log/tomcat5") + + # jabberd + # - logs to /var/log/messages + self.addCopySpec("/etc/jabberd") + + # SSL build + self.addCopySpec("/root/ssl-build") + + # monitoring logs + self.addCopySpec("/opt/notification/var/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + def setupProxy(self, proxy): + # squid + self.addCopySpec("/etc/squid") + self.addCopySpec("/var/log/squid") + diff --git a/trunk/src/lib/sos/plugins/rpm.py b/trunk/src/lib/sos/plugins/rpm.py new file mode 100644 index 00000000..b6fdb699 --- /dev/null +++ b/trunk/src/lib/sos/plugins/rpm.py @@ -0,0 +1,33 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class rpm(sos.plugintools.PluginBase): + """RPM information + """ + optionList = [("rpmq", "Queries for package information via rpm -q", "fast", 1), + ("rpmva", "Runs a verify on all packages", "slow", 1)] + + def setup(self): + self.addCopySpec("/var/log/rpmpkgs") + + if self.isOptionEnabled("rpmq"): + self.collectExtOutput("/bin/rpm -qa --qf \"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}\n\"", root_symlink = "installed-rpms") + + if self.isOptionEnabled("rpmva"): + self.eta_weight += 800 # this plugins takes 200x longer (for ETA) + self.collectExtOutput("/bin/rpm -Va", root_symlink = "rpm-Va") + return + diff --git a/trunk/src/lib/sos/plugins/samba.py b/trunk/src/lib/sos/plugins/samba.py new file mode 100644 index 00000000..ec65370e --- /dev/null +++ b/trunk/src/lib/sos/plugins/samba.py @@ -0,0 +1,26 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class samba(sos.plugintools.PluginBase): + """Samba related information + """ + def setup(self): + self.addCopySpec("/etc/samba") + self.addCopySpec("/var/log/samba/*") + self.collectExtOutput("/usr/bin/wbinfo -g") + self.collectExtOutput("/usr/bin/wbinfo -u") + return + diff --git a/trunk/src/lib/sos/plugins/satellite.py b/trunk/src/lib/sos/plugins/satellite.py new file mode 100644 index 00000000..930d11b0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/satellite.py @@ -0,0 +1,53 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class satellite(sos.plugintools.PluginBase): + """RHN Satellite related information + """ + + def defaultenabled(self): + return False + + def setup(self): + self.addCopySpec("/etc/httpd/conf") + self.addCopySpec("/etc/rhn") + self.addCopySpec("/etc/sysconfig/rhn") + self.addCopySpec("/etc/tnsnames.ora") + self.addCopySpec("/var/log/httpd") # httpd-logs + self.addCopySpec("/var/log/rhn*") # rhn-logs + self.addCopySpec("/var/log/rhn/rhn-database-installation.log") + self.addCopySpec("/etc/jabberd") + + # tomcat for satellite 400+ + self.addCopySpec("/etc/tomcat5") + self.addCopySpec("/var/log/tomcat5") + + # all these used to go in $DIR/mon-logs + self.addCopySpec("/opt/notification/var/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + self.addCopySpec("/home/nocpulse/var/*.log*") + self.addCopySpec("/home/nocpulse/var/commands/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + self.addCopySpec("/root/ssl-build") + self.addCopySpec("rpm -qa --last") # $DIR/rpm-manifest + self.addCopySpec("/usr/bin/rhn-schema-version") # $DIR/database-schema-version + self.addCopySpec("/usr/bin/rhn-charsets") # $DIR/database-character-sets + + return diff --git a/trunk/src/lib/sos/plugins/selinux.py b/trunk/src/lib/sos/plugins/selinux.py new file mode 100644 index 00000000..897c3991 --- /dev/null +++ b/trunk/src/lib/sos/plugins/selinux.py @@ -0,0 +1,36 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class selinux(sos.plugintools.PluginBase): + """selinux related information + """ + def setup(self): + self.addCopySpec("/etc/selinux/*") + self.collectExtOutput("/usr/bin/selinuxconfig") + self.collectExtOutput("/usr/sbin/sestatus", root_symlink = "sestatus") + self.collectExtOutput("/bin/rpm -q -V selinux-policy-targeted") + self.collectExtOutput("/bin/rpm -q -V selinux-policy-strict") + return + + def checkenabled(self): + # is selinux enabled ? + try: + if commands.getoutput("/usr/sbin/sestatus").split(":")[1].strip() == "disabled": + return False + except: + pass + return True diff --git a/trunk/src/lib/sos/plugins/sendmail.py b/trunk/src/lib/sos/plugins/sendmail.py new file mode 100644 index 00000000..d95b5d7b --- /dev/null +++ b/trunk/src/lib/sos/plugins/sendmail.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class sendmail(sos.plugintools.PluginBase): + """sendmail information + """ + def setup(self): + self.addCopySpec("/etc/mail/*") + self.addCopySpec("/var/log/maillog") + self.collectExtOutput("/sbin/chkconfig --list sendmail") + return + diff --git a/trunk/src/lib/sos/plugins/squid.py b/trunk/src/lib/sos/plugins/squid.py new file mode 100644 index 00000000..4544ef9a --- /dev/null +++ b/trunk/src/lib/sos/plugins/squid.py @@ -0,0 +1,23 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class squid(sos.plugintools.PluginBase): + """squid related information + """ + def setup(self): + self.addCopySpec("/etc/squid/squid.conf") + return + diff --git a/trunk/src/lib/sos/plugins/ssh.py b/trunk/src/lib/sos/plugins/ssh.py new file mode 100644 index 00000000..92a4c9e5 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ssh.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class ssh(sos.plugintools.PluginBase): + """ssh-related information + """ + def setup(self): + self.addCopySpec("/etc/ssh/ssh_config") + self.addCopySpec("/etc/ssh/sshd_config") + self.collectExtOutput("/sbin/chkconfig --list sshd") + return + diff --git a/trunk/src/lib/sos/plugins/startup.py b/trunk/src/lib/sos/plugins/startup.py new file mode 100644 index 00000000..02361393 --- /dev/null +++ b/trunk/src/lib/sos/plugins/startup.py @@ -0,0 +1,25 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class startup(sos.plugintools.PluginBase): + """startup information + """ + def setup(self): + self.addCopySpec("/etc/rc.d") + + self.collectExtOutput("/sbin/chkconfig --list", root_symlink = "chkconfig") + return + diff --git a/trunk/src/lib/sos/plugins/system.py b/trunk/src/lib/sos/plugins/system.py new file mode 100644 index 00000000..19d7859e --- /dev/null +++ b/trunk/src/lib/sos/plugins/system.py @@ -0,0 +1,31 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class system(sos.plugintools.PluginBase): + """core system related information + """ + def setup(self): + self.addCopySpec("/proc/sys") + self.addCopySpec("/etc/sysctl.conf") + self.addCopySpec("/etc/cron*") + self.addCopySpec("/etc/syslog.conf") + self.addCopySpec("/etc/ntp.conf") + self.addCopySpec("/etc/ntp/step-tickers") + self.addCopySpec("/etc/ntp/ntpservers") + self.addCopySpec("/etc/auto.*") + + return + diff --git a/trunk/src/lib/sos/plugins/systemtap.py b/trunk/src/lib/sos/plugins/systemtap.py new file mode 100644 index 00000000..b99ce0cf --- /dev/null +++ b/trunk/src/lib/sos/plugins/systemtap.py @@ -0,0 +1,29 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class systemtap(sos.plugintools.PluginBase): + """SystemTap pre-requisites information + """ + def setup(self): + # requires systemtap, systemtap-runtime, kernel-devel, + # kernel-debuginfo, kernel-debuginfo-common + self.collectExtOutput("/bin/rpm -qa | /bin/egrep -e kernel.*`uname -r` -e systemtap -e elfutils | sort") + self.collectExtOutput("/usr/bin/stap -V 2") + self.collectExtOutput("/bin/uname -r") + return + diff --git a/trunk/src/lib/sos/plugins/x11.py b/trunk/src/lib/sos/plugins/x11.py new file mode 100644 index 00000000..9b7b7ac9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/x11.py @@ -0,0 +1,26 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class x11(sos.plugintools.PluginBase): + """X related information + """ + def setup(self): + self.addCopySpec("/etc/X11") + self.addCopySpec("/var/log/Xorg.*.log") + self.addCopySpec("/var/log/XFree86.*.log") + self.collectExtOutput("/bin/dmesg | grep -e 'agpgart.'") + return + diff --git a/trunk/src/lib/sos/plugins/xen.py b/trunk/src/lib/sos/plugins/xen.py new file mode 100644 index 00000000..d6daec63 --- /dev/null +++ b/trunk/src/lib/sos/plugins/xen.py @@ -0,0 +1,82 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import os, commands +from stat import * + +class xen(sos.plugintools.PluginBase): + """Xen related information + """ + def determineXenHost(self): + if os.access("/proc/acpi/dsdt", os.R_OK): + (status, output) = commands.getstatusoutput("/usr/bin/strings /proc/acpi/dsdt | grep -q int-xen") + if status == 0: + return "hvm" + + if os.access("/proc/xen/capabilities", os.R_OK): + (status, output) = commands.getstatusoutput("grep -q control_d /proc/xen/capabilities") + if status == 0: + return "dom0" + else: + return "domU" + return "baremetal" + + def checkenabled(self): + if self.determineXenHost() == "baremetal": + return False + return True + + def domCollectProc(self): + self.addCopySpec("/proc/xen/balloon") + self.addCopySpec("/proc/xen/capabilities") + self.addCopySpec("/proc/xen/xsd_kva") + self.addCopySpec("/proc/xen/xsd_port") + # determine if CPU has PAE support + self.collectExtOutput("/bin/grep pae /proc/cpuinfo") + # determine if CPU has Intel-VT or AMD-V support + self.collectExtOutput("/bin/egrep -e 'vmx|svm' /proc/cpuinfo") + + def setup(self): + host_type = self.determineXenHost() + if host_type == "domU": + # we should collect /proc/xen and /sys/hypervisor + self.domCollectProc() + # determine if hardware virtualization support is enabled + # in BIOS: /sys/hypervisor/properties/capabilities + self.addCopySpec("/sys/hypervisor") + elif host_type == "hvm": + # what do we collect here??? + pass + elif host_type == "dom0": + # default of dom0, collect lots of system information + self.addCopySpec("/var/log/xen") + self.addCopySpec("/etc/xen") + self.collectExtOutput("/usr/bin/xenstore-ls") + self.collectExtOutput("/usr/sbin/xm dmesg") + self.collectExtOutput("/usr/sbin/xm info") + self.collectExtOutput("/usr/sbin/brctl show") + self.domCollectProc() + self.addCopySpec("/sys/hypervisor") + # FIXME: we *might* want to collect things in /sys/bus/xen*, + # /sys/class/xen*, /sys/devices/xen*, /sys/modules/blk*, + # /sys/modules/net*, but I've never heard of them actually being + # useful, so I'll leave it out for now + else: + # for bare-metal, we don't have to do anything special + return + + self.addCustomText("Xen hostType: "+host_type) + return + diff --git a/trunk/src/lib/sos/plugins/xinetd.py b/trunk/src/lib/sos/plugins/xinetd.py new file mode 100644 index 00000000..715c831f --- /dev/null +++ b/trunk/src/lib/sos/plugins/xinetd.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@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 sos.plugintools + +class xinetd(sos.plugintools.PluginBase): + """xinetd information + """ + def setup(self): + self.addCopySpec("/etc/xinetd.conf") + self.addCopySpec("/etc/xinetd.d/*") + self.collectExtOutput("/sbin/chkconfig --list xinetd") + return + diff --git a/trunk/src/lib/sos/plugins/yum.py b/trunk/src/lib/sos/plugins/yum.py new file mode 100644 index 00000000..89102c38 --- /dev/null +++ b/trunk/src/lib/sos/plugins/yum.py @@ -0,0 +1,37 @@ +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class yum(sos.plugintools.PluginBase): + """yum information + """ + + def defaultenabled(self): + # enable with -e or -o + return False + + def setup(self): + # Pull all yum related information + self.addCopySpec("/etc/yum") + self.addCopySpec("/etc/yum.repos.d") + self.addCopySpec("/etc/yum.conf") + self.addCopySpec("/var/log/yum.log") + + # Get a list of channels the machine is subscribed to. + self.collectExtOutput("/bin/echo \"repo list\" | /usr/bin/yum shell") + # List various information about available packages + self.collectExtOutput("/usr/bin/yum list") + + return diff --git a/trunk/src/lib/sos/plugintools.py b/trunk/src/lib/sos/plugintools.py new file mode 100644 index 00000000..238036ce --- /dev/null +++ b/trunk/src/lib/sos/plugintools.py @@ -0,0 +1,568 @@ +## plugintools.py +## This exports methods available for use by plugins for sos + +## Copyright (C) 2006 Steve Conklin <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 threading import Thread, activeCount +import os, os.path, sys, string, itertools, glob, re, traceback +import logging +from stat import * +from time import time + +class PluginBase: + """ + Base class for plugins + """ + def __init__(self, pluginname, commons): + # pylint: disable-msg = E0203 + try: + len(self.optionList) + except: + self.optionList = [] + # pylint: enable-msg = E0203 + self.copiedFiles = [] + self.copiedDirs = [] + self.executedCommands = [] + self.diagnose_msgs = [] + self.alerts = [] + self.customText = "" + self.optNames = [] + self.optParms = [] + self.piName = pluginname + self.cInfo = commons + self.forbiddenPaths = [] + self.copyPaths = [] + self.collectProgs = [] + self.thread = None + self.pid = None + self.eta_weight = 1 + self.time_start = None + self.time_stop = None + + self.soslog = logging.getLogger('sos') + + # get the option list into a dictionary + for opt in self.optionList: + self.optNames.append(opt[0]) + self.optParms.append({'desc':opt[1], 'speed':opt[2], 'enabled':opt[3]}) + + # Method for applying regexp substitutions + def doRegexSub(self, srcpath, regexp, subst): + '''Apply a regexp substitution to a file archived by sosreport. + ''' + if len(self.copiedFiles): + for afile in self.copiedFiles: + if afile['srcpath'] == srcpath: + abspath = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) + try: + fp = open(abspath, 'r') + tmpout, occurs = re.subn( regexp, subst, fp.read() ) + fp.close() + if occurs > 0: + fp = open(abspath,'w') + fp.write(tmpout) + fp.close() + return occurs + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (abspath,e)) + break + return False + + # Methods for copying files and shelling out + def doCopyFileOrDir(self, srcpath): + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0915 + ''' Copy file or directory to the destination tree. If a directory, then everything + below it is recursively copied. A list of copied files are saved for use later + in preparing a report + ''' + copyProhibited = 0 + for path in self.forbiddenPaths: + if ( srcpath.count(path) > 0 ): + copyProhibited = 1 + + if copyProhibited: + return '' + + if os.path.islink(srcpath): + # This is a symlink - We need to also copy the file that it points to + # file and dir symlinks ar ehandled the same + link = os.readlink(srcpath) + if os.path.isabs(link): + # the link was an absolute path, and will not point to the new + # tree. We must adjust it. + + # What's the name of the symlink on the dest tree? + dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) + + # make sure the dst dir exists + if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))): + # create the directory + os.makedirs(os.path.dirname(dstslname)) + + dstsldir = os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep)) + # Create the symlink on the dst tree + rpth = sosRelPath(os.path.dirname(dstslname), dstsldir) + os.symlink(rpth, dstslname) + else: + # no adjustment, symlink is the relative path + dstslname = link + + if os.path.isdir(srcpath): + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': + pass + else: + try: + abspath = self.doCopyFileOrDir(srcpath+'/'+afile) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.warning(traceback.format_exc()) + + # if on forbidden list, abspath is null + if not abspath == '': + dstslname = sosRelPath(self.cInfo['rptdir'], abspath) + self.copiedDirs.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) + else: + try: + dstslname, abspath = self.__copyFile(srcpath) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) + self.cInfo['xmlreport'].add_file(srcpath,os.stat(srcpath)) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (srcpath, e)) + + return abspath + + else: + if not os.path.exists(srcpath): + self.soslog.debug("File or directory %s does not exist\n" % srcpath) + elif os.path.isdir(srcpath): + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': + pass + else: + self.doCopyFileOrDir(srcpath+'/'+afile) + else: + # This is not a directory or a symlink + tdstpath, abspath = self.__copyFile(srcpath) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list + return abspath + + def __copyFile(self, src): + """ call cp to copy a file, collect return status and output. Returns the + destination file name. + """ + try: + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput("/bin/cp --parents -P --preserve=mode,ownership,timestamps,links " + src +" " + self.cInfo['dstroot']) + if status: + self.soslog.debug(shout) + abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) + relpath = sosRelPath(self.cInfo['rptdir'], abspath) + return relpath, abspath + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception,e: + self.soslog.warning("Problem copying file %s (%s)" % (src, e)) + + def addForbiddenPath(self, forbiddenPath): + """Specify a path to not copy, even if it's part of a copyPaths[] entry. + Note: do NOT use globs here. + """ + self.forbiddenPaths.append(forbiddenPath) + + def getAllOptions(self): + """ + return a list of all options selected + """ + return (self.optNames, self.optParms) + + def setOption(self, optionname, enable): + ''' enable or disable the named option. + ''' + for name, parms in zip(self.optNames, self.optParms): + if name == optionname: + parms['enabled'] = enable + + def isOptionEnabled(self, optionname): + ''' see whether the named option is enabled. + ''' + for name, parms in zip(self.optNames, self.optParms): + if name == optionname: + return parms['enabled'] + # nonexistent options aren't enabled. + return 0 + + def addCopySpecLimit(self,fname,sizelimit = None): + """Add a file specification (with limits) + """ + files = glob.glob(fname) + files.sort() + cursize = 0 + for flog in files: + cursize += os.stat(flog)[ST_SIZE] + if sizelimit and (cursize / 1024 / 1024) > sizelimit: + break + self.addCopySpec(flog) + + def addCopySpec(self, copyspec): + """ Add a file specification (can be file, dir,or shell glob) to be + copied into the sosreport by this module + """ + # Glob case handling is such that a valid non-glob is a reduced glob + for filespec in glob.glob(copyspec): + self.copyPaths.append(filespec) + + def copyFileGlob(self, srcglob): + """ Deprecated - please modify modules to use addCopySpec() + """ + sys.stderr.write("Warning: thecopyFileGlob() function has been deprecated. Please") + sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.") + self.addCopySpec(srcglob) + + def copyFileOrDir(self, srcpath): + """ Deprecated - please modify modules to use addCopySpec() + """ + sys.stderr.write("Warning: the copyFileOrDir() function has been deprecated. Please\n") + sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.\n") + raise ValueError + #self.addCopySpec(srcpath) + + def runExeInd(self, exe): + """ Deprecated - use callExtProg() + """ + sys.stderr.write("Warning: the runExeInd() function has been deprecated. Please use\n") + sys.stderr.write("the callExtProg() function. This should only be called\n") + sys.stderr.write("if collect() is overridden.") + pass + + def callExtProg(self, prog): + """ Execute a command independantly of the output gathering part of + sosreport + """ + # Log if binary is not runnable or does not exist + if not os.access(prog.split()[0], os.X_OK): + self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable" % prog.split()[0]) + + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(prog) + return status + + def runExe(self, exe): + """ Deprecated - use collectExtOutput() + """ + sys.stderr.write("Warning: the runExe() function has been deprecated. Please use\n") + sys.stderr.write("the collectExtOutput() function.\n") + pass + + def collectExtOutput(self, exe, suggest_filename = None, root_symlink = None): + """ + Run a program and collect the output + """ + self.collectProgs.append( (exe,suggest_filename,root_symlink) ) + + def makeCommandFilename(self, exe): + """ The internal function to build up a filename based on a command """ + + mangledname = re.sub(r"[^\w\-\.\/]+", "_", exe) + mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64] + + outfn = self.cInfo['cmddir'] + "/" + self.piName + "/" + mangledname + + # check for collisions + inc = 0 + if os.path.exists(outfn): + inc = 2 + while True: + newfn = outfn + "_" + inc + if not os.path.exists(newfn): + break + inc +=1 + + return outfn + + def collectOutputNow(self, exe, suggest_filename = None, root_symlink = False): + """ Execute a command and save the output to a file for inclusion in + the report + """ + # First check to make sure the binary exists and is runnable. + if not os.access(exe.split()[0], os.X_OK): + self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable, trying anyways" % exe.split()[0]) + + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(exe) + + if suggest_filename: + outfn = self.makeCommandFilename(suggest_filename) + else: + outfn = self.makeCommandFilename(exe) + + if not os.path.isdir(os.path.dirname(outfn)): + os.mkdir(os.path.dirname(outfn)) + + outfd = open(outfn, "w") + if status == 127: outfd.write("# the command returned exit status 127, this normally means that the command was not found.\n\n") + if len(shout): outfd.write(shout+"\n") + outfd.close() + + if root_symlink: + curdir = os.getcwd() + os.chdir(self.cInfo['dstroot']) + os.symlink(outfn[len(self.cInfo['dstroot'])+1:], root_symlink.strip("/.")) + os.chdir(curdir) + + outfn = outfn[len(self.cInfo['cmddir'])+1:] + + # sosStatus(status) + # save info for later + self.executedCommands.append({'exe': exe, 'file':outfn}) # save in our list + self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn,runtime=runtime) + return outfn + + def writeTextToCommand(self, exe, text): + """ A function that allows you to write a random text string to the + command output location referenced by exe; this is useful if you want + to conditionally collect information, but still want the output file + to exist so as not to confuse readers """ + + outfn = self.makeCommandFilename(exe) + + if not os.path.isdir(os.path.dirname(outfn)): + os.mkdir(os.path.dirname(outfn)) + + outfd = open(outfn, "w") + outfd.write(text) + outfd.close() + + self.executedCommands.append({'exe': exe, 'file': outfn}) # save in our list + return outfn + + # For adding warning messages regarding configuration sanity + def addDiagnose(self, alertstring): + """ Add a configuration sanity warning for this plugin. These + will be displayed on-screen before collection and in the report as well. + """ + self.diagnose_msgs.append(alertstring) + return + + # For adding output + def addAlert(self, alertstring): + """ Add an alert to the collection of alerts for this plugin. These + will be displayed in the report + """ + self.alerts.append(alertstring) + return + + + def addCustomText(self, text): + """ Append text to the custom text that is included in the report. This + is freeform and can include html. + """ + self.customText = self.customText + text + return + + def doCollect(self): + """ This function has been replaced with copyStuff(threaded = True). Please change your + module calls. Calling setup() now. + """ + return self.copyStuff(threaded = True) + + def isRunning(self): + """ + if threaded, is thread running ? + """ + if self.thread: return self.thread.isAlive() + return None + + def wait(self,timeout=None): + """ + wait for a thread to complete - only called for threaded execution + """ + self.thread.join(timeout) + return self.thread.isAlive() + + def copyStuff(self, threaded = False, semaphore = None): + """ + Collect the data for a plugin + """ + if threaded and self.thread == None: + self.thread = Thread(target=self.copyStuff, name=self.piName+'-thread', args = [True, semaphore] ) + self.thread.start() + return self.thread + + if semaphore: semaphore.acquire() + + self.soslog.log(logging.VERBOSE, "starting threaded plugin %s" % self.piName) + + self.time_start = time() + self.time_stop = None + + for path in self.copyPaths: + self.soslog.debug("copying pathspec %s" % path) + try: + self.doCopyFileOrDir(path) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "error copying from pathspec %s (%s), traceback follows:" % (path,e)) + self.soslog.log(logging.VERBOSE, traceback.format_exc()) + for (prog,suggest_filename,root_symlink) in self.collectProgs: + self.soslog.debug("collecting output of '%s'" % prog) + try: + self.collectOutputNow(prog, suggest_filename, root_symlink) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except: + self.soslog.log(logging.VERBOSE, "error collection output of '%s', traceback follows:" % prog) + self.soslog.log(logging.VERBOSE, traceback.format_exc()) + + self.time_stop = time() + + if semaphore: semaphore.release() + self.soslog.log(logging.VERBOSE, "plugin %s returning" % self.piName) + + def get_description(self): + """ This function will return the description for the plugin""" + try: + return self.__doc__.strip() + except: + return "<no description available>" + + def checkenabled(self): + """ This function can be overidden to let the plugin decide whether + it should run or not. + """ + return True + + def defaultenabled(self): + """This devices whether a plugin should be automatically loaded or + only if manually specified in the command line.""" + return True + + def collect(self): + """ This function has been replaced with setup(). Please change your + module calls. Calling setup() now. + """ + self.setup() + + def diagnose(self): + """This class must be overridden to check the sanity of the system's + configuration before the collection begins. + """ + pass + + def setup(self): + """This class must be overridden to add the copyPaths, forbiddenPaths, + and external programs to be collected at a minimum. + """ + pass + + def analyze(self): + """ + perform any analysis. To be replaced by a plugin if desired + """ + pass + + def postproc(self): + """ + perform any postprocessing. To be replaced by a plugin if desired + """ + pass + + def report(self): + """ Present all information that was gathered in an html file that allows browsing + the results. + """ + # make this prettier + html = '<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: + cmdOutRelPath = sosRelPath(self.cInfo['rptdir'], cmd['file']) + html = html + '<li><a href="%s">%s</a></li>\n' % (cmdOutRelPath, 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 + + diff --git a/trunk/src/lib/sos/policyredhat.py b/trunk/src/lib/sos/policyredhat.py new file mode 100755 index 00000000..d4a4074d --- /dev/null +++ b/trunk/src/lib/sos/policyredhat.py @@ -0,0 +1,151 @@ +## policy-redhat.py +## Implement policies required for the sos system support tool + +## Copyright (C) Steve Conklin <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 commands +import sys +import string +from tempfile import gettempdir +from sos.helpers import * +import random + +SOME_PATH = "/tmp/SomePath" + +#class SosError(Exception): +# def __init__(self, code, message): +# self.code = code +# self.message = message +# +# def __str__(self): +# return 'Sos Error %s: %s' % (self.code, self.message) + + +class SosPolicy: + "This class implements various policies for sos" + def __init__(self): + #print "Policy init" + return + + def setCommons(self, commons): + self.cInfo = commons + return + + def validatePlugin(self, pluginpath): + "Validates the plugin as being acceptable to run" + # return value + # TODO implement this + #print "validating %s" % pluginpath + return True + + def allPkgsByName(self, name): + # FIXME: we're relying on rpm to sort the output list + cmd = "/bin/rpm --qf '%%{N}-%%{V}-%%{R}-%%{ARCH}\n' -q %s" % (name,) + pkgs = os.popen(cmd).readlines() + return [pkg[:-1] for pkg in pkgs if pkg.startswith(name)] + + def pkgByName(self, name): + # TODO: do a full NEVRA compare and return newest version, best arch + try: + # lame attempt at locating newest + pkg = self.allPkgsByName(name)[-1] + except IndexError: + pkg = None + + return pkg + + def pkgDictByName(self, name): + pkgName = self.pkgByName(name) + if pkgName and len(pkgName) > len(name): + return pkgName[len(name)+1:].split("-") + else: + return None + + def runlevelByService(self, name): + ret = [] + try: + for tabs in commands.getoutput("/sbin/chkconfig --list %s" % name).split(): + (runlevel, onoff) = tabs.split(":") + if onoff == "on": + ret.append(int(runlevel)) + except: + pass + return ret + + def runlevelDefault(self): + # FIXME: get this from /etc/inittab + return 3 + + def pkgNVRA(self, pkg): + fields = pkg.split("-") + version, release, arch = fields[-3:] + name = "-".join(fields[:-3]) + return (name, version, release, arch) + + def packageResults(self): + localname = commands.getoutput("/bin/uname -n").split(".")[0] + + try: + name = raw_input("Please enter your first initial and last name [%s]: " % localname) + if len(name) == 0: name = localname + + ticketNumber = raw_input("Please enter the case number that you are generating this report for: ") + except KeyboardInterrupt: + print _("<interrupted>") + print _("Temporary files have been stored in ") % self.cInfo['dstroot'] + return + + if len(ticketNumber): + namestr = name + "." + ticketNumber + else: + namestr = name + + ourtempdir = gettempdir() + tarballName = os.path.join(ourtempdir, "sosreport-" + namestr + ".tar.bz2") + + namestr = namestr + "-" + str(random.randint(1, 999999)) + + aliasdir = os.path.join(ourtempdir, namestr) + + tarcmd = "/bin/tar -jcf %s %s" % (tarballName, namestr) + + print + print "Creating compressed tar archive..." + if not os.access(string.split(tarcmd)[0], os.X_OK): + print "Unable to create tarball" + return + + # FIXME: gotta be a better way... + os.system("/bin/mv %s %s" % (self.cInfo['dstroot'], aliasdir)) + curwd = os.getcwd() + os.chdir(ourtempdir) + oldmask = os.umask(077) + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(tarcmd) + os.umask(oldmask) + os.chdir(curwd) + # FIXME: use python internal command + os.system("/bin/mv %s %s" % (aliasdir, self.cInfo['dstroot'])) + + sys.stdout.write("\n") + print "Your sosreport has been generated and saved in %s" % tarballName + print "Please send this file to your support representative." + sys.stdout.write("\n") + + return + |