diff options
Diffstat (limited to 'src/sosreport')
-rwxr-xr-x | src/sosreport | 341 |
1 files changed, 124 insertions, 217 deletions
diff --git a/src/sosreport b/src/sosreport index 37a88a47..888e0b54 100755 --- a/src/sosreport +++ b/src/sosreport @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python """ Gather information about a system and report it using plugins supplied for application-specific information @@ -28,26 +28,19 @@ supplied for application-specific information import sys import os from optparse import OptionParser, Option -import ConfigParser import sos.policyredhat from sos.helpers import * from snack import * from threading import Thread, activeCount import signal +import logging from stat import * from time import strftime, localtime, time from pwd import getpwuid import gettext from threading import Semaphore -# RHEL3 doesn't have a logging module -try: - import logging -except ImportError: - import sos.rhel3_logging - logging = sos.rhel3_logging - -__version__ = 1.8 +__version__ = 1.7 __breakHits__ = 0 # Use this to track how many times we enter the exit routine @@ -57,15 +50,10 @@ def exittermhandler(signum, frame): def doExitCode(): from threading import enumerate - global __breakHits__, loadedplugins, dstroot - + global __breakHits__ __breakHits__ += 1 if ( ( activeCount() > 1 ) and ( __breakHits__ == 1 ) ): print "SIGTERM received, multiple threads detected, waiting for all threads to exit" - - for plugname, plug in loadedplugins: - plug.exit_please() - for thread in enumerate(): if thread.getName() == "MainThread": continue @@ -74,41 +62,20 @@ def doExitCode(): try: thread.join() except KeyboardInterrupt: - doExitCode() + pass else: print "All threads ended, cleaning up." - doExit(1) - - if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): - print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately." + if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): + print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately" ## FIXME: Add thread-kill code (see FIXME below) - return - elif ( ( activeCount() > 1 ) and ( __breakHits__ > 2 ) ): - print "Multiple SIGTERMs, multiple threads, process suicides." - os.kill(os.getpid(), signal.SIGKILL) - elif ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): +# os.kill(os.getpid(), signal.SIGKILL) + print "Threads dead, cleaning up." + if ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): print "Multiple SIGTERMs, single thread, exiting without cleaning up." - doExit(3) - - doExit("Abnormal exit") - -def doExit(error=0): - global policy - policy.cleanDstroot() - sys.exit(error) - -def doException(type, value, tb): - if hasattr(sys, 'ps1') or not sys.stderr.isatty(): - # we are in interactive mode or we don't have a tty-like - # device, so we call the default hook - sys.__excepthook__(type, value, tb) - else: - import traceback, pdb - # we are NOT in interactive mode, print the exception... - traceback.print_exception(type, value, tb) - print - # ...then start the debugger in post-mortem mode. - pdb.pm() + sys.exit(3) + + # FIXME: Add code here to clean up /tmp + sys.exit("Abnormal exit") # Handle any sort of exit signal cleanly # Currently, we intercept only sig 15 (TERM) @@ -117,6 +84,9 @@ signal.signal(signal.SIGTERM, exittermhandler) ## FIXME: Need to figure out how to IPC with child threads in case of ## multiple SIGTERMs. +# for debugging +__raisePlugins__ = 0 + class OptionParser_extended(OptionParser): def print_help(self): OptionParser.print_help(self) @@ -125,10 +95,10 @@ class OptionParser_extended(OptionParser): print print " enable cluster plugin only and collect dlm lockdumps:" print " # sosreport -o cluster -k cluster.lockdump" - print + print print " disable memory and samba plugins, turn off rpm -Va collection:" print " # sosreport -n memory,samba -k rpm.rpmva=off" - print + print class SosOption (Option): """Allow to specify comma delimited list of plugins""" @@ -163,51 +133,39 @@ __cmdParser__.add_option("-k", action="extend", \ __cmdParser__.add_option("-a", "--alloptions", action="store_true", \ dest="usealloptions", default=False, \ help="enable all options for loaded plugins") -__cmdParser__.add_option("-u", "--upload", action="store_true", \ - dest="upload", default=False, \ - help="upload the report to Red Hat support") -#__cmdParser__.add_option("--encrypt", action="store_true", \ -# dest="encrypt", default=False, \ -# help="encrypt with GPG using Red Hat support's public key") -__cmdParser__.add_option("--batch", action="store_true", \ - dest="batch", default=False, \ - help="do not ask any question (batch mode)") -__cmdParser__.add_option("--no-colors", action="store_true", \ - dest="nocolors", default=False, \ - help="do not use terminal colors for text") __cmdParser__.add_option("-v", "--verbose", action="count", \ dest="verbosity", \ help="increase verbosity") -__cmdParser__.add_option("--debug", action="count", \ - dest="debug", \ - help="enabling debugging") __cmdParser__.add_option("--no-progressbar", action="store_false", \ dest="progressbar", default=True, \ help="do not display a progress bar.") __cmdParser__.add_option("--no-multithread", action="store_true", \ dest="nomultithread", \ help="disable multi-threaded gathering mode (slower)", default=False) +__cmdParser__.add_option("--name", action="store", \ + dest="name",type="string", \ + help="first initial and last name.", default="") +__cmdParser__.add_option("--ticket-number", action="store", \ + dest="ticketnumber", type="string", \ + help="ticket number.", default="") if sys.argv[0].endswith("sysreport"): - print - print "WARNING: sysreport is deprecated, please use sosreport instead." - if not sys.stdin.isatty(): - print + try: + ppid = os.getppid() + fp = open("/proc/%d/cmdline" % ppid, "r") + cmd = fp.read() + fp.close() + except: + cmd = "" + if not sys.stdin.isatty() or cmd.find("bash") < 0: os.execl("/bin/sh", "/bin/sh", "-c", "/usr/sbin/sysreport.legacy") - sys.exit(-1) - -if "-norpm" in sys.argv: + os.exit(-1) print - print """WARNING: sysreport's "-norpm" option is deprecated, please use "-k rpm.rpmva=off" instead.""" - print - sys.exit(1) + print "WARNING: sysreport is deprecated, please use sosreport instead." (__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args() def textcolor(text, fg, raw=0): - global __cmdLineOpts__ - if __cmdLineOpts__.nocolors: - return text colors = { "black":"30", "red":"31", "green":"32", "brown":"33", "blue":"34", "purple":"35", "cyan":"36", "lgray":"37", "gray":"1;30", "lred":"1;31", "lgreen":"1;32", "yellow":"1;33", "lblue":"1;34", "pink":"1;35", @@ -230,11 +188,11 @@ class progressBar: self.last_amount_update = time() self.update() - def updateAmount(self, newAmount = 0, finished = False): + def updateAmount(self, newAmount = 0): if newAmount < self.min: newAmount = self.min if newAmount > self.max: - newAmount = self.max - 1 + newAmount = self.max if self.amount != newAmount: self.last_amount_update = time() self.amount = newAmount @@ -249,8 +207,6 @@ class progressBar: percentDone = round(timeElapsed * 100 / self.eta) except: percentDone = 0 - if percentDone >= 100 and not finished: - percentDone = 99 if percentDone > 100: percentDone = 100 ETA = timeElapsed @@ -285,7 +241,7 @@ class progressBar: self.updateAmount(self.amount+toInc) def finished(self): - self.updateAmount(self.max, finished = True) + self.updateAmount(self.max) sys.stdout.write(self.progBar + '\n') sys.stdout.flush() @@ -359,14 +315,6 @@ class XmlReport: outfn.write(self.doc.serialize(None,1)) outfn.close() -# if debugging is enabled, allow plugins to raise exceptions - -if __cmdLineOpts__.debug: - sys.excepthook = doException - __raisePlugins__ = 1 -else: - __raisePlugins__ = 0 - def sosreport(): # pylint: disable-msg = R0912 # pylint: disable-msg = R0914 @@ -374,13 +322,6 @@ def sosreport(): """ This is the top-level function that gathers and processes all sosreport information """ - - global loadedplugins, dstroot, policy - - config = ConfigParser.ConfigParser() - try: config.readfp(open('/etc/sos.conf')) - except IOError: pass - loadedplugins = [] skippedplugins = [] alloptions = [] @@ -395,12 +336,8 @@ def sosreport(): pluginpath = path + "/sos/plugins" # Set up common info and create destinations - - dstroot = policy.getDstroot() - if not dstroot: - print _("Could not create temporary directory.") - doExit() + dstroot = sosFindTmpDir() cmddir = os.path.join(dstroot, "sos_commands") logdir = os.path.join(dstroot, "sos_logs") rptdir = os.path.join(dstroot, "sos_reports") @@ -409,7 +346,7 @@ def sosreport(): os.mkdir(rptdir, 0755) # initialize i18n language localization - gettext.install('sos', '/usr/share/locale', unicode=False) + gettext.install('sos', '/usr/share/locale', unicode=False) # initialize logging soslog = logging.getLogger('sos') @@ -417,15 +354,12 @@ def sosreport(): logging.VERBOSE = logging.INFO - 1 logging.VERBOSE2 = logging.INFO - 2 - logging.VERBOSE3 = logging.INFO - 3 + logging.VERBOSE3 = logging.INFO - 3 logging.addLevelName(logging.VERBOSE, "verbose") logging.addLevelName(logging.VERBOSE2,"verbose2") logging.addLevelName(logging.VERBOSE3,"verbose3") - # if stdin is not a tty, disable colors and don't ask questions - if not sys.stdin.isatty(): - __cmdLineOpts__.nocolors = True - __cmdLineOpts__.batch = True + # FIXME: strip colours if terminal doesn't allow it # log to a file flog = logging.FileHandler(logdir + "/sos.log") @@ -448,7 +382,7 @@ def sosreport(): # set up dict so everyone can share the following commons = {'dstroot': dstroot, 'cmddir': cmddir, 'logdir': logdir, 'rptdir': rptdir, 'soslog': soslog, 'policy': policy, 'verbosity' : __cmdLineOpts__.verbosity, - 'xmlreport' : xmlrep, 'cmdlineopts':__cmdLineOpts__, 'config':config } + 'xmlreport' : xmlrep } # Make policy aware of the commons policy.setCommons(commons) @@ -456,11 +390,12 @@ def sosreport(): print soslog.info ( _("sosreport (version %s)") % __version__) print - + # generate list of available plugins plugins = os.listdir(pluginpath) plugins.sort() - plugin_names = [] + + # FIXME: should at least print a warning if the user references a plugin which does not exist # validate and load plugins for plug in plugins: @@ -468,33 +403,36 @@ def sosreport(): if not plug[-3:] == '.py' or plugbase == "__init__": continue try: - if policy.validatePlugin(pluginpath + plug): - pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) - else: - soslog.warning(_("plugin %s does not validate, skipping") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - # plug-in is valid, let's decide whether run it or not - plugin_names.append(plugbase) - if plugbase in __cmdLineOpts__.noplugins: - soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, _("plugin %s not specified in --only-plugins list") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - loadedplugins.append((plugbase, pluginClass(plugbase, commons))) + #print "importing plugin: %s" % plugbase + try: + if policy.validatePlugin(pluginpath + plug): + pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) + else: + soslog.warning(_("plugin %s does not validate, skipping") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if plugbase in __cmdLineOpts__.noplugins: + soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s not specified in --only-plugins list") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + loadedplugins.append((plugbase, pluginClass(plugbase, commons))) + except: + soslog.warning(_("plugin %s does not install, skipping") % plug) + raise except: - soslog.warning(_("plugin %s does not install, skipping") % plug) + soslog.warning(_("could not load plugin %s") % plug) if __raisePlugins__: raise @@ -506,14 +444,6 @@ def sosreport(): if type(parms["enabled"])==bool: parms["enabled"] = True - # read plugin tunables from configuration file - if config.has_section("tunables"): - if not __cmdLineOpts__.plugopts: - __cmdLineOpts__.plugopts = [] - - for opt, val in config.items("tunables"): - __cmdLineOpts__.plugopts.append(opt + "=" + val) - if __cmdLineOpts__.plugopts: opts = {} for opt in __cmdLineOpts__.plugopts: @@ -523,7 +453,7 @@ def sosreport(): except: val=True else: - if val.lower() in ["off", "disable", "disabled", "false"]: + if val == "off" or val == "disable" or val == "disabled": val = False else: # try to convert string "val" to int() @@ -531,11 +461,7 @@ def sosreport(): except: pass # split up "general.syslogsize" - try: - plug, opt = opt.split(".") - except: - plug = opt - opt = True + plug, opt = opt.split(".") try: opts[plug] except KeyError: opts[plug] = [] @@ -544,26 +470,10 @@ def sosreport(): for plugname, plug in loadedplugins: if opts.has_key(plugname): for opt,val in opts[plugname]: - soslog.log(logging.VERBOSE, 'setting option "%s" for plugin (%s) to "%s"' % (plugname,opt,val)) - if not plug.setOption(opt,val): - soslog.error('no such option "%s" for plugin (%s)' % (opt,plugname)) - doExit(1) - del opts[plugname] - for plugname in opts.keys(): - soslog.error('unable to set option for disabled or non-existing plugin (%s)' % (plugname)) - doExit(1) + soslog.log(logging.VERBOSE, "setting option %s for plugin %s to %s" % (plugname,opt,val)) + plug.setOption(opt,val) del opt,opts,val - # error if the user references a plugin which does not exist - unk_plugs = [plugname.split(".")[0] for plugname in __cmdLineOpts__.onlyplugins if not plugname.split(".")[0] in plugin_names] - unk_plugs += [plugname.split(".")[0] for plugname in __cmdLineOpts__.noplugins if not plugname.split(".")[0] in plugin_names] - unk_plugs += [plugname.split(".")[0] for plugname in __cmdLineOpts__.enableplugins if not plugname.split(".")[0] in plugin_names] - if len(unk_plugs): - for plugname in unk_plugs: - soslog.error('a non-existing plugin (%s) was specified in the command line' % (plugname)) - doExit(1) - del unk_plugs - for plugname, plug in loadedplugins: soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname) names, parms = plug.getAllOptions() @@ -577,7 +487,7 @@ def sosreport(): if __cmdLineOpts__.listPlugins: if not len(loadedplugins) and not len(skippedplugins): soslog.error(_("no valid plugins found")) - doExit(1) + sys.exit(1) # FIXME: make -l output more concise if len(loadedplugins): @@ -620,22 +530,26 @@ def sosreport(): print _("No plugin options available.") print - doExit() + sys.exit() # to go anywhere further than listing the plugins we will need root permissions. # if os.getuid() != 0: print _('sosreport requires root permissions to run.') - doExit(1) + sys.exit(1) # we don't need to keep in memory plugins we are not going to use del skippedplugins if not len(loadedplugins): soslog.error(_("no valid plugins were enabled")) - doExit(1) + sys.exit(1) - msg = _("""This utility will collect some detailed information about the + isUnattended = False + if len(__cmdLineOpts__.name) > 0 and len(__cmdLineOpts__.ticketnumber) > 0: isUnattended = True + if not isUnattended: + try: + raw_input(_("""This utility will collect some detailed information about the hardware and setup of your Red Hat Enterprise Linux system. The information is collected and an archive is packaged under /tmp, which you can send to a support representative. @@ -645,14 +559,11 @@ and it will be considered confidential information. This process may take a while to complete. No changes will be made to your system. -""") - if __cmdLineOpts__.batch: - print msg - else: - msg += _("""Press ENTER to continue, or CTRL-C to quit.\n""") - try: raw_input(msg) - except: print ; doExit() - del msg +Press ENTER to continue, or CTRL-C to quit. +""")) + except KeyboardInterrupt: + print + sys.exit(0) # Call the diagnose() method for each plugin tmpcount = 0 @@ -679,32 +590,37 @@ No changes will be made to your system. fp.close() print - if not __cmdLineOpts__.batch: - try: - while True: + try: + while True: + if not isUnattended: yorno = raw_input( _("Are you sure you would like to continue (y/n) ? ") ) - if yorno == _("y") or yorno == _("Y"): - print - break - elif yorno == _("n") or yorno == _("N"): - doExit(0) - del yorno - except KeyboardInterrupt: - print - doExit(0) - - policy.preWork() + else: + yorno = _("Y") + + if yorno == _("y") or yorno == _("Y"): + print + break + elif yorno == _("n") or yorno == _("N"): + sys.exit(0) + del yorno + except KeyboardInterrupt: + print + sys.exit(0) + + if isUnattended: + policy.preWork(__cmdLineOpts__.name, __cmdLineOpts__.ticketnumber) + else: + policy.preWork() + # Call the setup() method for each plugin for plugname, plug in loadedplugins: soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname) try: - plug.setup() - except KeyboardInterrupt: - raise + plug.setup() except: - if __raisePlugins__: - raise + if __raisePlugins__: + raise # Setup the progress bar if __cmdLineOpts__.progressbar: @@ -723,17 +639,15 @@ No changes will be made to your system. # Call the collect method for each plugin plugrunning = Semaphore(2) for plugname, plug in loadedplugins: - soslog.log(logging.VERBOSE, "requesting plugin %s" % plugname) + soslog.log(logging.VERBOSE, "executing plugin %s" % plugname) try: if not __cmdLineOpts__.nomultithread: plug.copyStuff(threaded = True, semaphore = plugrunning) else: - plug.copyStuff() + plug.copyStuff() if __cmdLineOpts__.progressbar: pbar.incAmount(plug.eta_weight) pbar.update() - except KeyboardInterrupt: - raise except: if __raisePlugins__: raise @@ -841,6 +755,8 @@ No changes will be made to your system. rfd.close() + # Collect any needed user information (name, etc) + # Call the postproc method for each plugin for plugname, plug in loadedplugins: try: @@ -851,23 +767,14 @@ No changes will be made to your system. # package up the results for the support organization policy.packageResults() - # delete gathered files - policy.cleanDstroot() - - # let's encrypt the tar-ball - #if __cmdLineOpts__.encrypt: - # policy.encryptResults() - + os.system("/bin/rm -rf %s" % dstroot) # automated submission will go here - if not __cmdLineOpts__.upload: - policy.displayResults() - else: - policy.uploadResults() # Close all log files and perform any cleanup logging.shutdown() - + + if __name__ == '__main__': try: sosreport() |