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