#!/usr/bin/python
"""
This is the main script that will extract reports and then run various
plugins on them.

All reports have to be a a compressed tarfile of type bzip2/gunzip.

@author    :  Shane Bradley
@contact   :  sbradley@redhat.com
@version   :  2.03
@copyright :  GPLv2
"""
import sys
import string
import os
import os.path
import shutil
from optparse import OptionParser, Option
import time
import logging

import sx
from sx.reportextractor import ReportExtractor
from sx.reports import Report
from sx.rploader import RPLoader
from sx.rploader import PluginsLoader
from sx.tarball import TarBall
from sx.logwriter import LogWriter

"""
@cvar VERSION_NUMBER: The current version number of sxconsole.
@type VERSION_NUMBER: String
@cvar UID_TIMESTAMP: This is the time format for the unique directory id.
@type UID_TIMESTAMP: String
@cvar REPORT_CORE_IMPORT: The name of the core reports python library.
@type REPORT_CORE_IMPORT: String
@cvar PLUGIN_CORE_IMPORT: The name of the core plugin python library.
@type PLUGIN_CORE_IMPORT: String
@cvar CONFIG_DIR: The root directory of the user configuration files.
@type CONFIG_DIR: String
@cvar REPORT_USER_IMPORT: The name of the user reports python library.
@type REPORT_USER_IMPORT: String
@cvar PLUGIN_USER_IMPORT: The name of the user plugin python library.
@type PLUGIN_USER_IMPORT: String
"""
VERSION_NUMBER = "2.03-8"
UID_TIMESTAMP = "%Y-%m-%d_%H%M%S"
REPORT_CORE_IMPORT="sx.reports"
PLUGIN_CORE_IMPORT="sx.plugins"

CONFIG_DIR=os.path.join(os.environ['HOME'],".sx")
REPORT_USER_IMPORT="sxreports"
PLUGIN_USER_IMPORT="sxplugins"

