aboutsummaryrefslogblamecommitdiffstats
path: root/src/sosreport
blob: b00dfd6a3d7f773b00133656c5c8908e7042a21d (plain) (tree)
1
2
3
4
5
6
7
                     



                                                             
               
                                                  
















                                                                        


                             

          
              
                                         


                         

                                                    
              


                                    
 

                 
                                                                               

                                                                   
                                   


                 


                                                            
                                                                                            
                                  

                                               


                                                                                                    
                                          
                                                            
                                                                              
                   

    
                                           

                                              
    
                                        
                                            
                                              



                                                                           
 
               

                    




                                                          


                                                                    


                                                              



                                                                              
                                                                     
                                                           



                                                                      


                                                                                   


                                                                       
                                                               
                                                       
                                                                        
                                                                 
                                                         
                                                                  

                                                                   
                                                                    


                                                                              


                                                                                               


                                                                          


                                                                  



                                                                                                                   

                                  


                                                        
                                                                                   

                 

                                           


                                   

                   
                                               
 
                 
 

                     


                                           

                                                             




                                     



                                                                                                                      






                                                                
                        


                   


                                            


                                                                   


              














































                                                                                                                                  



































































                                                                                                                     
                





                                                                                       
                      
                       

                   
                                                                






                                                                    
                                                  
 


                                                






                                                  









                                                                                  
                                                                             
                                               

                                                        
                                           

                                      
                                                          

                              





                                                     
 

                        
                                                     
                                                                                        

                                                                                           

 

                                      



                                                          
    
                                        
                                    
                  
 
                               
                        


                                                            

                                                    
                


                                                                                   





                                                                                         
                            










                                                                                                                                                                         

                                                                                
                   


                                                                             
                                                             

                               















                                                                                     
                                                                 




                                                              
                                                                                                    




                                                             
                                                                      











                                                                                    
 
                              
                                                     

                   
        
                                                                                   



                                                               
 

                                          
 




                                           
 
                                         





                                            

                                                                     





                                             
                                                                                                            
                                          
                        
                                                                               
                                                          



                                             
                                     

                                                               

                                              
                     
                                              
                                       

                                                               
                                          
 

                                   
                                                                       
 

                                           
                                                                              
                    


                                       
 

                                             
                                                                     
                                       
                            
             
                                
                                       



                                           
                         
 

                                                            
                                                 

                                            
                                                                                      
                       
                                           
                                  
                             


                                             
                                                                                  




                                                            



                                       

                                   
                              

















                                                                                                                 





                                                             







                                                                                      


                                                                                   
                            























                                                     
                                              



                                        

                                                                                



                                                             
                                             
                                           


                                                 
 

                          


                             
                    
#!/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
from pwd import getpwuid

__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():
            thread.wait()
        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
    print "Calling sys.exit from doExitCode()"
    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="list of plugins _not_ to load", default = [])
__cmdParser__.add_option("-o", "--onlyplugin", action="extend", \
                     dest="onlyplugins", type="string", \
                     help="list of plugins to load", default = [])
__cmdParser__.add_option("-e", "--enableplugin", action="extend", \
                     dest="enableplugins", type="string", \
                     help="list of plugins to enable", 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("-b", "--no-progressbar", action="store_false", \
                     dest="progressbar", default=True, \
                     help="Do not display a progress bar.")
__cmdParser__.add_option("-c", "--curses", action="store_false", \
                     dest="use_curses", default=False, \
                     help="Do not display a progress bar.")
__cmdParser__.add_option("-m", "--multithreaded", action="store_true", \
                     dest="multithread", \
                     help="Use the multithreaded information gathering mode to speed up the report (experimental)")
(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args()

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=12):
        self.progBar = "[]"   # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0       # When amount == max, we are 100% done 
        self.updateAmount(0)  # Build progress bar string

    def updateAmount(self, newAmount = 0):
        if newAmount < self.min: newAmount = self.min
        if newAmount > self.max: newAmount = self.max
        self.amount = newAmount

        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = round(percentDone)
        percentDone = int(percentDone)

        # 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):]

    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):
        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 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)

    sys.stdout.write("\n")
    soslog.info("sosreport (version %s)" % (__version__) )
    sys.stdout.write("\n")
    
    # 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("plugin load failed for %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)

        print "The following plugins are currently enabled:"
        print
        for (plugname,plug) in loadedplugins:
           print " %-25s  %s" % (plugname,plug.get_description())

        print
        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"])

        print
        print "The following plugins are currently disabled:"
        print
        for (plugname,plugclass) in skippedplugins:
           print " %-25s  %s" % (plugname,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)

    # Iterate over plugins for each stage
    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)

    # Setup the progress bar
    if __cmdLineOpts__.progressbar:
        pbar = progressBar(0, len(loadedplugins) * 33, totalWidth = 60)

    # Call the setup method for each plugin
    for plugname, plug in loadedplugins:
        soslog.log(logging.VERBOSE2, "Setting up plugin module %s" % plugname)
        plug.setup()
        if __cmdLineOpts__.progressbar:
            pbar.incAmount()
            pbar.update()

    # Call the collect method for each plugin
    for plugname, plug in loadedplugins:
        soslog.log(logging.VERBOSE, "Executing plugin %s" % plugname)
        if __cmdLineOpts__.multithread:
            plug.doCollect()
        else:
            plug.copyStuff()    
        if __cmdLineOpts__.progressbar:
            if __cmdLineOpts__.multithread:
                pbar.incAmount()
            else:
                pbar.incAmount(30)
            pbar.update()

    xmlrep.serialize_to_file(rptdir + "/" + "sosreport.xml")

    # Wait for all the collection threads to exit
    if __cmdLineOpts__.multithread:
        for plugname, plug in loadedplugins:
            soslog.log(logging.VERBOSE2, "Waiting for plugin %s to return" % plugname)
            plug.wait()
            if __cmdLineOpts__.progressbar:
                pbar.incAmount(30)
                pbar.update()

    # 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")

    # Sort the module names to do the report in alphabetical order
    loadedplugins.sort()

    # 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()