diff options
Diffstat (limited to 'trunk/src/sosreport')
-rwxr-xr-x | trunk/src/sosreport | 725 |
1 files changed, 725 insertions, 0 deletions
diff --git a/trunk/src/sosreport b/trunk/src/sosreport new file mode 100755 index 00000000..86d66b4d --- /dev/null +++ b/trunk/src/sosreport @@ -0,0 +1,725 @@ +#!/usr/bin/env python +""" +Gather information about a system and report it using plugins +supplied for application-specific information +""" +## sosreport.py +## gather information about a system and report it + +## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com> + +### This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# pylint: disable-msg = W0611 +# pylint: disable-msg = W0702 + +import sys +import os +#import curses +from optparse import OptionParser, Option +import sos.policyredhat +from sos.helpers import * +from snack import * +from threading import Thread, activeCount, enumerate +import signal +import logging +from stat import * +from time import strftime, localtime, time +from pwd import getpwuid +import gettext +from threading import Semaphore + +__version__ = 1.7 + +__breakHits__ = 0 # Use this to track how many times we enter the exit routine + +## Set up routines to be linked to signals for termination handling +def exittermhandler(signum, frame): + doExitCode() + +def doExitCode(): + global __breakHits__ + __breakHits__ += 1 + if ( ( activeCount() > 1 ) and ( __breakHits__ == 1 ) ): + print "SIGTERM received, multiple threads detected, waiting for all threads to exit" + for thread in enumerate(): + if thread.getName() != "MainThread": + thread.join() + print "All threads ended, cleaning up." + if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): + print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately" + ## FIXME: Add thread-kill code (see FIXME below) + print "Threads dead, cleaning up." + if ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): + print "Multiple SIGTERMs, single thread, exiting without cleaning up." + sys.exit(3) + + # FIXME: Add code here to clean up /tmp + sys.exit("Abnormal exit") + +# Handle any sort of exit signal cleanly +# Currently, we intercept only sig 15 (TERM) +signal.signal(signal.SIGTERM, exittermhandler) + +## FIXME: Need to figure out how to IPC with child threads in case of +## multiple SIGTERMs. +## FIXME: Need to figure out how to handle SIGKILL - we can't intercept it. + +# for debugging +__raisePlugins__ = 1 + +class SosOption (Option): + """Allow to specify comma delimited list of plugins""" + ACTIONS = Option.ACTIONS + ("extend",) + STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) + TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) + + def take_action(self, action, dest, opt, value, values, parser): + if action == "extend": + try: lvalue = value.split(",") + except: pass + else: values.ensure_value(dest, []).extend(lvalue) + else: + Option.take_action(self, action, dest, opt, value, values, parser) + +__cmdParser__ = OptionParser(option_class=SosOption) +__cmdParser__.add_option("-a", "--alloptions", action="store_true", \ + dest="usealloptions", default=False, \ + help="Use all options for loaded plugins") +__cmdParser__.add_option("-f", "--fastoptions", action="store_true", \ + dest="fastoptions", default=False, \ + help="Use only fast options for loaded plugins") +__cmdParser__.add_option("-g", "--gatheronly", action="store_true", \ + dest="gatheronly", default=False, \ + help="Gather information locally but don't package or submit") +__cmdParser__.add_option("-l", "--list-plugins", action="store_true", \ + dest="listPlugins", default=False, \ + help="list existing plugins") +__cmdParser__.add_option("-n", "--noplugin", action="extend", \ + dest="noplugins", type="string", \ + help="skip these plugins", default = []) +__cmdParser__.add_option("-o", "--onlyplugin", action="extend", \ + dest="onlyplugins", type="string", \ + help="enable these plugins only", default = []) +__cmdParser__.add_option("-e", "--enableplugin", action="extend", \ + dest="enableplugins", type="string", \ + help="list of inactive plugins to be enabled", default = []) +__cmdParser__.add_option("-k", "--pluginopts", action="extend", \ + dest="plugopts", type="string", \ + help="plugin options in plugin_name.option=value format") +__cmdParser__.add_option("-v", "--verbose", action="count", \ + dest="verbosity", \ + help="How obnoxious we're being about telling the user what we're doing.") +__cmdParser__.add_option("-c", "--curses", action="store_true", \ + dest="use_curses", default=False, \ + help="Display a text GUI menu to modify plugin options.") +__cmdParser__.add_option("--no-progressbar", action="store_false", \ + dest="progressbar", default=True, \ + help="Do not display a progress bar.") +__cmdParser__.add_option("--no-multithread", action="store_true", \ + dest="nomultithread", \ + help="Disable multi-threaded gathering mode (slower)", default=False) +(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args() + +def textcolor(text, fg, bg=None, raw=0): + colors = { "black":"30", "red":"31", "green":"32", "brown":"33", "blue":"34", + "purple":"35", "cyan":"36", "lgray":"37", "gray":"1;30", "lred":"1;31", + "lgreen":"1;32", "yellow":"1;33", "lblue":"1;34", "pink":"1;35", + "lcyan":"1;36", "white":"1;37" } + opencol = "\033[" + closecol = "m" + clear = opencol + "0" + closecol + f = opencol + colors[fg] + closecol + return "%s%s%s" % (f, text, clear) + +def get_curse_options(alloptions): + """ + use curses to enable the user to select some options + """ + # alloptions is an array of (plug, plugname, optname, parms(dictionary)) tuples + plugName = [] + out = [] + + # get a sorted list of all plugin names + for rrr in alloptions: + if rrr[1] not in plugName: + plugName.append(rrr[1]) + + plugName.sort() + plugCbox = CheckboxTree(height=5, scroll=1) + + countOpt = -1 + + optDic = {} + optDicCounter = 0 + + # iterate over all plugins with options + for curPlugName in plugName: + plugCbox.addItem(curPlugName, (snackArgs['append'],)) + countOpt = countOpt+1 + + for opt in alloptions: + if opt[1] != curPlugName: + continue + + snt = opt[2] + " ("+opt[3]['desc']+") is " + opt[3]['speed'] + plugCbox.addItem(snt, (countOpt, snackArgs['append']), item = optDicCounter, selected = opt[3]['enabled']) + optDic[optDicCounter] = opt + optDicCounter += 1 + + + screen = SnackScreen() + bb = ButtonBar(screen, (("Ok", "ok"), ("Cancel", "cancel"))) + g = GridForm(screen, "Select Sosreport Options", 1, 10) + g.add(plugCbox, 0, 0) + g.add(bb, 0, 1, growx = 1) + result = g.runOnce() + + screen.finish() + + if bb.buttonPressed(result) == "cancel": + raise "Cancelled" + + for rrr in range(0, optDicCounter): + optDic[rrr][3]['enabled'] = plugCbox.getEntryValue(rrr)[1] + out.append((optDic[rrr])) + + return out + +class progressBar: + def __init__(self, minValue = 0, maxValue = 10, totalWidth=40): + self.progBar = "[]" # This holds the progress bar string + self.min = minValue + self.max = maxValue + self.width = totalWidth + self.amount = 0 # When amount == max, we are 100% done + self.time_start = time() + self.eta = 0 + self.last_amount_update = time() + self.update() + + def updateAmount(self, newAmount = 0): + if newAmount < self.min: newAmount = self.min + if newAmount > self.max: newAmount = self.max + if self.amount != newAmount: + self.last_amount_update = time() + self.amount = newAmount + last_update_relative = round(self.last_amount_update - self.time_start) + self.eta = round(last_update_relative * self.max / self.amount) + + # generate ETA + timeElapsed = round(time() - self.time_start) + last_update_relative = round(self.last_amount_update - self.time_start) + if timeElapsed >= 10 and self.amount > 0: + percentDone = round(timeElapsed * 100 / self.eta) + if percentDone > 100: + percentDone = 100 + ETA = timeElapsed + elif self.eta < timeElapsed: + ETA = timeElapsed + else: + ETA = self.eta + ETA = "[%02d:%02d/%02d:%02d]" % (round(timeElapsed/60), timeElapsed % 60, round(ETA/60), ETA % 60) + else: + ETA = "[%02d:%02d/--:--]" % (round(timeElapsed/60), timeElapsed % 60) + if self.amount < self.max: + percentDone = 0 + else: + percentDone = 100 + + # Figure out how many hash bars the percentage should be + allFull = self.width - 2 + numHashes = (percentDone / 100.0) * allFull + numHashes = int(round(numHashes)) + + # build a progress bar with hashes and spaces + self.progBar = " [" + '#'*numHashes + ' '*(allFull-numHashes) + "]" + + # figure out where to put the percentage, roughly centered + percentPlace = (len(self.progBar) / 2) - len(str(percentDone)) + percentString = str(percentDone) + "%" + + # slice the percentage into the bar + self.progBar = " Progress" + self.progBar[0:percentPlace] + percentString + self.progBar[percentPlace+len(percentString):] + ETA + + def incAmount(self, toInc = 1): + self.updateAmount(self.amount+toInc) + + def finished(self): + self.updateAmount(self.max) + sys.stdout.write(self.progBar + '\n') + sys.stdout.flush() + + def update(self): + self.updateAmount(self.amount) + sys.stdout.write(self.progBar + '\r') + sys.stdout.flush() + +class XmlReport: + def __init__(self): + try: + import libxml2 + except: + self.enabled = False + return + else: + self.enabled = True + self.doc = libxml2.newDoc("1.0") + self.root = self.doc.newChild(None, "sos", None) + self.commands = self.root.newChild(None, "commands", None) + self.files = self.root.newChild(None, "files", None) + + def add_command(self,cmdline,exitcode,stdout = None,stderr = None,f_stdout=None,f_stderr=None, runtime=None): + if not self.enabled: return + + cmd = self.commands.newChild(None, "cmd", None) + + cmd.setNsProp(None, "cmdline", cmdline) + + cmdchild = cmd.newChild(None, "exitcode", str(exitcode)) + + if runtime: + cmd.newChild(None, "runtime", str(runtime)) + + if stdout or f_stdout: + cmdchild = cmd.newChild(None, "stdout", stdout) + if f_stdout: + cmdchild.setNsProp(None, "file", f_stdout) + + if stderr or f_stderr: + cmdchild = cmd.newChild(None, "stderr", stderr) + if f_stderr: + cmdchild.setNsProp(None, "file", f_stderr) + + def add_file(self,fname,stats): + if not self.enabled: return + + cfile = self.files.newChild(None,"file",None) + + cfile.setNsProp(None, "fname", fname) + + cchild = cfile.newChild(None, "uid", str(stats[ST_UID])) + cchild.setNsProp(None,"name", getpwuid(stats[ST_UID])[0]) + cchild = cfile.newChild(None, "gid", str(stats[ST_GID])) + cchild.setNsProp(None,"name", getpwuid(stats[ST_GID])[0]) + cfile.newChild(None, "mode", str(oct(S_IMODE(stats[ST_MODE])))) + cchild = cfile.newChild(None, "ctime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_CTIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_CTIME])) + cchild = cfile.newChild(None, "atime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_ATIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_ATIME])) + cchild = cfile.newChild(None, "mtime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_MTIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_MTIME])) + + def serialize(self): + if not self.enabled: return + + print self.doc.serialize(None, 1) + + def serialize_to_file(self,fname): + if not self.enabled: return + + outfn = open(fname,"w") + outfn.write(self.doc.serialize(None,1)) + outfn.close() + +def sosreport(): + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0914 + # pylint: disable-msg = R0915 + """ + This is the top-level function that gathers and processes all sosreport information + """ + loadedplugins = [] + skippedplugins = [] + alloptions = [] + + # perhaps we should automatically locate the policy module?? + policy = sos.policyredhat.SosPolicy() + + # find the plugins path + paths = sys.path + for path in paths: + if path.strip()[-len("site-packages"):] == "site-packages": + pluginpath = path + "/sos/plugins" + reporterpath = path + "/sos/reporters" + + # Set up common info and create destinations + + dstroot = sosFindTmpDir() + cmddir = os.path.join(dstroot, "sos_commands") + logdir = os.path.join(dstroot, "sos_logs") + rptdir = os.path.join(dstroot, "sos_reports") + os.mkdir(cmddir, 0755) + os.mkdir(logdir, 0755) + os.mkdir(rptdir, 0755) + + # initialize i18n language localization + gettext.install('sos', '/usr/share/locale', unicode=False) + + # initialize logging + soslog = logging.getLogger('sos') + soslog.setLevel(logging.DEBUG) + + # log to a file + flog = logging.FileHandler(logdir + "/sos.log") + flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) + flog.setLevel(logging.DEBUG) + soslog.addHandler(flog) + + # define a Handler which writes INFO messages or higher to the sys.stderr + console = logging.StreamHandler(sys.stderr) + if __cmdLineOpts__.verbosity > 0: + console.setLevel(20 - __cmdLineOpts__.verbosity) + __cmdLineOpts__.progressbar = False + else: + console.setLevel(logging.INFO) + console.setFormatter(logging.Formatter('%(message)s')) + soslog.addHandler(console) + + logging.VERBOSE = logging.INFO - 1 + logging.VERBOSE2 = logging.INFO - 2 + logging.VERBOSE3 = logging.INFO - 3 + logging.addLevelName(logging.VERBOSE, "verbose") + logging.addLevelName(logging.VERBOSE2,"verbose2") + logging.addLevelName(logging.VERBOSE3,"verbose3") + + xmlrep = XmlReport() + + # set up dict so everyone can share the following + commons = {'dstroot': dstroot, 'cmddir': cmddir, 'logdir': logdir, 'rptdir': rptdir, + 'soslog': soslog, 'policy': policy, 'verbosity' : __cmdLineOpts__.verbosity, + 'xmlreport' : xmlrep } + + # Make policy aware of the commons + policy.setCommons(commons) + + print + soslog.info ( _("sosreport (version %s)") % __version__) + print + + # generate list of available plugins + plugins = os.listdir(pluginpath) + plugins.sort() + + # validate and load plugins + for plug in plugins: + plugbase = plug[:-3] + if not plug[-3:] == '.py' or plugbase == "__init__": + continue + try: + #print "importing plugin: %s" % plugbase + try: + if policy.validatePlugin(pluginpath + plug): + pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) + else: + soslog.warning(_("plugin %s does not validate, skipping") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if plugbase in __cmdLineOpts__.noplugins: + soslog.log(logging.VERBOSE, _("plug %s skipped (noplugins)") % plugbase) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s not specified in --onlyplugin list") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + loadedplugins.append((plugbase, pluginClass(plugbase, commons))) + except: + soslog.warning(_("plugin %s does not install, skipping") % plug) + raise + except: + soslog.warning(_("could not load plugin %s") % plug) + if __raisePlugins__: + raise + + # First, gather and process options + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname) + names, parms = plug.getAllOptions() + for optname, optparm in zip(names, parms): + alloptions.append((plug, plugname, optname, optparm)) + + if __cmdLineOpts__.listPlugins: + if not len(loadedplugins) and not len(skippedplugins): + soslog.error(_("no valid plugins found")) + sys.exit(1) + + if len(loadedplugins): + print _("The following plugins are currently enabled:") + print + for (plugname,plug) in loadedplugins: + print " %-25s %s" % (textcolor(plugname,"lblue"),plug.get_description()) + else: + print _("No plugin enabled.") + print + + if len(alloptions): + print _("The following plugin options are available:") + print + for (plug, plugname, optname, optparm) in alloptions: + print " %-25s %s [%d]" % (plugname + "." + optname, optparm["desc"], optparm["enabled"]) + else: + print _("No plugin options available.") + + if len(skippedplugins): + print + print _("The following plugins are currently disabled:") + print + for (plugname,plugclass) in skippedplugins: + print " %-25s %s" % (textcolor(plugname,"blue"),plugclass.get_description()) + + print + sys.exit() + + # to go anywhere further than listing the plugins we will need root permissions. + # + if os.getuid() != 0: + print _('sosreport requires root permissions to run.') + sys.exit(1) + + # we don't need to keep in memory plugins we are not going to use + del skippedplugins + + if not len(loadedplugins): + soslog.error(_("no valid plugins were enabled")) + sys.exit(1) + + try: + raw_input(_("""This utility will collect some detailed information about the +hardware and setup of your Red Hat Enterprise Linux system. +This information will be used to diagnose problems with your +system and will be considered confidential information. +Red Hat will use this information for diagnostic purposes ONLY. + +This process may take a while to complete. +No changes will be made to your system. + +Press ENTER to continue, or CTRL-C to quit. +""")) + except KeyboardInterrupt: + print + sys.exit(0) + + # setup plugin options + if __cmdLineOpts__.plugopts: + opts = {} + for opt in __cmdLineOpts__.plugopts: + try: opt, val = opt.split("=") + except: val=1 + plug, opt = opt.split(".") + try: val = int(val) # try to convert string "val" to int() + except: pass + try: opts[plug] + except KeyError: opts[plug] = [] + opts[plug].append( (opt,val) ) + for plugname, plug in loadedplugins: + if opts.has_key(plugname): + for opt,val in opts[plugname]: + soslog.log(logging.VERBOSE, "setting option %s for plugin %s to %s" % (plugname,opt,val)) + plug.setOption(opt,val) + del opt,opts,val + elif not __cmdLineOpts__.fastoptions and not __cmdLineOpts__.usealloptions: + if len(alloptions) and __cmdLineOpts__.use_curses: + try: + get_curse_options(alloptions) + except "Cancelled": + sys.exit(_("Exiting.")) + elif __cmdLineOpts__.fastoptions: + for i in range(len(alloptions)): + for plug, plugname, optname, optparm in alloptions: + if optparm['speed'] == 'fast': + plug.setOption(optname, 1) + else: + plug.setOption(optname, 0) + elif __cmdLineOpts__.usealloptions: + for i in range(len(alloptions)): + for plug, plugname, optname, optparm in alloptions: + plug.setOption(optname, 1) + + # Call the diagnose() method for each plugin + tmpcount = 0 + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Performing sanity check for plugin %s" % plugname) + plug.diagnose() + tmpcount += len(plug.diagnose_msgs) + if tmpcount > 0: + print _("One or more plugin has detected a problem in your configuration.") + print _("Please review the following messages:") + print + for plugname, plug in loadedplugins: + for msg in plug.diagnose_msgs: + soslog.warning(" * %s: %s", plugname, msg) + print + try: + raw_input( _("Press ENTER to continue, or CTRL-C to quit.\n") ) + except KeyboardInterrupt: + print + sys.exit(0) + + # Call the setup() method for each plugin + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname) + plug.setup() + + # Setup the progress bar + if __cmdLineOpts__.progressbar: + # gather information useful for generating ETA + eta_weight = len(loadedplugins) + for plugname, plug in loadedplugins: + eta_weight += plug.eta_weight + pbar = progressBar(minValue = 0, maxValue = eta_weight) + # pbar.max = number_of_plugins + weight (default 1 per plugin) + + if __cmdLineOpts__.nomultithread: + soslog.log(logging.VERBOSE, "using single-threading") + else: + soslog.log(logging.VERBOSE, "using multi-threading") + + # Call the collect method for each plugin + plugrunning = Semaphore(2) + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE, "executing plugin %s" % plugname) + if not __cmdLineOpts__.nomultithread: + plug.copyStuff(threaded = True, semaphore = plugrunning) + else: + plug.copyStuff() + if __cmdLineOpts__.progressbar: + pbar.incAmount(plug.eta_weight) + pbar.update() + del plugrunning + + # Wait for all the collection threads to exit + if not __cmdLineOpts__.nomultithread: + finishedplugins = [] + while len(loadedplugins) > 0: + plugname, plug = loadedplugins.pop(0) + if not plug.wait(0.5): + finishedplugins.append((plugname,plug)) + soslog.log(logging.VERBOSE2, "plugin %s has returned" % plugname) + if __cmdLineOpts__.progressbar: + pbar.incAmount(plug.eta_weight) + else: + soslog.log(logging.VERBOSE3, "plugin %s still hasn't returned" % plugname) + loadedplugins.append((plugname,plug)) + if __cmdLineOpts__.progressbar: + pbar.update() + loadedplugins = finishedplugins + del finishedplugins + + xmlrep.serialize_to_file(rptdir + "/" + "sosreport.xml") + + # Call the analyze method for each plugin + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Analyzing results of plugin %s" % plugname,) + try: + plug.analyze() + except: + # catch exceptions in analyse() and keep working + pass + if __cmdLineOpts__.progressbar: + pbar.incAmount() + pbar.update() + + if __cmdLineOpts__.progressbar: + pbar.finished() + sys.stdout.write("\n") + + # Generate the header for the html output file + rfd = open(rptdir + "/" + "sosreport.html", "w") + rfd.write(""" + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> + <head> + <link rel="stylesheet" type="text/css" media="screen" href="donot.css" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Sos System Report</title> + </head> + + <body> + """) + + + # Make a pass to gather Alerts and a list of module names + allAlerts = [] + plugNames = [] + for plugname, plug in loadedplugins: + for alert in plug.alerts: + allAlerts.append('<a href="#%s">%s</a>: %s' % (plugname, plugname, alert)) + plugNames.append(plugname) + + + + # Create a table of links to the module info + rfd.write("<hr/><h3>Loaded Plugins:</h3>") + rfd.write("<table><tr>\n") + rr = 0 + for i in range(len(plugNames)): + rfd.write('<td><a href="#%s">%s</a></td>\n' % (plugNames[i], plugNames[i])) + rr = divmod(i, 4)[1] + if (rr == 3): + rfd.write('</tr>') + if not (rr == 3): + rfd.write('</tr>') + rfd.write('</table>\n') + + rfd.write('<hr/><h3>Alerts:</h3>') + rfd.write('<ul>') + for alert in allAlerts: + rfd.write('<li>%s</li>' % alert) + rfd.write('</ul>') + + + # Call the report method for each plugin + for plugname, plug in loadedplugins: + html = plug.report() + rfd.write(html) + + rfd.write("</body></html>") + + rfd.close() + + # Collect any needed user information (name, etc) + + # Call the postproc method for each plugin + for plugname, plug in loadedplugins: + plug.postproc() + + if __cmdLineOpts__.gatheronly: + soslog.info(_("Collected information is in ") + dstroot) + soslog.info(_("Your html report is in ") + rptdir + "/" + "sosreport.html") + else: + # package up the results for the support organization + policy.packageResults() + # delete gathered files + os.system("/bin/rm -rf %s" % dstroot) + # automated submission will go here + + # Close all log files and perform any cleanup + logging.shutdown() + + +if __name__ == '__main__': + try: + sosreport() + except KeyboardInterrupt: + doExitCode() |