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