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