Пример #1
0
    def __init__(self, *params, **kwds):
        self.listplugins = False
        self.noplugins = []
        self.enabledplugins = []
        self.onlyplugins = []
        self.plugopts = None
        self.usealloptions = False
        self.upload = False
        self.batch = False
        self.nocolors = False
        self.verbosity = 0
        self.debug = 0
        self.progressbar = True
        self.nomultithread = False
        self.checkrootuid = True
        self.autoloadplugins = True
        self.pluginnamesspaces = dict()

        self.loadedplugins = []
        self.skippedplugins = []
        self.alloptions = []

        self.plugins = list()
        self.plugin_names = list()

        import ConfigParser
        self.config = ConfigParser.ConfigParser()
        try:
            self.config.readfp(open(self.CONFFILENAME))
        except IOError:
            pass

        # generically set attributes from outside
        # The first param if there is supposed to be Values from optparse
        if len(params) > 0:
            self._fromOptparseValues(params[0])
        elif kwds:
            self._fromKwds(kwds)

        # perhaps we should automatically locate the policy module??
        import sos.policyredhat
        self.policy = sos.policyredhat.SosPolicy()

        # find the plugins path
        self.pluginpaths = dict()
        paths = sys.path
        for path in paths:
            if path.strip()[-len("site-packages"):] == "site-packages":
                self.appendPluginsPath(path + self.PLUGINPATH_SUFFIX)

        self.setupPathEnvironment()
        self.xmlreport = XmlReport()

        self.setupi18n()
        self.setupLogging()

        # Make policy aware of the commons
        self.policy.setCommons(self.getCommons())
    def __init__(self, *params, **kwds):
        self.listplugins=False
        self.noplugins=[]
        self.enabledplugins=[]
        self.onlyplugins=[]
        self.plugopts=None
        self.usealloptions=False
        self.upload=False
        self.batch=False
        self.nocolors=False
        self.verbosity=0
        self.debug=0
        self.progressbar=True
        self.nomultithread=False
        self.checkrootuid=True
        self.autoloadplugins=True
        self.pluginnamesspaces=dict()
        
        self.loadedplugins = []
        self.skippedplugins = []
        self.alloptions = []

        self.plugins=list()
        self.plugin_names=list()
        
        import ConfigParser
        self.config = ConfigParser.ConfigParser()
        try: self.config.readfp(open(self.CONFFILENAME))
        except IOError: pass

        # generically set attributes from outside
        # The first param if there is supposed to be Values from optparse
        if len(params) > 0:
            self._fromOptparseValues(params[0])
        elif kwds:
            self._fromKwds(kwds)
        
        # perhaps we should automatically locate the policy module??
        import sos.policyredhat
        self.policy = sos.policyredhat.SosPolicy()

        # find the plugins path
        self.pluginpaths=dict()
        paths = sys.path
        for path in paths:
            if path.strip()[-len("site-packages"):] == "site-packages":
                self.appendPluginsPath(path + self.PLUGINPATH_SUFFIX)

        self.setupPathEnvironment()
        self.xmlreport = XmlReport()
        
        self.setupi18n()
        self.setupLogging()
        
        # Make policy aware of the commons
        self.policy.setCommons(self.getCommons())