def sxconsoleRun(pathToBaseDir) :
    """
    This function is what will take the user options and do the action
    that user has specified.

    @param pathToBaseDir: This is string that can set base to plugin
    base directory. For example: "/usr/lib/python/site-packages"
    @type pathToBaseDir: String
    """
    (cmdLineOpts, cmdLineArgs) = getOptions(VERSION_NUMBER)
    if (cmdLineOpts.enableDebugLogging) :
        logging.getLogger(sx.MAIN_LOGGER_NAME).setLevel(logging.DEBUG)
    if ((not os.path.isdir(pathToBaseDir)) and (len(pathToBaseDir) > 0)):
        message =  "The path to base directory does not exist: %s" % (pathToBaseDir)
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        message = "This application will exit"
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
        sys.exit()

    # #######################################################################
    # List the plugins if option enabled
    # #######################################################################
    # Print list of plugins if selected and exit
    if (cmdLineOpts.listModules):
        versionMessage = "%s %s\n" %(os.path.basename(sys.argv[0]), VERSION_NUMBER)
        versionMessage += "This program was written by Shane Bradley(sbradley@redhat.com): http://tools.gss.redhat.com/trac/sx \n"
        print versionMessage
        listPlugins(pathToBaseDir)
        sys.exit()

    # #######################################################################
    # Create the report classes, report paths. Then extract the
    # reports. The report classes will be passed to reportextractor so
    # that reportextractor can identify the report types.
    # #######################################################################
    # Get all the known classes that reportextractor can use
    reportLoader = RPLoader()
    loadedReportClasses = reportLoader.getClasses(pathToBaseDir, REPORT_CORE_IMPORT)
    if (not cmdLineOpts.disableUserDefinedPlugins) :
        loadedReportClasses += reportLoader.getClasses(CONFIG_DIR, REPORT_USER_IMPORT)

    # #######################################################################
    # Validate that either a uid or extracted path was given.
    # #######################################################################
    if ((not len(cmdLineArgs) > 0) and (not len(cmdLineOpts.pathToExtractedReports) > 0)):
        # No uid or path args are given
        message =  "No args given. A unique idenification number as an argument or a path to extracted reports with -p option is required."
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    elif ((len(cmdLineArgs) > 0) and (len(cmdLineOpts.pathToExtractedReports) > 0)):
        # Both options were given
        message =  "A uid and path (-p option) was given. Please choose only 1 option."
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    elif ((len(cmdLineOpts.pathToExtractedReports) > 0) and (not os.path.exists(cmdLineOpts.pathToExtractedReports))):
        # If path has greater length than zero and path does not exist
        message = "The path passed with -p option is not a valid path of archived reports."
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    elif (len(cmdLineArgs) > 0):
        if (not cmdLineArgs[0].isalnum()):
            # If no path was given and cmdLineArgs is greater than is not alphanumeric uid 
            message = "Only alphanumeric uids are valid."
            logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
            sys.exit(2)

    # #######################################################################
    # Create the reportextractor class to begin extraction or loading
    # #######################################################################
    compressedReportPath = ""
    extractedReportPath = ""
    if (len(cmdLineOpts.pathToExtractedReports) > 0) :
        # Reports have been previously extracted
        extractedReportPath = cmdLineOpts.pathToExtractedReports
        compressedReportPath = cmdLineOpts.pathToExtractedReports.replace("ereports", "creports")
    else:
        (compressedReportPath, extractedReportPath) = generatePaths(
            os.path.join(os.path.join(cmdLineOpts.archivePath, "creports"), cmdLineArgs[0]), 
            os.path.join(os.path.join(cmdLineOpts.archivePath, "ereports"), cmdLineArgs[0]))
    # Create the reporter object
    reportExtractor = ReportExtractor(compressedReportPath, extractedReportPath, loadedReportClasses)
    reportsExtracted = []
 
    # #######################################################################
    # Extract or load the reports
    # #######################################################################
    if (len(cmdLineOpts.pathToExtractedReports) > 0) :
        reportsExtracted = reportExtractor.load()
        message = "There was %d reports loaded." %(len(reportsExtracted))
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
    else:
        # Create array of the files selected as reports
        listOfReports = validateListOfReports(getListOfReports(cmdLineOpts.listOfReports, cmdLineOpts.reportPath))
        reportsExtracted = reportExtractor.extract(listOfReports)
        message = "There was %d reports extracted." %(len(reportsExtracted))
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)

    # #######################################################################
    # Load and set state of each known plugin
    # #######################################################################
    # Load up all plugins and pass the directory to write reports
    pluginLoader = PluginsLoader()
    loadedPlugins = pluginLoader.load(pathToBaseDir, PLUGIN_CORE_IMPORT, 
                                      reportExtractor.getExtractedReportPathDir())
    if (not cmdLineOpts.disableUserDefinedPlugins) :
        loadedPlugins += pluginLoader.load(CONFIG_DIR, PLUGIN_USER_IMPORT, reportExtractor.getExtractedReportPathDir())

    # Enable/Disable all
    if (cmdLineOpts.enableAllPlugins) :
        logging.getLogger(sx.MAIN_LOGGER_NAME).log(LogWriter.STATUS_LEVEL, "Enabling all plugins.")
        pluginLoader.setPluginsState(loadedPlugins, True)
    elif (cmdLineOpts.disableAllPlugins) :
        logging.getLogger(sx.MAIN_LOGGER_NAME).log(LogWriter.STATUS_LEVEL, "Disabling all plugins.")
        pluginLoader.setPluginsState(loadedPlugins, False)

    # Enable singletons
    if (len(cmdLineOpts.enablePlugins) > 0):
        for ePlug in cmdLineOpts.enablePlugins:
            for lPlug in loadedPlugins:
                if (lPlug.isNamed(string.lower(ePlug))):
                    lPlug.setEnabled(True)
                    break;

    # Disable Singletons
    if (len(cmdLineOpts.disablePlugins) > 0):
        for dPlug in cmdLineOpts.disablePlugins:
            for lPlug in loadedPlugins:
                if (lPlug.isNamed(string.lower(dPlug))):
                    lPlug.setEnabled(False)
                    break;
        
    # #######################################################################
    # Get a list of only the enabled plugins and set the options for
    # each.
    # #######################################################################
    enabledPlugins = []
    for plugin in loadedPlugins:
        if (plugin.isEnabled()):
            enabledPlugins.append(plugin)
        
    # Set the plugin options for each plugin that was passed on
    # commandline
    pluginLoader.mapPluginOptions(enabledPlugins, getPluginOptions(cmdLineOpts.pluginOptions))

    if (len(enabledPlugins) > 0) :
        message = "The following plugins will be enabled(Some plugins will required extracted reports to run):"
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
        for plugin in enabledPlugins:
            if plugin.isEnabled() :
                print "\t  %s" %(plugin)
    else:
        logging.getLogger(sx.MAIN_LOGGER_NAME).warning("There are no plugins enabled.")

    # #######################################################################
    # Execute the enabled plugins
    # #######################################################################
    # Setup: gather files needed from each report
    for plugin in enabledPlugins:
        if ((plugin.isReportsRequired()) and (not len(reportsExtracted) > 0)):
            # If no reports then start return to start of loop
            continue
        plugin.setup(reportsExtracted)

    # Execute: run some intense operation that could be used in report/action
    for plugin in enabledPlugins:
        if ((plugin.isReportsRequired()) and (not len(reportsExtracted) > 0)):
            continue
        plugin.execute()

    # Reports: write a report to console or file for each plugin
    for plugin in enabledPlugins:
        if ((plugin.isReportsRequired()) and (not len(reportsExtracted) > 0)):
            continue
        plugin.report()

    # Actions: does something that is outside of sx such as opening a
    # browser, filemanager, etc.
    for plugin in enabledPlugins:
        if ((plugin.isReportsRequired()) and (not len(reportsExtracted) > 0)):
            continue
        plugin.action()
        
    # #######################################################################
    # List the files that were created for each plugin:
    # #######################################################################
    headerPrinted = False
    for plugin in enabledPlugins:
        listOfFiles = plugin.getFileList()
        if (len(listOfFiles) > 0) :
            print ""
            if (not headerPrinted) :
                headerPrinted = True
            message = "The following files were created by the plugin: %s" %(plugin.getName())
            logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
            for filename in listOfFiles:
                print "\t  %s" %(filename)

    # #######################################################################
    # Add any additional files that are not reports
    # #######################################################################
    if (len(cmdLineOpts.filePathArray) > 0):
        # removes the timestamp and just uses root dir
        (head, tail) = os.path.split(extractedReportPath) 
        pathToNonReportFiles = os.path.join(head, "files")
        filesMoved = moveNonReportFiles(pathToNonReportFiles, cmdLineOpts.filePathArray)
        if (len(filesMoved) > 0) :
            message = "%s file(s) were moved to the non-report archive directory: \n\t  %s" %(str(len(filesMoved)),
                                                                                              pathToNonReportFiles)
            logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
    # #######################################################################
    # Remove tmp files since we are done with reportExtractor object
    # #######################################################################
    for report in reportsExtracted:
        report.clean()

    # #######################################################################
    # Return Path of the extracted reports
    # #######################################################################
    if (len(reportsExtracted) > 0):
        print
        message = "The compressed reports are located in the directory: \n\t  %s" %(reportExtractor.getCompressedReportPathDir())
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)
        message = "The extracted reports are located in the directory: \n\t  %s\n" %(reportExtractor.getExtractedReportPathDir())
        logging.getLogger(sx.MAIN_LOGGER_NAME).info(message)

