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