Пример #3
0
class SosReport(object):
    # pylint: disable-msg = R0912
    # pylint: disable-msg = R0914
    # pylint: disable-msg = R0915
    # for debugging
    """
    This is the top-level class that gathers and processes all sosreport information
    """
    __raisePlugins__ = 0

    # dictionary of attribute of class and keyname of returned dict
    __commons_attrs = {
        'dstroot': 'dstroot',
        'cmddir': 'cmddir',
        'logdir': 'logdir',
        'rptdir': 'rptdir',
        'soslog': 'soslog',
        'policy': 'policy',
        'verbosity': 'verbosity',
        'xmlreport': 'xmlreport',
        'options': 'cmdlineopts',
        'config': 'config'
    }
    # Only needed if started not with commandlineoptions but with keywords of other source
    # Then we need to build commandlineoptions from the given keywords. Here are all attributes and cmdlineoptionsnames specified.
    __kwd_attrs = {
        "listPlugins": "listPlugins",
        "noplugins": "noplugins",
        "enabledplugins": "enableplugins",
        "onlyplugins": "onlyplugins",
        "plugopts": "plugopts",
        "usealloptions": "usealloptions",
        "upload": "upload",
        "batch": "batch",
        "nocolors": "nocolors",
        "verbosity": "verbose",
        "debug": "debug",
        "progressbar": "progressbar",
        "nomultithread": "nomultithread"
    }

    # to being able to overwrite filenames
    LOGFILENAME = "sos.log"
    CONFFILENAME = "/etc/sos.conf"
    PLUGINPATH_SUFFIX = "/sos/plugins"
    LOCALE_DIR = "/usr/share/locale"

    def __init__(self, *params, **kwds):
        self.listplugins = False
        self.noplugins = []
        self.enabledplugins = []
        self.onlyplugins = []
        self.plugopts = None
        self.usealloptions = False
        self.upload = False
        self.batch = False
        self.nocolors = False
        self.verbosity = 0
        self.debug = 0
        self.progressbar = True
        self.nomultithread = False
        self.checkrootuid = True
        self.autoloadplugins = True
        self.pluginnamesspaces = dict()

        self.loadedplugins = []
        self.skippedplugins = []
        self.alloptions = []

        self.plugins = list()
        self.plugin_names = list()

        import ConfigParser
        self.config = ConfigParser.ConfigParser()
        try:
            self.config.readfp(open(self.CONFFILENAME))
        except IOError:
            pass

        # generically set attributes from outside
        # The first param if there is supposed to be Values from optparse
        if len(params) > 0:
            self._fromOptparseValues(params[0])
        elif kwds:
            self._fromKwds(kwds)

        # perhaps we should automatically locate the policy module??
        import sos.policyredhat
        self.policy = sos.policyredhat.SosPolicy()

        # find the plugins path
        self.pluginpaths = dict()
        paths = sys.path
        for path in paths:
            if path.strip()[-len("site-packages"):] == "site-packages":
                self.appendPluginsPath(path + self.PLUGINPATH_SUFFIX)

        self.setupPathEnvironment()
        self.xmlreport = XmlReport()

        self.setupi18n()
        self.setupLogging()

        # Make policy aware of the commons
        self.policy.setCommons(self.getCommons())

    def _createCmdLineOpts(self):
        from optparse import Values
        _values = Values()
        for (_key1, _key2) in self.__kwd_attrs.items():
            setattr(_values, _key2,
                    getattr(self, _key1, getattr(self, _key2, None)))
        return _values

    def _fromOptparseValues(self, values):
        self._fromKwds(values.__dict__)

    def _fromKwds(self, kwds):
        for (_key1, _key2) in self.__kwd_attrs.items():
            if kwds.has_key(_key1):
                _key = _key1
            elif kwds.has_key(_key2):
                _key = _key2
            else:
                continue
            setattr(self, _key1, kwds[_key])
        for (_key, _value) in kwds.items():
            if not _key in self.__kwd_attrs:
                setattr(self, _key, _value)

    def setupPathEnvironment(self):
        # Set up common info and create destinations
        self.dstroot = sos.helpers.sosFindTmpDir()
        if not self.dstroot:
            raise IOError("Could not create temporary directory %s." %
                          self.dstroot)
        self.cmddir = os.path.join(self.dstroot, "sos_commands")
        self.logdir = os.path.join(self.dstroot, "sos_logs")
        self.rptdir = os.path.join(self.dstroot, "sos_reports")
        os.mkdir(self.cmddir, 0755)
        os.mkdir(self.logdir, 0755)
        os.mkdir(self.rptdir, 0755)

    def appendPluginsPath(self, _path, _namespace="sos.plugins"):
        self.pluginpaths[_path] = _namespace

    def setupi18n(self):
        import gettext
        # initialize i18n language localization
        gettext.install('sos', self.LOCALE_DIR, unicode=False)

    def setupLogging(self):
        # initialize logging
        self.soslog = logging.getLogger('sos')
        self.soslog.setLevel(logging.DEBUG)

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

        # if stdin is not a tty, disable colors and don't ask questions
        if not sys.stdin.isatty():
            self.nocolors = True
            self.batch = True

        # log to a file
        flog = logging.FileHandler(self.logdir + self.LOGFILENAME)
        flog.setFormatter(
            logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
        flog.setLevel(logging.VERBOSE3)
        self.soslog.addHandler(flog)

        # define a Handler which writes INFO messages or higher to the sys.stderr
        console = logging.StreamHandler(sys.stderr)
        if self.verbosity > 0 or self.debug > 0:
            if self.verbosity > 0:
                console.setLevel(logging.INFO - self.verbosity)
            if self.debug > 0:
                console.setLevel(logging.DEBUG - self.debug + 1)
            self.progressbar = False
        else:
            console.setLevel(logging.INFO)
        console.setFormatter(logging.Formatter('%(message)s'))
        self.soslog.addHandler(console)

    def loadplugins(self):
        # generate list of available plugins
        for pluginpath in self.pluginpaths.keys():
            if not os.path.isdir(pluginpath):
                continue
            _plugins = os.listdir(pluginpath)
            for _plug in _plugins:
                if not _plug[-3:] == '.py' or _plug[:-3] == "__init__":
                    continue
                self.pluginnamesspaces[_plug] = self.pluginpaths[pluginpath]
                self.plugins.append(_plug)
        self.plugins.sort()

        # validate and load plugins
        for plug in self.plugins:
            plugbase = plug[:-3]
            try:
                if self.validatePlugin(plug):
                    pluginClass = sos.helpers.importPlugin(
                        ".".join([self.pluginnamesspaces[plug], plugbase]),
                        plugbase)
                else:
                    self.soslog.warning(
                        _("plugin %s does not validate, skipping") % plug)
                    self.skippedplugins.append(
                        (plugbase, pluginClass(plugbase, self)))
                    continue
                self.plugin_names.append(plugbase)
                # plug-in is valid, let's decide whether run it or not
                if plugbase in self.noplugins:
                    self.soslog.log(
                        logging.VERBOSE,
                        _("plugin %s skipped (--skip-plugins)") % plugbase)
                    self.skippedplugins.append(
                        (plugbase, pluginClass(plugbase, self)))
                    continue
                if not pluginClass:
                    self.soslog.log(logging.VERBOSE,
                                    _("skipping unknown %s.") % plug)
                    continue
                if not pluginClass(plugbase, self.getCommons()).checkenabled(
                ) and not plugbase in self.enabledplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(
                        logging.VERBOSE,
                        _("plugin %s is inactive (use -e or -o to enable).") %
                        plug)
                    self.skippedplugins.append(
                        (plugbase, pluginClass(plugbase, self)))
                    continue
                if not pluginClass(plugbase, self.getCommons()).defaultenabled(
                ) and not plugbase in self.enabledplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(
                        logging.VERBOSE,
                        "plugin %s not loaded by default (use -e or -o to enable)."
                        % plug)
                    self.skippedplugins.append(
                        (plugbase, pluginClass(plugbase, self)))
                    continue
                if self.onlyplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(
                        logging.VERBOSE,
                        _("plugin %s not specified in --only-plugins list") %
                        plug)
                    self.skippedplugins.append(
                        (plugbase, pluginClass(plugbase, self)))
                    continue
                self.loadedplugins.append(
                    (plugbase, pluginClass(plugbase, self.getCommons())))
            except:
                self.soslog.warning(
                    _("plugin %s does not install, skipping") % plug)
                import traceback
                from StringIO import StringIO
                buf = StringIO()
                traceback.print_exc(None, buf)
                self.soslog.log(logging.DEBUG, buf.getvalue())
                if self.__raisePlugins__:
                    raise

    def validatePlugin(self, plugin):
        for pluginpath in self.pluginpaths.keys():
            if self.policy.validatePlugin(pluginpath + plugin):
                return True
        return False

    def getCommons(self):
        _commons = dict()

        # Needed for the plugins if not it should get away.
        for (_attr, _key) in self.__commons_attrs.items():
            _commons[_key] = getattr(self, _attr, None)
        _commons["cmdlineopts"] = self._createCmdLineOpts()
        return _commons

    def setupOptions(self):
        # First, gather and process options
        # using the options specified in the command line (if any)
        if self.usealloptions:
            for plugname, plug in self.loadedplugins:
                for name, parms in zip(plug.optNames, plug.optParms):
                    if type(parms["enabled"]) == bool:
                        parms["enabled"] = True
        # read plugin tunables from configuration file
        if self.config.has_section("tunables"):
            if not self.plugopts:
                self.plugopts = []

            for opt, val in self.config.items("tunables"):
                self.plugopts.append(opt + "=" + val)

        if self.plugopts:
            opts = {}
            for opt in self.plugopts:
                # split up "general.syslogsize=5"
                try:
                    opt, val = opt.split("=")
                except:
                    val = True
                else:
                    if val.lower() in ["off", "disable", "disabled", "false"]:
                        val = False
                    else:
                        # try to convert string "val" to int()
                        try:
                            val = int(val)
                        except:
                            pass

                # split up "general.syslogsize"
                try:
                    plug, opt = opt.split(".")
                except:
                    plug = opt
                    opt = True

                try:
                    opts[plug]
                except KeyError:
                    opts[plug] = []
                opts[plug].append((opt, val))

            for plugname, plug in self.loadedplugins:
                if opts.has_key(plugname):
                    for opt, val in opts[plugname]:
                        self.soslog.log(
                            logging.VERBOSE,
                            'setting option "%s" for plugin (%s) to "%s"' %
                            (plugname, opt, val))
                        if not plug.setOption(opt, val):
                            self.soslog.error(
                                'no such option "%s" for plugin (%s)' %
                                (opt, plugname))
                            raise SosReportException(
                                'no such option "%s" for plugin (%s)' %
                                (opt, plugname))
                    del opts[plugname]
            for plugname in opts.keys():
                self.soslog.error(
                    'unable to set option for disabled or non-existing plugin (%s)'
                    % (plugname))
                raise SosReportException(
                    'unable to set option for disabled or non-existing plugin (%s)'
                    % (plugname))
            del opt, opts, val

        # error if the user references a plugin which does not exist
        unk_plugs = [
            plugname.split(".")[0] for plugname in self.onlyplugins
            if not plugname.split(".")[0] in self.plugin_names
        ]
        unk_plugs += [
            plugname.split(".")[0] for plugname in self.noplugins
            if not plugname.split(".")[0] in self.plugin_names
        ]
        unk_plugs += [
            plugname.split(".")[0] for plugname in self.enabledplugins
            if not plugname.split(".")[0] in self.plugin_names
        ]
        if len(unk_plugs):
            for plugname in unk_plugs:
                self.soslog.error(
                    'a non-existing plugin (%s) was specified in the command line'
                    % (plugname))
            raise SosReportException(
                "Non existend plugins were specified at runtime: %s" %
                (unk_plugs))
        del unk_plugs

        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE3,
                            _("processing options from plugin: %s") % plugname)
            names, parms = plug.getAllOptions()
            for optname, optparm in zip(names, parms):
                self.alloptions.append((plug, plugname, optname, optparm))

        # when --listplugins is specified we do a dry-run
        # which tells the user which plugins are going to be enabled
        # and with what options.

    def listPlugins(self):
        if not len(self.loadedplugins) and not len(self.skippedplugins):
            self.soslog.error(_("no valid plugins found"))
            raise SosReportException(_("no valid plugins found"))

        # FIXME: make -l output more concise
        if len(self.loadedplugins):
            print _("The following plugins are currently enabled:")
            print
            for (plugname, plug) in self.loadedplugins:
                print " %-25s  %s" % (textcolor(
                    plugname, "lblue"), plug.get_description())
        else:
            print _("No plugin enabled.")
        print

        if len(self.skippedplugins):
            print _("The following plugins are currently disabled:")
            print
            for (plugname, plugclass) in self.skippedplugins:
                print " %-25s  %s" % (textcolor(
                    plugname, "cyan"), plugclass.get_description())
        print

        if len(self.alloptions):
            print _("The following plugin options are available:")
            print
            for (plug, plugname, optname, optparm) in self.alloptions:
                # format and colorize option value based on its type (int or bool)
                if type(optparm["enabled"]) == bool:
                    if optparm["enabled"] == True:
                        tmpopt = textcolor("on", "lred")
                    else:
                        tmpopt = textcolor("off", "red")
                elif type(optparm["enabled"]) == int:
                    if optparm["enabled"] > 0:
                        tmpopt = textcolor(optparm["enabled"], "lred")
                    else:
                        tmpopt = textcolor(optparm["enabled"], "red")
                else:
                    tmpopt = optparm["enabled"]

                print " %-21s %-5s %s" % (plugname + "." + optname, tmpopt,
                                          optparm["desc"])
                del tmpopt
        else:
            print _("No plugin options available.")

        print

    def diagnosePlugins(self):
        # TODO: Move this in methods
        # Call the diagnose() method for each plugin
        tmpcount = 0
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE2,
                            "Performing sanity check for plugin %s" % plugname)
            try:
                plug.diagnose()
            except:
                if self.__raisePlugins__:
                    raise
            tmpcount += len(plug.diagnose_msgs)
        if tmpcount > 0:
            print _(
                "One or more plugins have detected a problem in your configuration."
            )
            print _("Please review the following messages:")
            print

            fp = open(rptdir + "/diagnose.txt", "w")
            for plugname, plug in self.loadedplugins:
                for tmpcount2 in range(0, len(plug.diagnose_msgs)):
                    if tmpcount2 == 0:
                        self.soslog.warning(textcolor("%s:" % plugname, "red"))
                    self.soslog.warning("    * %s" %
                                        plug.diagnose_msgs[tmpcount2])
                    fp.write("%s: %s\n" %
                             (plugname, plug.diagnose_msgs[tmpcount2]))
            fp.close()

            print
            if not __cmdLineOpts__.batch:
                try:
                    while True:
                        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"):
                            return
                    del yorno
                except KeyboardInterrupt:
                    print
                    return

    def helloMsg(self):
        msg = _(
            """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.
Red Hat will use this information for diagnostic purposes ONLY
and it will be considered confidential information.

This process may take a while to complete.
No changes will be made to your system.

""")
        if self.batch:
            print msg
        else:
            msg += _("""Press ENTER to continue, or CTRL-C to quit.\n""")
            try:
                raw_input(msg)
            except:
                print
                return
        del msg

    def setupPlugins(self):
        # Call the setup() method for each plugin
        for plugname, plug in self.loadedplugins:
            self.soslog.log(
                logging.VERBOSE2,
                "Preloading files and commands to be gathered by plugin %s" %
                plugname)
            try:
                plug.setup()
            except KeyboardInterrupt:
                raise
            except:
                if self.__raisePlugins__:
                    raise

    def startPluginsCopyFiles(self, pbar):
        # Call the collect method for each plugin
        plugrunning = Semaphore(2)
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE, "executing plugin %s" % plugname)
            try:
                if not self.nomultithread:
                    plug.copyStuff(threaded=True, semaphore=plugrunning)
                else:
                    plug.copyStuff()
                    if self.progressbar:
                        pbar.incAmount(plug.eta_weight)
                        pbar.update()
            except KeyboardInterrupt:
                raise
            except:
                if self.__raisePlugins__:
                    raise
        del plugrunning

    def setupProgressBar(self):
        # Setup the progress bar
        # gather information useful for generating ETA
        eta_weight = len(self.loadedplugins)
        for plugname, plug in self.loadedplugins:
            eta_weight += plug.eta_weight
        pbar = progressBar(minValue=0, maxValue=eta_weight)
        # pbar.max = number_of_plugins + weight (default 1 per plugin)
        return pbar

    def waitForPlugins(self, pbar):
        finishedplugins = []
        while len(self.loadedplugins) > 0:
            plugname, plug = self.loadedplugins.pop(0)
            if not plug.wait(0.5):
                finishedplugins.append((plugname, plug))
                self.soslog.log(logging.DEBUG,
                                "plugin %s has returned" % plugname)
                if self.progressbar:
                    pbar.incAmount(plug.eta_weight)
            else:
                self.soslog.log(logging.DEBUG,
                                "plugin %s still hasn't returned" % plugname)
                self.loadedplugins.append((plugname, plug))
                if self.progressbar:
                    pbar.update()
        self.loadedplugins = finishedplugins
        del finishedplugins

    def saveCopiedFiles(self):
        for plugname, plug in self.loadedplugins:
            for oneFile in plug.copiedFiles:
                try:
                    self.xmlreport.add_file(oneFile["srcpath"],
                                            os.stat(oneFile["srcpath"]))
                except:
                    pass

    def analysePlugins(self, pbar):
        # Call the analyze method for each plugin
        for plugname, plug in self.loadedplugins:
            self.soslog.log(
                logging.VERBOSE2,
                "Analyzing results of plugin %s" % plugname,
            )
            try:
                plug.analyze()
            except:
                # catch exceptions in analyse() and keep working
                pass
            if self.progressbar:
                pbar.incAmount()
                pbar.update()

    def generateReportHeader(self, rfd):
        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>
    """)

    def generateReportPluginNames(self, rfd, plugNames):
        # 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')

    def generateReportAlerts(self, rfd, allAlerts):
        rfd.write('<hr/><h3>Alerts:</h3>')
        rfd.write('<ul>')
        for alert in allAlerts:
            rfd.write('<li>%s</li>' % alert)
        rfd.write('</ul>')

    def generateReportPluginReports(self, rfd):
        # Call the report method for each plugin
        for plugname, plug in self.loadedplugins:
            try:
                html = plug.report()
            except:
                if self.__raisePlugins__:
                    raise
                else:
                    rfd.write(html)

    def generateReportFooter(self, rfd):
        rfd.write("</body></html>")

    def generateReport(self):
        # Make a pass to gather Alerts and a list of module names
        allAlerts = []
        plugNames = []
        for plugname, plug in self.loadedplugins:
            for alert in plug.alerts:
                allAlerts.append('<a href="#%s">%s</a>: %s' %
                                 (plugname, plugname, alert))
            plugNames.append(plugname)

        # Generate the header for the html output file
        rfd = open(self.rptdir + "/" + "sosreport.html", "w")
        self.generateReportHeader(rfd)
        self.generateReportPluginNames(rfd, plugNames)
        self.generateReportAlerts(rfd, allAlerts)
        self.generateReportPluginReports(rfd)
        self.generateReportFooter(rfd)
        rfd.close()

    def postprocPlugins(self):
        # Call the postproc method for each plugin
        for plugname, plug in self.loadedplugins:
            try:
                plug.postproc()
            except:
                if self.__raisePlugins__:
                    raise

    def main(self):
        print
        self.soslog.info(_("sosreport (version %s)") % __version__)
        print

        if self.autoloadplugins:
            self.loadplugins()
        self.setupOptions()

        if self.listplugins:
            self.listPlugins()
            return

        # to go anywhere further than listing the plugins we will need root permissions.
        #
        if self.checkrootuid and os.getuid() != 0:
            print _('sosreport requires root permissions to run.')
            raise SosReportException(
                _('sosreport requires root permissions to run.'))

        # we don't need to keep in memory plugins we are not going to use
        # del self.skippedplugins

        if not len(self.loadedplugins):
            self.soslog.error(_("no valid plugins were enabled"))
            raise SosReportException(_("no valid plugins were enabled"))

        self.helloMsg()

        self.diagnosePlugins()

        self.policy.preWork()

        self.setupPlugins()

        if self.progressbar:
            pbar = self.setupProgressBar()
        else:
            pbar = None

        if self.nomultithread:
            self.soslog.log(logging.VERBOSE, "using single-threading")
        else:
            self.soslog.log(logging.VERBOSE, "using multi-threading")

        self.startPluginsCopyFiles(pbar)

        # Wait for all the collection threads to exit
        if not self.nomultithread:
            self.waitForPlugins(pbar)

        self.saveCopiedFiles()
        self.xmlreport.serialize_to_file(self.rptdir + "/sosreport.xml")

        self.analysePlugins(pbar)

        if self.progressbar:
            pbar.finished()
            sys.stdout.write("\n")

        self.generateReport()

        self.postprocPlugins()

        # package up the results for the support organization
        self.policy.packageResults()

        # delete gathered files
        self.policy.cleanDstroot()

        # automated submission will go here
        if not self.upload:
            self.policy.displayResults()
        else:
            self.policy.uploadResults()
class SosReport(object):
    # pylint: disable-msg = R0912
    # pylint: disable-msg = R0914
    # pylint: disable-msg = R0915
    # for debugging
    """
    This is the top-level class that gathers and processes all sosreport information
    """
    __raisePlugins__ = 0

    # dictionary of attribute of class and keyname of returned dict
    __commons_attrs = {'dstroot': 'dstroot', 'cmddir': 'cmddir', 'logdir': 'logdir', 'rptdir': 'rptdir',
                       'soslog': 'soslog', 'policy': 'policy', 'verbosity' : 'verbosity',
                       'xmlreport' : 'xmlreport', 'options': 'cmdlineopts', 'config':'config' }
    # Only needed if started not with commandlineoptions but with keywords of other source
    # Then we need to build commandlineoptions from the given keywords. Here are all attributes and cmdlineoptionsnames specified.
    __kwd_attrs= {"listPlugins": "listPlugins", "noplugins": "noplugins", "enabledplugins": "enableplugins", "onlyplugins": "onlyplugins", 
                  "plugopts": "plugopts", "usealloptions": "usealloptions", "upload": "upload", "batch": "batch", "nocolors": "nocolors", 
                  "verbosity": "verbose", "debug": "debug", 
                  "progressbar": "progressbar", "nomultithread": "nomultithread"}
    
    # to being able to overwrite filenames
    LOGFILENAME="sos.log"
    CONFFILENAME="/etc/sos.conf"
    PLUGINPATH_SUFFIX="/sos/plugins"
    LOCALE_DIR="/usr/share/locale"

    def __init__(self, *params, **kwds):
        self.listplugins=False
        self.noplugins=[]
        self.enabledplugins=[]
        self.onlyplugins=[]
        self.plugopts=None
        self.usealloptions=False
        self.upload=False
        self.batch=False
        self.nocolors=False
        self.verbosity=0
        self.debug=0
        self.progressbar=True
        self.nomultithread=False
        self.checkrootuid=True
        self.autoloadplugins=True
        self.pluginnamesspaces=dict()
        
        self.loadedplugins = []
        self.skippedplugins = []
        self.alloptions = []

        self.plugins=list()
        self.plugin_names=list()
        
        import ConfigParser
        self.config = ConfigParser.ConfigParser()
        try: self.config.readfp(open(self.CONFFILENAME))
        except IOError: pass

        # generically set attributes from outside
        # The first param if there is supposed to be Values from optparse
        if len(params) > 0:
            self._fromOptparseValues(params[0])
        elif kwds:
            self._fromKwds(kwds)
        
        # perhaps we should automatically locate the policy module??
        import sos.policyredhat
        self.policy = sos.policyredhat.SosPolicy()

        # find the plugins path
        self.pluginpaths=dict()
        paths = sys.path
        for path in paths:
            if path.strip()[-len("site-packages"):] == "site-packages":
                self.appendPluginsPath(path + self.PLUGINPATH_SUFFIX)

        self.setupPathEnvironment()
        self.xmlreport = XmlReport()
        
        self.setupi18n()
        self.setupLogging()
        
        # Make policy aware of the commons
        self.policy.setCommons(self.getCommons())

    def _createCmdLineOpts(self):
        from optparse import Values
        _values=Values()
        for (_key1, _key2) in self.__kwd_attrs.items():
            setattr(_values, _key2, getattr(self, _key1, getattr(self, _key2, None)))
        return _values
    
    def _fromOptparseValues(self, values):
        self._fromKwds(values.__dict__)

    def _fromKwds(self, kwds):
        for (_key1, _key2) in self.__kwd_attrs.items():
            if kwds.has_key(_key1):
                _key=_key1
            elif kwds.has_key(_key2):
                _key=_key2
            else:
                continue
            setattr(self, _key1, kwds[_key])
        for (_key, _value) in kwds.items():
            if not _key in self.__kwd_attrs:
                setattr(self, _key, _value)

    def setupPathEnvironment(self):
        # Set up common info and create destinations
        self.dstroot = sos.helpers.sosFindTmpDir()
        if not self.dstroot:
            raise IOError("Could not create temporary directory %s." %self.dstroot)
        self.cmddir = os.path.join(self.dstroot, "sos_commands")
        self.logdir = os.path.join(self.dstroot, "sos_logs")
        self.rptdir = os.path.join(self.dstroot, "sos_reports")
        os.mkdir(self.cmddir, 0755)
        os.mkdir(self.logdir, 0755)
        os.mkdir(self.rptdir, 0755)
                
    def appendPluginsPath(self, _path, _namespace="sos.plugins"):
        self.pluginpaths[_path]=_namespace

    def setupi18n(self):
        import gettext
        # initialize i18n language localization
        gettext.install('sos', self.LOCALE_DIR, unicode=False)

    def setupLogging(self):
        # initialize logging
        self.soslog = logging.getLogger('sos')
        self.soslog.setLevel(logging.DEBUG)

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

        # if stdin is not a tty, disable colors and don't ask questions
        if not sys.stdin.isatty():
            self.nocolors = True
            self.batch = True

        # log to a file
        flog = logging.FileHandler(self.logdir + self.LOGFILENAME)
        flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
        flog.setLevel(logging.VERBOSE3)
        self.soslog.addHandler(flog)

        # define a Handler which writes INFO messages or higher to the sys.stderr
        console = logging.StreamHandler(sys.stderr)
        if self.verbosity > 0 or self.debug > 0:
            if self.verbosity > 0:
                console.setLevel(logging.INFO - self.verbosity)
            if self.debug > 0:
                console.setLevel(logging.DEBUG - self.debug + 1)
            self.progressbar = False
        else:
            console.setLevel(logging.INFO)
        console.setFormatter(logging.Formatter('%(message)s'))
        self.soslog.addHandler(console)

    def loadplugins(self):
        # generate list of available plugins
        for pluginpath in self.pluginpaths.keys():
            if not os.path.isdir(pluginpath):
                continue
            _plugins = os.listdir(pluginpath)
            for _plug in _plugins:
                if not _plug[-3:] == '.py' or _plug[:-3] == "__init__":
                    continue
                self.pluginnamesspaces[_plug]=self.pluginpaths[pluginpath]
                self.plugins.append(_plug)
        self.plugins.sort()

        # validate and load plugins
        for plug in self.plugins:
            plugbase =  plug[:-3]
            try:
                if self.validatePlugin(plug):
                    pluginClass = sos.helpers.importPlugin(".".join([self.pluginnamesspaces[plug], plugbase]), plugbase)
                else:
                    self.soslog.warning(_("plugin %s does not validate, skipping") % plug)
                    self.skippedplugins.append((plugbase, pluginClass(plugbase, self)))
                    continue
                self.plugin_names.append(plugbase)
                # plug-in is valid, let's decide whether run it or not
                if plugbase in self.noplugins:
                    self.soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase)
                    self.skippedplugins.append((plugbase, pluginClass(plugbase, self)))
                    continue
                if not pluginClass:
                    self.soslog.log(logging.VERBOSE, _("skipping unknown %s.") % plug)
                    continue
                if not pluginClass(plugbase, self.getCommons()).checkenabled() and not plugbase in self.enabledplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug)
                    self.skippedplugins.append((plugbase, pluginClass(plugbase, self)))
                    continue
                if not pluginClass(plugbase, self.getCommons()).defaultenabled() and not plugbase in self.enabledplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug)
                    self.skippedplugins.append((plugbase, pluginClass(plugbase, self)))
                    continue
                if self.onlyplugins and not plugbase in self.onlyplugins:
                    self.soslog.log(logging.VERBOSE, _("plugin %s not specified in --only-plugins list") % plug)
                    self.skippedplugins.append((plugbase, pluginClass(plugbase, self)))
                    continue
                self.loadedplugins.append((plugbase, pluginClass(plugbase, self.getCommons())))
            except:
                self.soslog.warning(_("plugin %s does not install, skipping") % plug)
                import traceback
                from StringIO import StringIO
                buf=StringIO()
                traceback.print_exc(None, buf)
                self.soslog.log(logging.DEBUG, buf.getvalue())
                if self.__raisePlugins__:
                    raise
                
    def validatePlugin(self, plugin):
        for pluginpath in self.pluginpaths.keys():
            if self.policy.validatePlugin(pluginpath + plugin):
                return True
        return False
    
    def getCommons(self):
        _commons=dict()

        # Needed for the plugins if not it should get away.
        for (_attr, _key) in self.__commons_attrs.items():
            _commons[_key]=getattr(self, _attr, None)
        _commons["cmdlineopts"]=self._createCmdLineOpts()
        return _commons

    def setupOptions(self):
        # First, gather and process options
        # using the options specified in the command line (if any)
        if self.usealloptions:
            for plugname, plug in self.loadedplugins:
                for name, parms in zip(plug.optNames, plug.optParms):
                    if type(parms["enabled"])==bool:
                        parms["enabled"] = True
        # read plugin tunables from configuration file
        if self.config.has_section("tunables"):
            if not self.plugopts:
                self.plugopts = []

            for opt, val in self.config.items("tunables"):
                self.plugopts.append(opt + "=" + val)

        if self.plugopts:
            opts = {}
            for opt in self.plugopts:
                # split up "general.syslogsize=5"
                try:
                    opt, val = opt.split("=")
                except:
                    val=True
                else:
                    if val.lower() in ["off", "disable", "disabled", "false"]:
                        val = False
                    else:
                        # try to convert string "val" to int()
                        try:    val = int(val)
                        except: pass

                # split up "general.syslogsize"
                try:
                    plug, opt = opt.split(".")
                except:
                    plug = opt
                    opt = True

                try: opts[plug]
                except KeyError: opts[plug] = []
                opts[plug].append( (opt,val) )

            for plugname, plug in self.loadedplugins:
                if opts.has_key(plugname):
                    for opt,val in opts[plugname]:
                        self.soslog.log(logging.VERBOSE, 'setting option "%s" for plugin (%s) to "%s"' % (plugname,opt,val))
                        if not plug.setOption(opt,val):
                            self.soslog.error('no such option "%s" for plugin (%s)' % (opt,plugname))
                            raise SosReportException('no such option "%s" for plugin (%s)' % (opt,plugname))
                    del opts[plugname]
            for plugname in opts.keys():
                self.soslog.error('unable to set option for disabled or non-existing plugin (%s)' % (plugname))
                raise SosReportException('unable to set option for disabled or non-existing plugin (%s)' % (plugname))
            del opt,opts,val

        # error if the user references a plugin which does not exist
        unk_plugs =  [plugname.split(".")[0] for plugname in self.onlyplugins    if not plugname.split(".")[0] in self.plugin_names]
        unk_plugs += [plugname.split(".")[0] for plugname in self.noplugins      if not plugname.split(".")[0] in self.plugin_names]
        unk_plugs += [plugname.split(".")[0] for plugname in self.enabledplugins if not plugname.split(".")[0] in self.plugin_names]
        if len(unk_plugs):
            for plugname in unk_plugs:
                self.soslog.error('a non-existing plugin (%s) was specified in the command line' % (plugname))
            raise SosReportException("Non existend plugins were specified at runtime: %s" %(unk_plugs))
        del unk_plugs

        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname)
            names, parms = plug.getAllOptions()
            for optname, optparm  in zip(names, parms):
                self.alloptions.append((plug, plugname, optname, optparm))

        # when --listplugins is specified we do a dry-run
        # which tells the user which plugins are going to be enabled
        # and with what options.

    def listPlugins(self):
        if not len(self.loadedplugins) and not len(self.skippedplugins):
            self.soslog.error(_("no valid plugins found"))
            raise SosReportException(_("no valid plugins found"))

        # FIXME: make -l output more concise
        if len(self.loadedplugins):
            print _("The following plugins are currently enabled:")
            print
            for (plugname,plug) in self.loadedplugins:
                print " %-25s  %s" % (textcolor(plugname,"lblue"),plug.get_description())
        else:
            print _("No plugin enabled.")
        print

        if len(self.skippedplugins):
            print _("The following plugins are currently disabled:")
            print
            for (plugname,plugclass) in self.skippedplugins:
                print " %-25s  %s" % (textcolor(plugname,"cyan"),plugclass.get_description())
        print

        if len(self.alloptions):
            print _("The following plugin options are available:")
            print
            for (plug, plugname, optname, optparm)  in self.alloptions:
                # format and colorize option value based on its type (int or bool)
                if type(optparm["enabled"])==bool:
                    if optparm["enabled"]==True:
                        tmpopt = textcolor("on","lred")
                    else:
                        tmpopt = textcolor("off","red")
                elif type(optparm["enabled"])==int:
                    if optparm["enabled"] > 0:
                        tmpopt = textcolor(optparm["enabled"],"lred")
                    else:
                        tmpopt = textcolor(optparm["enabled"],"red")
                else:
                    tmpopt = optparm["enabled"]

                print " %-21s %-5s %s" % (plugname + "." + optname, tmpopt, optparm["desc"])
                del tmpopt
        else:
            print _("No plugin options available.")

        print

    def diagnosePlugins(self):
        # TODO: Move this in methods
        # Call the diagnose() method for each plugin
        tmpcount = 0
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE2, "Performing sanity check for plugin %s" % plugname)
            try:
                plug.diagnose()
            except:
                if self.__raisePlugins__:
                    raise
            tmpcount += len(plug.diagnose_msgs)
        if tmpcount > 0:
            print _("One or more plugins have detected a problem in your configuration.")
            print _("Please review the following messages:")
            print

            fp = open(rptdir + "/diagnose.txt", "w")
            for plugname, plug in self.loadedplugins:
                for tmpcount2 in range(0,len(plug.diagnose_msgs)):
                    if tmpcount2 == 0:
                        self.soslog.warning( textcolor("%s:" % plugname, "red") )
                    self.soslog.warning("    * %s" % plug.diagnose_msgs[tmpcount2])
                    fp.write("%s: %s\n" % (plugname, plug.diagnose_msgs[tmpcount2]) )
            fp.close()

            print
            if not __cmdLineOpts__.batch:
                try:
                    while True:
                        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"):
                            return
                    del yorno
                except KeyboardInterrupt:
                    print
                    return


    def helloMsg(self):
        msg = _("""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.