# ##############################################################################
# Commandline Helper Functions
# ##############################################################################
def listPlugins(pathToBaseDir):
    """
    Do a dry run on creating reports and plugins to print information
    to the console for the user.

    @param pathToBaseDir: This is string that can set base to plugin
    base directory. 
    For example: "/usr/lib/python/site-packages" or  "/home/user/svn/sx"
    @type pathToBaseDir: String
    """
    # List Report Types
    reportLoader = RPLoader()
    loadedReport = reportLoader.load(pathToBaseDir, REPORT_CORE_IMPORT)
    loadedReport += reportLoader.load(CONFIG_DIR, REPORT_USER_IMPORT)
    if (not len(loadedReport) > 0):
        logging.getLogger(sx.MAIN_LOGGER_NAME).error("There were no reports found.")
    else:
        print "List of installed report types:"
        for report in loadedReport:
            print "%s(%s):  %s" %(textcolor(report.getName(),"lblue"),
                                  report.TYPE_DETECTION_FILE, 
                                  report.getDescription())
    # List plugins types
    pluginLoader = RPLoader()
    loadedPlugins = pluginLoader.load(pathToBaseDir, PLUGIN_CORE_IMPORT)
    loadedPlugins += pluginLoader.load(CONFIG_DIR, PLUGIN_USER_IMPORT)
    if (not len(loadedPlugins) > 0):
        logging.getLogger(sx.MAIN_LOGGER_NAME).error("There were no plugins found.")
    else:
        enabledMessage = "The following plugins are currently enabled by default:\n"
        disabledMessage = "The following plugins are currently disabled by default:\n"
        for plugin in loadedPlugins:
            if plugin.isEnabled():
                enabledMessage = "%s%s: %s\n" %(enabledMessage, 
                                                textcolor(str(plugin.getName()),"red"), 
                                                plugin.getDescription())
            else:
                disabledMessage = "%s%s: %s\n" %(disabledMessage, 
                                                 textcolor(str(plugin.getName()),"red"), 
                                                 plugin.getDescription())
        if (not len(enabledMessage) > 0):
            enabledMessage = "There was no plugins enabled."
        if (not len(disabledMessage) > 0):
            disabledMessage = "There was no plugins disabled."
        print  "\n%s\n%s" %(enabledMessage, disabledMessage)
        
        print "The list of available options for plugins:"
        for plugin in loadedPlugins:
            optionNames = plugin.getOptions() 
            if (len(optionNames) > 0):
                for optionName in optionNames :
                    optionDescription = plugin.getOptionDescription(optionName)
                    print "%s.%s: %s" %(textcolor(str(plugin.getName()),"red"), 
                                        textcolor(optionName,"red"), 
                                        optionDescription)

