blob: 2bc75fc65e43f61a7ee0291b7c4160acbe8cb9f2 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001def tinder_http_post(server, selector, content_type, body):
2 import httplib
3 # now post it
4 for i in range(0,5):
5 try:
6 h = httplib.HTTP(server)
7 h.putrequest('POST', selector)
8 h.putheader('content-type', content_type)
9 h.putheader('content-length', str(len(body)))
10 h.endheaders()
11 h.send(body)
12 errcode, errmsg, headers = h.getreply()
13 #print errcode, errmsg, headers
14 return (errcode,errmsg, headers, h.file)
15 except:
16 print "Error sending the report!"
17 # try again
18 pass
19
20 # return some garbage
21 return (-1, "unknown", "unknown", None)
22
23def tinder_form_data(bound, dict, log):
24 output = []
25 # for each key in the dictionary
26 for name in dict:
27 assert dict[name]
28 output.append( "--" + bound )
29 output.append( 'Content-Disposition: form-data; name="%s"' % name )
30 output.append( "" )
31 output.append( dict[name] )
32 if log:
33 output.append( "--" + bound )
34 output.append( 'Content-Disposition: form-data; name="log"; filename="log.txt"' )
35 output.append( '' )
36 output.append( log )
37 output.append( '--' + bound + '--' )
38 output.append( '' )
39
40 return "\r\n".join(output)
41
42def tinder_time_string():
43 """
44 Return the time as GMT
45 """
46 return ""
47
48def tinder_format_http_post(d,status,log):
49 """
50 Format the Tinderbox HTTP post with the data needed
51 for the tinderbox to be happy.
52 """
53
54 import random
55
56 # the variables we will need to send on this form post
57 variables = {
58 "tree" : d.getVar('TINDER_TREE', True),
59 "machine_name" : d.getVar('TINDER_MACHINE', True),
60 "os" : os.uname()[0],
61 "os_version" : os.uname()[2],
62 "compiler" : "gcc",
63 "clobber" : d.getVar('TINDER_CLOBBER', True) or "0",
64 "srcdate" : d.getVar('SRCDATE', True),
65 "PN" : d.getVar('PN', True),
66 "PV" : d.getVar('PV', True),
67 "PR" : d.getVar('PR', True),
68 "FILE" : d.getVar('FILE', True) or "N/A",
69 "TARGETARCH" : d.getVar('TARGET_ARCH', True),
70 "TARGETFPU" : d.getVar('TARGET_FPU', True) or "Unknown",
71 "TARGETOS" : d.getVar('TARGET_OS', True) or "Unknown",
72 "MACHINE" : d.getVar('MACHINE', True) or "Unknown",
73 "DISTRO" : d.getVar('DISTRO', True) or "Unknown",
74 "zecke-rocks" : "sure",
75 }
76
77 # optionally add the status
78 if status:
79 variables["status"] = str(status)
80
81 # try to load the machine id
82 # we only need on build_status.pl but sending it
83 # always does not hurt
84 try:
85 f = file(d.getVar('TMPDIR',True)+'/tinder-machine.id', 'r')
86 id = f.read()
87 variables['machine_id'] = id
88 except:
89 pass
90
91 # the boundary we will need
92 boundary = "----------------------------------%d" % int(random.random()*1000000000000)
93
94 # now format the body
95 body = tinder_form_data( boundary, variables, log )
96
97 return ("multipart/form-data; boundary=%s" % boundary),body
98
99
100def tinder_build_start(d):
101 """
102 Inform the tinderbox that a build is starting. We do this
103 by posting our name and tree to the build_start.pl script
104 on the server.
105 """
106
107 # get the body and type
108 content_type, body = tinder_format_http_post(d,None,None)
109 server = d.getVar('TINDER_HOST', True )
110 url = d.getVar('TINDER_URL', True )
111
112 selector = url + "/xml/build_start.pl"
113
114 #print "selector %s and url %s" % (selector, url)
115
116 # now post it
117 errcode, errmsg, headers, h_file = tinder_http_post(server,selector,content_type, body)
118 #print errcode, errmsg, headers
119 report = h_file.read()
120
121 # now let us find the machine id that was assigned to us
122 search = "<machine id='"
123 report = report[report.find(search)+len(search):]
124 report = report[0:report.find("'")]
125
126 bb.note("Machine ID assigned by tinderbox: %s" % report )
127
128 # now we will need to save the machine number
129 # we will override any previous numbers
130 f = file(d.getVar('TMPDIR', True)+"/tinder-machine.id", 'w')
131 f.write(report)
132
133
134def tinder_send_http(d, status, _log):
135 """
136 Send this log as build status
137 """
138
139 # get the body and type
140 server = d.getVar('TINDER_HOST', True)
141 url = d.getVar('TINDER_URL', True)
142
143 selector = url + "/xml/build_status.pl"
144
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500145 # now post it - in chunks of 10.000 characters
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146 new_log = _log
147 while len(new_log) > 0:
148 content_type, body = tinder_format_http_post(d,status,new_log[0:18000])
149 errcode, errmsg, headers, h_file = tinder_http_post(server,selector,content_type, body)
150 #print errcode, errmsg, headers
151 #print h.file.read()
152 new_log = new_log[18000:]
153
154
155def tinder_print_info(d):
156 """
157 Print the TinderBox Info
158 Including informations of the BaseSystem and the Tree
159 we use.
160 """
161
162 # get the local vars
163 time = tinder_time_string()
164 ops = os.uname()[0]
165 version = os.uname()[2]
166 url = d.getVar( 'TINDER_URL' , True )
167 tree = d.getVar( 'TINDER_TREE', True )
168 branch = d.getVar( 'TINDER_BRANCH', True )
169 srcdate = d.getVar( 'SRCDATE', True )
170 machine = d.getVar( 'MACHINE', True )
171 distro = d.getVar( 'DISTRO', True )
172 bbfiles = d.getVar( 'BBFILES', True )
173 tarch = d.getVar( 'TARGET_ARCH', True )
174 fpu = d.getVar( 'TARGET_FPU', True )
175 oerev = d.getVar( 'OE_REVISION', True ) or "unknown"
176
177 # there is a bug with tipple quoted strings
178 # i will work around but will fix the original
179 # bug as well
180 output = []
181 output.append("== Tinderbox Info" )
182 output.append("Time: %(time)s" )
183 output.append("OS: %(ops)s" )
184 output.append("%(version)s" )
185 output.append("Compiler: gcc" )
186 output.append("Tinderbox Client: 0.1" )
187 output.append("Tinderbox Client Last Modified: yesterday" )
188 output.append("Tinderbox Protocol: 0.1" )
189 output.append("URL: %(url)s" )
190 output.append("Tree: %(tree)s" )
191 output.append("Config:" )
192 output.append("branch = '%(branch)s'" )
193 output.append("TARGET_ARCH = '%(tarch)s'" )
194 output.append("TARGET_FPU = '%(fpu)s'" )
195 output.append("SRCDATE = '%(srcdate)s'" )
196 output.append("MACHINE = '%(machine)s'" )
197 output.append("DISTRO = '%(distro)s'" )
198 output.append("BBFILES = '%(bbfiles)s'" )
199 output.append("OEREV = '%(oerev)s'" )
200 output.append("== End Tinderbox Client Info" )
201
202 # now create the real output
203 return "\n".join(output) % vars()
204
205
206def tinder_print_env():
207 """
208 Print the environment variables of this build
209 """
210 time_start = tinder_time_string()
211 time_end = tinder_time_string()
212
213 # build the environment
214 env = ""
215 for var in os.environ:
216 env += "%s=%s\n" % (var, os.environ[var])
217
218 output = []
219 output.append( "---> TINDERBOX RUNNING env %(time_start)s" )
220 output.append( env )
221 output.append( "<--- TINDERBOX FINISHED (SUCCESS) %(time_end)s" )
222
223 return "\n".join(output) % vars()
224
225def tinder_tinder_start(d, event):
226 """
227 PRINT the configuration of this build
228 """
229
230 time_start = tinder_time_string()
231 config = tinder_print_info(d)
232 #env = tinder_print_env()
233 time_end = tinder_time_string()
234 packages = " ".join( event.getPkgs() )
235
236 output = []
237 output.append( "---> TINDERBOX PRINTING CONFIGURATION %(time_start)s" )
238 output.append( config )
239 #output.append( env )
240 output.append( "<--- TINDERBOX FINISHED PRINTING CONFIGURATION %(time_end)s" )
241 output.append( "---> TINDERBOX BUILDING '%(packages)s'" )
242 output.append( "<--- TINDERBOX STARTING BUILD NOW" )
243
244 output.append( "" )
245
246 return "\n".join(output) % vars()
247
248def tinder_do_tinder_report(event):
249 """
250 Report to the tinderbox:
251 On the BuildStart we will inform the box directly
252 On the other events we will write to the TINDER_LOG and
253 when the Task is finished we will send the report.
254
255 The above is not yet fully implemented. Currently we send
256 information immediately. The caching/queuing needs to be
257 implemented. Also sending more or less information is not
258 implemented yet.
259
260 We have two temporary files stored in the TMP directory. One file
261 contains the assigned machine id for the tinderclient. This id gets
262 assigned when we connect the box and start the build process the second
263 file is used to workaround an EventHandler limitation. If BitBake is ran
264 with the continue option we want the Build to fail even if we get the
265 BuildCompleted Event. In this case we have to look up the status and
266 send it instead of 100/success.
267 """
268 import glob
269
270 # variables
271 name = bb.event.getName(event)
272 log = ""
273 status = 1
274 # Check what we need to do Build* shows we start or are done
275 if name == "BuildStarted":
276 tinder_build_start(event.data)
277 log = tinder_tinder_start(event.data,event)
278
279 try:
280 # truncate the tinder log file
281 f = file(event.data.getVar('TINDER_LOG', True), 'w')
282 f.write("")
283 f.close()
284 except:
285 pass
286
287 try:
288 # write a status to the file. This is needed for the -k option
289 # of BitBake
290 g = file(event.data.getVar('TMPDIR', True)+"/tinder-status", 'w')
291 g.write("")
292 g.close()
293 except IOError:
294 pass
295
296 # Append the Task-Log (compile,configure...) to the log file
297 # we will send to the server
298 if name == "TaskSucceeded" or name == "TaskFailed":
299 log_file = glob.glob("%s/log.%s.*" % (event.data.getVar('T', True), event.task))
300
301 if len(log_file) != 0:
302 to_file = event.data.getVar('TINDER_LOG', True)
303 log += "".join(open(log_file[0], 'r').readlines())
304
305 # set the right 'HEADER'/Summary for the TinderBox
306 if name == "TaskStarted":
307 log += "---> TINDERBOX Task %s started\n" % event.task
308 elif name == "TaskSucceeded":
309 log += "<--- TINDERBOX Task %s done (SUCCESS)\n" % event.task
310 elif name == "TaskFailed":
311 log += "<--- TINDERBOX Task %s failed (FAILURE)\n" % event.task
312 elif name == "PkgStarted":
313 log += "---> TINDERBOX Package %s started\n" % event.data.getVar('PF', True)
314 elif name == "PkgSucceeded":
315 log += "<--- TINDERBOX Package %s done (SUCCESS)\n" % event.data.getVar('PF', True)
316 elif name == "PkgFailed":
317 if not event.data.getVar('TINDER_AUTOBUILD', True) == "0":
318 build.exec_task('do_clean', event.data)
319 log += "<--- TINDERBOX Package %s failed (FAILURE)\n" % event.data.getVar('PF', True)
320 status = 200
321 # remember the failure for the -k case
322 h = file(event.data.getVar('TMPDIR', True)+"/tinder-status", 'w')
323 h.write("200")
324 elif name == "BuildCompleted":
325 log += "Build Completed\n"
326 status = 100
327 # Check if we have a old status...
328 try:
329 h = file(event.data.getVar('TMPDIR',True)+'/tinder-status', 'r')
330 status = int(h.read())
331 except:
332 pass
333
334 elif name == "MultipleProviders":
335 log += "---> TINDERBOX Multiple Providers\n"
336 log += "multiple providers are available (%s);\n" % ", ".join(event.getCandidates())
337 log += "consider defining PREFERRED_PROVIDER_%s\n" % event.getItem()
338 log += "is runtime: %d\n" % event.isRuntime()
339 log += "<--- TINDERBOX Multiple Providers\n"
340 elif name == "NoProvider":
341 log += "Error: No Provider for: %s\n" % event.getItem()
342 log += "Error:Was Runtime: %d\n" % event.isRuntime()
343 status = 200
344 # remember the failure for the -k case
345 h = file(event.data.getVar('TMPDIR', True)+"/tinder-status", 'w')
346 h.write("200")
347
348 # now post the log
349 if len(log) == 0:
350 return
351
352 # for now we will use the http post method as it is the only one
353 log_post_method = tinder_send_http
354 log_post_method(event.data, status, log)
355
356
357# we want to be an event handler
358addhandler tinderclient_eventhandler
359python tinderclient_eventhandler() {
360 if e.data is None or bb.event.getName(e) == "MsgNote":
361 return
362
363 do_tinder_report = e.data.getVar('TINDER_REPORT', True)
364 if do_tinder_report and do_tinder_report == "1":
365 tinder_do_tinder_report(e)
366
367 return
368}