1 | #!/usr/bin/env python |
---|
2 | |
---|
3 | # This library is free software; you can redistribute it and/or |
---|
4 | # modify it under the terms of version 2.1 of the GNU Lesser General Public |
---|
5 | # License as published by the Free Software Foundation. |
---|
6 | # |
---|
7 | # This library is distributed in the hope that it will be useful, |
---|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
10 | # Lesser General Public License for more details. |
---|
11 | # |
---|
12 | # You should have received a copy of the GNU Lesser General Public |
---|
13 | # License along with this library; if not, write to the Free Software |
---|
14 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
15 | # |
---|
16 | # Copyright (c) 2005, XenSource Ltd. |
---|
17 | |
---|
18 | |
---|
19 | import errno |
---|
20 | import getpass |
---|
21 | import httplib |
---|
22 | import re |
---|
23 | import os |
---|
24 | import os.path |
---|
25 | import StringIO |
---|
26 | import sys |
---|
27 | import tarfile |
---|
28 | import tempfile |
---|
29 | import time |
---|
30 | import urllib |
---|
31 | |
---|
32 | import xen.lowlevel.xc |
---|
33 | |
---|
34 | from xen.xend import encode |
---|
35 | |
---|
36 | |
---|
37 | SERVER = 'bugzilla.xensource.com' |
---|
38 | SHOW_BUG_PATTERN = 'http://%s/bugzilla/show_bug.cgi?id=%%d' % SERVER |
---|
39 | ATTACH_PATTERN = \ |
---|
40 | 'http://%s/bugzilla/attachment.cgi?bugid=%%d&action=enter' % SERVER |
---|
41 | |
---|
42 | TITLE_RE = re.compile(r'<title>(.*)</title>') |
---|
43 | |
---|
44 | FILES_TO_SEND = [ '/var/log/' + x for x in |
---|
45 | [ 'syslog', 'messages', 'debug', |
---|
46 | 'xen/xend-debug.log', 'xen/xenstored-trace.log', |
---|
47 | 'xen/xen-hotplug.log', 'xen/xend.log' ] + |
---|
48 | [ 'xen/xend.log.%d' % z for z in range(1,6) ] ] |
---|
49 | #FILES_TO_SEND = [ ] |
---|
50 | |
---|
51 | |
---|
52 | def main(argv = None): |
---|
53 | if argv is None: |
---|
54 | argv = sys.argv |
---|
55 | |
---|
56 | print ''' |
---|
57 | This application will collate the Xen dmesg output, details of the hardware |
---|
58 | configuration of your machine, information about the build of Xen that you are |
---|
59 | using, plus, if you allow it, various logs. |
---|
60 | |
---|
61 | The information collated can either be posted to a Xen Bugzilla bug (this bug |
---|
62 | must already exist in the system, and you must be a registered user there), or |
---|
63 | it can be saved as a .tar.bz2 for sending or archiving. |
---|
64 | |
---|
65 | The collated logs may contain private information, and if you are at all |
---|
66 | worried about that, you should exit now, or you should explicitly exclude |
---|
67 | those logs from the archive. |
---|
68 | |
---|
69 | ''' |
---|
70 | |
---|
71 | bugball = [] |
---|
72 | |
---|
73 | xc = xen.lowlevel.xc.xc() |
---|
74 | |
---|
75 | def do(n, f): |
---|
76 | try: |
---|
77 | s = f() |
---|
78 | except Exception, exn: |
---|
79 | s = str(exn) |
---|
80 | bugball.append(string_iterator(n, s)) |
---|
81 | |
---|
82 | do('xen-dmesg', lambda: xc.readconsolering()) |
---|
83 | do('physinfo', lambda: prettyDict(xc.physinfo())) |
---|
84 | do('xeninfo', lambda: prettyDict(xc.xeninfo())) |
---|
85 | |
---|
86 | for filename in FILES_TO_SEND: |
---|
87 | if not os.path.exists(filename): |
---|
88 | continue |
---|
89 | |
---|
90 | if yes('Include %s? [Y/n] ' % filename): |
---|
91 | bugball.append(file(filename)) |
---|
92 | |
---|
93 | maybeAttach(bugball) |
---|
94 | |
---|
95 | if (yes(''' |
---|
96 | Do you wish to save these details as a tarball (.tar.bz2)? [Y/n] ''')): |
---|
97 | tar(bugball) |
---|
98 | |
---|
99 | return 0 |
---|
100 | |
---|
101 | |
---|
102 | def maybeAttach(bugball): |
---|
103 | if not yes(''' |
---|
104 | Do you wish to attach these details to a Bugzilla bug? [Y/n] '''): |
---|
105 | return |
---|
106 | |
---|
107 | bug = int(raw_input('Bug number? ')) |
---|
108 | |
---|
109 | bug_title = getBugTitle(bug) |
---|
110 | |
---|
111 | if bug_title == 'Search by bug number' or bug_title == 'Invalid Bug ID': |
---|
112 | print >>sys.stderr, 'Bug %d does not exist!' % bug |
---|
113 | maybeAttach(bugball) |
---|
114 | elif yes('Are you sure that you want to attach to %s? [Y/n] ' % |
---|
115 | bug_title): |
---|
116 | attach(bug, bugball) |
---|
117 | else: |
---|
118 | maybeAttach(bugball) |
---|
119 | |
---|
120 | |
---|
121 | def attach(bug, bugball): |
---|
122 | username = raw_input('Bugzilla username: ') |
---|
123 | password = getpass.getpass('Bugzilla password: ') |
---|
124 | |
---|
125 | conn = httplib.HTTPConnection(SERVER) |
---|
126 | try: |
---|
127 | for f in bugball: |
---|
128 | send(bug, conn, f, f.name, username, password) |
---|
129 | finally: |
---|
130 | conn.close() |
---|
131 | |
---|
132 | |
---|
133 | def getBugTitle(bug): |
---|
134 | f = urllib.urlopen(SHOW_BUG_PATTERN % bug) |
---|
135 | |
---|
136 | try: |
---|
137 | for line in f: |
---|
138 | m = TITLE_RE.search(line) |
---|
139 | if m: |
---|
140 | return m.group(1) |
---|
141 | finally: |
---|
142 | f.close() |
---|
143 | |
---|
144 | raise "Could not find title of bug %d!" % bug |
---|
145 | |
---|
146 | |
---|
147 | def send(bug, conn, fd, filename, username, password): |
---|
148 | |
---|
149 | print "Attaching %s to bug %d." % (filename, bug) |
---|
150 | |
---|
151 | headers, data = encode.encode_data( |
---|
152 | { 'bugid' : str(bug), |
---|
153 | 'action' : 'insert', |
---|
154 | 'data' : fd, |
---|
155 | 'description' : '%s from %s' % (filename, username), |
---|
156 | 'contenttypeselection' : 'text/plain', |
---|
157 | 'contenttypemethod' : 'list', |
---|
158 | 'ispatch' : '0', |
---|
159 | 'GoAheadAndLogIn' : '1', |
---|
160 | 'Bugzilla_login' : username, |
---|
161 | 'Bugzilla_password' : password, |
---|
162 | }) |
---|
163 | |
---|
164 | conn.request('POST',ATTACH_PATTERN % bug, data, headers) |
---|
165 | response = conn.getresponse() |
---|
166 | try: |
---|
167 | body = response.read() |
---|
168 | m = TITLE_RE.search(body) |
---|
169 | |
---|
170 | if response.status != 200: |
---|
171 | print >>sys.stderr, ( |
---|
172 | 'Attach failed: %s %s.' % (response.status, response.reason)) |
---|
173 | elif not m or m.group(1) != 'Changes Submitted': |
---|
174 | print >>sys.stderr, ( |
---|
175 | 'Attach failed: got a page titled %s.' % m.group(1)) |
---|
176 | else: |
---|
177 | print "Attaching %s to bug %d succeeded." % (filename, bug) |
---|
178 | finally: |
---|
179 | response.close() |
---|
180 | |
---|
181 | |
---|
182 | def tar(bugball): |
---|
183 | filename = raw_input('Tarball destination filename? ') |
---|
184 | |
---|
185 | now = time.time() |
---|
186 | |
---|
187 | tf = tarfile.open(filename, 'w:bz2') |
---|
188 | |
---|
189 | try: |
---|
190 | for f in bugball: |
---|
191 | ti = tarfile.TarInfo(f.name.split('/')[-1]) |
---|
192 | if hasattr(f, 'size'): |
---|
193 | ti.size = f.size() |
---|
194 | else: |
---|
195 | ti.size = os.stat(f.name).st_size |
---|
196 | |
---|
197 | ti.mtime = now |
---|
198 | ti.type = tarfile.REGTYPE |
---|
199 | ti.uid = 0 |
---|
200 | ti.gid = 0 |
---|
201 | ti.uname = 'root' |
---|
202 | ti.gname = 'root' |
---|
203 | |
---|
204 | f.seek(0) # If we've added this file to a bug, it will have been |
---|
205 | # read once already, so reset it. |
---|
206 | tf.addfile(ti, f) |
---|
207 | finally: |
---|
208 | tf.close() |
---|
209 | |
---|
210 | print 'Writing tarball %s successful.' % filename |
---|
211 | |
---|
212 | |
---|
213 | def prettyDict(d): |
---|
214 | format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()])) |
---|
215 | return '\n'.join([format % i for i in d.items()]) + '\n' |
---|
216 | |
---|
217 | |
---|
218 | class string_iterator(StringIO.StringIO): |
---|
219 | def __init__(self, name, val): |
---|
220 | StringIO.StringIO.__init__(self, val) |
---|
221 | self.name = name |
---|
222 | |
---|
223 | def size(self): |
---|
224 | return len(self.getvalue()) |
---|
225 | |
---|
226 | |
---|
227 | def yes(prompt): |
---|
228 | yn = raw_input(prompt) |
---|
229 | |
---|
230 | return len(yn) == 0 or yn.lower()[0] == 'y' |
---|
231 | |
---|
232 | |
---|
233 | if __name__ == "__main__": |
---|
234 | sys.exit(main()) |
---|