def getListOfReports(cmdLineListOfReports, cmdLineReportPath):
    """
    This function returns a list of paths to reports based on
    arguments given. The function takes params that is a list of
    reports or path to where reports could be located to add to list.

    @return: Returns a list of paths to reports based on
    arguments given.

    @param cmdLineListOfReports: A list of paths to reports that will
    be extracted.
    @type cmdLineListOfReports: Array
    @param cmdLineReportPath: A path to a directory that contains 
    reports. 
    @type cmdLineReportPath: String
    """
    # Temporary list of reports that have not been validated
    listOfReports = []
    # Create a list of all possible reports.
    if (len (cmdLineListOfReports) > 0):
        for pathToFile in cmdLineListOfReports:
            if (not os.path.exists(pathToFile)):
                message = "The path to the report does not exist: %s" %(pathToFile)
                logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
            else:
                listOfReports.append(pathToFile)
    elif (os.path.isdir(cmdLineReportPath)): 
        dirList = []
        try:
            # Add all files in this directory to the list and sort later.
            dirList = os.listdir(cmdLineReportPath)
            for filename in dirList:
                listOfReports.append(os.path.join(cmdLineReportPath, filename))
        except OSError:
            message = "There was an error getting a directory list for reports: %s." %(cmdLineReportPath)
            logging.getLogger(sx.MAIN_LOGGER_NAME).warn(message)
    return listOfReports

def validateListOfReports(listOfReports) :
    """
    This function does some simply validation on the files. Only files
    that are compressed with bz2/gunzip and file type tar will be
    considered validated reports.

    All reports have to be a bzip2/gunzip compressed file type tar or
    they will be skipped.

    @return: Returns a list of paths to report files.
    @rtype: Array

    @param listOfReports: A list of filepaths to possible reports
    @type listOfReports: Array
    """
    # List of reports that have been validated.
    validListOfReports = []
    for pathToFilename in listOfReports:
        # #######################################################################
        # All reports have to be a bzip2/gunzip compressed and file
        # type tar. Example: .tar.bz2 tar.gz
        # #######################################################################
        if (TarBall.isCompressedTarFile(pathToFilename)):
            validListOfReports.append(pathToFilename)
        else:
            message = "Skipped file since it does not appear to be a known report type: %s" %(pathToFilename)
            logging.getLogger(sx.MAIN_LOGGER_NAME).debug(message)
    return validListOfReports

