ia64/xen-unstable

changeset 8089:c5ee3b6f25b3

Added xen-bugtool, an application that collects various system logs and can
save them as a tarball, or submit them to a pre-existing bugzilla bug.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Mon Nov 28 01:47:28 2005 +0000 (2005-11-28)
parents eb1169f92d81
children a20a9ec0e510
files tools/misc/Makefile tools/misc/xen-bugtool tools/python/xen/util/bugtool.py
line diff
     1.1 --- a/tools/misc/Makefile	Sun Nov 27 13:09:46 2005 +0000
     1.2 +++ b/tools/misc/Makefile	Mon Nov 28 01:47:28 2005 +0000
     1.3 @@ -16,7 +16,7 @@ HDRS     = $(wildcard *.h)
     1.4  TARGETS  = xenperf xc_shadow
     1.5  
     1.6  INSTALL_BIN  = $(TARGETS) xencons
     1.7 -INSTALL_SBIN = netfix xm xend xenperf
     1.8 +INSTALL_SBIN = netfix xm xen-bugtool xend xenperf
     1.9  
    1.10  all: build
    1.11  build: $(TARGETS)
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/misc/xen-bugtool	Mon Nov 28 01:47:28 2005 +0000
     2.3 @@ -0,0 +1,20 @@
     2.4 +#!/usr/bin/env python
     2.5 +
     2.6 +#  -*- mode: python; -*-
     2.7 +
     2.8 +# Copyright (c) 2005, XenSource Ltd.
     2.9 +
    2.10 +import sys
    2.11 +
    2.12 +sys.path.append('/usr/lib/python')
    2.13 +sys.path.append('/usr/lib64/python')
    2.14 +
    2.15 +from xen.util import bugtool
    2.16 +
    2.17 +
    2.18 +if __name__ == "__main__":
    2.19 +    try:
    2.20 +        sys.exit(bugtool.main())
    2.21 +    except KeyboardInterrupt:
    2.22 +        print "\nInterrupted."
    2.23 +        sys.exit(1)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/tools/python/xen/util/bugtool.py	Mon Nov 28 01:47:28 2005 +0000
     3.3 @@ -0,0 +1,242 @@
     3.4 +#!/usr/bin/env python
     3.5 +
     3.6 +# This library is free software; you can redistribute it and/or
     3.7 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     3.8 +# License as published by the Free Software Foundation.
     3.9 +#
    3.10 +# This library is distributed in the hope that it will be useful,
    3.11 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.12 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    3.13 +# Lesser General Public License for more details.
    3.14 +#
    3.15 +# You should have received a copy of the GNU Lesser General Public
    3.16 +# License along with this library; if not, write to the Free Software
    3.17 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    3.18 +#
    3.19 +# Copyright (c) 2005, XenSource Ltd.
    3.20 +
    3.21 +
    3.22 +import errno
    3.23 +import getpass
    3.24 +import httplib
    3.25 +import re
    3.26 +import os
    3.27 +import os.path
    3.28 +import sys
    3.29 +import tarfile
    3.30 +import tempfile
    3.31 +import time
    3.32 +import urllib
    3.33 +
    3.34 +import xen.lowlevel.xc
    3.35 +
    3.36 +from xen.xend import encode
    3.37 +
    3.38 +
    3.39 +SERVER = 'bugzilla.xensource.com'
    3.40 +SHOW_BUG_PATTERN = 'http://%s/bugzilla/show_bug.cgi?id=%%d' % SERVER
    3.41 +ATTACH_PATTERN = \
    3.42 + 'http://%s/bugzilla/attachment.cgi?bugid=%%d&action=enter' % SERVER
    3.43 +
    3.44 +TITLE_RE = re.compile(r'<title>(.*)</title>')
    3.45 +
    3.46 +FILES_TO_SEND = [ '/var/log/syslog', '/var/log/messages', '/var/log/debug',
    3.47 +                  '/var/log/xend.log', '/var/log/xend-debug.log',
    3.48 +                  '/var/log/xenstored-trace.log' ]
    3.49 +#FILES_TO_SEND = [  ]
    3.50 +
    3.51 +
    3.52 +def main(argv = None):
    3.53 +    if argv is None:
    3.54 +        argv = sys.argv
    3.55 +
    3.56 +    print '''
    3.57 +This application will collate the Xen dmesg output, details of the hardware
    3.58 +configuration of your machine, information about the build of Xen that you are
    3.59 +using, plus, if you allow it, various logs.  These logs may contain private
    3.60 +information, and if you are at all worried about that, you should exit now.
    3.61 +
    3.62 +The information collated can either be posted to a Xen Bugzilla bug (this bug
    3.63 +must already exist in the system, and you must be a registered user there), or
    3.64 +it can be saved as a .tar.bz2 for sending or archiving.
    3.65 +'''
    3.66 +    
    3.67 +    bugball = []
    3.68 +
    3.69 +    xc = xen.lowlevel.xc.xc()
    3.70 +    bugball.append(string_iterator('xen-dmesg', xc.readconsolering()))
    3.71 +    bugball.append(string_iterator('physinfo',  prettyDict(xc.physinfo())))
    3.72 +    bugball.append(string_iterator('xeninfo',   prettyDict(xc.xeninfo())))
    3.73 +    del xc
    3.74 +
    3.75 +    for filename in FILES_TO_SEND:
    3.76 +        if not os.path.exists(filename):
    3.77 +            continue
    3.78 +
    3.79 +        if yes('Include %s? [Y/n] ' % filename):
    3.80 +            bugball.append(file(filename))
    3.81 +
    3.82 +    maybeAttach(bugball)
    3.83 +
    3.84 +    if (yes('''
    3.85 +Do you wish to save these details as a tarball (.tar.bz2)? [Y/n] ''')):
    3.86 +        tar(bugball)
    3.87 +
    3.88 +    return 0
    3.89 +
    3.90 +
    3.91 +def maybeAttach(bugball):
    3.92 +    if not yes('''
    3.93 +Do you wish to attach these details to a Bugzilla bug? [Y/n] '''):
    3.94 +        return
    3.95 +
    3.96 +    bug = int(raw_input('Bug number? '))
    3.97 +
    3.98 +    bug_title = getBugTitle(bug)
    3.99 +
   3.100 +    if bug_title == 'Search by bug number' or bug_title == 'Invalid Bug ID':
   3.101 +        print >>sys.stderr, 'Bug %d does not exist!' % bug
   3.102 +        maybeAttach(bugball)
   3.103 +    elif yes('Are you sure that you want to attach to %s? [Y/n] ' %
   3.104 +             bug_title):
   3.105 +        attach(bug, bugball)
   3.106 +    else:
   3.107 +        maybeAttach(bugball)
   3.108 +
   3.109 +
   3.110 +def attach(bug, bugball):
   3.111 +    username = raw_input('Bugzilla username: ')
   3.112 +    password = getpass.getpass('Bugzilla password: ')
   3.113 +
   3.114 +    conn = httplib.HTTPConnection(SERVER)
   3.115 +    try:
   3.116 +        for f in bugball:
   3.117 +            send(bug, conn, f, f.name, username, password)
   3.118 +    finally:
   3.119 +        conn.close()
   3.120 +
   3.121 +
   3.122 +def getBugTitle(bug):
   3.123 +    f = urllib.urlopen(SHOW_BUG_PATTERN % bug)
   3.124 +
   3.125 +    try:
   3.126 +        for line in f:
   3.127 +            m = TITLE_RE.search(line)
   3.128 +            if m:
   3.129 +                return m.group(1)
   3.130 +    finally:
   3.131 +        f.close()
   3.132 +
   3.133 +    raise "Could not find title of bug %d!" % bug
   3.134 +
   3.135 +
   3.136 +def send(bug, conn, fd, filename, username, password):
   3.137 +
   3.138 +    print "Attaching %s to bug %d." % (filename, bug)
   3.139 +    
   3.140 +    headers, data = encode.encode_data(
   3.141 +        { 'bugid'                : str(bug),
   3.142 +          'action'               : 'insert',
   3.143 +          'data'                 : fd,
   3.144 +          'description'          : '%s from %s' % (filename, username),
   3.145 +          'contenttypeselection' : 'text/plain',
   3.146 +          'contenttypemethod'    : 'list',
   3.147 +          'ispatch'              : '0',
   3.148 +          'GoAheadAndLogIn'      : '1',
   3.149 +          'Bugzilla_login'       : username,
   3.150 +          'Bugzilla_password'    : password,
   3.151 +          })
   3.152 +    
   3.153 +    conn.request('POST',ATTACH_PATTERN % bug, data, headers)
   3.154 +    response = conn.getresponse()
   3.155 +    try:
   3.156 +        body = response.read()
   3.157 +        m = TITLE_RE.search(body)
   3.158 +
   3.159 +        if response.status != 200:
   3.160 +            print >>sys.stderr, (
   3.161 +                'Attach failed: %s %s.' % (response.status, response.reason))
   3.162 +        elif not m or m.group(1) != 'Changes Submitted':
   3.163 +            print >>sys.syderr, (
   3.164 +                'Attach failed: got a page titled %s.' % m.group(1))
   3.165 +        else:
   3.166 +            print "Attaching %s to bug %d succeeded." % (filename, bug)
   3.167 +    finally:
   3.168 +        response.close()
   3.169 +
   3.170 +
   3.171 +def tar(bugball):
   3.172 +    filename = raw_input('Tarball destination filename? ')
   3.173 +
   3.174 +    now = time.time()
   3.175 +
   3.176 +    tf = tarfile.open(filename, 'w:bz2')
   3.177 +
   3.178 +    try:
   3.179 +        for f in bugball:
   3.180 +            ti = tarfile.TarInfo(f.name.split('/')[-1])
   3.181 +            if hasattr(f, 'size'):
   3.182 +                ti.size = f.size()
   3.183 +            else:
   3.184 +                ti.size = os.stat(f.name).st_size
   3.185 +
   3.186 +            ti.mtime = now
   3.187 +            ti.type = tarfile.REGTYPE
   3.188 +            ti.uid = 0
   3.189 +            ti.gid = 0
   3.190 +            ti.uname = 'root'
   3.191 +            ti.gname = 'root'
   3.192 +
   3.193 +            f.seek(0) # If we've added this file to a bug, it will have been
   3.194 +                      # read once already, so reset it.
   3.195 +            tf.addfile(ti, f)
   3.196 +    finally:
   3.197 +        tf.close()
   3.198 +
   3.199 +    print 'Writing tarball %s successful.' % filename
   3.200 +
   3.201 +
   3.202 +def prettyDict(d):
   3.203 +    format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
   3.204 +    return '\n'.join([format % i for i in d.items()]) + '\n'
   3.205 +
   3.206 +
   3.207 +class string_iterator:
   3.208 +    def __init__(self, name, val):
   3.209 +        self.name = name
   3.210 +        self.val = val
   3.211 +        self.vallist = val.splitlines(True)
   3.212 +        self.line = 0
   3.213 +    
   3.214 +    def readlines(self):
   3.215 +        return self.vallist
   3.216 +
   3.217 +    def readline(self):
   3.218 +        result = self.vallist[line]
   3.219 +        line += 1
   3.220 +        return result
   3.221 +
   3.222 +    def read(self, n = None):
   3.223 +        if n is None:
   3.224 +            return self.val
   3.225 +        else:
   3.226 +            return self.val[0:n]
   3.227 +
   3.228 +    def close(self):
   3.229 +        pass
   3.230 +
   3.231 +    def size(self):
   3.232 +        return len(self.val)
   3.233 +
   3.234 +    def seek(self, _1, _2 = None):
   3.235 +        pass
   3.236 +
   3.237 +
   3.238 +def yes(prompt):
   3.239 +    yn = raw_input(prompt)
   3.240 +
   3.241 +    return len(yn) == 0 or yn.lower()[0] == 'y'
   3.242 +
   3.243 +
   3.244 +if __name__ == "__main__":
   3.245 +    sys.exit(main())