diff options
author | astokes <astokes@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2008-10-01 13:17:46 +0000 |
---|---|---|
committer | astokes <astokes@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2008-10-01 13:17:46 +0000 |
commit | 6f1e13198edb22c3d47d4c151dbe1d4b7e0fc4c6 (patch) | |
tree | 4ab072b6412797d4e65bc3594e01fd877d88914e /src/lib | |
parent | 46071f546f4718d80e2c54c938f774ed40a171c7 (diff) | |
download | sos-6f1e13198edb22c3d47d4c151dbe1d4b7e0fc4c6.tar.gz |
RHEL-5 branch update
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/sos/trunk@524 ef72aa8b-4018-0410-8976-d6e080ef94d8
Diffstat (limited to 'src/lib')
27 files changed, 626 insertions, 872 deletions
diff --git a/src/lib/sos/helpers.py b/src/lib/sos/helpers.py index 77f85b01..bcdee6fb 100755 --- a/src/lib/sos/helpers.py +++ b/src/lib/sos/helpers.py @@ -25,29 +25,9 @@ """ helper functions used by sosreport and plugins """ -import os, popen2, fcntl, select, sys, commands, signal -from time import time, sleep - -if sys.version_info[0] <= 2 and sys.version_info[1] <= 2: - # it's a RHEL3, activate work-arounds - # - import sos.rhel3_logging - logging = sos.rhel3_logging - - def mkdtemp(suffix = "", prefix = "temp_"): - import random - while True: - tempdir = "/tmp/%s_%d%s" % (prefix, random.randint(1,9999999), suffix) - if not os.path.exists(tempdir): break - os.mkdir(tempdir) - return tempdir - - os.path.sep = "/" - os.path.pardir = ".." -else: - # RHEL4+, business as usual - import logging - from tempfile import mkdtemp +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 @@ -77,72 +57,17 @@ def makeNonBlocking(afd): fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY) -def sosGetCommandOutput(command, timeout = 300): +def sosGetCommandOutput(command): """ 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) - - # these are file descriptors, not file objects - r, w = os.pipe() - - pid = os.fork() - - if pid: - # we are the parent - os.close(w) # use os.close() to close a file descriptor - r_fd = os.fdopen(r) # turn r into a file object - stime=time() - txt = "" - sts = -1 - soslog.log(logging.VERBOSE2, 'forked command "%s" with pid %d, timeout is %d' % (command, pid, timeout) ) - while True: - # read output from pipe - ready = select.select([r], [], [], 1) - if r in ready[0]: - txt = txt + r_fd.read() - # is child still running ? - try: os.waitpid(pid, os.WNOHANG) - except: - # not running, make sure the child process gets cleaned up - try: sts = os.waitpid(pid, 0)[1] - except: pass - break - # has timeout passed ? - if time() - stime > timeout: - soslog.log(logging.VERBOSE, 'killing hung child with pid %s after %d seconds (command was "%s")' % (pid,timeout,command) ) - try: os.kill(pid, signal.SIGKILL) - except: pass - break - if txt[-1:] == '\n': txt = txt[:-1] - return (sts, txt, time()-stime) - else: - # we are the child - os.dup2(r, 0) - os.dup2(w, 1) - os.dup2(w, 2) - - import resource - maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] - if not hasattr(resource, "RLIM_INFINITY"): - resource.RLIM_INFINITY = -1L - if (maxfd == resource.RLIM_INFINITY): - maxfd = MAXFD - for fd in range(3, maxfd): - try: os.close(fd) - except OSError: pass - os.execl("/bin/sh", "/bin/sh", "-c", command) - os._exit(127) + stime = time() + inpipe, pipe = os.popen4(command, 'r') + inpipe.close() + text = pipe.read() + sts = pipe.close() + if sts is None: sts = 0 + if text[-1:] == '\n': text = text[:-1] + return (sts, text, 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 @@ -167,11 +92,23 @@ def allEqual(elements): return False return True -def commonPrefix(l1, l2, common = []): + +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 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]]) + # 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. @@ -187,9 +124,3 @@ def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir): return path2 # leave path absolute if nothing at all in common return sep.join( [pardir]*len(u1) + u2 ) -def sosReadFile(fname): - ''' reads a file and returns its contents''' - fp = open(fname,"r") - content = fp.read() - fp.close() - return content diff --git a/src/lib/sos/plugins/apache.py b/src/lib/sos/plugins/apache.py index 10061918..c5250fd9 100644 --- a/src/lib/sos/plugins/apache.py +++ b/src/lib/sos/plugins/apache.py @@ -13,6 +13,7 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools +from threading import Thread class apache(sos.plugintools.PluginBase): """Apache related information diff --git a/src/lib/sos/plugins/autofs.py b/src/lib/sos/plugins/autofs.py index 16a8d3fb..48dd57b4 100644 --- a/src/lib/sos/plugins/autofs.py +++ b/src/lib/sos/plugins/autofs.py @@ -15,7 +15,7 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools -import os +import os, re class autofs(sos.plugintools.PluginBase): """autofs server-related information @@ -24,8 +24,38 @@ class autofs(sos.plugintools.PluginBase): if self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService("autofs"): return True return False - + + def checkdebug(self): + """ testing if autofs debug has been enabled anywhere + """ + # Global debugging + optlist=[] + opt = self.fileGrep(r"^(DEFAULT_LOGGING|DAEMONOPTIONS)=(.*)", "/etc/sysconfig/autofs") + for opt1 in opt: + for opt2 in opt1.split(" "): + optlist.append(opt2) + for dtest in optlist: + if dtest == "--debug" or dtest == "debug": + return True + + 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.addCopySpec("/etc/sysconfig/autofs") - self.addCopySpec("/etc/rc.d/init.d/autofs") + self.addCopySpec("/etc/init.d/autofs") + self.collectExtOutput("/bin/rpm -qV autofs") + self.collectExtOutput("/etc/init.d/autofs status") + self.collectExtOutput("ps auxwww | grep automount") + self.collectExtOutput("/bin/egrep -e 'automount|pid.*nfs' /proc/mounts") + self.collectExtOutput("/bin/mount | egrep -e 'automount|pid.*nfs'") + self.collectExtOutput("/sbin/chkconfig --list autofs") + if self.checkdebug(): + self.addCopySpec(self.getdaemondebug()) + return + diff --git a/src/lib/sos/plugins/cluster.py b/src/lib/sos/plugins/cluster.py index 6d4de2a0..f6169ddb 100644 --- a/src/lib/sos/plugins/cluster.py +++ b/src/lib/sos/plugins/cluster.py @@ -25,178 +25,139 @@ class cluster(sos.plugintools.PluginBase): ('taskdump', 'trigger 3 sysrq+t dumps every 5 seconds (dangerous)', 'slow', False)] def checkenabled(self): - rhelver = self.cInfo["policy"].rhelVersion() - if rhelver == 4: - self.packages = [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", - "rgmanager", "fence", "dlm", "dlm-kernel", "gulm", - "GFS", "GFS-kernel", "lvm2-cluster" ] - elif rhelver == 5: - self.packages = [ "rgmanager", "luci", "ricci", "system-config-cluster", - "gfs-utils", "gnbd", "kmod-gfs", "kmod-gnbd", "lvm2-cluster" ] - - self.files = [ "/etc/cluster/cluster.conf", "/proc/cluster" ] - return sos.plugintools.PluginBase.checkenabled(self) + # enable if any related package is installed + for pkg in [ "rgmanager", "luci", "ricci", "system-config-cluster", + "gfs-utils", "gnbd", "kmod-gfs", "kmod-gnbd", "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", "/proc/cluster" ]: + try: os.stat(fname) + except:pass + else: return True + + # no data related to RHCS/GFS exists + return False def has_gfs(self): - try: - if len(self.doRegexFindAll(r'^\S+\s+\S+\s+gfs\s+.*$', "/etc/mtab")): - return True - except: - return False + fp = open("/proc/mounts","r") + for line in fp.readlines(): + mntline = line.split(" ") + if mntline[2] == "gfs": + return True + fp.close() + return False def diagnose(self): - rhelver = self.cInfo["policy"].rhelVersion() - - # 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) - - pkgs_check = [] - mods_check = [] - serv_check = [] - - if rhelver == 4: - pkgs_check.extend( [ "ccs", "cman", "magma", "magma-plugins", "perl-Net-Telnet", "rgmanager", "fence" ] ) - mods_check.extend( [ "cman", "dlm" ] ) - if self.has_gfs(): - mods_check.append("gfs") - serv_check.extend( [ "cman", "ccsd", "rgmanager", "fenced" ] ) - if self.has_gfs(): - serv_check.extend( ["gfs", "clvmd"] ) - elif rhelver == 5: - pkgs_check.extend ( [ "cman", "perl-Net-Telnet", "rgmanager" ] ) - mods_check.extend( [ "dlm" ] ) - if self.has_gfs(): - mods_check.extend( ["gfs", "gfs2"] ) - serv_check.extend( [ "cman", "rgmanager" ] ) - if self.has_gfs(): - serv_check.extend( ["gfs", "clvmd"] ) - - # check that kernel module packages are installed for - # running kernel version - - for modname in mods_check: - found = 0 - - if self.cInfo["policy"].allPkgsByNameRegex( "^" + modname ): - found = 1 - - status, output = commands.getstatusoutput('/sbin/modinfo -F vermagic ' + modname) - - if status == 0: - found = 2 - - if len(self.fileGrep("^%s\s+" % modname, "/proc/modules")) > 0: - found = 3 - - if found == 0: - self.addDiagnose("required kernel module is missing: %s" % modname) - elif found == 1: - self.addDiagnose("required module is not available for current kernel: %s" % modname) - elif found == 2: - self.addDiagnose("required module is available but not loaded: %s" % modname) - - for pkg in pkgs_check: - if self.cInfo["policy"].pkgByName(pkg) == None: - self.addDiagnose("required package is missing: %s" % pkg) - - if rhelver == "4": - # (dlm, dlm-kernel) || gulm - if not ((self.cInfo["policy"].pkgByName("dlm") and self.cInfo["policy"].pkgByName("dlm-kernel")) or self.cInfo["policy"].pkgByName("gulm")): - self.addDiagnose("required packages are missing: (dlm, dlm-kernel) || gulm") - - # 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 serv_check: - status, output = commands.getstatusoutput("/sbin/service %s status &> /dev/null" % service) - if status != 0: - 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: missing important cman services - # FIXME: any cman service whose state != run ? - # Fence Domain: "default" 2 2 run - - - # is cluster quorate - if not self.is_cluster_quorate(): - self.addDiagnose("cluster node is not quorate") - - # if there is no cluster.conf, diagnose() finishes here. - try: - os.stat("/etc/cluster/cluster.conf") - except: - self.addDiagnose("/etc/cluster/cluster.conf is missing") - return - - # setup XML xpath context - xml = libxml2.parseFile("/etc/cluster/cluster.conf") - xpathContext = xml.xpathNewContext() - - # make sure that the node names are valid according to RFC 2181 - for hostname in xpathContext.xpathEval('/cluster/clusternodes/clusternode/@name'): - if not re.match('^[a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$', hostname.content): - self.addDiagnose("node name (%s) contains invalid characters" % hostname.content) - - # do not rely on DNS to resolve node names, must have them in /etc/hosts - for hostname in xpathContext.xpathEval('/cluster/clusternodes/clusternode/@name'): - if len(self.fileGrep(r'^.*\W+%s' % hostname.content , "/etc/hosts")) == 0: - self.addDiagnose("node %s is not defined in /etc/hosts" % hostname.content) - - # check fencing (warn on no fencing) - if len(xpathContext.xpathEval("/cluster/clusternodes/clusternode[not(fence/method/device)]")): - if self.has_gfs(): - self.addDiagnose("one or more nodes have no fencing agent configured: fencing is required for GFS to work") - else: - self.addDiagnose("one or more nodes have no fencing agent configured: the cluster infrastructure might not work as intended") - - # check fencing (warn on manual) - if len(xpathContext.xpathEval("/cluster/clusternodes/clusternode[/cluster/fencedevices/fencedevice[@agent='fence_manual']/@name=fence/method/device/@name]")): - self.addDiagnose("one or more nodes have manual fencing agent configured (data integrity is not guaranteed)") - - # if fence_ilo or fence_drac, make sure acpid is not running - hostname = commands.getoutput("/bin/uname -n").split(".")[0] - if len(xpathContext.xpathEval('/cluster/clusternodes/clusternode[@name = "%s" and /cluster/fencedevices/fencedevice[@agent="fence_rsa" or @agent="fence_drac"]/@name=fence/method/device/@name]' % hostname )): - status, output = commands.getstatusoutput("/sbin/service acpid status") - if status == 0 or self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService("acpid"): - self.addDiagnose("acpid is enabled, this may cause problems with your fencing method.") - - # check for fs exported via nfs without nfsid attribute - if len(xpathContext.xpathEval("/cluster/rm/service//fs[not(@fsid)]/nfsexport")): - self.addDiagnose("one or more nfs export do not have a fsid attribute set.") - - # cluster.conf file version and the in-memory cluster configuration version matches - status, cluster_version = commands.getstatusoutput("cman_tool status | grep 'Config version'") - if not status: cluster_version = cluster_version[16:] - else: cluster_version = None - conf_version = xpathContext.xpathEval("/cluster/@config_version")[0].content - - if status == 0 and conf_version != cluster_version: - self.addDiagnose("cluster.conf and in-memory configuration version differ (%s != %s)" % (conf_version, cluster_version) ) - - status, output = commands.getstatusoutput("/usr/sbin/rg_test test /etc/cluster/cluster.conf") - if output.find("Error: ") > 0: - self.addDiagnose("configuration errors are present according to rg_test") - - # make sure the first part of the lock table matches the cluster name - # and that the locking protocol is sane - cluster_name = xpathContext.xpathEval("/cluster/@name")[0].content - - for fs in self.fileGrep(r'^[^#][/\w]*\W*[/\w]*\W*gfs', "/etc/fstab"): - # for each gfs entry - fs = fs.split() - lockproto = self.get_gfs_sb_field(fs[0], "sb_lockproto") - if lockproto and lockproto != self.get_locking_proto(): - self.addDiagnose("gfs mountpoint (%s) is using the wrong locking protocol (%s)" % (fs[0], lockproto) ) - - locktable = self.get_gfs_sb_field(fs[0], "sb_locktable") - try: locktable = locktable.split(":")[0] - except: continue - if locktable != cluster_name: - self.addDiagnose("gfs mountpoint (%s) is using the wrong locking table" % fs[0]) + try: rhelver = self.cInfo["policy"].pkgByName("redhat-release")[1] + except: rhelver = None + + # FIXME: we should only run tests specific for the version, now just do them all regardless + if rhelver.startswith("5"): + # check that kernel module packages are installed for + # running kernel version + pkgs_check = [ ] + if self.has_gfs(): pkgs_check.append("kmod-gfs") + + for pkgname in pkgs_check: + if not self.cInfo["policy"].pkgByName(pkgname): + self.addDiagnose("required package is missing: %s" % pkgname) + + # 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 [ "cman", "perl-Net-Telnet", "rgmanager" ]: + if self.cInfo["policy"].pkgByName(pkg) == None: + self.addDiagnose("required package is missing: %s" % pkg) + + # let's make modules sure are loaded + mods_check = [ "dlm" ] + if self.has_gfs(): mods_check.append("gfs") + for module in mods_check: + if len(self.fileGrep("^%s " % module, "/proc/modules")) == 0: + self.addDiagnose("required module is not loaded: %s" % module) + + # check if all the needed daemons are active at sosreport time + # check if they are started at boot time in RHEL5 RHCS (rgmanager, cman) + # and GFS (gfs, ccsd, clvmd, fenced) + checkserv = [ "cman", "rgmanager" ] + if self.has_gfs(): checkserv.extend( ["gfs", "clvmd"] ) + for service in checkserv: + status, output = commands.getstatusoutput("/sbin/service %s status" % service) + if status: + 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: any cman service whose state != run ? + # Fence Domain: "default" 2 2 run - + + # is cluster quorate + if not self.is_cluster_quorate(): + self.addDiagnose("cluster node is not quorate") + + # if there is no cluster.conf, diagnose() finishes here. + try: + os.stat("/etc/cluster/cluster.conf") + except: + self.addDiagnose("/etc/cluster/cluster.conf is missing") + return + + # setup XML xpath context + xml = libxml2.parseFile("/etc/cluster/cluster.conf") + xpathContext = xml.xpathNewContext() + + # check fencing (warn on no fencing) + if len(xpathContext.xpathEval("/cluster/clusternodes/clusternode[not(fence/method/device)]")): + if self.has_gfs(): + self.addDiagnose("one or more nodes have no fencing agent configured: fencing is required for GFS to work") + else: + self.addDiagnose("one or more nodes have no fencing agent configured: the cluster infrastructure might not work as intended") + + # check fencing (warn on manual) + if len(xpathContext.xpathEval("/cluster/clusternodes/clusternode[/cluster/fencedevices/fencedevice[@agent='fence_manual']/@name=fence/method/device/@name]")): + self.addDiagnose("one or more nodes have manual fencing agent configured (data integrity is not guaranteed)") + + # if fence_ilo or fence_drac, make sure acpid is not running + hostname = commands.getoutput("/bin/uname -n").split(".")[0] + if len(xpathContext.xpathEval('/cluster/clusternodes/clusternode[@name = "%s" and /cluster/fencedevices/fencedevice[@agent="fence_rsa" or @agent="fence_drac"]/@name=fence/method/device/@name]' % hostname )): + status, output = commands.getstatusoutput("/sbin/service acpid status") + if status == 0 or self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService("acpid"): + self.addDiagnose("acpid is enabled, this may cause problems with your fencing method.") + + # check for fs exported via nfs without nfsid attribute + if len(xpathContext.xpathEval("/cluster/rm/service//fs[not(@fsid)]/nfsexport")): + self.addDiagnose("one or more nfs export do not have a fsid attribute set.") + + # cluster.conf file version and the in-memory cluster configuration version matches + status, cluster_version = commands.getstatusoutput("cman_tool status | grep 'Config version'") + if not status: cluster_version = cluster_version[16:] + else: cluster_version = None + conf_version = xpathContext.xpathEval("/cluster/@config_version")[0].content + + if status == 0 and conf_version != cluster_version: + self.addDiagnose("cluster.conf and in-memory configuration version differ (%s != %s)" % (conf_version, cluster_version) ) + + # make sure the first part of the lock table matches the cluster name + # and that the locking protocol is sane + cluster_name = xpathContext.xpathEval("/cluster/@name")[0].content + + for fs in self.fileGrep(r'^[^#][/\w]*\W*[/\w]*\W*gfs', "/etc/fstab"): + # for each gfs entry + fs = fs.split() + + lockproto = self.get_gfs_sb_field(fs[0], "sb_lockproto") + if lockproto and lockproto != self.get_locking_proto(): + self.addDiagnose("gfs mountpoint (%s) is using the wrong locking protocol (%s)" % (fs[0], lockproto) ) + + locktable = self.get_gfs_sb_field(fs[0], "sb_locktable") + try: locktable = locktable.split(":")[0] + except: continue + if locktable != cluster_name: + self.addDiagnose("gfs mountpoint (%s) is using the wrong locking table" % fs[0]) def setup(self): self.collectExtOutput("/sbin/fdisk -l") @@ -214,9 +175,9 @@ class cluster(sos.plugintools.PluginBase): self.collectExtOutput("/sbin/ipvsadm -L") - if self.getOption('gfslockdump'): self.do_gfslockdump() - if self.getOption('lockdump'): self.do_lockdump() - if self.getOption('taskdump'): self.do_taskdump() + if self.isOptionEnabled('gfslockdump'): self.do_gfslockdump() + if self.isOptionEnabled('lockdump'): self.do_lockdump() + if self.isOptionEnabled('taskdump'): self.do_taskdump() return @@ -233,24 +194,20 @@ class cluster(sos.plugintools.PluginBase): self.addCopySpec("/var/log/messages") def do_lockdump(self): - status, output = commands.getstatusoutput("cman_tool services") - if status: - # command somehow failed - return False - - import re - - rhelver = self.get_redhat_release() - - if rhelver == "4": - regex = r'^DLM Lock Space:\s*"([^"]*)".*$' - elif rhelver == "5Server" or rhelver == "5Client": - regex = r'^dlm\s+[^\s]+\s+([^\s]+)\s.*$' - - reg=re.compile(regex,re.MULTILINE) - for lockspace in reg.findall(output): - commands.getstatusoutput("echo %s > /proc/cluster/dlm_locks" % lockspace) - self.collectOutputNow("cat /proc/cluster/dlm_locks", root_symlink = "dlm_locks_%s" % lockspace) + try: + fp = open("/proc/cluster/services","r") + except: + return + for line in fp.readlines(): + if line[0:14] == "DLM Lock Space": + try: + lockspace = line.split('"')[1] + except: + pass + else: + commands.getstatusoutput("echo %s > /proc/cluster/dlm_locks" % lockspace) + self.collectOutputNow("cat /proc/cluster/dlm_locks", root_symlink = "dlm_locks_%s" % lockspace) + fp.close() def get_locking_proto(self): # FIXME: what's the best way to find out ? @@ -258,11 +215,15 @@ class cluster(sos.plugintools.PluginBase): return "lock_gulm" def do_gfslockdump(self): - for mntpoint in self.doRegexFindAll(r'^\S+\s+([^\s]+)\s+gfs\s+.*$', "/proc/mounts"): - self.collectExtOutput("/sbin/gfs_tool lockdump %s" % mntpoint, root_symlink = "gfs_lockdump_" + self.mangleCommand(mntpoint) ) - - def do_rgmanager_bt(self): - # FIXME: threads backtrace via SIGALRM + fp = open("/proc/mounts","r") + for line in fp.readlines(): + mntline = line.split(" ") + if mntline[2] == "gfs": + self.collectExtOutput("/sbin/gfs_tool lockdump %s" % mntline[1], root_symlink = "gfs_lockdump_" + self.mangleCommand(mntline[1]) ) + fp.close() + + def do_rgmgr_bt(self): + # FIXME: threads backtrace return def postproc(self): @@ -272,7 +233,7 @@ class cluster(sos.plugintools.PluginBase): def is_cluster_quorate(self): output = commands.getoutput("cman_tool status | grep '^Membership state: '") try: - if output[18:] == "Cluster-Member": + if output.split(":")[1].strip() == "Cluster-Member": return True else: return False diff --git a/src/lib/sos/plugins/devicemapper.py b/src/lib/sos/plugins/devicemapper.py index 5a1b63c2..38009689 100644 --- a/src/lib/sos/plugins/devicemapper.py +++ b/src/lib/sos/plugins/devicemapper.py @@ -22,6 +22,11 @@ class devicemapper(sos.plugintools.PluginBase): optionList = [("lvmdump", 'collect raw metadata from PVs', 'slow', False)] + def do_lvmdump(self): + """Collects raw metadata directly from the PVs using dd + """ + sosGetCommandOutput("lvmdump -d %s" % os.path.join(self.cInfo['dstroot'],"lvmdump")) + def setup(self): self.collectExtOutput("/sbin/dmsetup info -c") self.collectExtOutput("/sbin/dmsetup table") @@ -40,18 +45,17 @@ class devicemapper(sos.plugintools.PluginBase): self.addCopySpec("/var/lib/multipath/bindings") self.collectExtOutput("/sbin/multipath -v4 -ll") - self.collectExtOutput("/usr/bin/systool -v -C -b scsi") + self.collectExtOutput("/usr/bin/systool -v -c -b scsi") self.collectExtOutput("/bin/ls -laR /dev") self.collectExtOutput("/bin/ls -laR /sys/block") if self.getOption('lvmdump'): - sosGetCommandOutput("lvmdump -d %s" % os.path.join(self.cInfo['dstroot'],"lvmdump")) + self.do_lvmdump() - if os.path.isdir("/sys/block"): - for disk in os.listdir("/sys/block"): - if disk in [ ".", ".." ] or disk.startswith("ram"): - continue - self.collectExtOutput("/usr/bin/udevinfo -ap /sys/block/%s" % (disk)) + for disk in os.listdir("/sys/block"): + if disk in [ ".", ".." ] or disk.startswith("ram"): + continue + self.collectExtOutput("/usr/bin/udevinfo -ap /sys/block/%s" % (disk)) return diff --git a/src/lib/sos/plugins/emc.py b/src/lib/sos/plugins/emc.py index 24b3e04c..16f039a8 100644 --- a/src/lib/sos/plugins/emc.py +++ b/src/lib/sos/plugins/emc.py @@ -37,7 +37,7 @@ class emc(sos.plugintools.PluginBase): def get_pp_files(self): """ EMC PowerPath specific information - files """ - self.collectExtOutput("/sbin/powermt version") + self.collectExtOutput("/sbin/powermt version") self.addCopySpec("/etc/init.d/PowerPath") self.addCopySpec("/etc/powermt.custom") self.addCopySpec("/etc/emcp_registration") @@ -50,7 +50,7 @@ class emc(sos.plugintools.PluginBase): return def get_pp_config(self): - """ EMC PowerPath specific information - commands + """ EMC PowerPath specific information - commands """ self.collectExtOutput("/sbin/powermt display") self.collectExtOutput("/sbin/powermt display dev=all") @@ -59,12 +59,12 @@ class emc(sos.plugintools.PluginBase): self.collectExtOutput("/sbin/powermt display ports") self.collectExtOutput("/sbin/powermt display paths") self.collectExtOutput("/sbin/powermt dump") - return + return def get_symcli_files(self): """ EMC Solutions Enabler SYMCLI specific information - files """ - self.addCopySpec("/var/symapi/db/symapi_db.bin") + self.addCopySpec("/var/symapi/db/symapi_db.bin") self.addCopySpec("/var/symapi/config/[a-z]*") self.addCopySpec("/var/symapi/log/[a-z]*") return @@ -116,7 +116,7 @@ class emc(sos.plugintools.PluginBase): self.collectExtOutput("/usr/symcli/bin/symmask list hba") self.collectExtOutput("/usr/symcli/bin/symmask list logins") self.collectExtOutput("/usr/symcli/bin/symmaskdb list database") - self.collectExtOutput("/usr/symcli/bin/symmaskdb -v list database") + self.collectExtOutput("/usr/symcli/bin/symmaskdb -v list database") return def get_navicli_config(self): @@ -139,14 +139,14 @@ class emc(sos.plugintools.PluginBase): self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getdisk" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getcache" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getlun" % SP_address) - self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getlun -rg -type -default -owner -crus -capacity" % SP_address) + self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getlun -rg -type -default -owner -crus -capacity" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s lunmapinfo" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s getcrus" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s port -list -all" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s storagegroup -list" % SP_address) self.collectExtOutput("/opt/Navisphere/bin/navicli -h %s spportspeed -get" % SP_address) return - + def setup(self): ## About EMC Corporation default no if no EMC products are installed add_about_emc="no" @@ -184,9 +184,9 @@ class emc(sos.plugintools.PluginBase): self.get_navicli_config() print " Gathering Navisphere NAVICLI Host Agent information..." print " Please enter a CLARiiON SP IP address. In order to collect" - print " information for both SPA and SPB as well as multiple" + print " information for both SPA and SPB as well as multiple" print " CLARiiON arrays (if desired) you will be prompted multiple times." - print " To exit simply press [Enter]" + print " To exit simply press [Enter]" print "" add_about_emc = "yes" CLARiiON_IP_address_list = [] @@ -194,23 +194,23 @@ class emc(sos.plugintools.PluginBase): while CLARiiON_IP_loop == "stay_in": ans = raw_input("CLARiiON SP IP Address or [Enter] to exit: ") ## Check to make sure the CLARiiON SP IP address provided is valid - status, output = commands.getstatusoutput("/opt/Navisphere/bin/navicli -h %s getsptime" % ans) - if status == 0: + status, output = commands.getstatusoutput("/opt/Navisphere/bin/navicli -h %s getsptime" % ans) + if status == 0: CLARiiON_IP_address_list.append(ans) else: if ans != "": - print "The IP address you entered, %s, is not to an active CLARiiON SP." % ans + print "The IP address you entered, %s, is not to an active CLARiiON SP." % ans if ans == "": CLARiiON_IP_loop = "get_out" ## Sort and dedup the list of CLARiiON IP Addresses CLARiiON_IP_address_list.sort() for SP_address in CLARiiON_IP_address_list: if CLARiiON_IP_address_list.count(SP_address) > 1: - CLARiiON_IP_address_list.remove(SP_address) + CLARiiON_IP_address_list.remove(SP_address) for SP_address in CLARiiON_IP_address_list: if SP_address != "": print " Gathering NAVICLI information for %s..." % SP_address - self.get_navicli_SP_info(SP_address) + self.get_navicli_SP_info(SP_address) ## Only provide About EMC if EMC products are installed if add_about_emc != "no": diff --git a/src/lib/sos/plugins/filesys.py b/src/lib/sos/plugins/filesys.py index 3e3a7fba..bec046f2 100644 --- a/src/lib/sos/plugins/filesys.py +++ b/src/lib/sos/plugins/filesys.py @@ -13,7 +13,7 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools -import os +import commands class filesys(sos.plugintools.PluginBase): """information on filesystems @@ -29,11 +29,12 @@ class filesys(sos.plugintools.PluginBase): self.addCopySpec("/etc/mdadm.conf") self.collectExtOutput("/bin/df -al", root_symlink = "df") + self.collectExtOutput("/usr/sbin/lsof -b +M -n -l", root_symlink = "lsof") self.collectExtOutput("/sbin/blkid") - + self.collectExtOutput("/sbin/fdisk -l", root_symlink = "fdisk-l") for extfs in self.doRegexFindAll(r"^(/dev/.+) on .+ type ext.\s+", mounts): self.collectExtOutput("/sbin/dumpe2fs %s" % (extfs)) - return + diff --git a/src/lib/sos/plugins/general.py b/src/lib/sos/plugins/general.py index 434bfdaa..062e0674 100644 --- a/src/lib/sos/plugins/general.py +++ b/src/lib/sos/plugins/general.py @@ -20,35 +20,25 @@ class general(sos.plugintools.PluginBase): """basic system information """ - optionList = [("syslogsize", "max size (MiB) to collect per syslog file", "", 15), - ("all_logs", "collect all log files defined in syslog.conf", "", False)] + optionList = [("syslogsize", "max size (MiB) to collect per syslog file", "", 15)] def setup(self): self.addCopySpec("/etc/redhat-release") self.addCopySpec("/etc/fedora-release") - self.addCopySpec("/etc/inittab") - self.addCopySpec("/etc/sos.conf") 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.getOption("syslogsize")) + self.addCopySpecLimit("/var/log/messages.*", sizelimit = self.isOptionEnabled("syslogsize")) self.addCopySpec("/var/log/secure") - self.addCopySpecLimit("/var/log/secure.*", sizelimit = self.getOption("syslogsize")) + 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") - self.collectExtOutput("/bin/env") - - if self.getOption('all_logs'): - logs=self.doRegexFindAll(r"^\S+\s+(\S+)", "/etc/syslog.conf") - for i in logs: - i = i.lstrip("-") - if not os.path.isfile(i): continue - self.addCopySpec(i) - + self.addCopySpec("/root/anaconda-ks.cfg") return def postproc(self): diff --git a/src/lib/sos/plugins/hardware.py b/src/lib/sos/plugins/hardware.py index ad4a5537..ba10a356 100644 --- a/src/lib/sos/plugins/hardware.py +++ b/src/lib/sos/plugins/hardware.py @@ -36,12 +36,23 @@ class hardware(sos.plugintools.PluginBase): self.addCopySpec("/proc/dasd") self.addCopySpec("/proc/s390dbf/tape") self.collectExtOutput("/usr/share/rhn/up2dateclient/hardware.py") - self.collectExtOutput("""/bin/echo -e "lspci:\n" ; /sbin/lspci ; /bin/echo -e "\nlspci -nvv:\n" ; /sbin/lspci -nvv ; /bin/echo -e "\nlspci -tv:\n" ; /sbin/lspci -tv""", suggest_filename = "lspci", root_symlink = "lspci") + self.collectExtOutput("""/bin/echo "lspci:" ; /bin/echo ; /sbin/lspci ; /bin/echo ; /bin/echo "lspci -nvv:" ; /bin/echo ; /sbin/lspci -nvv""", suggest_filename = "lspci", root_symlink = "lspci") self.collectExtOutput("/usr/sbin/dmidecode", root_symlink = "dmidecode") - if self.cInfo["policy"].getArch().endswith("386"): - self.collectExtOutput("/usr/sbin/x86info -a") + # FIXME: if arch == i386: +# self.collectExtOutput("/usr/sbin/x86info -a") + + # 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") diff --git a/src/lib/sos/plugins/initrd.py b/src/lib/sos/plugins/initrd.py index 7bd7e1f3..83356548 100644 --- a/src/lib/sos/plugins/initrd.py +++ b/src/lib/sos/plugins/initrd.py @@ -20,9 +20,9 @@ class initrd(sos.plugintools.PluginBase): """ def setup(self): for initrd in glob.glob('/boot/initrd-*.img'): - self.collectExtOutput("/bin/zcat "+initrd+" | /bin/cpio "+ + self.collectExtOutput("/bin/zcat "+initrd+" | /bin/cpio "+ "--extract --to-stdout init" ) return def defaultenabled(self): - return False + return False diff --git a/src/lib/sos/plugins/kernel.py b/src/lib/sos/plugins/kernel.py index 4fb0a864..8ca53b66 100644 --- a/src/lib/sos/plugins/kernel.py +++ b/src/lib/sos/plugins/kernel.py @@ -18,7 +18,7 @@ import commands, os, re class kernel(sos.plugintools.PluginBase): """kernel related information """ - optionList = [("modinfo", 'gathers information on all kernel modules', 'fast', True), + optionList = [("modinfo", 'gathers module information on all modules', 'fast', True), ('sysrq', 'trigger sysrq+[m,p,t] dumps', 'fast', False)] moduleFile = "" taintList = [ @@ -28,7 +28,7 @@ class kernel(sos.plugintools.PluginBase): {'regex':'vxportal*', 'description':'Veritas module'}, {'regex':'vxdmp*', 'description':'Veritas dynamic multipathing module'}, {'regex':'vxio*', 'description':'Veritas module'}, - {'regex':'vxspec*', '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'}, @@ -49,22 +49,22 @@ class kernel(sos.plugintools.PluginBase): def setup(self): self.collectExtOutput("/bin/uname -a", root_symlink = "uname") self.moduleFile = self.collectOutputNow("/sbin/lsmod", root_symlink = "lsmod") - - if self.getOption('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) - + 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/sysctl -a") self.collectExtOutput("/sbin/ksyms") self.addCopySpec("/sys/module/*/parameters") self.addCopySpec("/proc/filesystems") self.addCopySpec("/proc/ksyms") self.addCopySpec("/proc/slabinfo") - self.addCopySpec("/lib/modules/%s/modules.dep" % self.cInfo["policy"].kernelVersion()) + 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") @@ -72,18 +72,25 @@ class kernel(sos.plugintools.PluginBase): self.addCopySpec("/proc/cmdline") self.addCopySpec("/proc/driver") self.addCopySpec("/proc/sys/kernel/tainted") - - if self.getOption('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK): - for key in ['m', 'p', 't']: - commands.getoutput("/bin/echo %s > /proc/sysrq-trigger" % (key,)) - self.addCopySpec("/var/log/messages") - + # FIXME: both RHEL4 and RHEL5 don't need sysrq to be enabled to trigger via sysrq-trigger + 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 diagnose(self): - + def analyze(self): infd = open("/proc/modules", "r") - for modname in infd.readlines(): + 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): @@ -92,17 +99,13 @@ class kernel(sos.plugintools.PluginBase): sys_srcver = infd.read().strip("\n") infd.close() if modinfo_srcver != sys_srcver: - self.addDiagnose("loaded module %s differs from the one present on the file-system" % modname) + 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. - infd.close() - - def analyze(self): - savedtaint = os.path.join(self.cInfo['dstroot'], "/proc/sys/kernel/tainted") infd = open(savedtaint, "r") line = infd.read() @@ -111,10 +114,12 @@ class kernel(sos.plugintools.PluginBase): 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: diff --git a/src/lib/sos/plugins/ldap.py b/src/lib/sos/plugins/ldap.py index b1a48420..318a3ba9 100644 --- a/src/lib/sos/plugins/ldap.py +++ b/src/lib/sos/plugins/ldap.py @@ -13,37 +13,14 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools -import os class ldap(sos.plugintools.PluginBase): """LDAP related information """ - def checkenabled(self): - self.packages = [ "openldap" ] - self.files = [ "/etc/openldap/ldap.conf" ] - return sos.plugintools.PluginBase.checkenabled(self) - - def get_ldap_opts(self): - # capture /etc/openldap/ldap.conf options in dict - # FIXME: possibly not hardcode these options in? - ldapopts=["URI","BASE","TLS_CACERTDIR"] - results={} - tmplist=[] - for i in ldapopts: - t=self.doRegexFindAll(r"^(%s)\s+(.*)" % i,"/etc/openldap/ldap.conf") - for x in t: - results[x[0]]=x[1].rstrip("\n") - return results - - def diagnose(self): - # Validate ldap client options - ldapopts=self.get_ldap_opts() - if ldapopts.has_key("TLS_CACERTDIR") and not os.path.exists(ldapopts["TLS_CACERTDIR"]): - self.addDiagnose("%s does not exist and can cause connection issues involving TLS" % ldapopts["TLS_CACERTDIR"]) - 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***") diff --git a/src/lib/sos/plugins/named.py b/src/lib/sos/plugins/named.py index bb0d079e..421143cb 100644 --- a/src/lib/sos/plugins/named.py +++ b/src/lib/sos/plugins/named.py @@ -20,14 +20,21 @@ class named(sos.plugintools.PluginBase): """named related information """ def checkenabled(self): - self.files = [ "/etc/named.conf", "/etc/sysconfig/named" ] - self.packages = [ "bind", "bind-chroot" ] - return sos.plugintools.PluginBase.checkenabled(self) + if self.cInfo["policy"].pkgByName("bind") or os.path.exists("/etc/named.conf") or os.path.exists("/etc/sysconfig/named"): + return True + return False def setup(self): - self.addCopySpec("/etc/named.boot") - self.addCopySpec("/etc/named.conf") - self.addCopySpec("/etc/sysconfig/named") - self.addCopySpec("/var/named") - self.addForbiddenPath('/var/named/chroot/proc') - self.addForbiddenPath('/var/named/chroot/dev') + 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/src/lib/sos/plugins/networking.py b/src/lib/sos/plugins/networking.py index 164ed43a..080ae694 100644 --- a/src/lib/sos/plugins/networking.py +++ b/src/lib/sos/plugins/networking.py @@ -24,7 +24,14 @@ class networking(sos.plugintools.PluginBase): """Return a dictionary for which key are interface name according to the output of ifconifg-a stored in ifconfigFile. """ - out=self.doRegexFindAll(r"^(eth\d+)\D", 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): @@ -53,19 +60,20 @@ class networking(sos.plugintools.PluginBase): self.addCopySpec("/etc/resolv.conf") ifconfigFile=self.collectOutputNow("/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 -s") self.collectExtOutput("/bin/netstat -neopa", root_symlink = "netstat") - self.collectExtOutput("/sbin/ip route show table all") + # FIXME: we should collect "ip route table <tablename>" for all tables (from "ip rule") self.collectExtOutput("/sbin/ip link") self.collectExtOutput("/sbin/ip address") self.collectExtOutput("/sbin/ifenslave -a") if ifconfigFile: for eth in self.get_interface_name(ifconfigFile): self.collectExtOutput("/sbin/ethtool "+eth) - if self.getOption("traceroute"): + if self.isOptionEnabled("traceroute"): self.collectExtOutput("/bin/traceroute -n rhn.redhat.com") return diff --git a/src/lib/sos/plugins/nfsserver.py b/src/lib/sos/plugins/nfsserver.py index 1e352032..9564aa22 100644 --- a/src/lib/sos/plugins/nfsserver.py +++ b/src/lib/sos/plugins/nfsserver.py @@ -35,10 +35,7 @@ class nfsserver(sos.plugintools.PluginBase): def setup(self): self.addCopySpec("/etc/exports") - self.addCopySpec("/var/lib/nfs/etab") - self.addCopySpec("/var/lib/nfs/xtab") - self.addCopySpec("/var/lib/nfs/rmtab") self.collectExtOutput("/usr/sbin/rpcinfo -p localhost") - self.collectExtOutput("/usr/sbin/nfsstat -a") + self.collectExtOutput("/usr/sbin/nfsstat") return diff --git a/src/lib/sos/plugins/pam.py b/src/lib/sos/plugins/pam.py index 1a50811b..8164bba3 100644 --- a/src/lib/sos/plugins/pam.py +++ b/src/lib/sos/plugins/pam.py @@ -20,6 +20,6 @@ class pam(sos.plugintools.PluginBase): def setup(self): self.addCopySpec("/etc/pam.d") self.addCopySpec("/etc/security") - self.collectExtOutput("/bin/ls -laF /lib/security") + self.collectExtOutput("/bin/ls -laF /lib/security/pam_*so") return diff --git a/src/lib/sos/plugins/process.py b/src/lib/sos/plugins/process.py index 0d89e06b..47e37edf 100644 --- a/src/lib/sos/plugins/process.py +++ b/src/lib/sos/plugins/process.py @@ -25,7 +25,6 @@ class process(sos.plugintools.PluginBase): self.collectExtOutput("/bin/ps auxwwwm") self.collectExtOutput("/bin/ps alxwww") self.collectExtOutput("/usr/bin/pstree", root_symlink = "pstree") - self.collectExtOutput("/usr/sbin/lsof -b +M -n -l", root_symlink = "lsof") return def find_mountpoint(s): @@ -41,7 +40,7 @@ class process(sos.plugintools.PluginBase): line = line.split() if line[0] == "D": # keep an eye on the process to see if the stat changes - for inc in range(1,10): + for inc in range(1,5): try: if len(self.fileGrep("^State: D", " /proc/%d/status" % int(line[1]))) == 0: # status is not D, good. let's get out of the loop. diff --git a/src/lib/sos/plugins/rpm.py b/src/lib/sos/plugins/rpm.py index f1c09e33..d9f1e79b 100644 --- a/src/lib/sos/plugins/rpm.py +++ b/src/lib/sos/plugins/rpm.py @@ -22,12 +22,12 @@ class rpm(sos.plugintools.PluginBase): def setup(self): self.addCopySpec("/var/log/rpmpkgs") - - if self.getOption("rpmq"): - self.collectExtOutput("/bin/rpm -qa --qf \"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}\n\"", root_symlink = "installed-rpms") - - if self.getOption("rpmva"): - self.eta_weight += 1500 # this plugins takes 200x longer (for ETA) - self.collectExtOutput("/bin/rpm -Va", root_symlink = "rpm-Va", timeout = 3600) + + if self.isOptionEnabled("rpmq"): + self.collectExtOutput("/bin/rpm -qa --qf=\"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}~~%{INSTALLTIME:date}\n\"|/bin/awk -F ~~ '{printf \"%-60s%s\\n\",$1,$2}'", 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/src/lib/sos/plugins/selinux.py b/src/lib/sos/plugins/selinux.py index 5f0c1f40..897c3991 100644 --- a/src/lib/sos/plugins/selinux.py +++ b/src/lib/sos/plugins/selinux.py @@ -19,32 +19,18 @@ class selinux(sos.plugintools.PluginBase): """selinux related information """ def setup(self): - # sestatus is always collected in checkenabled() - self.addCopySpec("/etc/selinux") + self.addCopySpec("/etc/selinux/*") self.collectExtOutput("/usr/bin/selinuxconfig") - self.eta_weight += 120 # this plugins takes 120x longer (for ETA) - self.collectExtOutput("/sbin/fixfiles check") - - self.addForbiddenPath("/etc/selinux/targeted") - + 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 self.collectOutputNow("/usr/sbin/sestatus", root_symlink = "sestatus").split(":")[1].strip() == "disabled": + if commands.getoutput("/usr/sbin/sestatus").split(":")[1].strip() == "disabled": return False except: pass return True - - def analyze(self): - # Check for SELinux denials and capture raw output from sealert - if self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService("setroubleshoot"): - # TODO: fixup regex for more precise matching - sealert=doRegexFindAll(r"^.*setroubleshoot:.*(sealert\s-l\s.*)","/var/log/messages") - if sealert: - for i in sealert: - self.collectExtOutput("%s" % i) - self.addAlert("There are numerous selinux errors present and "+ - "possible fixes stated in the sealert output.") diff --git a/src/lib/sos/plugins/squid.py b/src/lib/sos/plugins/squid.py index 7de66100..fdd3b8cf 100644 --- a/src/lib/sos/plugins/squid.py +++ b/src/lib/sos/plugins/squid.py @@ -19,12 +19,11 @@ class squid(sos.plugintools.PluginBase): """squid related information """ def checkenabled(self): - self.files = [ "/etc/squid/squid.conf" ] - self.packages = [ "squid" ] - return sos.plugintools.PluginBase.checkenabled(self) + if self.cInfo["policy"].pkgByName("squid") != None or os.path.exists("/etc/squid/squid.conf"): + return True + return False def setup(self): self.addCopySpec("/etc/squid/squid.conf") - self.addCopySpec("/var/log/squid") return diff --git a/src/lib/sos/plugins/startup.py b/src/lib/sos/plugins/startup.py index a0d3e400..59014aaa 100644 --- a/src/lib/sos/plugins/startup.py +++ b/src/lib/sos/plugins/startup.py @@ -19,6 +19,7 @@ class startup(sos.plugintools.PluginBase): """ def setup(self): self.addCopySpec("/etc/rc.d") + self.collectExtOutput("/sbin/chkconfig --list", root_symlink = "chkconfig") self.collectExtOutput("/sbin/runlevel") return diff --git a/src/lib/sos/plugins/systemtap.py b/src/lib/sos/plugins/systemtap.py index 0f5dda19..b99ce0cf 100644 --- a/src/lib/sos/plugins/systemtap.py +++ b/src/lib/sos/plugins/systemtap.py @@ -17,15 +17,13 @@ import sos.plugintools class systemtap(sos.plugintools.PluginBase): - """SystemTap information + """SystemTap pre-requisites information """ - def checkenabled(self): - self.files = [ "/usr/bin/stap" ] - self.packages = [ "systemtap", "systemtap-runtime" ] - return sos.plugintools.PluginBase.checkenabled(self) - 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/src/lib/sos/plugins/x11.py b/src/lib/sos/plugins/x11.py index 755352ef..4abd8782 100644 --- a/src/lib/sos/plugins/x11.py +++ b/src/lib/sos/plugins/x11.py @@ -19,19 +19,16 @@ class x11(sos.plugintools.PluginBase): """X related information """ def checkenabled(self): - try:os.stat("/etc/X11") - except:pass - else:return True - return False + try: os.stat("/etc/X11") + except: pass + else: return True + + return False 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.'") - - self.addForbiddenPath("/etc/X11/X") - self.addForbiddenPath("/etc/X11/fontpath.d") - return diff --git a/src/lib/sos/plugins/xen.py b/src/lib/sos/plugins/xen.py index 89ed359f..28c0ed43 100644 --- a/src/lib/sos/plugins/xen.py +++ b/src/lib/sos/plugins/xen.py @@ -38,6 +38,11 @@ class xen(sos.plugintools.PluginBase): return False return True + def is_running_xenstored(self): + xs_pid = os.popen("pidof xenstored").read() + xs_pidnum = re.split('\n$',xs_pid)[0] + return xs_pidnum.isdigit() + def domCollectProc(self): self.addCopySpec("/proc/xen/balloon") self.addCopySpec("/proc/xen/capabilities") @@ -68,10 +73,17 @@ class xen(sos.plugintools.PluginBase): self.collectExtOutput("/usr/sbin/xm info") self.collectExtOutput("/usr/sbin/brctl show") self.domCollectProc() - # work-around for BZ#371251 which eventually needs to be fixed in xen - status, output = commands.getstatusoutput("/sbin/service xend status &> /dev/null") - if status == 0: - self.addCopySpec("/sys/hypervisor") + self.addCopySpec("/sys/hypervisor/version") + self.addCopySpec("/sys/hypervisor/compilation") + self.addCopySpec("/sys/hypervisor/properties") + self.addCopySpec("/sys/hypervisor/type") + if is_xenstored_running(): + self.addCopySpec("/sys/hypervisor/uuid") + self.collectExtOutput("/usr/bin/xenstore-ls") + else: + # we need tdb instead of xenstore-ls if cannot get it. + self.addCopySpec("/var/lib/xenstored/tdb") + # 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 diff --git a/src/lib/sos/plugins/yum.py b/src/lib/sos/plugins/yum.py index 672451ee..1f389d30 100644 --- a/src/lib/sos/plugins/yum.py +++ b/src/lib/sos/plugins/yum.py @@ -22,17 +22,22 @@ class yum(sos.plugintools.PluginBase): optionList = [("yumlist", "list repositories and packages", "slow", False)] def checkenabled(self): - self.files = [ "/etc/yum.conf" ] - self.packages = [ "yum" ] - return sos.plugintools.PluginBase.checkenabled(self) + if self.cInfo["policy"].pkgByName("yum") or os.path.exists("/etc/yum.conf"): + return True + return False - def analyze(self): + def diagnose(self): + # FIXME: diagnose should only report actual problems, disabling this for now. + return True # repo sanity checking # TODO: elaborate/validate actual repo files, however this directory should # be empty on RHEL 5+ systems. - if self.cInfo["policy"].rhelVersion() == 5: - if len(os.listdir("/etc/yum.repos.d/")): - self.addAlert("/etc/yum.repos.d/ contains additional repository "+ + try: rhelver = self.cInfo["policy"].pkgDictByName("redhat-release")[0] + except: rhelver = None + + if rhelver == "5" or True: + if len(os.listdir('/etc/yum.repos.d/')): + self.addDiagnose("/etc/yum.repos.d/ contains additional repository "+ "information and can cause rpm conflicts.") def setup(self): @@ -42,7 +47,7 @@ class yum(sos.plugintools.PluginBase): self.addCopySpec("/etc/yum.conf") self.addCopySpec("/var/log/yum.log") - if self.getOption("yumlist"): + if self.isOptionEnabled("yumlist"): # 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 diff --git a/src/lib/sos/plugintools.py b/src/lib/sos/plugintools.py index f9e10ae1..fc4d4b97 100644 --- a/src/lib/sos/plugintools.py +++ b/src/lib/sos/plugintools.py @@ -30,31 +30,11 @@ This is the base class for sosreport plugins """ from sos.helpers import * from threading import Thread, activeCount -import os, os.path, sys, string, glob, re, traceback -import shutil +import os, os.path, sys, string, itertools, glob, re, traceback +import logging from stat import * from time import time -# RHEL3 doesn't have a logging module, activate work-around -try: - import logging -except ImportError: - import sos.rhel3_logging - logging = sos.rhel3_logging - -# python < 2.4 (RHEL3 and RHEL4) doesn't have format_exc, activate work-around -if sys.version_info[0] <= 2 and sys.version_info[1] < 4: - def format_exc(): - import StringIO - - output = StringIO.StringIO() - traceback.print_exc(file = output) - toret = output.getvalue() - output.close() - return toret - - traceback.format_exc = format_exc - class PluginBase: """ Base class for plugins @@ -85,11 +65,6 @@ class PluginBase: self.time_start = None self.time_stop = None - self.packages = [] - self.files = [] - - self.must_exit = False - self.soslog = logging.getLogger('sos') # get the option list into a dictionary @@ -119,10 +94,10 @@ class PluginBase: except KeyboardInterrupt: raise KeyboardInterrupt except Exception, e: - self.soslog.log(logging.VERBOSE, "problem at path %s (%s)" % (abspath,e)) + self.soslog.log(logging.VERBOSE, "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) ''' @@ -134,7 +109,7 @@ class PluginBase: for i in reg.findall(content): out.append(i) return out - + # Methods for copying files and shelling out def doCopyFileOrDir(self, srcpath): # pylint: disable-msg = R0912 @@ -151,103 +126,113 @@ class PluginBase: 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 - + # file and dir symlinks ar ehandled the same 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))) + + # 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 - rpth = link + dstslname = link - # make sure the link doesn't already exists - if os.path.exists(dstslname): - self.soslog.log(logging.DEBUG, "skipping symlink creation: already exists (%s)" % dstslname) + if os.path.isdir(srcpath): # symlink to a directory + # FIXME: don't recurse symlinks until vicious loops are detected 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)) + abslink = os.path.abspath(os.path.dirname(srcpath) + "/" + link) + self.soslog.log(logging.VERBOSE2, "DIRLINK %s to %s [%s]" % (srcpath,link,abslink)) + for tmplink in self.copiedDirs: + if tmplink["srcpath"] == abslink or tmplink["pointsto"] == abslink: + self.soslog.log(logging.VERBOSE2, "already copied [%s]" % srcpath) + return - self.soslog.log(logging.VERBOSE3, "creating symlink %s -> %s" % (dstslname, rpth)) - os.symlink(rpth, dstslname) - self.copiedFiles.append({'srcpath':srcpath, 'dstpath':rpth, 'symlink':"yes", 'pointsto':link}) - self.doCopyFileOrDir(link) - return + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': + pass + else: + self.soslog.log(logging.VERBOSE2, "copying (file or dir) %s" % srcpath+'/'+afile) + try: + abspath = self.doCopyFileOrDir(srcpath+'/'+afile) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.verbose(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':os.path.abspath(srcpath+'/'+afile) }) + else: + self.soslog.log(logging.VERBOSE2, "copying symlink %s" % srcpath) + try: + dstslname, abspath = self.__copyFile(srcpath) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) + 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: # not a symlink - if os.path.isdir(srcpath): + 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) - return - - # if we get here, it's definitely a regular file (not a symlink or dir) - - self.soslog.log(logging.VERBOSE3, "copying file %s" % srcpath) - try: - tdstpath, abspath = self.__copyFile(srcpath) - except "AlreadyExists": - self.soslog.log(logging.DEBUG, "error copying file %s (already exists)" % (srcpath)) - return - except IOError: - self.soslog.log(logging.VERBOSE2, "error copying file %s (IOError)" % (srcpath)) - return - except: - self.soslog.log(logging.VERBOSE2, "error copying file %s (SOMETHING HAPPENED)" % (srcpath)) - - self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list - - return abspath + 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. """ - rel_dir = os.path.dirname(src).lstrip(os.path.sep) -# if rel_dir[0] == "/": rel_dir = rel_dir[1:] - 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: - shutil.copy2(src, new_dir) - else: - raise "AlreadyExists" - - abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) - relpath = sosRelPath(self.cInfo['rptdir'], abspath) - return (relpath, abspath) + 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. """ - # Glob case handling is such that a valid non-glob is a reduced glob - for filespec in glob.glob(forbiddenPath): - self.forbiddenPaths.append(filespec) + self.forbiddenPaths.append(forbiddenPath) def getAllOptions(self): """ @@ -255,22 +240,14 @@ class PluginBase: """ return (self.optNames, self.optParms) - def setOption(self, optionname, value): - ''' set the named option to value. + 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'] = value - return True - else: - return False + parms['enabled'] = enable 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 zip(self.optNames, self.optParms): @@ -282,9 +259,6 @@ class PluginBase: 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 @@ -298,26 +272,57 @@ class PluginBase: """ 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): 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) + 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, timeout = 300): + 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, timeout) ) + self.collectProgs.append( (exe,suggest_filename,root_symlink) ) def fileGrep(self, regexp, fname): results = [] @@ -353,13 +358,18 @@ class PluginBase: return outfn - def collectOutputNow(self, exe, suggest_filename = None, root_symlink = False, timeout = 300): + 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]) + + # FIXME: we should have a timeout or we may end waiting forever # pylint: disable-msg = W0612 - status, shout, runtime = sosGetCommandOutput(exe, timeout = timeout) + status, shout, runtime = sosGetCommandOutput(exe) if suggest_filename: outfn = self.makeCommandFilename(suggest_filename) @@ -369,7 +379,7 @@ class PluginBase: 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 + if not (status == 127 or status == 32512): outfd = open(outfn, "w") if len(shout): outfd.write(shout+"\n") outfd.close() @@ -377,8 +387,7 @@ class PluginBase: if root_symlink: curdir = os.getcwd() os.chdir(self.cInfo['dstroot']) - try: os.symlink(outfn[len(self.cInfo['dstroot'])+1:], root_symlink.strip("/.")) - except: pass + os.symlink(outfn[len(self.cInfo['dstroot'])+1:], root_symlink.strip("/.")) os.chdir(curdir) outfn_strip = outfn[len(self.cInfo['cmddir'])+1:] @@ -467,10 +476,6 @@ class PluginBase: if semaphore: semaphore.acquire() - if self.must_exit: - semaphore.release() - return - self.soslog.log(logging.VERBOSE, "starting threaded plugin %s" % self.piName) self.time_start = time() @@ -481,29 +486,33 @@ class PluginBase: try: self.doCopyFileOrDir(path) except SystemExit: - if semaphore: semaphore.release() - if threaded: return KeyboardInterrupt - else: raise KeyboardInterrupt + if threaded: + return SystemExit + else: + raise SystemExit except KeyboardInterrupt: - if semaphore: semaphore.release() - if threaded: return KeyboardInterrupt - else: raise KeyboardInterrupt + if threaded: + return KeyboardInterrupt + else: + raise KeyboardInterrupt except Exception, e: self.soslog.log(logging.VERBOSE2, "error copying from pathspec %s (%s), traceback follows:" % (path,e)) self.soslog.log(logging.VERBOSE2, traceback.format_exc()) - for (prog, suggest_filename, root_symlink, timeout) in self.collectProgs: + 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, timeout) + self.collectOutputNow(prog, suggest_filename, root_symlink) except SystemExit: - if semaphore: semaphore.release() - if threaded: return SystemExit - else: raise SystemExit + if threaded: + return SystemExit + else: + raise SystemExit except KeyboardInterrupt: - if semaphore: semaphore.release() - if threaded: return KeyboardInterrupt - else: raise KeyboardInterrupt - except Exception, e: + if threaded: + return KeyboardInterrupt + else: + raise KeyboardInterrupt + except: self.soslog.log(logging.VERBOSE2, "error collection output of '%s', traceback follows:" % prog) self.soslog.log(logging.VERBOSE2, traceback.format_exc()) @@ -512,10 +521,6 @@ class PluginBase: if semaphore: semaphore.release() self.soslog.log(logging.VERBOSE, "plugin %s returning" % self.piName) - 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: @@ -527,17 +532,7 @@ class PluginBase: """ 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.cInfo["policy"].pkgByName(pkgname): - return True - return False - - return True + return True def defaultenabled(self): """This devices whether a plugin should be automatically loaded or @@ -609,11 +604,11 @@ class PluginBase: 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']) + 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 @@ -629,3 +624,5 @@ class PluginBase: html = html + self.customText + "</p>\n" return html + + diff --git a/src/lib/sos/policyredhat.py b/src/lib/sos/policyredhat.py index a10e7dfb..c25a9d55 100755 --- a/src/lib/sos/policyredhat.py +++ b/src/lib/sos/policyredhat.py @@ -26,45 +26,22 @@ from sos.helpers import * import random import re import md5 -import rpm -import time - -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 + +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) -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_md5 = "" - self.reportName = "" - self.ticketNumber = "" + #print "Policy init" return def setCommons(self, commons): @@ -78,45 +55,35 @@ class SosPolicy: #print "validating %s" % pluginpath return True - def pkgProvides(self, name): - pkg = self.pkgByName(name) - return pkg['providename'] - def pkgRequires(self, name): - pkg = self.pkgByName(name) - return pkg['requirename'] + # FIXME: we're relying on rpm to sort the output list + cmd = "/bin/rpm -q --requires %s" % (name) + return [requires[:-1].split() for requires in os.popen(cmd).readlines()] 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'])] + # 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].split() 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 - return self.allPkgsByName(name)[-1] - except: - pass - return None - - 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) + pkg = self.allPkgsByName(name)[-1] + except IndexError: + pkg = None + + return pkg + + def pkgDictByName(self, name): + # FIXME: what does this do again ?? + pkgName = self.pkgByName(name) + print pkgName + if pkgName and len(pkgName) > len(name): + return pkgName[len(name)+1:].split("-") 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) ] + return None def runlevelByService(self, name): ret = [] @@ -124,7 +91,7 @@ class SosPolicy: for tabs in commands.getoutput("/sbin/chkconfig --list %s" % name).split(): try: (runlevel, onoff) = tabs.split(":", 1) - except: + except ValueError: pass else: if onoff == "on": @@ -135,7 +102,7 @@ class SosPolicy: def runlevelDefault(self): try: - reg=self.doRegexFindAll(r"^id:(\d{1}):initdefault:", "/etc/inittab") + reg=self.fileGrep(r"^id:(\d{1}):initdefault:", "/etc/inittab") for initlevel in reg: return initlevel except: @@ -144,36 +111,9 @@ class SosPolicy: def kernelVersion(self): return commands.getoutput("/bin/uname -r").strip("\n") - def hostName(self): - return commands.getoutput("/bin/hostname").split(".")[0] - - def rhelVersion(self): - try: - pkgname = self.pkgByName("redhat-release")["version"] - if pkgname[0] == "4": - return 4 - elif pkgname in [ "5Server", "5Client" ]: - return 5 - 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): - if commands.getoutput("/bin/uname -v").split()[1] == "SMP": - return True - else: - return False - - def getArch(self): - return commands.getoutput("/bin/uname -m").strip() + if self.kernelVersion()[-3:]=="smp": return True + else: return False def pkgNVRA(self, pkg): fields = pkg.split("-") @@ -181,188 +121,85 @@ class SosPolicy: name = "-".join(fields[:-3]) return (name, version, release, arch) - def getDstroot(self): - """Find a temp directory to form the root for our gathered information - and reports. - """ - dstroot = "/tmp/%s-%s" % (self.hostName(), time.strftime("%Y%m%d%H%M%S")) - try: - os.mkdir(dstroot, 0700) - except: - return False - return dstroot - - def preWork(self): + def preWork(self, name="", ticket=""): # this method will be called before the gathering begins - localname = self.rhnUsername() - if len(localname) == 0: localname = self.hostName() + localname = commands.getoutput("/bin/uname -n").split(".")[0] - if not self.cInfo['cmdlineopts'].batch: - try: + try: + if len(name) == 0: 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) + else: + self.reportName = str(name) + 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 len(self.reportName) == 0: - self.reportName = localname - - return + if len(ticket) == 0: + self.ticketNumber = raw_input(_("Please enter the case number that you are generating this report for: ")) + else: + self.ticketNumber = str(ticket) + self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber) - def renameResults(self, newName): - newName = os.path.join(gettempdir(), 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 + print + except KeyboardInterrupt: + print + sys.exit(0) def packageResults(self): - self.renameResults("sosreport-%s-%s.tar.bz2" % (self.reportName, time.strftime("%Y%m%d%H%M%S"))) + if len(self.ticketNumber): + namestr = self.reportName + "." + self.ticketNumber + else: + namestr = self.reportName + + ourtempdir = gettempdir() + tarballName = os.path.join(ourtempdir, "sosreport-" + namestr + ".tar.bz2") - tarcmd = "/bin/tar -jcf %s %s" % (self.report_file, os.path.basename(self.cInfo['dstroot'])) + namestr = namestr + "-" + str(random.randint(1, 999999)) + + aliasdir = os.path.join(ourtempdir, namestr) + + tarcmd = "/bin/tar -jcf %s %s" % (tarballName, namestr) print _("Creating compressed 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(os.path.dirname(self.cInfo['dstroot'])) + os.chdir(ourtempdir) oldmask = os.umask(077) - status, shout = commands.getstatusoutput(tarcmd) + # 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'])) - 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" - - status, output = commands.getstatusoutput("""/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)) - if status == 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.new(fp.read()).hexdigest() + # add last 6 chars from md5sum to file name + fp = open(tarballName, "r") + md5out = md5.new(fp.read()).hexdigest() fp.close() - - self.renameResults("sosreport-%s-%s-%s.tar.bz2" % (self.reportName, time.strftime("%Y%m%d%H%M%S"), self.report_md5[-4:])) - - # store md5 into file - fp = open(self.report_file + ".md5", "w") - fp.write(self.report_md5 + "\n") + oldtarballName = tarballName + tarballName = os.path.join(ourtempdir, "sosreport-%s-%s.tar.bz2" % (namestr, md5out[-6:]) ) + os.system("/bin/mv %s %s" % (oldtarballName, tarballName) ) + # store md5 to a file + fp = open(tarballName + ".md5", "w") + fp.write(md5out + "\n") fp.close() + sys.stdout.write("\n") + print _("Your sosreport has been generated and saved in:\n %s") % tarballName 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 + if md5out: + print _("The md5sum is: ") + md5out 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 + sys.stdout.write("\n") - # make sure it's readable - try: - fp = open(self.report_file, "r") - except: - return False - - # read ftp URL from configuration - try: - upload_url = self.cInfo['config'].get("general", "upload_url") - except: - upload_url = "ftp://dropbox.redhat.com/incoming" - - 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 Red Hat's ftp server with name:") - print " " + upload_name - print - print _("Please communicate this name to your support representative.") - print + return - fp.close() |