def getPluginOptions(cmdLineListOfPluginOptions) :
    """
    Check if plugin has an option in list of plugin options and if so enable
    it.Returns a dictionary of dictionaries.

    Example:
    {'cluster': {'locks_check': 'on'}, 'Opensosreport': {'filebrowser': 'konqueror', 'browser': 'firefox'}}

    @return: Returns a dictionary of dictionaries.
    @rtype: Dictionary

    @param cmdLineListOfPluginOptions: This is a list of options for various
    plugins.
    @type cmdLineListOfPluginOptions: Array
    """
    pluginOptionsMap = {}
    for option in cmdLineListOfPluginOptions :
        keyEqualSplit = option.split("=")
        peroidEqualSplit = option.split(".")
        if ((len(keyEqualSplit) == 2) and (len(peroidEqualSplit) == 2)) :
            pluginName = peroidEqualSplit[0].lower()
            pluginOptionName = keyEqualSplit[0].split(".")[1]
            pluginOptionValue = keyEqualSplit[1]

            if (pluginOptionsMap.has_key(pluginName)) :
                optionMap = pluginOptionsMap.get(pluginName)
                optionMap[pluginOptionName] = pluginOptionValue
            else:
                pluginOptionsMap[pluginName] = {pluginOptionName:pluginOptionValue}
    return pluginOptionsMap

# ##############################################################################
# Helper functions
# ##############################################################################
def generatePaths(compressedReportPath, extractedReportPath) :
    """
    This function will create a unique path for the archived
    reports. A unique timestamp directory will be created under each
    of these paths to hold the compressed and extracted reports.

    The path should be unique since it takes a UID field and creates a
    timestampe directory.

    @return: Returns two arguments which is path to the compressed and
    extracted report paths
    @rtype: String

    @param compressedReportPath: The root path to the location where
    the compressed reports will be kept.
    @type compressedReportPath: String
    @param extractedReportPath: The root path to the location where
    the extracted reports will be kept.
    @type extractedReportPath: String
    """
    timestamp = time.strftime(UID_TIMESTAMP)
    # path to the compressed sysreports
    compressedReportPath = os.path.join(compressedReportPath, timestamp)
    # path to location of extracted sreports
    extractedReportPath = os.path.join(extractedReportPath, timestamp)
    try:
        if (not os.access(compressedReportPath, os.F_OK)):
            os.makedirs(compressedReportPath)
    except IOError:
        message = "Could not create the directory: %s." % (compressedReportPath)
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    try:
        if not os.access(extractedReportPath, os.F_OK):
            os.makedirs(extractedReportPath)
    except IOError:
        message = "Could not create the directory: %s" % (extractedReportPath)
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    return (compressedReportPath, extractedReportPath)

def createConfigDirectory() :
    """
    This function will create a configuration directory for sxconsole.
    
    @return: Returns True if configuration files/directories were
    created correctly.
    @rtype: Boolean
    """
    # Create the user defined directories if needed
    for path in (os.path.join(CONFIG_DIR, REPORT_USER_IMPORT), 
                 os.path.join(CONFIG_DIR, PLUGIN_USER_IMPORT)):
        if not os.access(path, os.F_OK):
            message = "Creating the configuration directory: %s" % (path)
            logging.getLogger(sx.MAIN_LOGGER_NAME).log(LogWriter.STATUS_LEVEL, message)
            try:
                os.makedirs(path)
            except IOError:
                message = "Could not create the directory: %s." % (path)
                logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
                return False

    # Create the python module __init_.py file so modules can be imported
    timestamp = time.strftime(UID_TIMESTAMP)
    for path in (os.path.join(os.path.join(CONFIG_DIR, REPORT_USER_IMPORT), "__init__.py"), 
                 os.path.join(os.path.join(CONFIG_DIR, PLUGIN_USER_IMPORT), "__init__.py")) :
        if (not os.access(path, os.F_OK)) :
            try:
                fout = open(path, "w")
                fout.write("# created by sxconsole: %s\n" %(timestamp))
                fout.close()
            except IOError:
                message = "There was an error writing the file: %s." %(path)
                logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
                return False
    return True

