diff options
-rw-r--r-- | src/Makefile | 1 | ||||
-rwxr-xr-x | src/lib/sos/helpers.py | 38 | ||||
-rw-r--r-- | src/lib/sos/plugintools.py | 118 | ||||
-rw-r--r-- | src/pylintrc | 354 | ||||
-rwxr-xr-x | src/sosreport | 174 |
5 files changed, 542 insertions, 143 deletions
diff --git a/src/Makefile b/src/Makefile index 79653d13..b87d7d14 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,6 +41,5 @@ install: version: @echo "The version is $(NAME)-$(VERSION)-$(RELEASE)" - clean: @rm -fv *~ .*~ changenew ChangeLog.old $(NAME)-$(VERSION)-$(RELEASE).tar.bz2 diff --git a/src/lib/sos/helpers.py b/src/lib/sos/helpers.py index 447c164f..5524c0e4 100755 --- a/src/lib/sos/helpers.py +++ b/src/lib/sos/helpers.py @@ -22,11 +22,12 @@ ## (O'Reilly Media, 2005) 0-596-00797-3 ## +""" +helper functions used by sosreport and plugins +""" import os, popen2, fcntl, select, itertools, sys from tempfile import mkdtemp -workingBase = None - def importPlugin(pluginname, name): """ Import a plugin to extend capabilities of sosreport """ @@ -45,14 +46,14 @@ def sosFindTmpDir(): return workingBase -def makeNonBlocking(fd): +def makeNonBlocking(afd): """ Make the file desccriptor non-blocking. This prevents deadlocks. """ - fl = fcntl.fcntl(fd, fcntl.F_GETFL) + fl = fcntl.fcntl(afd, fcntl.F_GETFL) try: - fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY) + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.O_NDELAY) except AttributeError: - fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.FNDELAY) + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY) def sosGetCommandOutput(command): @@ -86,12 +87,12 @@ def sosGetCommandOutput(command): errdata.append(errchunk) if outeof and erreof: break - select.select([],[],[],.1) # Allow a little time for buffers to fill + select.select([], [], [], .1) # Allow a little time for buffers to fill err = child.wait() return (err, ''.join(outdata), ''.join(errdata)) -# TODO - this needs to be made clean and moved to the plugin tools, so +# this needs to be made clean and moved to the plugin tools, so # that it prints nice color output like sysreport def sosStatus(stat): """ Complete a status line that has been output to the console, @@ -109,7 +110,8 @@ def allEqual(elements): ''' return True if all the elements are equal, otherwise False. ''' first_element = elements[0] for other_element in elements[1:]: - if other_element != first_element: return False + if other_element != first_element: + return False return True @@ -117,24 +119,26 @@ def commonPrefix(*sequences): ''' return a list of common elements at the start of all sequences, then a list of lists that are the unique tails of each sequence. ''' # if there are no sequences at all, we're done - if not sequences: return [], [] + if not sequences: + return [], [] # loop in parallel on the sequences common = [] for elements in itertools.izip(*sequences): # unless all elements are equal, bail out of the loop - if not allEqual(elements): break + if not allEqual(elements): + break # got one more common element, append it and keep looping common.append(elements[0]) # return the common prefix and unique tails return common, [ sequence[len(common):] for sequence in sequences ] -def sosRelPath(p1, p2, sep=os.path.sep, pardir=os.path.pardir): - ''' return a relative path from p1 equivalent to path p2. - In particular: the empty string, if p1 == p2; - p2, if p1 and p2 have no common prefix. +def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir): + ''' return a relative path from path1 equivalent to path path2. + In particular: the empty string, if path1 == path2; + path2, if path1 and path2 have no common prefix. ''' - common, (u1, u2) = commonPrefix(p1.split(sep), p2.split(sep)) + common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep)) if not common: - return p2 # leave path absolute if nothing at all in common + return path2 # leave path absolute if nothing at all in common return sep.join( [pardir]*len(u1) + u2 ) diff --git a/src/lib/sos/plugintools.py b/src/lib/sos/plugintools.py index 3e788199..ac057dba 100644 --- a/src/lib/sos/plugintools.py +++ b/src/lib/sos/plugintools.py @@ -17,6 +17,17 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# pylint: disable-msg = R0902 +# pylint: disable-msg = R0904 +# pylint: disable-msg = W0702 +# pylint: disable-msg = W0703 +# pylint: disable-msg = R0201 +# pylint: disable-msg = W0611 +# pylint: disable-msg = W0613 + +""" +This is the base class for sosreport plugins +""" from sos.helpers import * from threading import Thread import os, os.path, sys, string, itertools, glob @@ -26,10 +37,12 @@ class PluginBase: Base class for plugins """ def __init__(self, pluginname, commons): + # pylint: disable-msg = E0203 try: - foo = len(self.optionList) + len(self.optionList) except: self.optionList = [] + # pylint: enable-msg = E0203 self.copiedFiles = [] self.copiedDirs = [] self.executedCommands = [] @@ -42,15 +55,17 @@ class PluginBase: self.forbiddenPaths = [] self.copyPaths = [] self.collectProgs = [] + self.thread = None # get the option list into a dictionary for opt in self.optionList: self.optNames.append(opt[0]) self.optParms.append({'desc':opt[1], 'speed':opt[2], 'enabled':opt[3]}) - return # Methods for copying files and shelling out def doCopyFileOrDir(self, srcpath): + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0915 ''' Copy file or directory to the destination tree. If a directory, then everything below it is recursively copied. A list of copied files are saved for use later in preparing a report @@ -62,7 +77,7 @@ class PluginBase: if copyProhibited: sys.stderr.write("%s is on the copyProhibited list\n" % srcpath) - sys.stderr.flush + sys.stderr.flush() return '' if os.path.islink(srcpath): @@ -90,49 +105,49 @@ class PluginBase: dstslname = link if os.path.isdir(srcpath): - for file in os.listdir(srcpath): - if file == '.' or file == '..': + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': pass else: try: - abspath = self.doCopyFileOrDir(srcpath+'/'+file) + abspath = self.doCopyFileOrDir(srcpath+'/'+afile) except: - sys.stderr.write("1Problem at path %s\n" % srcpath+'/'+file,) - sys.stderr.flush() + sys.stderr.write("1Problem at path %s\n" % srcpath+'/'+afile,) + sys.stderr.flush() # if on forbidden list, abspath is null if not abspath == '': dstslname = sosRelPath(self.cInfo['rptdir'], abspath) self.copiedDirs.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) else: try: - dstslname, abspath = self.__copyFile(srcpath) + dstslname, abspath = self.__copyFile(srcpath) except: - sys.stderr.write("2Problem at path %s\n" % srcpath) - sys.stderr.flush() + sys.stderr.write("2Problem at path %s\n" % srcpath) + sys.stderr.flush() self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) # Recurse to copy whatever it points to newpath = os.path.normpath(os.path.join(os.path.dirname(srcpath), link)) try: - self.doCopyFileOrDir(newpath) + self.doCopyFileOrDir(newpath) except: - sys.stderr.write("3Problem at path %s" % newpath,) - sys.stderr.flush + sys.stderr.write("3Problem at path %s" % newpath,) + sys.stderr.flush() return abspath else: if not os.path.exists(srcpath): self.cInfo['logfd'].write("File or directory %s does not exist\n" % srcpath) elif os.path.isdir(srcpath): - for file in os.listdir(srcpath): - if file == '.' or file == '..': + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': pass else: - self.doCopyFileOrDir(srcpath+'/'+file) + self.doCopyFileOrDir(srcpath+'/'+afile) else: # This is not a directory or a symlink tdstpath, abspath = self.__copyFile(srcpath) - self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath,'symlink':"no"}) # save in our list + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list return abspath def __copyFile(self, src): @@ -140,16 +155,16 @@ class PluginBase: destination file name. """ try: - status, shout, sherr = sosGetCommandOutput("/bin/cp --parents " + src +" " + self.cInfo['dstroot']) - self.cInfo['logfd'].write(shout) - self.cInfo['logfd'].write(sherr) - #sosStatus(status) - abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) - relpath = sosRelPath(self.cInfo['rptdir'], abspath) - return relpath, abspath + # pylint: disable-msg = W0612 + status, shout, sherr = sosGetCommandOutput("/bin/cp --parents " + src +" " + self.cInfo['dstroot']) + self.cInfo['logfd'].write(shout) + self.cInfo['logfd'].write(sherr) + abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) + relpath = sosRelPath(self.cInfo['rptdir'], abspath) + return relpath, abspath except Exception,e: - sys.stderr.write("4Problem copying file %s\n" % src,) - print e + sys.stderr.write("4Problem copying file %s\n" % src,) + print e def addForbiddenPath(self, forbiddenPath): """Specify a path to not copy, even if it's part of a copyPaths[] entry. @@ -158,6 +173,9 @@ class PluginBase: self.forbiddenPaths.append(forbiddenPath) def getAllOptions(self): + """ + return a list of all options selected + """ return (self.optNames, self.optParms) def setOption(self, optionname, enable): @@ -182,7 +200,7 @@ class PluginBase: """ # Glob case handling is such that a valid non-glob is a reduced glob for filespec in glob.glob(copyspec): - self.copyPaths.append(filespec) + self.copyPaths.append(filespec) def copyFileGlob(self, srcglob): """ Deprecated - please modify modules to use addCopySpec() @@ -197,7 +215,7 @@ class PluginBase: sys.stderr.write("Warning: the copyFileOrDir() function has been deprecated. Please\n") sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.\n") raise ValueError - self.addCopySpec(srcpath) + #self.addCopySpec(srcpath) def runExeInd(self, exe): """ Deprecated - use callExtProg() @@ -214,7 +232,8 @@ class PluginBase: # First check to make sure the binary exists and is runnable. if not os.access(prog.split()[0], os.X_OK): return - + + # pylint: disable-msg = W0612 status, shout, sherr = sosGetCommandOutput(prog) return status @@ -226,6 +245,9 @@ class PluginBase: pass def collectExtOutput(self, exe): + """ + Run a program and collect the output + """ self.collectProgs.append(exe) def collectOutputNow(self, exe): @@ -236,6 +258,7 @@ class PluginBase: if not os.access(exe.split()[0], os.X_OK): return + # pylint: disable-msg = W0612 status, shout, sherr = sosGetCommandOutput(exe) # build file name for output @@ -277,13 +300,22 @@ class PluginBase: return def doCollect(self, verbosity): - self.thread = Thread(target=self.copyStuff,name=self.piName+'-thread',args=(verbosity,)) + """ + create a thread which calls the copyStuff method for a plugin + """ + self.thread = Thread(target=self.copyStuff, name=self.piName+'-thread', args=(verbosity,)) self.thread.start() def wait(self): + """ + wait for a thread to complete - only called for threaded execution + """ self.thread.join() def copyStuff(self, verbosity): + """ + Collect the data for a plugin + """ for path in self.copyPaths: try: self.doCopyFileOrDir(path) @@ -311,16 +343,22 @@ class PluginBase: pass def analyze(self, verbosity): + """ + perform any analysis. To be replaced by a plugin if desired + """ pass def postproc(self, dstroot): + """ + perform any postprocessing. To be replaced by a plugin if desired + """ pass def report(self): """ Present all information that was gathered in an html file that allows browsing the results. """ - # TODO make this prettier, this is a first pass + # make this prettier html = '<hr/><a name="%s"></a>\n' % self.piName # Intro @@ -329,20 +367,20 @@ class PluginBase: # Files if len(self.copiedFiles): html = html + "<p>Files copied:<br><ul>\n" - for file in self.copiedFiles: - html = html + '<li><a href="%s">%s</a>' % (file['dstpath'], file['srcpath']) - if (file['symlink'] == "yes"): - html = html + " (symlink to %s)" % file['pointsto'] + for afile in self.copiedFiles: + html = html + '<li><a href="%s">%s</a>' % (afile['dstpath'], afile['srcpath']) + if (afile['symlink'] == "yes"): + html = html + " (symlink to %s)" % afile['pointsto'] html = html + '</li>\n' html = html + "</ul></p>\n" # Dirs if len(self.copiedDirs): html = html + "<p>Directories Copied:<br><ul>\n" - for dir in self.copiedDirs: - html = html + '<li><a href="%s">%s</a>\n' % (dir['dstpath'], dir['srcpath']) - if (dir['symlink'] == "yes"): - html = html + " (symlink to %s)" % dir['pointsto'] + for adir in self.copiedDirs: + html = html + '<li><a href="%s">%s</a>\n' % (adir['dstpath'], adir['srcpath']) + if (adir['symlink'] == "yes"): + html = html + " (symlink to %s)" % adir['pointsto'] html = html + '</li>\n' html = html + "</ul></p>\n" diff --git a/src/pylintrc b/src/pylintrc new file mode 100644 index 00000000..934f5255 --- /dev/null +++ b/src/pylintrc @@ -0,0 +1,354 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +# This checker also defines the following reports: +# * R0001: Total errors / warnings +# * R0002: % errors / warnings by module +# * R0003: Messages +# * R0004: Global evaluation +[MASTER] + +# Profiled execution. +profile=no + +# Add <file or directory> to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[REPORTS] + +# Tells wether to display a full report or only the messages +reports=yes + +# Use HTML as output format instead of text +html=no + +# Use a parseable text output format, so your favorite text editor will be able +# to jump to the line corresponding to a message. +parseable=yes + +# Colorizes text output using ansi escape codes +color=no + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Include message's id in output +include-ids=yes + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Enable / disable this checker +enable-variables=yes + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Enable / disable this checker +enable-typecheck=yes + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=no + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered. +acquired-members=REQUEST,acl_users,aq_parent + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +# This checker also defines the following reports: +# * R0101: Statistics by type +[BASIC] + +# Enable / disable this checker +enable-basic=yes + +#disable-msg=C0121 + +# Required attributes for module, separated by a comma +required-attributes= + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][A-Za-z0-9_]{0,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Enable / disable this checker +enable-design=yes + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for : +# * methods without self as first argument +# * overriden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# Enable / disable this checker +enable-classes=yes + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +# This checker also defines the following reports: +# * R0401: External dependencies +# * R0402: Modules dependencies graph +[IMPORTS] + +# Enable / disable this checker +enable-imports=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for usage of new style capabilities on old style classes and +# other new/old styles conflicts problems +# * use of property, __slots__, super +# * "super" usage +# * raising a new style class as exception +# +[NEWSTYLE] + +# Enable / disable this checker +enable-newstyle=yes + + +# checks for +# * excepts without exception filter +# * string exceptions +# +[EXCEPTIONS] + +# Enable / disable this checker +enable-exceptions=yes + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Enable / disable this checker +enable-format=yes + +# Maximum number of characters on a single line. +max-line-length=132 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +# This checker also defines the following reports: +# * R0801: Duplication +[SIMILARITIES] + +# Enable / disable this checker +enable-similarities=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# Enable / disable this checker +enable-miscellaneous=yes + +# List of note tags to take in consideration, separated by a comma. Default to +# FIXME, XXX, TODO +notes=FIXME,XXX,TODO + + +# does not check anything but gives some raw metrics : +# * total number of lines +# * total number of code lines +# * total number of docstring lines +# * total number of comments lines +# * total number of empty lines +# +# This checker also defines the following reports: +# * R0701: Raw metrics +[METRICS] + +# Enable / disable this checker +enable-metrics=no diff --git a/src/sosreport b/src/sosreport index dd12c2fb..a842f3ee 100755 --- a/src/sosreport +++ b/src/sosreport @@ -1,7 +1,10 @@ #!/usr/bin/env python - +""" +Gather information about a system and report it using plugins +supplied for application-specific information +""" ## sosreport.py -## Implement policies required for the sos system support tool +## gather information about a system and report it ## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com> @@ -19,9 +22,12 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# pylint: disable-msg = W0611 +# pylint: disable-msg = W0702 + import sys import os -import curses +#import curses from optparse import OptionParser import sos.policyredhat from sos.helpers import * @@ -33,61 +39,64 @@ if os.getuid() != 0: sys.exit(1) # for debugging -raiseplugins = 1 - - -cmdParser=OptionParser() -cmdParser.add_option("-a","--alloptions",action="store_true",dest="alloptions",default=False,help="Use all options for loaded plugins") -cmdParser.add_option("-f","--fastoptions",action="store_true",dest="fastoptions",default=False,help="Use only fast options for loaded plugins") -cmdParser.add_option("-l","--list-plugins",action="store_true",dest="listPlugins",default=False,help="list existing plugins") -cmdParser.add_option("-n","--noplugin",action="append",dest="noplugins",help="list of plugin _not_ to load") -cmdParser.add_option("-v","--verbose",action="count",dest="verbosity",help="How obnoxious we're being about telling the user what we're doing.") -cmdParser.add_option("-m","--multithreaded",action="store_true",dest="multithread",help="Use the multithreaded information gathering mode to speed up the report") -(cmdLineOpts,cmdLineArgs)=cmdParser.parse_args() -#print cmdLineOpts +__raisePlugins__ = 1 + + +__cmdParser__ = OptionParser() +__cmdParser__.add_option("-a", "--alloptions", action="store_true", \ + dest="alloptions", default=False, \ + help="Use all options for loaded plugins") +__cmdParser__.add_option("-f", "--fastoptions", action="store_true", \ + dest="fastoptions", default=False, \ + help="Use only fast options for loaded plugins") +__cmdParser__.add_option("-l", "--list-plugins", action="store_true", \ + dest="listPlugins", default=False, \ + help="list existing plugins") +__cmdParser__.add_option("-n", "--noplugin", action="append", \ + dest="noplugins",\ + help="list of plugin _not_ to load") +__cmdParser__.add_option("-v", "--verbose", action="count", \ + dest="verbosity", \ + help="How obnoxious we're being about telling the user what we're doing.") +__cmdParser__.add_option("-m", "--multithreaded", action="store_true", \ + dest="multithread", \ + help="Use the multithreaded information gathering mode to speed up the report (experimental)") +(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args() def get_curse_options(alloptions): - #http://www.amk.ca/python/howto/curses/ - #http://gnosis.cx/publish/programming/charming_python_6.html - #http://www.wanware.com/tsgdocs/snack.html - # /usr/share/doc/newt-devel-0.51.6/ - # - + """ + use curses to enable the user to select some options + """ # allooptions is an array of (plug, plugname, optname, parms(dictionary)) tuples - plugName=[] - out=[] + plugName = [] + out = [] # get a sorted list of all plugin names - for p in alloptions: - if p[1] not in plugName: - plugName.append(p[1]) + for rrr in alloptions: + if rrr[1] not in plugName: + plugName.append(rrr[1]) plugName.sort() - plugCbox=CheckboxTree(height=5,scroll=1) + plugCbox = CheckboxTree(height=5, scroll=1) - countOpt=-1 + countOpt = -1 - optDic={} - optDicCounter=0 + optDic = {} + optDicCounter = 0 # iterate over all plugins with options for curPlugName in plugName: - plugCbox.addItem(curPlugName,(snackArgs['append'],)) - countOpt=countOpt+1 + plugCbox.addItem(curPlugName, (snackArgs['append'],)) + countOpt = countOpt+1 for opt in alloptions: if opt[1] != curPlugName: continue - optName=opt[2] - enabled=opt[3]['enabled'] - speed=opt[3]['speed'] - optDesc=opt[3]['desc'] - - snt=optName+" ("+optDesc+") is "+speed - plugCbox.addItem(snt,(countOpt,snackArgs['append']),item=optDicCounter,selected=enabled) - optDic[optDicCounter]=opt - optDicCounter+=1 + snt = opt[2] + " ("+opt[3]['desc']+") is " + opt[3]['speed'] + plugCbox.addItem(snt, (countOpt, snackArgs['append']), item = optDicCounter, selected = opt[3]['enabled']) + optDic[optDicCounter] = opt + optDicCounter += 1 screen = SnackScreen() @@ -95,25 +104,28 @@ def get_curse_options(alloptions): g = GridForm(screen, "Select Sosreport Options", 1, 10) g.add(plugCbox, 0, 0) g.add(bb, 0, 1, growx = 1) - result = g.runOnce() + g.runOnce() screen.finish() - for i in range(0, optDicCounter): - froo, wantit = plugCbox.getEntryValue(i) - optDic[i][3]['enabled'] = wantit - out.append((optDic[i])) + for rrr in range(0, optDicCounter): + optDic[rrr][3]['enabled'] = plugCbox.getEntryValue(rrr)[1] + out.append((optDic[rrr])) return out def sosreport(): - "This is the top-level function that gathers and processes all sosreport information" + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0914 + # pylint: disable-msg = R0915 + """ + This is the top-level function that gathers and processes all sosreport information + """ loadedplugins = [] alloptions = [] - threads = [] - # TODO automatically locate the policy module?? + # perhaps we should automatically locate the policy module?? policy = sos.policyredhat.SosPolicy() # find the plugins path @@ -122,11 +134,6 @@ def sosreport(): if path.strip()[-len("site-packages"):] == "site-packages": pluginpath = path + "/sos/plugins" - # TODO process command line to: - # load unsigned plugins - # disable one or more plugins - # set fast or slow collection without options menu presentation - # Set up common info and create destinations dstroot = sosFindTmpDir() @@ -147,7 +154,7 @@ def sosreport(): # validate and load plugins plugins = os.listdir(pluginpath) - if cmdLineOpts.listPlugins: + if __cmdLineOpts__.listPlugins: for plug in plugins: try: if ((plug[-3:] == '.py') and (plug != "__init__.py")): @@ -166,18 +173,16 @@ def sosreport(): #print "importing plugin: %s" % plugbase try: if policy.validatePlugin(pluginpath + plug): - PluginClass = importPlugin(pidot, plugbase) + pluginClass = importPlugin(pidot, plugbase) else: print "Plugin %s does not validate, skipping" % plug continue - loadedplugins.append((plugbase, PluginClass(plugbase, commons))) + loadedplugins.append((plugbase, pluginClass(plugbase, commons))) except: print "Plugin %s does not install, skipping" % plug raise - continue except: - # TODO do better - if raiseplugins: + if __raisePlugins__: raise print "plugin load failed for %s" % plug @@ -185,11 +190,11 @@ def sosreport(): # First, gather and process options for plugname, plug in loadedplugins: - if cmdLineOpts.verbosity > 3: + if __cmdLineOpts__.verbosity > 3: print "processing options from plugin: %s" % plugname try: - len(cmdLineOpts.noplugins) - if plugname not in cmdLineOpts.noplugins: + len(__cmdLineOpts__.noplugins) + if plugname not in __cmdLineOpts__.noplugins: names, parms = plug.getAllOptions() for optname, optparm in zip(names, parms): alloptions.append((plug, plugname, optname, optparm)) @@ -198,44 +203,45 @@ def sosreport(): for optname, optparm in zip(names, parms): alloptions.append((plug, plugname, optname, optparm)) - if not cmdLineOpts.fastoptions and not cmdLineOpts.alloptions: - cursedOptions=get_curse_options(alloptions) - elif cmdLineOpts.fastoptions: + if not __cmdLineOpts__.fastoptions and not __cmdLineOpts__.alloptions: + get_curse_options(alloptions) + elif __cmdLineOpts__.fastoptions: for i in range(len(alloptions)): for plug, plugname, optname, optparm in alloptions: - if optparm['speed']=='fast': - plug.setOption(optname,1) - elif cmdLineOpts.alloptions: + if optparm['speed'] == 'fast': + plug.setOption(optname, 1) + elif __cmdLineOpts__.alloptions: for i in range(len(alloptions)): for plug, plugname, optname, optparm in alloptions: - plug.setOption(optname,1) + plug.setOption(optname, 1) # Call the setup method for each plugin for plugname, plug in loadedplugins: - if cmdLineOpts.verbosity > 1: + if __cmdLineOpts__.verbosity > 1: print "Setting up plugin module %s" % plugname, plug.setup() # Call the collect method for each plugin for plugname, plug in loadedplugins: - if cmdLineOpts.verbosity > 0: - print "Executing plugin %s" % plugname, - if cmdLineOpts.multithread: - plug.doCollect(cmdLineOpts.verbosity) + if __cmdLineOpts__.verbosity > 0: + print "Executing plugin %s" % plugname + if __cmdLineOpts__.multithread: + plug.doCollect(__cmdLineOpts__.verbosity) else: - plug.copyStuff(cmdLineOpts.verbosity) + plug.copyStuff(__cmdLineOpts__.verbosity) # Wait for all the collection threads to exit - for plugname, plug in loadedplugins: - if cmdLineOpts.verbosity > 1: - print "Waiting for plugin %s to return" % plugname, - plug.wait() + if __cmdLineOpts__.multithread: + for plugname, plug in loadedplugins: + if __cmdLineOpts__.verbosity > 1: + print "Waiting for plugin %s to return" % plugname, + plug.wait() # Call the analyze method for each plugin for plugname, plug in loadedplugins: - if cmdLineOpts.verbosity > 1: + if __cmdLineOpts__.verbosity > 1: print "Analyzing results of plugin %s" % plugname, - plug.analyze(cmdLineOpts.verbosity) + plug.analyze(__cmdLineOpts__.verbosity) # Sort the module names to do the report in alphabetical order loadedplugins.sort() @@ -264,7 +270,6 @@ def sosreport(): plugNames = [] for plugname, plug in loadedplugins: for alert in plug.alerts: - # TODO include the plugin as a target of a link here allAlerts.append('<a href="#%s">%s</a>: %s' % (plugname, plugname, alert)) plugNames.append(plugname) @@ -273,11 +278,10 @@ def sosreport(): # Create a table of links to the module info rfd.write("<hr/><h3>Loaded Plugins:</h3>") rfd.write("<table><tr>\n") - qq = 0 rr = 0 for i in range(len(plugNames)): rfd.write('<td><a href="#%s">%s</a></td>\n' % (plugNames[i], plugNames[i])) - qq, rr = divmod(i,4) + rr = divmod(i, 4)[1] if (rr == 3): rfd.write('</tr>') if not (rr == 3): |