Red Hat will use this information for diagnostic purposes ONLY
and it will be considered confidential information.

This process may take a while to complete.
No changes will be made to your system.

""")
        if self.batch:
            print msg
        else:
            msg += _("""Press ENTER to continue, or CTRL-C to quit.\n""")
            try:    raw_input(msg)
            except: print ; return
        del msg
        
    def setupPlugins(self):
        # Call the setup() method for each plugin
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname)
            try:
                plug.setup()
            except KeyboardInterrupt:
                raise
            except:
                if self.__raisePlugins__:
                    raise

    def startPluginsCopyFiles(self, pbar):
        # Call the collect method for each plugin
        plugrunning = Semaphore(2)
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE, "executing plugin %s" % plugname)
            try:
                if not self.nomultithread:
                    plug.copyStuff(threaded = True, semaphore = plugrunning)
                else:
                    plug.copyStuff()
                    if self.progressbar:
                        pbar.incAmount(plug.eta_weight)
                        pbar.update()
            except KeyboardInterrupt:
                raise
            except:
                if self.__raisePlugins__:
                    raise
        del plugrunning

    def setupProgressBar(self):
        # Setup the progress bar
        # gather information useful for generating ETA
        eta_weight = len(self.loadedplugins)
        for plugname, plug in self.loadedplugins:
            eta_weight += plug.eta_weight
        pbar = progressBar(minValue = 0, maxValue = eta_weight)
        # pbar.max = number_of_plugins + weight (default 1 per plugin)
        return pbar

    def waitForPlugins(self, pbar):
        finishedplugins = []
        while len(self.loadedplugins) > 0:
            plugname, plug = self.loadedplugins.pop(0)
            if not plug.wait(0.5):
                finishedplugins.append((plugname,plug))
                self.soslog.log(logging.DEBUG, "plugin %s has returned" % plugname)
                if self.progressbar:
                    pbar.incAmount(plug.eta_weight)
            else:
                self.soslog.log(logging.DEBUG, "plugin %s still hasn't returned" % plugname)
                self.loadedplugins.append((plugname,plug))
                if self.progressbar:
                    pbar.update()
        self.loadedplugins = finishedplugins
        del finishedplugins

    def saveCopiedFiles(self):
        for plugname, plug in self.loadedplugins:
            for oneFile in plug.copiedFiles:
                try:
                    self.xmlreport.add_file(oneFile["srcpath"], os.stat(oneFile["srcpath"]))
                except:
                    pass

    def analysePlugins(self, pbar):
        # Call the analyze method for each plugin
        for plugname, plug in self.loadedplugins:
            self.soslog.log(logging.VERBOSE2, "Analyzing results of plugin %s" % plugname,)
            try:
                plug.analyze()
            except:
                # catch exceptions in analyse() and keep working
                pass
            if self.progressbar:
                pbar.incAmount()
                pbar.update()

    def generateReportHeader(self, rfd):
        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>
    """)

    def generateReportPluginNames(self, rfd, plugNames):
        # 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')

    def generateReportAlerts(self, rfd, allAlerts):
        rfd.write('<hr/><h3>Alerts:</h3>')
        rfd.write('<ul>')
        for alert in allAlerts:
            rfd.write('<li>%s</li>' % alert)
        rfd.write('</ul>')
        
    def generateReportPluginReports(self, rfd):
        # Call the report method for each plugin
        for plugname, plug in self.loadedplugins:
            try:
                html = plug.report()
            except:
                if self.__raisePlugins__:
                    raise
                else:
                    rfd.write(html)

    def generateReportFooter(self, rfd):
        rfd.write("</body></html>")

    def generateReport(self):
        # Make a pass to gather Alerts and a list of module names
        allAlerts = []
        plugNames = []
        for plugname, plug in self.loadedplugins:
            for alert in plug.alerts:
                allAlerts.append('<a href="#%s">%s</a>: %s' % (plugname, plugname, alert))
            plugNames.append(plugname)

        # Generate the header for the html output file
        rfd = open(self.rptdir + "/" + "sosreport.html", "w")
        self.generateReportHeader(rfd)        
        self.generateReportPluginNames(rfd, plugNames)
        self.generateReportAlerts(rfd, allAlerts)
        self.generateReportPluginReports(rfd)
        self.generateReportFooter(rfd)
        rfd.close()

    def postprocPlugins(self):
        # Call the postproc method for each plugin
        for plugname, plug in self.loadedplugins:
            try:
                plug.postproc()
            except:
                if self.__raisePlugins__:
                    raise


    def main(self):
        print
        self.soslog.info ( _("sosreport (version %s)") % __version__)
        print

        if self.autoloadplugins:
            self.loadplugins()
        self.setupOptions()
        
        if self.listplugins:
            self.listPlugins()
            return

        # to go anywhere further than listing the plugins we will need root permissions.
        #
        if self.checkrootuid and os.getuid() != 0:
            print _('sosreport requires root permissions to run.')
            raise SosReportException(_('sosreport requires root permissions to run.'))

        # we don't need to keep in memory plugins we are not going to use
        # del self.skippedplugins

        if not len(self.loadedplugins):
            self.soslog.error(_("no valid plugins were enabled"))
            raise SosReportException(_("no valid plugins were enabled"))

        self.helloMsg()

        self.diagnosePlugins()

        self.policy.preWork()

        self.setupPlugins()

        if self.progressbar:
            pbar=self.setupProgressBar()
        else:
            pbar=None
            
        if self.nomultithread:
            self.soslog.log(logging.VERBOSE, "using single-threading")
        else:
            self.soslog.log(logging.VERBOSE, "using multi-threading")

        self.startPluginsCopyFiles(pbar)
        
        # Wait for all the collection threads to exit
        if not self.nomultithread:
            self.waitForPlugins(pbar)

        self.saveCopiedFiles()
        self.xmlreport.serialize_to_file(self.rptdir + "/sosreport.xml")

        self.analysePlugins(pbar)
        
        if self.progressbar:
            pbar.finished()
            sys.stdout.write("\n")

        self.generateReport()
        
        self.postprocPlugins()

        # package up the results for the support organization
        self.policy.packageResults()

        # delete gathered files
        self.policy.cleanDstroot()

        # automated submission will go here
        if not self.upload:
            self.policy.displayResults()
        else:
            self.policy.uploadResults()