def moveNonReportFiles(dst, filePathArray) :
    """
    This function will move all nonreports files that were specified
    on commandline.

    @return: Returns an array of files that were moved.
    @rtype: Array

    @param dst: Path to location where the paths in array should be
    moved to.
    @type dst: String
    @param filePathArray: An array of paths to files.
    @type filePathArray: Array
    """
    filePathsAdded = []
    if (not os.access(dst, os.F_OK)):
        try:
            os.makedirs(dst)
        except (IOError, os.error):
            message = "Could not create the directory: \n\t%s" % (dst)
            logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
            return filePathsAdded

    for src in filePathArray:
        if (os.path.exists(src)):
            (dir, filename) = os.path.split(src)
            dst_filename = os.path.join(dst, filename)
            try:
                shutil.move(src, dst_filename)
                filePathsAdded.append(dst_filename)
            except IOError:
                message = "Cannot move the file %s to %s " %(src, dst_filename)
                logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
    return filePathsAdded

def textcolor(text, color):
    """ 
    Terminal text coloring function for a string. 

    The following colors are valid:
    black, red, green, brown, blue, purple, cyan, lgray, gray, lred,
    lgreen, yellow, lblue, pink, lcyan, white.

    @return: Returns a string reprensentation of the string with
    color.
    @rtype: String
    
    @param text: The string that will be colorized.
    @type text: String
    @param color: The color that will be used to colorize the text.
    @type color: String
    """
    colors = {  "black":"30", "red":"31", "green":"32", "brown":"33", "blue":"34",
                "purple":"35", "cyan":"36", "lgray":"37", "gray":"1;30", "lred":"1;31",
                "lgreen":"1;32", "yellow":"1;33", "lblue":"1;34", "pink":"1;35",
                "lcyan":"1;36", "white":"1;37" }
    if (not colors.has_key(color)) :
            return  text
    opencol = "\033["
    closecol = "m"
    clear = opencol + "0" + closecol
    f = opencol + colors[color] + closecol
    return "%s%s%s" % (f, text, clear)

# ##############################################################################
# OptParse classes for commandline options
# ##############################################################################
class OptionParserExtended(OptionParser):
    """
    This is the class that gets the command line options the end user
    selects.
    """
    def __init__(self, version) :
        """
        @param version: The version of the this script.
        @type version: String
        """
        self.__commandName = os.path.basename(sys.argv[0])
        versionMessage = "%s %s\n" %(self.__commandName, version)
        versionMessage += "This program was written by Shane Bradley(sbradley@redhat.com): http://tools.gss.redhat.com/trac/sx \n"

        commandDescription  ="%s will extract different report types to an "%(self.__commandName)
        commandDescription += "archived directory and archive the report file.\n"
        commandDescription += "Then various plugins can be enabled or disabled to run diagnostics on the reports."
        OptionParser.__init__(self, option_class=ExtendOption,
                              version=versionMessage,
                              description=commandDescription)

    def print_help(self):
        """
        Print examples at the bottom of the help message.
        """
        self.print_version()
        examplesMessage =  "\n\nExamples:\n\n"
        examplesMessage += "To list the different plugin and report types:\n"
        examplesMessage += "$ sxconsole -l\n\n"
        examplesMessage += "To run default plugins on selected reports and disable user defined reports/plugins:\n"
        examplesMessage += "$ sxconsole 144441 -r ~/tmp/rh4node1-sysreport.tar.bz2 -r ~/tmp/rh4node2-sysreport.tar.bz2 -U\n\n"
        examplesMessage += "To run default plugins on directory of reports(non-reports will not be processed):\n"
        examplesMessage += "$ sxconsole 144441 -R ~/tmp/\n\n"
        examplesMessage += "To disable all plugins on selected reports:\n"
        examplesMessage += "$ sxconsole 144441 -N -r ~/tmp/rh4node1-sysreport.tar.bz2 -r ~/tmp/rh4node2-sysreport.tar.bz2\n\n"
        examplesMessage += "To enable all plugins on directory of reports(non-reports will not be processed):\n"
        examplesMessage += "$ sxconsole 144441 -E -R ~/tmp/\n\n"
        examplesMessage += "To disable all plugins then enable specific plugins on directory of reports(non-reports will not be processed):\n"
        examplesMessage += "$ sxconsole 144441 -N -e cluster,checksysreport -R ~/tmp/\n\n"
        examplesMessage += "To enable all plugins then disable specific plugins on directory of reports(non-reports will not be processed):\n"
        examplesMessage += "$ sxconsole 144441 -E -e cluster,checksysreport -R ~/tmp/\n\n"
        examplesMessage += "To add a file to the archive that is not a report that will also do action on reports:\n"
        examplesMessage += "$ sxconsole 144441 -N -e cluster,checksysreport -R ~/tmp/ -f ~/tmp/tcpdump.log\n\n"
        examplesMessage += "To add a file to the archive that is not a report with no reports to process:\n"
        examplesMessage += "$ sxconsole 144441 -f ~/tmp/tcpdump2.log\n\n"
        examplesMessage += "After extraction is complete then open with a fileviewer and pass the option for a particular fileviewer."
        examplesMessage += "$ sxconsole 144441 -e OpenSOSReport OpenSOSReport.fileviewer=konqueror"
        OptionParser.print_help(self)
        print examplesMessage

