1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
|
#
# Small event handler to automatically open URLs and file
# bug reports at a bugzilla of your choiche
#
# This class requires python2.4 because of the urllib2 usage
#
def seppuku_spliturl(url):
"""
Split GET URL to return the host base and the query
as a param dictionary
"""
import urllib
(uri,query) = urllib.splitquery(url)
param = {}
for par in query.split("&"):
(key,value) = urllib.splitvalue(par)
if not key or len(key) == 0 or not value:
continue
key = urllib.unquote(key)
value = urllib.unquote(value)
param[key] = value
return (uri,param)
def seppuku_login(opener, login, user, password):
"""
We need to post to query.cgi with the parameters
Bugzilla_login and Bugzilla_password and will scan
the resulting page then
@param opened = cookie enabled urllib2 opener
@param login = http://bugs.openembedded.net/query.cgi?
@param user = Your username
@param password = Your password
"""
import urllib
param = urllib.urlencode( {"GoAheadAndLogIn" : 1, "Bugzilla_login" : user, "Bugzilla_password" : password } )
result = opener.open(login + param)
if result.code != 200:
return False
txt = result.read()
if not '<a href="relogin.cgi">Log out</a>' in txt:
return False
return True
def seppuku_find_bug_report_old():
from HTMLParser import HTMLParser
class BugQueryExtractor(HTMLParser):
STATE_NONE = 0
STATE_FOUND_TR = 1
STATE_FOUND_NUMBER = 2
STATE_FOUND_PRIO = 3
STATE_FOUND_PRIO2 = 4
STATE_FOUND_NAME = 5
STATE_FOUND_PLATFORM = 6
STATE_FOUND_STATUS = 7
STATE_FOUND_WHATEVER = 8 # I don't know this field
STATE_FOUND_DESCRIPTION =9
def __init__(self):
HTMLParser.__init__(self)
self.state = self.STATE_NONE
self.bugs = []
self.bug = None
def handle_starttag(self, tag, attr):
if self.state == self.STATE_NONE and tag.lower() == "tr":
if len(attr) == 1 and attr[0][0] == 'class' and \
('bz_normal' in attr[0][1] or 'bz_blocker' in attr[0][1] or 'bz_enhancement' in attr[0][1] or 'bz_major' in attr[0][1] or 'bz_minor' in attr[0][1] or 'bz_trivial' in attr[0][1] or 'bz_critical' in attr[0][1] or 'bz_wishlist' in attr[0][1]) \
and 'bz_P' in attr[0][1]:
self.state = self.STATE_FOUND_TR
elif self.state == self.STATE_FOUND_TR and tag.lower() == "td":
self.state += 1
def handle_endtag(self, tag):
if tag.lower() == "tr":
if self.state != self.STATE_NONE:
self.bugs.append( (self.bug,self.status) )
self.state = self.STATE_NONE
self.bug = None
if self.state > 1 and tag.lower() == "td":
self.state += 1
def handle_data(self,data):
data = data.strip()
# skip garbage
if len(data) == 0:
return
if self.state == self.STATE_FOUND_NUMBER:
"""
#1995 in bugs.oe.org has [SEC] additionally to the number and we want to ignore it
"""
if not self.bug:
self.bug = data
elif self.state == self.STATE_FOUND_STATUS:
self.status = data
def result(self):
return self.bugs
return BugQueryExtractor()
def seppuku_find_bug_report(debug_file, opener, query, product, component, bugname):
"""
Find a bug report with the sane name and return the bug id
and the status.
@param opener = urllib2 opener
@param query = e.g. http://bugs.openembedded.net/query.cgi?
@param product = search for this product
@param component = search for this component
@param bugname = the bug to search for
http://bugs.openembedded.net/buglist.cgi?short_desc_type=substring&short_desc=manual+test+bug&product=Openembedded&emailreporter2=1&emailtype2=substring&email2=freyther%40yahoo.com
but it does not support ctype=csv...
"""
import urllib
product = urllib.quote(product)
component = urllib.quote(component)
bugname = urllib.quote(bugname)
file = "%(query)sproduct=%(product)s&component=%(component)s&short_desc_type=substring&short_desc=%(bugname)s" % vars()
print >> debug_file, "Trying %s" % file
result = opener.open(file)
if result.code != 200:
raise "Can not query the bugzilla at all"
txt = result.read()
scanner = seppuku_find_bug_report_old()
scanner.feed(txt)
if len(scanner.result()) == 0:
print >> debug_file, "Scanner failed to scan the html site"
print >> debug_file, "%(query)sproduct=%(product)s&component=%(component)s&short_desc_type=substring&short_desc=%(bugname)s" % vars()
#print >> debug_file, txt
return (False,None)
else: # silently pick the first result
print >> debug_file, "Result of bug search is "
#print >> debug_file, txt
(number,status) = scanner.result()[0]
return (not status in ["CLOS", "RESO", "VERI"],number)
def seppuku_reopen_bug(poster, file, product, component, bug_number, bugname, text):
"""
Reopen a bug report and append to the comment
Same as with opening a new report, some bits need to be inside the url
http://bugs.openembedded.net/process_bug.cgi?id=239&bug_file_loc=http%3A%2F%2F&version=Angstrom&longdesclength=2&product=Openembedded&component=Build&comment=bla&priority=P2&bug_severity=normal&op_sys=Linux&rep_platform=Other&knob=reopen&short_desc=foo
"""
import urllib2
(uri, param) = seppuku_spliturl( file )
# Prepare the post
param["product"] = product
param["component"] = component
param["longdesclength"] = 2
param["short_desc"] = bugname
param["knob"] = "reopen"
param["id"] = bug_number
param["comment"] = text
try:
result = poster.open( uri, param )
except urllib2.HTTPError, e:
print e.geturl()
print e.info()
return False
except Exception, e:
print e
return False
if result.code != 200:
return False
else:
return True
def seppuku_file_bug(poster, file, product, component, bugname, text):
"""
Create a completely new bug report
http://bugs.openembedded.net/post_bug.cgi?bug_file_loc=http%3A%2F%2F&version=Angstrom&product=Openembedded&component=Build&short_desc=foo&comment=bla&priority=P2&bug_severity=normal&op_sys=Linux&rep_platform=Other
You are forced to add some default values to the bugzilla query and stop with '&'
@param opener urllib2 opener
@param file The url used to file a bug report
@param product Product
@param component Component
@param bugname Name of the to be created bug
@param text Text
"""
import urllib2
(uri, param) = seppuku_spliturl( file )
param["product"] = product
param["component"] = component
param["short_desc"] = bugname
param["comment"] = text
try:
result = poster.open( uri, param )
except urllib2.HTTPError, e:
print e.geturl()
print e.info()
return False
except Exception, e:
print e
return False
# scan the result for a bug number
# it will look like
# '<title>Bug 2742 Submitted</title>'
import re
res = re.findall(("\>Bug (?P<int>\d+) Submitted"), result.read() )
if result.code != 200 or len(res) != 1:
return None
else:
return res[0]
def seppuku_create_attachment(data, debug, poster, attach_query, product, component, bug_number, text, file):
"""
Create a new attachment for the failed report
"""
if not bug_number:
import bb
bb.note("Can't create an attachment, no bugnumber passed to method")
print >> debug, "Can't create an attachment, no bugnumber passed to method"
return False
if not attach_query:
import bb
bb.note("Can't create an attachment, no attach_query passed to method")
print >> debug, "Can't create an attachment, no attach_query passed to method"
return False
import bb
logdescription = "Build log for machine %s" % (bb.data.getVar('MACHINE', data, True))
import urllib2
param = { "bugid" : bug_number, "action" : "insert", "data" : file, "description" : logdescription, "ispatch" : "0", "contenttypemethod" : "list", "contenttypeselection" : "text/plain", "comment" : text }
try:
result = poster.open( attach_query, param )
except urllib2.HTTPError, e:
print e.geturl()
print e.info()
return False
except Exception, e:
print e
print >> debug, "Got exception in poster.open( attach_query, param )"
print >> debug, "attach_query: %s param: %s" % (attach_query, param )
return False
txt = result.read()
if result.code != 200:
print >> debug, "Got bad return code (%s)" % result.code
return False
else:
print >> debug, "Got good return code (200)"
return True
addhandler seppuku_eventhandler
python seppuku_eventhandler() {
"""
Report task failures to the bugzilla
and succeeded builds to the box
"""
from bb.event import NotHandled, getName
from bb import data, mkdirhier, build
import bb, os, glob
event = e
data = e.data
name = getName(event)
if name == "MsgNote":
# avoid recursion
return NotHandled
# Try to load our exotic libraries
try:
import MultipartPostHandler
except:
bb.note("You need to put the MultipartPostHandler into your PYTHONPATH. Download it from http://pipe.scs.fsu.edu/PostHandler/MultipartPostHandler.py")
return NotHandled
try:
import urllib2, cookielib
except:
bb.note("Failed to import the cookielib and urllib2, make sure to use python2.4")
return NotHandled
if name == "PkgFailed":
if not bb.data.getVar('SEPPUKU_AUTOBUILD', data, True) == "0":
build.exec_func('do_clean', data)
elif name == "TaskFailed":
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
poster = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj),MultipartPostHandler.MultipartPostHandler)
login = bb.data.getVar("SEPPUKU_LOGIN", data, True)
query = bb.data.getVar("SEPPUKU_QUERY", data, True)
newbug = bb.data.getVar("SEPPUKU_NEWREPORT", data, True)
reopen = bb.data.getVar("SEPPUKU_ADDCOMMENT", data, True)
attach = bb.data.getVar("SEPPUKU_ATTACHMENT", data, True)
user = bb.data.getVar("SEPPUKU_USER", data, True)
passw = bb.data.getVar("SEPPUKU_PASS", data, True)
product = bb.data.getVar("SEPPUKU_PRODUCT", data, True)
component = bb.data.getVar("SEPPUKU_COMPONENT", data, True)
proxy = bb.data.getVar('HTTP_PROXY', data, True )
if (proxy):
phl = urllib2.ProxyHandler({'http' : proxy})
poster.add_handler(phl)
opener.add_handler(phl)
# evil hack to figure out what is going on
debug_file = open(os.path.join(bb.data.getVar("TMPDIR", data, True),"..","seppuku-log"),"a")
if not seppuku_login(opener, login, user, passw):
bb.note("Login to bugzilla failed")
print >> debug_file, "Login to bugzilla failed"
return NotHandled
else:
print >> debug_file, "Logged into the box"
file = None
if name == "TaskFailed":
bugname = "%(package)s-%(pv)s-autobuild" % { "package" : bb.data.getVar("PN", data, True),
"pv" : bb.data.getVar("PV", data, True),
}
log_file = glob.glob("%s/log.%s.*" % (bb.data.getVar('T', event.data, True), event.task))
text = "The %s step in %s failed at %s for machine %s" % (e.task, bb.data.getVar("PN", data, True), bb.data.getVar('DATETIME', data, True), bb.data.getVar( 'MACHINE', data, True ) )
if len(log_file) != 0:
print >> debug_file, "Adding log file %s" % log_file[0]
file = open(log_file[0], 'r')
else:
print >> debug_file, "No log file found for the glob"
else:
print >> debug_file, "Unknown name '%s'" % name
assert False
(bug_open, bug_number) = seppuku_find_bug_report(debug_file, opener, query, product, component, bugname)
print >> debug_file, "Bug is open: %s and bug number: %s" % (bug_open, bug_number)
# The bug is present and still open, attach an error log
if bug_number and bug_open:
print >> debug_file, "The bug is known as '%s'" % bug_number
if file:
if not seppuku_create_attachment(data, debug_file, poster, attach, product, component, bug_number, text, file):
print >> debug_file, "Failed to attach the build log for bug #%s" % bug_number
else:
print >> debug_file, "Created an attachment for '%s' '%s' '%s'" % (product, component, bug_number)
else:
print >> debug_file, "Not trying to create an attachment for bug #%s" % bug_number
return NotHandled
if bug_number and not bug_open:
if not seppuku_reopen_bug(poster, reopen, product, component, bug_number, bugname, text):
print >> debug_file, "Failed to reopen the bug #%s" % bug_number
else:
print >> debug_file, "Reopened the bug #%s" % bug_number
else:
bug_number = seppuku_file_bug(poster, newbug, product, component, bugname, text)
if not bug_number:
print >> debug_file, "Couldn't acquire a new bug_numer, filing a bugreport failed"
else:
print >> debug_file, "The new bug_number: '%s'" % bug_number
if bug_number and file:
if not seppuku_create_attachment(data, debug_file, poster, attach, product, component, bug_number, text, file):
print >> debug_file, "Failed to attach the build log for bug #%s" % bug_number
else:
print >> debug_file, "Created an attachment for '%s' '%s' '%s'" % (product, component, bug_number)
else:
print >> debug_file, "Not trying to create an attachment for bug #%s" % bug_number
# store bug number for oestats-client
if bug_number:
bb.data.setVar('OESTATS_BUG_NUMBER', bug_number, event.data)
bb.data.setVar('OESTATS_BUG_TRACKER', "http://bugs.openembedded.net/", event.data)
return NotHandled
}
|