diff options
author | navid <navid@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2007-09-04 08:27:59 +0000 |
---|---|---|
committer | navid <navid@ef72aa8b-4018-0410-8976-d6e080ef94d8> | 2007-09-04 08:27:59 +0000 |
commit | 92bf7d380893d3e376a5e358f3d7fe03fec80aae (patch) | |
tree | 54b3457d1a525ffc5f6bb0da7ec8b3bd1d8a410d | |
parent | 347c67b5d8fd925216eebda502182b3d69094090 (diff) | |
download | sos-92bf7d380893d3e376a5e358f3d7fe03fec80aae.tar.gz |
Merged branch navid-dev r372:389 into the trunk.
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/sos/trunk@390 ef72aa8b-4018-0410-8976-d6e080ef94d8
-rw-r--r-- | src/README | 10 | ||||
-rwxr-xr-x | src/extras/htmlog | 407 | ||||
-rwxr-xr-x | src/lib/sos/helpers.py | 64 | ||||
-rw-r--r-- | src/lib/sos/plugins/autofs.py | 9 | ||||
-rw-r--r-- | src/lib/sos/plugins/cluster.py | 52 | ||||
-rw-r--r-- | src/lib/sos/plugins/devicemapper.py | 22 | ||||
-rw-r--r-- | src/lib/sos/plugins/filesys.py | 24 | ||||
-rw-r--r-- | src/lib/sos/plugins/general.py | 4 | ||||
-rw-r--r-- | src/lib/sos/plugins/kernel.py | 4 | ||||
-rw-r--r-- | src/lib/sos/plugins/named.py | 32 | ||||
-rw-r--r-- | src/lib/sos/plugins/networking.py | 2 | ||||
-rw-r--r-- | src/lib/sos/plugins/rpm.py | 4 | ||||
-rw-r--r-- | src/lib/sos/plugins/squid.py | 6 | ||||
-rw-r--r-- | src/lib/sos/plugins/yum.py | 8 | ||||
-rw-r--r-- | src/lib/sos/plugintools.py | 14 | ||||
-rwxr-xr-x | src/lib/sos/policyredhat.py | 126 | ||||
-rw-r--r-- | src/setup.py | 2 | ||||
-rw-r--r-- | src/sos.spec | 3 | ||||
-rwxr-xr-x | src/sosreport | 80 | ||||
-rw-r--r-- | src/sosreport.1 | 16 |
20 files changed, 534 insertions, 355 deletions
@@ -9,23 +9,23 @@ to contribute, and for more information, please visit there. To access to the public source code repository for this project run: - svn checkout https://sos.108.redhat.com/svn/sos/trunk sos + svn export http://svn.fedoraproject.org/svn/hosted/sos/trunk sos --username guest -(all the following as root) -to install locally ==> make install +to install locally (as root) ==> make install to build an rpm ==> make rpm See the Makefile. -Maintainers: +Maintainer: Navid Sheikhol-Eslami <navid@redhat.com> -Contributors: +Developers and Contributors: Steve Conklin <sconklin@redhat.com> Pierre Amadio <pamadio@redhat.com> John Berninger <jwb@redhat.com> + Adam Stokes <astokes@redhat.com> Thanks to: diff --git a/src/extras/htmlog b/src/extras/htmlog index 387a5d90..87d9e9c1 100755 --- a/src/extras/htmlog +++ b/src/extras/htmlog @@ -2,7 +2,7 @@ #from optparse import OptionParser, Option import time, sys, os, glob -import getopt +import getopt, re #__cmdParser__ = OptionParser() #__cmdParser__.add_option("-i", "--input", action="append", @@ -13,6 +13,178 @@ import getopt # help="How obnoxious we're being about telling the user what we're doing.") #(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args() +class htmlgen_class: + + def __init__(self): + self.html_fname_base = "/tmp/something" + self.page_num = 0 + self.page_line = 0 + self.page_fp = None + self.cur_time = None + self.old_time = None + + self.summary_fp = open(self.html_fname_base + ".html", "w") + self.summary_fp.write("<html><body><ul>") + + def new_page(self): + + # close previous page + if self.page_fp: + self.close_page() + + self.page_fp = open( "%s_%d.html" % (self.html_fname_base, self.page_num), "w") + self.page_num += 1 + self.page_line = 0 + + self.page_fp.write("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html> +<head> +<title></title> +<style type="text/css"> +<!-- + +body { + font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #4f6b72; + background: #E6EAE9; +} + +a { + color: #c75f3e; +} + +#mytable { + width: 97%; + padding: 0; + margin: 0; +} + +caption { + padding: 0 0 5px 0; + width: 97%; + font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + text-align: right; +} + +th { + font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #4f6b72; + letter-spacing: 2px; + text-transform: uppercase; + text-align: left; + padding: 6px 6px 6px 12px; + background: #a50000 url(images/bg_header.jpg) no-repeat; + color: white; +} + +th.nobg { + border-top: 0; + border-left: 0; + border-right: 1px solid #C1DAD7; + background: none; + color: black; +} + +td { + border-right: 1px solid #C1DAD7; + border-bottom: 1px solid #C1DAD7; + background: #fff; + padding: 6px 6px 6px 12px; + color: #4f6b72; + letter-spacing: -1px; + white-space:pre-wrap; + font-family: monospace; +} + +pre { + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +td.alt { + background: #ffbaba; + color: #797268; +} + +th.newday { + text-align: right; + padding: 2px; +} + +th.spec { + border-left: 1px solid #C1DAD7; + border-right: 1px solid #C1DAD7; + border-top: 0; + background: #fff url(images/bullet1.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #797268; +} + +th.specalt { + border-left: 1px solid #C1DAD7; + border-right: 1px solid #C1DAD7; + border-top: 0; + background: #cecfce url(images/bullet2.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #797268; +} + +--> +</style> + +</head> +<body> + +<table id="mytable" cellspacing="0" summary="The technical specifications of the Apple PowerMac G5 series"> +<caption>Generated by SOS htmlogger 1.0 on March 16th 2008</caption> +""") + + def new_line(self, new_time, events = []): + self.old_time = self.cur_time + self.cur_time = new_time + + # close previous line + if self.page_line > 0: + self.page_fp.write("</TR>") + + if self.page_line == 0 or self.page_line % 200 == 0: + self.page_fp.write("""<tr><th scope="col" abbr="Date/Time" class="nobg">Time</th><th scope="col" abbr="node-1">node-1</th><th scope="col" abbr="node-2">node-2</th><th scope="col" abbr="node-3">node-3</th></tr>""") + + if self.page_line == 0 or self.page_line % 200 == 0 or time.strftime("%b %d", self.old_time) != time.strftime("%b %d", new_time): + self.page_fp.write("""<tr><th scope="row" class="newday" colspan=4>%s</th></tr>""" % time.strftime("%A, %B %d", new_time)) + + if self.page_line % 2 == 0: row_class = "spec" + else: row_class = "specalt" + + if len(events) > 0: + stime = """<a name="row_%d">%s</a>""" % (self.page_line, time.strftime("%H:%M:%S", new_time)) + for event in events: + self.summary_fp.write('<li><a href="something_%d.html#row_%s">%s</a>' % (self.page_num-1,self.page_line,event) ) + else: + stime = time.strftime("%H:%M:%S", new_time) + + if not self.old_time or self.old_time != new_time: + self.page_fp.write("""<TR><th scope="row" class="%s">%s</th>""" % (row_class, stime)) + else: + self.page_fp.write("""<TR><th scope="row" class="%s" style="color: #cacaca">%s</th>""" % (row_class, stime)) + + self.page_line+=1 + + def new_tab(self,msg): + self.page_fp.write(" <TD>" + msg + "</TD>\n") + + def close_page(self): + self.page_fp.write("</table></html>\n") + self.page_fp.close() + + def finish(self): + self.close_page() + class host_class: def __init__(self): @@ -21,6 +193,8 @@ class host_class: self.log_idx = 0 # first log self.log_ptr = 0 # first char + self.cur_line = None + def add_log(self, logfile): # if not logfile == logfile_class: # raise "InvalidLogfile" @@ -74,6 +248,7 @@ class host_class: raise "Off_Boundaries" def time(self): + return time.strptime(self.cur_line[0:15], "%b %d %H:%M:%S") pos = self.tell() try: toret = time.strptime(self.readline()[0:15], "%b %d %H:%M:%S") @@ -85,6 +260,9 @@ class host_class: def fp(self): return self.logs[self.log_idx] + def backline(self): + self.seek(-len(self.cur_line), 1) + def readline(self): if self.eof(): return "" @@ -100,6 +278,7 @@ class host_class: return "" if self.validate_line(toret) or toret == "": + self.cur_line = toret return toret else: print "invalid line" @@ -111,10 +290,12 @@ class host_class: return False return True - def readmsg(self): - toret = self.readline() - if toret: - return toret[18:] + def cur_msg(self): + if not hasattr(self,"_regex_cur_msg"): + self._regex_cur_msg = re.compile(r"""^.* %s (\S+:.*)$""" % self.hostname()) + + try: return self._regex_cur_msg.findall(self.cur_line)[0] + except: return self.cur_line class logfile_class: @@ -161,38 +342,8 @@ class logfile_class: def tell(self): return self.fp.tell() - def parse(self): - self.seek(0) - while not self.eof: - self.readline() - self.parse_line() - self.seek(0) - - def parse_line(self): - - # is valid log line ? - if not self.time_current(): - return - - # store hostname, if we don't already have it - if len(self.hostname) == 0: - self.curline.split()[3] - - # system is booting - if self.curmessage().startswith("kernel: Linux version"): - self.add_event("system boot") - - # hostname has changed - if len(self.hostname) and self.hostname != self.curline.split()[3]: - self.add_event("hostname changed") - self.hostname = self.curline.split()[3] - - # the clock is wrong wrong - if self.prevline and time.strptime(self.prevline[0:15], "%b %d %H:%M:%S") > self.time_current(): - self.add_event("clock is messed up") - - def add_event(self, message): - self.events.append( (self.curline_pos,len(self.curline),message) ) + def validate_line(self): + return self.time_current(self.curmessage()) def time_current(self): if len(self.curline) == 0: return None @@ -239,136 +390,15 @@ for logname in cmdline["logfiles"]: sys.stderr.write("finished adding logs\n") -#print hosts["moka"].readline() -#print hosts["moka"].readline() -#print "DIRECT", hosts["moka"].fp().fp.tell() -#print "HOST TELL", hosts["moka"].tell() -#print hosts["moka"].readline() -#print hosts["moka"].time() -#print hosts["moka"].readline() -#print hosts["moka"].time() -#print hosts["moka"].readline() - -print """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" - "http://www.w3.org/TR/html4/strict.dtd"> - -<html> -<head> -<title></title> -<style type="text/css"> -<!-- - -body { - font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; - color: #4f6b72; - background: #E6EAE9; -} - -a { - color: #c75f3e; -} - -#mytable { - width: 97%; - padding: 0; - margin: 0; -} - -caption { - padding: 0 0 5px 0; - width: 97%; - font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; - text-align: right; -} - -th { - font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; - color: #4f6b72; - letter-spacing: 2px; - text-transform: uppercase; - text-align: left; - padding: 6px 6px 6px 12px; - background: #a50000 url(images/bg_header.jpg) no-repeat; - color: white; -} - -th.nobg { - border-top: 0; - border-left: 0; - border-right: 1px solid #C1DAD7; - background: none; - color: black; -} - -td { - border-right: 1px solid #C1DAD7; - border-bottom: 1px solid #C1DAD7; - background: #fff; - padding: 6px 6px 6px 12px; - color: #4f6b72; - letter-spacing: -1px; - white-space:pre-wrap; - font-family: monospace; -} - -pre { - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} - -td.alt { - background: #ffbaba; - color: #797268; -} - -th.newday { - text-align: right; - padding: 2px; -} - -th.spec { - border-left: 1px solid #C1DAD7; - border-right: 1px solid #C1DAD7; - border-top: 0; - background: #fff url(images/bullet1.gif) no-repeat; - font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; - color: #797268; -} - -th.specalt { - border-left: 1px solid #C1DAD7; - border-right: 1px solid #C1DAD7; - border-top: 0; - background: #cecfce url(images/bullet2.gif) no-repeat; - font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; - color: #797268; -} - ---> -</style> - -</head> -<body> -""" - #print '<ul id="toc">' #for log in logs: # print logs[idx].fname, logs[idx].events # for line, msglen, event in log.events: # print ' <li><span>%s</span> <a href="#">Link</a><br /></li>' % event -print '</ul>' - -print """ -<table id="mytable" cellspacing="0" summary="The technical specifications of the Apple PowerMac G5 series"> -<caption>Generated by SOS htmlogger 1.0 on March 16th 2008</caption> -""" +htmlgen = htmlgen_class() previous_date = None -inc = 0 while True: # who is next ? @@ -377,6 +407,8 @@ while True: if hosts[host].eof(): continue + hosts[host].readline() + if lowest_date == None or hosts[host].time() < lowest_date: lowest_date = hosts[host].time() @@ -384,40 +416,49 @@ while True: # all logs are EOF break - if inc == 0 or inc % 200 == 0: - print """<tr><th scope="col" abbr="Date/Time" class="nobg">Time</th><th scope="col" abbr="node-1">node-1</th><th scope="col" abbr="node-2">node-2</th><th scope="col" abbr="node-3">node-3</th></tr>""" + events = [] + for host in hosts: + if hosts[host].time() == lowest_date: - if not previous_date or time.strftime("%b %d", previous_date) != time.strftime("%b %d", lowest_date): - print """<tr><th scope="row" class="newday" colspan=4>%s</th></tr>""" % time.strftime("%A, %B %d", lowest_date) + if re.match(r'^kernel: Linux version', hosts[host].cur_msg()): + events.append("%s rebooted" % host) - if inc % 2 == 0: row_class = "spec" - else: row_class = "specalt" + elif re.match(r'^.*fencing node', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) - # FIXME: if this tick has an event, add <a name="..."> - if not previous_date or previous_date != lowest_date: - print """<TR><th scope="row" class="%s">%s</th>""" % (row_class, time.strftime("%H:%M:%S", lowest_date) ) - else: - print """<TR><th scope="row" class="%s" style="color: #cacaca">%s</th>""" % (row_class, time.strftime("%H:%M:%S", lowest_date) ) + elif re.match(r'.*fence ".*" success', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) - for host in hosts: - if hosts[host].time() == lowest_date: -# print " <TD>%d</TD>" % hosts[host].tell() - print " <TD>" + hosts[host].readmsg() + "</TD>" -# print " <TD>%d</TD>" % hosts[host].tell() - else: - print " <TD></TD>" - print " </TR>" + elif re.match(r'.*fence ".*" failed', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) + + elif re.match(r'.*quorum lost, blocking activity', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) + + elif re.match(r'.*quorum regained, resuming activity', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) + + elif re.match(r'.*segfault at', hosts[host].cur_msg()): + events.append(host + " " + hosts[host].cur_msg()) - previous_date = lowest_date - inc += 1 + elif not hosts[host].eof(): + hosts[host].backline() -print "</table>" - + if len(events): print time.strftime("%A %B %d %H:%M:%S", lowest_date), events - + if htmlgen.page_line == 0 or htmlgen.page_line > 10000: + print "creating new page #%d" % htmlgen.page_num + htmlgen.new_page() + htmlgen.new_line(lowest_date, events) + for host in hosts: + if hosts[host].time() == lowest_date: + htmlgen.new_tab(hosts[host].cur_msg()) + else: + htmlgen.new_tab("") +htmlgen.close_page() diff --git a/src/lib/sos/helpers.py b/src/lib/sos/helpers.py index bc9c51ff..7f2e335d 100755 --- a/src/lib/sos/helpers.py +++ b/src/lib/sos/helpers.py @@ -25,8 +25,8 @@ """ helper functions used by sosreport and plugins """ -import os, popen2, fcntl, select, itertools, sys, commands, logging -from time import time +import os, popen2, fcntl, select, itertools, sys, commands, logging, signal +from time import time, sleep from tempfile import mkdtemp def importPlugin(pluginname, name): @@ -57,7 +57,7 @@ def makeNonBlocking(afd): fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY) -def sosGetCommandOutput(command): +def sosGetCommandOutput(command, timeout = 300): """ Execute a command and gather stdin, stdout, and return status. """ soslog = logging.getLogger('sos') @@ -73,14 +73,49 @@ def sosGetCommandOutput(command): soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable" % cmdfile) return (127, "", 0) - stime = time() - inpipe, pipe = os.popen4(command, 'r') - inpipe.close() - text = pipe.read() - sts = pipe.close() - if sts is None: sts = 0 - if text[-1:] == '\n': text = text[:-1] - return (sts, text, time()-stime) + # these are file descriptors, not file objects + r, w = os.pipe() + + pid = os.fork() + + if pid: + # we are the parent + os.close(w) # use os.close() to close a file descriptor + r = os.fdopen(r) # turn r into a file object + stime=time() + txt = "" + while True: + # read output from pipe + txt = txt + r.read() + # is child still running ? + try: os.waitpid(pid, os.WNOHANG) + except: break + # has 5 secs timeout passed ? + if time() - stime > timeout: + soslog.log(logging.VERBOSE, 'killing hung child with pid %s after %d seconds (command was "%s")' % (pid,timeout,command) ) + os.kill(pid, signal.SIGKILL) + break + sleep(0.1) + # make sure the child process gets cleaned up + try: sts = os.waitpid(pid, 0)[1] + except: sts = -1 + if txt[-1:] == '\n': txt = txt[:-1] + return (sts, txt, time()-stime) + else: + # we are the child + os.dup2(r, 0) + os.dup2(w, 1) + os.dup2(w, 2) + + import resource + maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] + if (maxfd == resource.RLIM_INFINITY): + maxfd = MAXFD + for fd in range(3, maxfd): + try: os.close(fd) + except OSError: pass + os.execl("/bin/sh", "/bin/sh", "-c", command) + os._exit(127) # FIXME: this needs to be made clean and moved to the plugin tools, so # that it prints nice color output like sysreport if the progress bar @@ -136,3 +171,10 @@ def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir): if not common: return path2 # leave path absolute if nothing at all in common return sep.join( [pardir]*len(u1) + u2 ) + +def sosReadFile(fname): + ''' reads a file and returns its contents''' + fp = open(fname,"r") + content = fp.read() + fp.close() + return content diff --git a/src/lib/sos/plugins/autofs.py b/src/lib/sos/plugins/autofs.py index 2cb22767..5e8be418 100644 --- a/src/lib/sos/plugins/autofs.py +++ b/src/lib/sos/plugins/autofs.py @@ -51,13 +51,8 @@ class autofs(sos.plugintools.PluginBase): def setup(self): self.addCopySpec("/etc/auto*") self.addCopySpec("/etc/sysconfig/autofs") - self.addCopySpec("/etc/init.d/autofs") - self.collectExtOutput("/bin/rpm -qV autofs") - self.collectExtOutput("/etc/init.d/autofs status") - self.collectExtOutput("ps auxwww | grep automount") - self.collectExtOutput("/bin/egrep -e 'automount|pid.*nfs' /proc/mounts") - self.collectExtOutput("/bin/mount | egrep -e 'automount|pid.*nfs'") - self.collectExtOutput("/sbin/chkconfig --list autofs") + self.addCopySpec("/etc/rc.d/init.d/autofs") + self.collectExtOutput("service autofs status") # if debugging to file is enabled, grab that file too daemon_debug_file = self.getdaemondebug() diff --git a/src/lib/sos/plugins/cluster.py b/src/lib/sos/plugins/cluster.py index 6067f9c4..280ad3b1 100644 --- a/src/lib/sos/plugins/cluster.py +++ b/src/lib/sos/plugins/cluster.py @@ -25,31 +25,21 @@ class cluster(sos.plugintools.PluginBase): ('taskdump', 'trigger 3 sysrq+t dumps every 5 seconds (dangerous)', 'slow', False)] def checkenabled(self): - # enable if any related package is installed - rhelver = self.cInfo["policy"].rhelVersion() - if rhelver == 4: - pkgs_to_check = [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", - "rgmanager", "fence", "dlm", "dlm-kernel", "gulm", - "GFS", "GFS-kernel", "lvm2-cluster" ] - elif rhelver == 5: - pkgs_to_check = [ "rgmanager", "luci", "ricci", "system-config-cluster", - "gfs-utils", "gnbd", "kmod-gfs", "kmod-gnbd", "lvm2-cluster" ] - else: - # can't guess what RHEL version we are running - pkgs_to_check = [] - - for pkg in pkgs_to_check: - if self.cInfo["policy"].pkgByName(pkg) != None: - return True - - # enable if any related file is present - for fname in [ "/etc/cluster/cluster.conf", "/proc/cluster" ]: - try: os.stat(fname) - except:pass - else: return True - - # no data related to RHCS/GFS exists - return False + self.files = [ "/etc/yum.conf" ] + self.packages = [ "yum" ] + + def checkenabled(self): + rhelver = self.cInfo["policy"].rhelVersion() + if rhelver == 4: + self.packages = [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", + "rgmanager", "fence", "dlm", "dlm-kernel", "gulm", + "GFS", "GFS-kernel", "lvm2-cluster" ] + elif rhelver == 5: + self.packages = [ "rgmanager", "luci", "ricci", "system-config-cluster", + "gfs-utils", "gnbd", "kmod-gfs", "kmod-gnbd", "lvm2-cluster" ] + + self.files = [ "/etc/cluster/cluster.conf", "/proc/cluster" ] + return sos.plugintools.PluginBase.checkenabled(self) def has_gfs(self): try: @@ -109,6 +99,8 @@ class cluster(sos.plugintools.PluginBase): break except IndexError: pass + if found == 2: + break if found == 0: self.addDiagnose("required kernel package is missing: %s" % pkgname) @@ -190,6 +182,10 @@ class cluster(sos.plugintools.PluginBase): if status == 0 and conf_version != cluster_version: self.addDiagnose("cluster.conf and in-memory configuration version differ (%s != %s)" % (conf_version, cluster_version) ) + status, output = commands.getstatusoutput("/usr/sbin/rg_test test /etc/cluster/cluster.conf") + if output.find("Error: ") > 0: + self.addDiagnose("configuration errors are present according to rg_test") + # make sure the first part of the lock table matches the cluster name # and that the locking protocol is sane cluster_name = xpathContext.xpathEval("/cluster/@name")[0].content @@ -223,9 +219,9 @@ class cluster(sos.plugintools.PluginBase): self.collectExtOutput("/sbin/ipvsadm -L") - if self.isOptionEnabled('gfslockdump'): self.do_gfslockdump() - if self.isOptionEnabled('lockdump'): self.do_lockdump() - if self.isOptionEnabled('taskdump'): self.do_taskdump() + if self.getOption('gfslockdump'): self.do_gfslockdump() + if self.getOption('lockdump'): self.do_lockdump() + if self.getOption('taskdump'): self.do_taskdump() return diff --git a/src/lib/sos/plugins/devicemapper.py b/src/lib/sos/plugins/devicemapper.py index a670cae7..46662c70 100644 --- a/src/lib/sos/plugins/devicemapper.py +++ b/src/lib/sos/plugins/devicemapper.py @@ -13,19 +13,27 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools +import os +from sos.helpers import sosGetCommandOutput class devicemapper(sos.plugintools.PluginBase): """device-mapper related information (dm, lvm, multipath) """ + + optionList = [("lvmdump", 'collect raw metadata from PVs', 'slow', False)] + + def do_lvmdump(self): + """Collects raw metadata directly from the PVs using dd + """ + sosGetCommandOutput("lvmdump -d %s" % os.path.join(self.cInfo['dstroot'],"lvmdump"), 120) + def setup(self): self.collectExtOutput("/sbin/dmsetup info -c") self.collectExtOutput("/sbin/dmsetup table") self.collectExtOutput("/sbin/dmsetup status") - self.collectExtOutput("/usr/bin/systool -v -C -b scsi") - - self.collectExtOutput("/usr/sbin/vgscan -vvv") self.collectExtOutput("/usr/sbin/vgdisplay -vv", root_symlink = "vgdisplay") + self.collectExtOutput("/usr/sbin/vgscan -vvv") self.collectExtOutput("/usr/sbin/pvscan -v") self.collectExtOutput("/usr/sbin/lvs -a -o +devices") self.collectExtOutput("/usr/sbin/pvs -a -v") @@ -37,4 +45,12 @@ class devicemapper(sos.plugintools.PluginBase): self.addCopySpec("/var/lib/multipath/bindings") self.collectExtOutput("/sbin/multipath -v4 -ll") + self.collectExtOutput("/usr/bin/systool -v -C -b scsi") + + self.collectExtOutput("/bin/ls -laR /dev") + self.collectExtOutput("/bin/ls -laR /sys/block") + + if self.getOption('lvmdump'): + self.do_lvmdump() + return diff --git a/src/lib/sos/plugins/filesys.py b/src/lib/sos/plugins/filesys.py index 73bd3d88..46a3506d 100644 --- a/src/lib/sos/plugins/filesys.py +++ b/src/lib/sos/plugins/filesys.py @@ -13,7 +13,8 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import sos.plugintools -import commands +from sos.helpers import sosReadFile +import os class filesys(sos.plugintools.PluginBase): """information on filesystems @@ -25,21 +26,18 @@ class filesys(sos.plugintools.PluginBase): self.addCopySpec("/proc/mounts") self.addCopySpec("/proc/mdstat") self.addCopySpec("/etc/raidtab") + mounts = self.collectOutputNow("/bin/mount -l", root_symlink = "mount") self.addCopySpec("/etc/mdadm.conf") self.collectExtOutput("/bin/df -al", root_symlink = "df") - self.collectExtOutput("/bin/mount -l", root_symlink = "mount") self.collectExtOutput("/sbin/blkid") - raiddevs = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep \/ | /bin/egrep -v \"p[0123456789]$\"") - disks = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep -v / | /bin/egrep -v \"[0123456789]$\"") - for disk in raiddevs.split('\n'): - if '' != disk.strip(): - self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) - self.collectExtOutput("/sbin/tune2fs -l /dev/%s" % (disk,)) - for disk in disks.split('\n'): - if '' != disk.strip(): - self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) - self.collectExtOutput("/sbin/tune2fs -l /dev/%s" % (disk,)) - return + for disk in os.listdir("/sys/block"): + if disk in [ ".", ".." ] or disk.startswith("ram") or sosReadFile("/sys/block/%s/removable" % disk ).strip() == "1": + continue + self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk)) + + for extfs in self.doRegexFindAll(r"^(/dev/.+) on .+ type ext.\s+", mounts): + self.collectExtOutput("/sbin/tune2fs -l %s" % (extfs)) + return diff --git a/src/lib/sos/plugins/general.py b/src/lib/sos/plugins/general.py index e4be4941..6f1f912c 100644 --- a/src/lib/sos/plugins/general.py +++ b/src/lib/sos/plugins/general.py @@ -28,9 +28,9 @@ class general(sos.plugintools.PluginBase): self.addCopySpec("/proc/stat") self.addCopySpec("/var/log/dmesg") self.addCopySpec("/var/log/messages") - self.addCopySpecLimit("/var/log/messages.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpecLimit("/var/log/messages.*", sizelimit = self.getOption("syslogsize")) self.addCopySpec("/var/log/secure") - self.addCopySpecLimit("/var/log/secure.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpecLimit("/var/log/secure.*", sizelimit = self.getOption("syslogsize")) self.addCopySpec("/var/log/sa") self.addCopySpec("/var/log/up2date") self.collectExtOutput("/bin/hostname", root_symlink = "hostname") diff --git a/src/lib/sos/plugins/kernel.py b/src/lib/sos/plugins/kernel.py index a5ffd855..f2e6def3 100644 --- a/src/lib/sos/plugins/kernel.py +++ b/src/lib/sos/plugins/kernel.py @@ -50,7 +50,7 @@ class kernel(sos.plugintools.PluginBase): self.collectExtOutput("/bin/uname -a", root_symlink = "uname") self.moduleFile = self.collectOutputNow("/sbin/lsmod", root_symlink = "lsmod") - if self.isOptionEnabled('modinfo'): + if self.getOption('modinfo'): runcmd = "" for kmod in commands.getoutput('/sbin/lsmod | /bin/cut -f1 -d" " 2>/dev/null | /bin/grep -v Module 2>/dev/null').split('\n'): if '' != kmod.strip(): @@ -75,7 +75,7 @@ class kernel(sos.plugintools.PluginBase): self.addCopySpec("/proc/driver") self.addCopySpec("/proc/sys/kernel/tainted") - if self.isOptionEnabled('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK): + if self.getOption('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK): for key in ['m', 'p', 't']: commands.getoutput("/bin/echo %s > /proc/sysrq-trigger" % (key,)) self.addCopySpec("/var/log/messages") diff --git a/src/lib/sos/plugins/named.py b/src/lib/sos/plugins/named.py index 421143cb..f4d24e19 100644 --- a/src/lib/sos/plugins/named.py +++ b/src/lib/sos/plugins/named.py @@ -20,21 +20,21 @@ class named(sos.plugintools.PluginBase): """named related information """ def checkenabled(self): - if self.cInfo["policy"].pkgByName("bind") or os.path.exists("/etc/named.conf") or os.path.exists("/etc/sysconfig/named"): - return True - return False + self.files = [ "/etc/named.conf", "/etc/sysconfig/named" ] + self.packages = [ "bind" ] + return sos.plugintools.PluginBase.checkenabled(self) def setup(self): - dnsdir = "" - self.addCopySpec("/etc/named.boot") - self.addCopySpec("/etc/named.conf") - self.addCopySpec("/etc/sysconfig/named") - if os.access("/etc/named.conf", os.R_OK): - dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.conf | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") - if os.access("/etc/named.boot", os.R_OK): - dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.boot | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") - if '' != dnsdir.strip(): - self.addCopySpec(dnsdir) - self.addForbiddenPath('/var/named/chroot/proc') - self.addForbiddenPath('/var/named/chroot/dev') - return + dnsdir = "" + self.addCopySpec("/etc/named.boot") + self.addCopySpec("/etc/named.conf") + self.addCopySpec("/etc/sysconfig/named") + # FIXME: use internal fileGrep() instead + if os.access("/etc/named.conf", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.conf | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if os.access("/etc/named.boot", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.boot | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if '' != dnsdir.strip(): + self.addCopySpec(dnsdir) + self.addForbiddenPath('/var/named/chroot/proc') + self.addForbiddenPath('/var/named/chroot/dev') diff --git a/src/lib/sos/plugins/networking.py b/src/lib/sos/plugins/networking.py index aaf78234..164ed43a 100644 --- a/src/lib/sos/plugins/networking.py +++ b/src/lib/sos/plugins/networking.py @@ -65,7 +65,7 @@ class networking(sos.plugintools.PluginBase): if ifconfigFile: for eth in self.get_interface_name(ifconfigFile): self.collectExtOutput("/sbin/ethtool "+eth) - if self.isOptionEnabled("traceroute"): + if self.getOption("traceroute"): self.collectExtOutput("/bin/traceroute -n rhn.redhat.com") return diff --git a/src/lib/sos/plugins/rpm.py b/src/lib/sos/plugins/rpm.py index bcddaccb..279dde4c 100644 --- a/src/lib/sos/plugins/rpm.py +++ b/src/lib/sos/plugins/rpm.py @@ -23,10 +23,10 @@ class rpm(sos.plugintools.PluginBase): def setup(self): self.addCopySpec("/var/log/rpmpkgs") - if self.isOptionEnabled("rpmq"): + if self.getOption("rpmq"): self.collectExtOutput("/bin/rpm -qa --qf \"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}\n\"", root_symlink = "installed-rpms") - if self.isOptionEnabled("rpmva"): + if self.getOption("rpmva"): self.eta_weight += 800 # this plugins takes 200x longer (for ETA) self.collectExtOutput("/bin/rpm -Va", root_symlink = "rpm-Va") return diff --git a/src/lib/sos/plugins/squid.py b/src/lib/sos/plugins/squid.py index 7e0c3376..3e9b3d8b 100644 --- a/src/lib/sos/plugins/squid.py +++ b/src/lib/sos/plugins/squid.py @@ -18,8 +18,10 @@ import os class squid(sos.plugintools.PluginBase): """squid related information """ - files = [ "/etc/squid/squid.conf" ] - packages = [ "squid" ] + def checkenabled(self): + self.files = [ "/etc/squid/squid.conf" ] + self.packages = [ "squid" ] + return sos.plugintools.PluginBase.checkenabled(self) def setup(self): self.addCopySpec("/etc/squid/squid.conf") diff --git a/src/lib/sos/plugins/yum.py b/src/lib/sos/plugins/yum.py index 0f0d049e..672451ee 100644 --- a/src/lib/sos/plugins/yum.py +++ b/src/lib/sos/plugins/yum.py @@ -22,9 +22,9 @@ class yum(sos.plugintools.PluginBase): optionList = [("yumlist", "list repositories and packages", "slow", False)] def checkenabled(self): - if self.cInfo["policy"].pkgByName("yum") or os.path.exists("/etc/yum.conf"): - return True - return False + self.files = [ "/etc/yum.conf" ] + self.packages = [ "yum" ] + return sos.plugintools.PluginBase.checkenabled(self) def analyze(self): # repo sanity checking @@ -42,7 +42,7 @@ class yum(sos.plugintools.PluginBase): self.addCopySpec("/etc/yum.conf") self.addCopySpec("/var/log/yum.log") - if self.isOptionEnabled("yumlist"): + if self.getOption("yumlist"): # Get a list of channels the machine is subscribed to. self.collectExtOutput("/bin/echo \"repo list\" | /usr/bin/yum shell") # List various information about available packages diff --git a/src/lib/sos/plugintools.py b/src/lib/sos/plugintools.py index f678297f..303e4fc5 100644 --- a/src/lib/sos/plugintools.py +++ b/src/lib/sos/plugintools.py @@ -233,14 +233,22 @@ class PluginBase: """ return (self.optNames, self.optParms) - def setOption(self, optionname, enable): - ''' enable or disable the named option. + def setOption(self, optionname, value): + ''' set the named option to value. ''' for name, parms in zip(self.optNames, self.optParms): if name == optionname: - parms['enabled'] = enable + parms['enabled'] = value + return True + else: + return False def isOptionEnabled(self, optionname): + ''' Deprecated, use getOption() instead + ''' + return self.getOption(optionname) + + def getOption(self, optionname): ''' see whether the named option is enabled. ''' for name, parms in zip(self.optNames, self.optParms): diff --git a/src/lib/sos/policyredhat.py b/src/lib/sos/policyredhat.py index 9f61e68b..38ded430 100755 --- a/src/lib/sos/policyredhat.py +++ b/src/lib/sos/policyredhat.py @@ -27,6 +27,16 @@ import random import re import md5 import rpm +import time + +sys.path.insert(0, "/usr/share/rhn/") +try: + from up2date_client import up2dateAuth + from up2date_client import config + from rhn import rpclib +except: + # might fail if non-RHEL + pass #class SosError(Exception): # def __init__(self, code, message): @@ -73,9 +83,6 @@ class SosPolicy: pkg = self.pkgByName(name) return pkg['requirename'] - cmd = "/bin/rpm -q --requires %s" % (name) - return [requires[:-1].split() for requires in os.popen(cmd).readlines()] - def allPkgsByName(self, name): return self.allPkgs("name", name) @@ -101,14 +108,20 @@ class SosPolicy: return None def allPkgs(self, ds = None, value = None): + # if possible return the cached values + try: return self._cache_rpm[ "%s-%s" % (ds,value) ] + except AttributeError: self._cache_rpm = {} + except KeyError: pass + ts = rpm.TransactionSet() if ds and value: mi = ts.dbMatch(ds, value) else: mi = ts.dbMatch() - toret = [pkg for pkg in mi] + + self._cache_rpm[ "%s-%s" % (ds,value) ] = [pkg for pkg in mi] del mi, ts - return toret + return self._cache_rpm[ "%s-%s" % (ds,value) ] def runlevelByService(self, name): ret = [] @@ -136,6 +149,9 @@ class SosPolicy: def kernelVersion(self): return commands.getoutput("/bin/uname -r").strip("\n") + def hostName(self): + return commands.getoutput("/bin/hostname").split(".")[0] + def rhelVersion(self): try: pkgname = self.pkgByName("redhat-release")["version"] @@ -146,6 +162,15 @@ class SosPolicy: except: pass return False + def rhnUsername(self): + try: + cfg = config.initUp2dateConfig() + + return rpclib.xmlrpclib.loads(up2dateAuth.getSystemId())[0][0]['username'] + except: + # ignore any exception and return an empty username + return "" + def isKernelSMP(self): if commands.getoutput("/bin/uname -v").split()[1] == "SMP": return True @@ -158,10 +183,22 @@ class SosPolicy: name = "-".join(fields[:-3]) return (name, version, release, arch) + def getDstroot(self): + """Find a temp directory to form the root for our gathered information + and reports. + """ + dstroot = "/tmp/%s-%s" % (self.hostName(), time.strftime("%Y%m%d%H%M%S")) + try: + os.mkdir(dstroot, 0700) + except: + return False + return dstroot + def preWork(self): # this method will be called before the gathering begins - localname = commands.getoutput("/bin/uname -n").split(".")[0] + localname = self.rhnUsername() + if len(localname) == 0: localname = self.hostName() try: self.reportName = raw_input(_("Please enter your first initial and last name [%s]: ") % localname) @@ -178,70 +215,73 @@ class SosPolicy: def packageResults(self): - if len(self.ticketNumber): - namestr = self.reportName + "." + self.ticketNumber - else: - namestr = self.reportName - - ourtempdir = gettempdir() - tarballName = os.path.join(ourtempdir, "sosreport-" + namestr + ".tar.bz2") + self.report_file = os.path.join(gettempdir(), "sosreport-%s-%s.tar.bz2" % (self.reportName,time.strftime("%Y%m%d%H%M%S"))) - namestr = namestr + "-" + str(random.randint(1, 999999)) - - aliasdir = os.path.join(ourtempdir, namestr) - - tarcmd = "/bin/tar -jcf %s %s" % (tarballName, namestr) + tarcmd = "/bin/tar -jcf %s %s" % (self.report_file, os.path.basename(self.cInfo['dstroot'])) print _("Creating compressed archive...") - if not os.access(string.split(tarcmd)[0], os.X_OK): - print "Unable to create tarball" - return - # FIXME: gotta be a better way... - os.system("/bin/mv %s %s" % (self.cInfo['dstroot'], aliasdir)) curwd = os.getcwd() - os.chdir(ourtempdir) + os.chdir(os.path.dirname(self.cInfo['dstroot'])) oldmask = os.umask(077) - # pylint: disable-msg = W0612 status, shout, runtime = sosGetCommandOutput(tarcmd) os.umask(oldmask) os.chdir(curwd) - # FIXME: use python internal command - os.system("/bin/mv %s %s" % (aliasdir, self.cInfo['dstroot'])) - # FIXME: encrypt using gnupg - # gpg --trust-model always --batch --keyring /usr/share/sos/rhsupport.pub --no-default-keyring --compress-level 0 --encrypt --recipient support@redhat.com --output filename.gpg filename.tar + return - # add last 6 chars from md5sum to file name - fp = open(tarballName, "r") + def cleanDstroot(self): + if not os.path.isdir(os.path.join(self.cInfo['dstroot'],"sos_commands")): + # doesn't look like a dstroot, refusing to clean + return False + os.system("/bin/rm -rf %s" % self.cInfo['dstroot']) + + def encryptResults(self): + # make sure a report exists + if not self.report_file: + return False + + print _("Encrypting archive...") + gpgname = self.report_file + ".gpg" + status, output = commands.getstatusoutput("/usr/bin/gpg --trust-model always --batch --keyring /usr/share/sos/rhsupport.pub --no-default-keyring --compress-level 0 --encrypt --recipient support@redhat.com --output %s %s" % (gpgname,self.report_file)) + if status == 0: + os.unlink(self.report_file) + self.report_file = gpgname + else: + print _("There was a problem encrypting your report.") + sys.exit(1) + + def displayResults(self): + # make sure a report exists + if not self.report_file: + return False + + # calculate md5 + fp = open(self.report_file, "r") md5out = md5.new(fp.read()).hexdigest() fp.close() - oldtarballName = tarballName - tarballName = os.path.join(ourtempdir, "sosreport-%s-%s.tar.bz2" % (namestr, md5out[-6:]) ) - os.system("/bin/mv %s %s" % (oldtarballName, tarballName) ) + # store md5 to a file - fp = open(tarballName + ".md5", "w") + fp = open(self.report_file + ".md5", "w") fp.write(md5out + "\n") fp.close() - sys.stdout.write("\n") - print _("Your sosreport has been generated and saved in:\n %s") % tarballName + print + print _("Your sosreport has been generated and saved in:\n %s") % self.report_file print if md5out: print _("The md5sum is: ") + md5out print print _("Please send this file to your support representative.") - sys.stdout.write("\n") - - self.report_file = tarballName + print - return - def uploadResults(self): # make sure a report exists if not self.report_file: return False + print + # make sure it's readable try: fp = open(self.report_file, "r") @@ -261,7 +301,7 @@ class SosPolicy: except: print _("There was a problem uploading your report to Red Hat support.") else: - print _('Your report was uploaded successfully with name:') + print _("Your report was successfully uploaded to Red Hat's ftp server with name:") print " " + upload_name print print _("Please communicate this name to your support representative.") diff --git a/src/setup.py b/src/setup.py index b64c3593..3272531f 100644 --- a/src/setup.py +++ b/src/setup.py @@ -9,6 +9,6 @@ setup( packages = ['sos', 'sos.plugins'], scripts = [], package_dir = {'': 'lib',}, - data_files = [ ('/usr/sbin', ['sosreport', 'extras/sysreport/sysreport.legacy']), ('/usr/bin', ['extras/rh-upload-core']), ('/usr/share/sysreport', ['extras/sysreport/text.xsl', 'extras/sysreport/functions', 'extras/sysreport/sysreport-fdisk']), ('/usr/share/man/man1', ['sosreport.1']), ('/usr/share/locale/en', []), ('/usr/share/locale/it', []), ('/usr/share/locale/en/LC_MESSAGES', ['locale/en/LC_MESSAGES/sos.mo']), ('/usr/share/locale/it/LC_MESSAGES', ['locale/it/LC_MESSAGES/sos.mo']), ('/usr/share/locale/fr/LC_MESSAGES', ['locale/fr/LC_MESSAGES/sos.mo']), ('/usr/share/locale/ar/LC_MESSAGES', ['locale/ar/LC_MESSAGES/sos.mo']) + data_files = [ ('/usr/sbin', ['sosreport', 'extras/sysreport/sysreport.legacy']), ('/usr/bin', ['extras/rh-upload-core']), ('/usr/share/sos', ['gpgkeys/rhsupport.pub']), ('/usr/share/sysreport', ['extras/sysreport/text.xsl', 'extras/sysreport/functions', 'extras/sysreport/sysreport-fdisk']), ('/usr/share/man/man1', ['sosreport.1']), ('/usr/share/locale/en', []), ('/usr/share/locale/it', []), ('/usr/share/locale/en/LC_MESSAGES', ['locale/en/LC_MESSAGES/sos.mo']), ('/usr/share/locale/it/LC_MESSAGES', ['locale/it/LC_MESSAGES/sos.mo']), ('/usr/share/locale/fr/LC_MESSAGES', ['locale/fr/LC_MESSAGES/sos.mo']), ('/usr/share/locale/ar/LC_MESSAGES', ['locale/ar/LC_MESSAGES/sos.mo']) ] ) diff --git a/src/sos.spec b/src/sos.spec index 73182665..58a3f67b 100644 --- a/src/sos.spec +++ b/src/sos.spec @@ -2,7 +2,7 @@ %define name sos %define version 1.7 -%define release 10pre0 +%define release 10pre1 %define _localedir %_datadir/locale @@ -47,6 +47,7 @@ rm -rf ${RPM_BUILD_ROOT} %files %defattr(-,root,root,-) %{_sbindir}/sosreport +/usr/share/sos/rhsupport.pub /usr/bin/rh-upload-core /usr/sbin/sysreport /usr/sbin/sysreport.legacy diff --git a/src/sosreport b/src/sosreport index 8e9d2697..88e50a97 100755 --- a/src/sosreport +++ b/src/sosreport @@ -70,9 +70,7 @@ def doExitCode(): doExitCode() else: print "All threads ended, cleaning up." - if os.path.isdir(os.path.join(dstroot,"sos_commands")): - os.system("/bin/rm -rf %s" % dstroot) - sys.exit(1) + doExit(1) if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately" @@ -83,10 +81,15 @@ def doExitCode(): os.kill(os.getpid(), signal.SIGKILL) elif ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): print "Multiple SIGTERMs, single thread, exiting without cleaning up." - sys.exit(3) + doExit(3) # FIXME: Add code here to clean up /tmp - sys.exit("Abnormal exit") + doExit("Abnormal exit") + +def doExit(error=0): + global policy + policy.cleanDstroot() + sys.exit(error) def doException(type, value, tb): if hasattr(sys, 'ps1') or not sys.stderr.isatty(): @@ -157,6 +160,9 @@ __cmdParser__.add_option("-a", "--alloptions", action="store_true", \ __cmdParser__.add_option("-u", "--upload", action="store_true", \ dest="upload", default=False, \ help="upload the report to Red Hat support") +__cmdParser__.add_option("--encrypt", action="store_true", \ + dest="encrypt", default=False, \ + help="encrypt with GPG using Red Hat support's public key") __cmdParser__.add_option("-v", "--verbose", action="count", \ dest="verbosity", \ help="increase verbosity") @@ -337,7 +343,7 @@ def sosreport(): This is the top-level function that gathers and processes all sosreport information """ - global loadedplugins, dstroot + global loadedplugins, dstroot, policy loadedplugins = [] skippedplugins = [] @@ -354,7 +360,11 @@ def sosreport(): # Set up common info and create destinations - dstroot = sosFindTmpDir() + dstroot = policy.getDstroot() + if not dstroot: + print _("Could not create temporary directory.") + doExit() + cmddir = os.path.join(dstroot, "sos_commands") logdir = os.path.join(dstroot, "sos_logs") rptdir = os.path.join(dstroot, "sos_reports") @@ -411,8 +421,7 @@ def sosreport(): # generate list of available plugins plugins = os.listdir(pluginpath) plugins.sort() - - # FIXME: should at least print a warning if the user references a plugin which does not exist + plugin_names = [] # validate and load plugins for plug in plugins: @@ -426,6 +435,8 @@ def sosreport(): soslog.warning(_("plugin %s does not validate, skipping") % plug) skippedplugins.append((plugbase, pluginClass(plugbase, commons))) continue + # plug-in is valid, let's decide whether run it or not + plugin_names.append(plugbase) if plugbase in __cmdLineOpts__.noplugins: soslog.log(logging.VERBOSE, _("plugin %s skipped (--skip-plugins)") % plugbase) skippedplugins.append((plugbase, pluginClass(plugbase, commons))) @@ -473,7 +484,11 @@ def sosreport(): except: pass # split up "general.syslogsize" - plug, opt = opt.split(".") + try: + plug, opt = opt.split(".") + except: + plug = opt + opt = True try: opts[plug] except KeyError: opts[plug] = [] @@ -482,10 +497,26 @@ def sosreport(): for plugname, plug in loadedplugins: if opts.has_key(plugname): for opt,val in opts[plugname]: - soslog.log(logging.VERBOSE, "setting option %s for plugin %s to %s" % (plugname,opt,val)) - plug.setOption(opt,val) + soslog.log(logging.VERBOSE, 'setting option "%s" for plugin (%s) to "%s"' % (plugname,opt,val)) + if not plug.setOption(opt,val): + soslog.error('no such option "%s" for plugin (%s)' % (opt,plugname)) + doExit(1) + del opts[plugname] + for plugname in opts.keys(): + soslog.error('unable to set option for disabled or non-existing plugin (%s)' % (plugname)) + doExit(1) del opt,opts,val + # error if the user references a plugin which does not exist + unk_plugs = [plugname.split(".")[0] for plugname in __cmdLineOpts__.onlyplugins if not plugname.split(".")[0] in plugin_names] + unk_plugs += [plugname.split(".")[0] for plugname in __cmdLineOpts__.noplugins if not plugname.split(".")[0] in plugin_names] + unk_plugs += [plugname.split(".")[0] for plugname in __cmdLineOpts__.enableplugins if not plugname.split(".")[0] in plugin_names] + if len(unk_plugs): + for plugname in unk_plugs: + soslog.error('a non-existing plugin (%s) was specified in the command line' % (plugname)) + doExit(1) + del unk_plugs + for plugname, plug in loadedplugins: soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname) names, parms = plug.getAllOptions() @@ -499,7 +530,7 @@ def sosreport(): if __cmdLineOpts__.listPlugins: if not len(loadedplugins) and not len(skippedplugins): soslog.error(_("no valid plugins found")) - sys.exit(1) + doExit(1) # FIXME: make -l output more concise if len(loadedplugins): @@ -542,20 +573,20 @@ def sosreport(): print _("No plugin options available.") print - sys.exit() + doExit() # to go anywhere further than listing the plugins we will need root permissions. # if os.getuid() != 0: print _('sosreport requires root permissions to run.') - sys.exit(1) + doExit(1) # we don't need to keep in memory plugins we are not going to use del skippedplugins if not len(loadedplugins): soslog.error(_("no valid plugins were enabled")) - sys.exit(1) + doExit(1) try: raw_input(_("""This utility will collect some detailed information about the @@ -572,7 +603,7 @@ Press ENTER to continue, or CTRL-C to quit. """)) except: print - sys.exit(0) + doExit() # Call the diagnose() method for each plugin tmpcount = 0 @@ -606,11 +637,11 @@ Press ENTER to continue, or CTRL-C to quit. print break elif yorno == _("n") or yorno == _("N"): - sys.exit(0) + doExit(0) del yorno except KeyboardInterrupt: print - sys.exit(0) + doExit(0) policy.preWork() @@ -770,11 +801,18 @@ Press ENTER to continue, or CTRL-C to quit. # package up the results for the support organization policy.packageResults() + # delete gathered files - os.system("/bin/rm -rf %s" % dstroot) + policy.cleanDstroot() + + # let's encrypt the tar-ball + if __cmdLineOpts__.encrypt: + policy.encryptResults() # automated submission will go here - if __cmdLineOpts__.upload: + if not __cmdLineOpts__.upload: + policy.displayResults() + else: policy.uploadResults() # Close all log files and perform any cleanup diff --git a/src/sosreport.1 b/src/sosreport.1 index cd7d34df..3ae187a8 100644 --- a/src/sosreport.1 +++ b/src/sosreport.1 @@ -44,19 +44,21 @@ Do not display a progress bar (ETA will not be available). .TP .B \--no-multithread Disable multithreaded collection and analysis of the sosreport data. -.SH MAINTAINERS +.SH MAINTAINER .nf Navid Sheikhol-Eslami <navid@redhat.com> .fi -.SH TRANSLATIONS -.nf -Eva Schaller <eschalle@redhat.com> [Italian] -Imed Chihi <ichihi@redhat.com> [Arabic] [French] -.fi .SH AUTHORS .nf Steve Conklin <sconklin@redhat.com> John Berninger <jwb@redhat.com> -Navid Sheikhol-Eslami <navid@redhat.com> Pierre Amadio <pamadio@redhat.com> +Adam Stokes <astokes@redhat.com> +.fi +.SH THANKS TO +.nf +Eva Schaller <eschaller@redhat.com> for providing an Italian translation +Marco Ceci <mceci@redhat.com> for helping me out with the cluster plugin +Leonardo Macchia <lmacchia@redhat.com> for being my personal regexp generator +Imed Chihi <ichihi@redhat.com> for providing Arabic and French translations .fi |