diff options
author | navid <navid@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2007-08-20 08:08:53 +0000 |
---|---|---|
committer | navid <navid@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2007-08-20 08:08:53 +0000 |
commit | 347c67b5d8fd925216eebda502182b3d69094090 (patch) | |
tree | 1f3e68312adfb605dfbaeffa398fbf9613250539 /src | |
parent | a556dd7cb68504298f27c41684976578ec722218 (diff) | |
download | sos-347c67b5d8fd925216eebda502182b3d69094090.tar.gz |
merged branches/navid-dev -r 364:377 into trunk
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/sos/trunk@378 ef72aa8b-4018-0410-8976-d6e080ef94d8
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/sos/plugintools.py | 191 | ||||
-rwxr-xr-x | src/lib/sos/policyredhat.py | 13 | ||||
-rwxr-xr-x | src/sosreport | 111 |
3 files changed, 168 insertions, 147 deletions
diff --git a/src/lib/sos/plugintools.py b/src/lib/sos/plugintools.py index da323401..f678297f 100644 --- a/src/lib/sos/plugintools.py +++ b/src/lib/sos/plugintools.py @@ -31,7 +31,7 @@ This is the base class for sosreport plugins from sos.helpers import * from threading import Thread, activeCount import os, os.path, sys, string, itertools, glob, re, traceback -import logging +import logging, shutil from stat import * from time import time @@ -68,6 +68,8 @@ class PluginBase: self.packages = [] self.files = [] + self.must_exit = False + self.soslog = logging.getLogger('sos') # get the option list into a dictionary @@ -97,7 +99,7 @@ class PluginBase: except KeyboardInterrupt: raise KeyboardInterrupt except Exception, e: - self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (abspath,e)) + self.soslog.log(logging.VERBOSE, "problem at path %s (%s)" % (abspath,e)) break return False @@ -129,108 +131,95 @@ class PluginBase: if copyProhibited: return '' + if not os.path.exists(srcpath): + self.soslog.debug("file or directory %s does not exist" % srcpath) + return + if os.path.islink(srcpath): # This is a symlink - We need to also copy the file that it points to - # file and dir symlinks ar ehandled the same + + # FIXME: ignore directories for now + if os.path.isdir(srcpath): + return + link = os.readlink(srcpath) + # What's the name of the symlink on the dest tree? + dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) + if os.path.isabs(link): # the link was an absolute path, and will not point to the new # tree. We must adjust it. - - # What's the name of the symlink on the dest tree? - dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) - - # make sure the dst dir exists - if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))): - # create the directory - os.makedirs(os.path.dirname(dstslname)) - - dstsldir = os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep)) - # Create the symlink on the dst tree - rpth = sosRelPath(os.path.dirname(dstslname), dstsldir) - os.symlink(rpth, dstslname) + rpth = sosRelPath(os.path.dirname(dstslname), os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep))) else: # no adjustment, symlink is the relative path - dstslname = link + rpth = link - if os.path.isdir(srcpath): # symlink to a directory - # FIXME: don't recurse symlinks until vicious loops are detected + # make sure the link doesn't already exists + if os.path.exists(dstslname): + self.soslog.log(logging.DEBUG, "skipping symlink creation: already exists (%s)" % dstslname) return - abslink = os.path.abspath(os.path.dirname(srcpath) + "/" + link) - self.soslog.log(logging.VERBOSE2, "DIRLINK %s to %s [%s]" % (srcpath,link,abslink)) - for tmplink in self.copiedDirs: - if tmplink["srcpath"] == abslink or tmplink["pointsto"] == abslink: - self.soslog.log(logging.VERBOSE2, "already copied [%s]" % srcpath) - return + # make sure the dst dir exists + if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))): + os.makedirs(os.path.dirname(dstslname)) - for afile in os.listdir(srcpath): - if afile == '.' or afile == '..': - pass - else: - self.soslog.log(logging.VERBOSE2, "copying (file or dir) %s" % srcpath+'/'+afile) - try: - abspath = self.doCopyFileOrDir(srcpath+'/'+afile) - except SystemExit: - raise SystemExit - except KeyboardInterrupt: - raise KeyboardInterrupt - except Exception, e: - self.soslog.verbose(traceback.format_exc()) - - # 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':os.path.abspath(srcpath+'/'+afile) }) - else: - self.soslog.log(logging.VERBOSE3, "copying symlink %s" % srcpath) - try: - dstslname, abspath = self.__copyFile(srcpath) - self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) - except SystemExit: - raise SystemExit - except KeyboardInterrupt: - raise KeyboardInterrupt - except Exception, e: - self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (srcpath, e)) - - return abspath + self.soslog.log(logging.VERBOSE3, "creating symlink %s -> %s" % (dstslname, rpth)) + os.symlink(rpth, dstslname) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':rpth, 'symlink':"yes", 'pointsto':link}) + self.doCopyFileOrDir(link) + return else: # not a symlink - if not os.path.exists(srcpath): - self.soslog.debug("File or directory %s does not exist\n" % srcpath) - elif os.path.isdir(srcpath): + if os.path.isdir(srcpath): for afile in os.listdir(srcpath): if afile == '.' or afile == '..': pass else: self.doCopyFileOrDir(srcpath+'/'+afile) - else: - # This is not a directory or a symlink - tdstpath, abspath = self.__copyFile(srcpath) - self.soslog.log(logging.VERBOSE3, "copying file %s" % srcpath) - self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list - return abspath + + # if we get here, it's definitely a regular file (not a symlink or dir) + + self.soslog.log(logging.VERBOSE3, "copying file %s" % srcpath) + try: + tdstpath, abspath = self.__copyFile(srcpath) + except "AlreadyExists": + self.soslog.log(logging.DEBUG, "error copying file %s (already exists)" % (srcpath)) + return + except IOError: + self.soslog.log(logging.VERBOSE2, "error copying file %s (IOError)" % (srcpath)) + return + except: + self.soslog.log(logging.VERBOSE2, "error copying file %s (SOMETHING HAPPENED)" % (srcpath)) + + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list + + return abspath def __copyFile(self, src): """ call cp to copy a file, collect return status and output. Returns the destination file name. """ - try: - # pylint: disable-msg = W0612 - status, shout, runtime = sosGetCommandOutput("/bin/cp --parents -P --preserve=mode,ownership,timestamps,links " + src +" " + self.cInfo['dstroot']) - if status: - self.soslog.debug(shout) - abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) - relpath = sosRelPath(self.cInfo['rptdir'], abspath) - return relpath, abspath - except SystemExit: - raise SystemExit - except KeyboardInterrupt: - raise KeyboardInterrupt - except Exception,e: - self.soslog.warning("Problem copying file %s (%s)" % (src, e)) + rel_dir = os.path.dirname(src).lstrip(os.path.sep) +# if rel_dir[0] == "/": rel_dir = rel_dir[1:] + new_dir = os.path.join(self.cInfo['dstroot'], rel_dir) + new_fname = os.path.join(new_dir, os.path.basename(src)) + + if not os.path.exists(new_fname): + if not os.path.isdir(new_dir): + os.makedirs(new_dir) + + if os.path.islink(src): + linkto = os.readlink(src) + os.symlink(linkto, new_fname) + else: + shutil.copy2(src, new_dir) + else: + raise "AlreadyExists" + + abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) + relpath = sosRelPath(self.cInfo['rptdir'], abspath) + return (relpath, abspath) def addForbiddenPath(self, forbiddenPath): """Specify a path to not copy, even if it's part of a copyPaths[] entry. @@ -448,6 +437,10 @@ class PluginBase: if semaphore: semaphore.acquire() + if self.must_exit: + semaphore.release() + return + self.soslog.log(logging.VERBOSE, "starting threaded plugin %s" % self.piName) self.time_start = time() @@ -458,15 +451,13 @@ class PluginBase: try: self.doCopyFileOrDir(path) except SystemExit: - if threaded: - return SystemExit - else: - raise SystemExit + if semaphore: semaphore.release() + if threaded: return KeyboardInterrupt + else: raise KeyboardInterrupt except KeyboardInterrupt: - if threaded: - return KeyboardInterrupt - else: - raise KeyboardInterrupt + if semaphore: semaphore.release() + if threaded: return KeyboardInterrupt + else: raise KeyboardInterrupt except Exception, e: self.soslog.log(logging.VERBOSE2, "error copying from pathspec %s (%s), traceback follows:" % (path,e)) self.soslog.log(logging.VERBOSE2, traceback.format_exc()) @@ -475,16 +466,14 @@ class PluginBase: try: self.collectOutputNow(prog, suggest_filename, root_symlink) except SystemExit: - if threaded: - return SystemExit - else: - raise SystemExit + if semaphore: semaphore.release() + if threaded: return SystemExit + else: raise SystemExit except KeyboardInterrupt: - if threaded: - return KeyboardInterrupt - else: - raise KeyboardInterrupt - except: + if semaphore: semaphore.release() + if threaded: return KeyboardInterrupt + else: raise KeyboardInterrupt + except Exception, e: self.soslog.log(logging.VERBOSE2, "error collection output of '%s', traceback follows:" % prog) self.soslog.log(logging.VERBOSE2, traceback.format_exc()) @@ -493,6 +482,10 @@ class PluginBase: if semaphore: semaphore.release() self.soslog.log(logging.VERBOSE, "plugin %s returning" % self.piName) + def exit_please(self): + """ This function tells the plugin that it should exit ASAP""" + self.must_exit = True + def get_description(self): """ This function will return the description for the plugin""" try: @@ -506,15 +499,15 @@ class PluginBase: """ # some files or packages have been specified for this package if len(self.files) or len(self.packages): - for file in self.files: - if os.path.exists(files): + for fname in self.files: + if os.path.exists(fname): return True for pkgname in self.packages: if self.cInfo["policy"].pkgByName(pkgname): return True return False - return True + return True def defaultenabled(self): """This devices whether a plugin should be automatically loaded or diff --git a/src/lib/sos/policyredhat.py b/src/lib/sos/policyredhat.py index 54890b00..9f61e68b 100755 --- a/src/lib/sos/policyredhat.py +++ b/src/lib/sos/policyredhat.py @@ -101,13 +101,14 @@ class SosPolicy: return None def allPkgs(self, ds = None, value = None): - if not hasattr(self, "rpm_ts"): - self.rpm_ts = rpm.TransactionSet() + ts = rpm.TransactionSet() if ds and value: - mi = self.rpm_ts.dbMatch(ds, value) + mi = ts.dbMatch(ds, value) else: - mi = self.rpm_ts.dbMatch() - return [pkg for pkg in mi] + mi = ts.dbMatch() + toret = [pkg for pkg in mi] + del mi, ts + return toret def runlevelByService(self, name): ret = [] @@ -171,7 +172,7 @@ class SosPolicy: self.ticketNumber = raw_input(_("Please enter the case number that you are generating this report for: ")) self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber) print - except KeyboardInterrupt: + except: print sys.exit(0) diff --git a/src/sosreport b/src/sosreport index daf43116..8e9d2697 100755 --- a/src/sosreport +++ b/src/sosreport @@ -50,10 +50,15 @@ def exittermhandler(signum, frame): def doExitCode(): from threading import enumerate - global __breakHits__ + global __breakHits__, loadedplugins, dstroot + __breakHits__ += 1 if ( ( activeCount() > 1 ) and ( __breakHits__ == 1 ) ): print "SIGTERM received, multiple threads detected, waiting for all threads to exit" + + for plugname, plug in loadedplugins: + plug.exit_please() + for thread in enumerate(): if thread.getName() == "MainThread": continue @@ -62,21 +67,40 @@ def doExitCode(): try: thread.join() except KeyboardInterrupt: - pass + doExitCode() else: print "All threads ended, cleaning up." - if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): + if os.path.isdir(os.path.join(dstroot,"sos_commands")): + os.system("/bin/rm -rf %s" % dstroot) + sys.exit(1) + + if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately" ## FIXME: Add thread-kill code (see FIXME below) -# os.kill(os.getpid(), signal.SIGKILL) - print "Threads dead, cleaning up." - if ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): + return + elif ( ( activeCount() > 1 ) and ( __breakHits__ > 2 ) ): + print "Multiple SIGTERMs, multiple threads, process suicides." + os.kill(os.getpid(), signal.SIGKILL) + elif ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): print "Multiple SIGTERMs, single thread, exiting without cleaning up." sys.exit(3) # FIXME: Add code here to clean up /tmp sys.exit("Abnormal exit") - + +def doException(type, value, tb): + if hasattr(sys, 'ps1') or not sys.stderr.isatty(): + # we are in interactive mode or we don't have a tty-like + # device, so we call the default hook + sys.__excepthook__(type, value, tb) + else: + import traceback, pdb + # we are NOT in interactive mode, print the exception... + traceback.print_exception(type, value, tb) + print + # ...then start the debugger in post-mortem mode. + pdb.pm() + # Handle any sort of exit signal cleanly # Currently, we intercept only sig 15 (TERM) signal.signal(signal.SIGTERM, exittermhandler) @@ -300,9 +324,10 @@ class XmlReport: # if debugging is enabled, allow plugins to raise exceptions if __cmdLineOpts__.debug: - __raisePlugins__ = 1 + sys.excepthook = doException + __raisePlugins__ = 1 else: - __raisePlugins__ = 0 + __raisePlugins__ = 0 def sosreport(): # pylint: disable-msg = R0912 @@ -311,6 +336,9 @@ def sosreport(): """ This is the top-level function that gathers and processes all sosreport information """ + + global loadedplugins, dstroot + loadedplugins = [] skippedplugins = [] alloptions = [] @@ -392,36 +420,31 @@ def sosreport(): if not plug[-3:] == '.py' or plugbase == "__init__": continue try: - #print "importing plugin: %s" % plugbase - try: - if policy.validatePlugin(pluginpath + plug): - pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) - else: - soslog.warning(_("plugin %s does not validate, skipping") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if plugbase in __cmdLineOpts__.noplugins: - soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: - soslog.log(logging.VERBOSE, _("plugin %s not specified in --only-plugins list") % plug) - skippedplugins.append((plugbase, pluginClass(plugbase, commons))) - continue - loadedplugins.append((plugbase, pluginClass(plugbase, commons))) - except: - soslog.warning(_("plugin %s does not install, skipping") % plug) - raise + if policy.validatePlugin(pluginpath + plug): + pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) + else: + soslog.warning(_("plugin %s does not validate, skipping") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if plugbase in __cmdLineOpts__.noplugins: + soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s not specified in --only-plugins list") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + loadedplugins.append((plugbase, pluginClass(plugbase, commons))) except: - soslog.warning(_("could not load plugin %s") % plug) + soslog.warning(_("plugin %s does not install, skipping") % plug) if __raisePlugins__: raise @@ -595,10 +618,12 @@ Press ENTER to continue, or CTRL-C to quit. for plugname, plug in loadedplugins: soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname) try: - plug.setup() + plug.setup() + except KeyboardInterrupt: + raise except: - if __raisePlugins__: - raise + if __raisePlugins__: + raise # Setup the progress bar if __cmdLineOpts__.progressbar: @@ -617,7 +642,7 @@ Press ENTER to continue, or CTRL-C to quit. # Call the collect method for each plugin plugrunning = Semaphore(2) for plugname, plug in loadedplugins: - soslog.log(logging.VERBOSE, "executing plugin %s" % plugname) + soslog.log(logging.VERBOSE, "requesting plugin %s" % plugname) try: if not __cmdLineOpts__.nomultithread: plug.copyStuff(threaded = True, semaphore = plugrunning) @@ -626,6 +651,8 @@ Press ENTER to continue, or CTRL-C to quit. if __cmdLineOpts__.progressbar: pbar.incAmount(plug.eta_weight) pbar.update() + except KeyboardInterrupt: + raise except: if __raisePlugins__: raise |