aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile1
-rwxr-xr-xsrc/lib/sos/helpers.py38
-rw-r--r--src/lib/sos/plugintools.py118
-rw-r--r--src/pylintrc354
-rwxr-xr-xsrc/sosreport174
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):