class ExtendOption (Option):
        """
        Allow to specify comma delimited list of entries for arrays
        and dictionaries.
        """
        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):
            """
            This function is a wrapper to take certain options passed
            on command prompt and wrap them into an Array.

            @param action: The type of action that will be taken. For
            example: "store_true", "store_false", "extend".
            @type action: String
            @param dest: The name of the variable that will be used to
            store the option.  
            @type dest: String/Boolean/Array
            @param opt: The option string that triggered the action.
            @type opt: String
            @param value: The value of opt(option) if it takes a
            value, if not then None.  
            @type value:
            @param values: All the opt(options) in a dictionary.
            @type values: Dictionary
            @param parser: The option parser that was orginally called.
            @type parser: OptionParser
            """
            if (action == "extend") :
                valueList=[]
                try:
                    for v in value.split(","):
                        if ((opt == "-r") or (opt == "--report") or
                            (opt == "-f") or (opt == "--misc_files")) :
                            if (v[0] == '~' and not os.path.exists(v)):
                                v = os.path.expanduser(v)
                            elif (not v[0] == '/' and not os.path.exists(v)):
                                v = os.path.abspath(v)

                            if (os.path.exists(v)) :
                                # only append paths that exists.
                                valueList.append(v)
                            else:
                                message = "The filepath does not exist: %s" %(v)
                                logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
                        elif ((opt == "-o") or (opt == "--pluginOptions")):
                            # Verify that is the format: key=value, where key is of format parent.optionname
                            # Example: Opensosreport.browser=konqueror
                            keyEqualSplit = v.split("=")
                            peroidEqualSplit = v.split(".")
                            if ((len(keyEqualSplit) == 2) and (len(peroidEqualSplit) == 2)) :
                                valueList.append(v)
                            else:
                                logging.getLogger(sx.MAIN_LOGGER_NAME).error("The plugin option has invalid syntax:" %(v))
                        else:
                            # append everything else that does not deal with paths
                            valueList.append(v)
                except:
                    pass
                else:
                    values.ensure_value(dest, []).extend(valueList)
            else:
                Option.take_action(self, action, dest, opt, value, values, parser)

# ##############################################################################
# Get user selected options
# ##############################################################################
def getOptions(version) :
    """
    This function creates the OptionParser and returns commandline
    option and command args(thus 2 variables are returned).

    The cmdlineOpts which is the options user selected and cmdLineArgs
    which is value passed not associated with an option.

    @return: Returns commandline option and command args. Returns 2
    variables. The cmdlineOpts which is the options user selected and
    cmdLineArgs which is value passed not associated with an option.
    @rtype: Dictionary

    @param version: The version of the this script.
    @type version: String
    """
    cmdParser = OptionParserExtended(version)
    cmdParser.add_option("-d", "--debug",
                         action="store_true",
                         dest="enableDebugLogging",
                         help="Enables debug logging.",
                         default=False)
    cmdParser.add_option("-r", "--report",
                         action="extend",
                         dest="listOfReports",
                         help="Full Path to report file that will be extracted by sxconsole(multiple reports can be used with -r  or comma seperated).",
                         type="string",
                         default=[])
    cmdParser.add_option("-R", "--report_path",
                         action="store",
                         dest="reportPath",
                         help="Path to directory where reports are located that can be ran against this tool.",
                         type="string",
                         default="%s" %(os.path.join(os.environ["HOME"], "tmp")))
    cmdParser.add_option("-a", "--archive_path",
                         action="store",
                         dest="archivePath",
                         help="Path that will be used to archive the extracted reports.(default: ~/sxarchive).",
                         type="string",
                         default="%s" %(os.path.join(os.environ["HOME"], "sxarchive")))
    cmdParser.add_option("-p", "--path_extracted_reports",
                         action="store",
                         dest="pathToExtractedReports",
                         help="Path that will run plugins on reports that have already been extracted.",
                         type="string",
                         default="")
    cmdParser.add_option("-f", "--misc_files",
                         action="extend",
                         dest="filePathArray",
                         help="Misc files to be add to the archived reports(non report files).",
                         type="string",
                         default=[])
    cmdParser.add_option("-l", "--listmodules",
                         action="store_true",
                         dest="listModules",
                         help="Display list of plugins and report types that currently installed.",
                         default=False)
    cmdParser.add_option("-E", "--enable_all_plugins",
                         action="store_true",
                         dest="enableAllPlugins",
                         help="Enables all plugins.",
                         default=False)
    cmdParser.add_option("-e", "--enable_plugin",
                         action="extend",
                         dest="enablePlugins",
                         help="List of plugins that will be enabled.",
                         type="string",
                         default=[])
    cmdParser.add_option("-N", "--disable_all_plugins",
                         action="store_true",
                         dest="disableAllPlugins",
                         help="Disables all plugins.",
                         default=False)
    cmdParser.add_option("-n", "--disable_plugin",
                         action="extend",
                         dest="disablePlugins",
                         help="List of plugins that will be disabled.",
                         type="string",
                         default=[])
    cmdParser.add_option("-U", "--disable_user_plugins",
                         action="store_true",
                         dest="disableUserDefinedPlugins",
                         help="Disables support for user defined report types and plugins(path: ~/.sx/[reports/plugins]).",
                         default=False)
    cmdParser.add_option("-o", "--plugin_options",
                         action="extend",
                         dest="pluginOptions",
                         help="options that will be applied to plugin(s) which will over ride the defaults.",
                         type="string",
                         default=[])

    (cmdLineOpts, cmdLineArgs) = cmdParser.parse_args()
    return (cmdLineOpts, cmdLineArgs)

# ###############################################################################
if __name__ == "__main__":
    try:
        # #######################################################################
        # Setup the logger and create config directory
        # #######################################################################
        lwObjSXC = LogWriter(sx.MAIN_LOGGER_NAME,
                             logging.INFO,
                             sx.MAIN_LOGGER_FORMAT,
                             disableConsoleLog=False)
        # #######################################################################
        # Set the default base path for searching for python library
        # #######################################################################
        pathToBaseDir = ""

        # #######################################################################
        # Search for user defined paths to modules such as for devel.
        # #######################################################################
        for path in sys.path:
            result = path.find("sx/lib")
            if (result >= 0) :
                # Found a user defined path for sx, so that is base
                # path that will be used.
                pathToBaseDir = path
                break;

        # #######################################################################
        # If no user path is defined for sx then the python paths will
        # be searched.
        # #######################################################################
        if (not len(pathToBaseDir) > 0):
            for path in sys.path :
                # Find the first occurance
                if (((path.strip()[-len("site-packages"):]) == "site-packages") and  
                    (os.path.isdir(path + "/sx/reports") or 
                     os.path.isdir(path + "/sx/plugins"))) : 
                    pathToBaseDir = path
                    break;

        # If the base library is not found then we will exit.
        if (not len(pathToBaseDir) > 0) :
            message = "There was an error finding the python libraries."
            logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
            sys.exit(2) 

        # #######################################################################
        # Create configuration directory if it does not exist
        # #######################################################################
        if (not createConfigDirectory()):
            message = "There was an error creating the user configuration directory. sxconsole will proceed without it."
            logging.getLogger(sx.MAIN_LOGGER_NAME).warning(message)

        # #######################################################################
        # Exeute the main function to do the action
        # #######################################################################
        sxconsoleRun(pathToBaseDir)
        # #######################################################################
    except KeyboardInterrupt:
        message =  "This script will exit since control-c was executed by end user."
        logging.getLogger(sx.MAIN_LOGGER_NAME).error(message)
        sys.exit(2)
    sys.exit()
# ###############################################################################

