summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake')
-rwxr-xr-xbitbake/bin/bitbake17
-rw-r--r--bitbake/lib/bb/server/xmlrpc.py206
2 files changed, 215 insertions, 8 deletions
diff --git a/bitbake/bin/bitbake b/bitbake/bin/bitbake
index 6d4efe6bbf..f3bbeb4667 100755
--- a/bitbake/bin/bitbake
+++ b/bitbake/bin/bitbake
@@ -190,8 +190,13 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
190 190
191 parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to", 191 parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
192 action = "store", dest = "bind", default = False) 192 action = "store", dest = "bind", default = False)
193
193 parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds", 194 parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
194 action = "store_true", dest = "nosetscene", default = False) 195 action = "store_true", dest = "nosetscene", default = False)
196
197 parser.add_option("", "--remote-server", help = "Connect to the specified server",
198 action = "store", dest = "remote_server", default = False)
199
195 options, targets = parser.parse_args(sys.argv) 200 options, targets = parser.parse_args(sys.argv)
196 return options, targets[1:] 201 return options, targets[1:]
197 202
@@ -260,6 +265,9 @@ def main():
260 if configParams.bind and configParams.servertype != "xmlrpc": 265 if configParams.bind and configParams.servertype != "xmlrpc":
261 sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n") 266 sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n")
262 267
268 if configParams.remote_server and configParams.servertype != "xmlrpc":
269 sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n")
270
263 if "BBDEBUG" in os.environ: 271 if "BBDEBUG" in os.environ:
264 level = int(os.environ["BBDEBUG"]) 272 level = int(os.environ["BBDEBUG"])
265 if level > configuration.debug: 273 if level > configuration.debug:
@@ -281,8 +289,13 @@ def main():
281 else: 289 else:
282 configuration.extra_caches = getattr(ui_module, "extraCaches", []) 290 configuration.extra_caches = getattr(ui_module, "extraCaches", [])
283 291
284 # we start a server with a given configuration 292 if not configParams.remote_server:
285 server = start_server(servermodule, configParams, configuration) 293 # we start a server with a given configuration
294 server = start_server(servermodule, configParams, configuration)
295 else:
296 # we start a stub server that is actually a XMLRPClient to
297 server = servermodule.BitBakeXMLRPCClient()
298 server.saveConnectionDetails(configParams.remote_server)
286 299
287 logger.removeHandler(handler) 300 logger.removeHandler(handler)
288 301
diff --git a/bitbake/lib/bb/server/xmlrpc.py b/bitbake/lib/bb/server/xmlrpc.py
index e9c106b20e..56a643c576 100644
--- a/bitbake/lib/bb/server/xmlrpc.py
+++ b/bitbake/lib/bb/server/xmlrpc.py
@@ -35,6 +35,14 @@ import bb
35import xmlrpclib, sys 35import xmlrpclib, sys
36from bb import daemonize 36from bb import daemonize
37from bb.ui import uievent 37from bb.ui import uievent
38import hashlib, time
39import socket
40import os, signal
41import threading
42try:
43 import cPickle as pickle
44except ImportError:
45 import pickle
38 46
39DEBUG = False 47DEBUG = False
40 48
@@ -175,11 +183,145 @@ class BitBakeServerCommands():
175 print("Server (cooker) exiting") 183 print("Server (cooker) exiting")
176 return 184 return
177 185
178 def ping(self): 186 def addClient(self):
187 if self.has_client:
188 return None
189 token = hashlib.md5(str(time.time())).hexdigest()
190 self.server.set_connection_token(token)
191 self.has_client = True
192 return token
193
194 def removeClient(self):
195 if self.has_client:
196 self.server.set_connection_token(None)
197 self.has_client = False
198
199# This request handler checks if the request has a "Bitbake-token" header
200# field (this comes from the client side) and compares it with its internal
201# "Bitbake-token" field (this comes from the server). If the two are not
202# equal, it is assumed that a client is trying to connect to the server
203# while another client is connected to the server. In this case, a 503 error
204# ("service unavailable") is returned to the client.
205class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
206 def __init__(self, request, client_address, server):
207 self.connection_token = server.connection_token
208 SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
209
210 def do_POST(self):
211 try:
212 remote_token = self.headers["Bitbake-token"]
213 except:
214 remote_token = None
215 if remote_token != self.connection_token:
216 self.report_503()
217 else:
218 SimpleXMLRPCRequestHandler.do_POST(self)
219
220 def report_503(self):
221 self.send_response(503)
222 response = 'No more client allowed'
223 self.send_header("Content-type", "text/plain")
224 self.send_header("Content-length", str(len(response)))
225 self.end_headers()
226 self.wfile.write(response)
227
228
229class BitBakeUIEventServer(threading.Thread):
230 class EventAdapter():
179 """ 231 """
180 Dummy method which can be used to check the server is still alive 232 Adapter to wrap our event queue since the caller (bb.event) expects to
233 call a send() method, but our actual queue only has put()
181 """ 234 """
182 return True 235 def __init__(self, notify):
236 self.queue = []
237 self.notify = notify
238 self.qlock = threading.Lock()
239
240 def send(self, event):
241 self.qlock.acquire()
242 self.queue.append(event)
243 self.qlock.release()
244 self.notify.set()
245
246 def get(self):
247 self.qlock.acquire()
248 if len(self.queue) == 0:
249 self.qlock.release()
250 return None
251 e = self.queue.pop(0)
252 if len(self.queue) == 0:
253 self.notify.clear()
254 self.qlock.release()
255 return e
256
257 def __init__(self, connection):
258 self.connection = connection
259 self.notify = threading.Event()
260 self.event = BitBakeUIEventServer.EventAdapter(self.notify)
261 self.quit = False
262 threading.Thread.__init__(self)
263
264 def terminateServer(self):
265 self.quit = True
266
267 def run(self):
268 while not self.quit:
269 self.notify.wait(0.1)
270 evt = self.event.get()
271 if evt:
272 self.connection.event.sendpickle(pickle.dumps(evt))
273
274class BitBakeXMLRPCEventServerController(SimpleXMLRPCServer):
275 def __init__(self, interface):
276 SimpleXMLRPCServer.__init__(self, interface, logRequests=False, allow_none=True)
277 self.register_function(self.registerEventHandler, "registerEventHandler")
278 self.register_function(self.unregisterEventHandler, "unregisterEventHandler")
279 self.register_function(self.terminateServer, "terminateServer")
280 #self.register_function(self.runCommand, "runCommand")
281 self.quit = False
282 self.clients = {}
283 self.client_ui_ids = {}
284
285 def registerEventHandler(self, host, port):
286 """
287 Register a remote UI Event Handler
288 """
289 print "registering handler %s:%s" % (host,port)
290 connection = xmlrpclib.ServerProxy("http://%s:%d/" % (host, port), allow_none=True)
291 client_hash = "%s:%d" % (host, port)
292 if self.clients.has_key(client_hash):
293 return None
294 client_ui_server = BitBakeUIEventServer(connection)
295 self.client_ui_ids[client_hash] = bb.event.register_UIHhandler(client_ui_server)
296 client_ui_server.start()
297 self.clients[client_hash] = client_ui_server
298 return client_hash
299
300 def unregisterEventHandler(self, client_hash):
301 """
302 Unregister a remote UI Event Handler
303 """
304 print "unregistering handler %s:%s" % (host,port)
305 client_thread = self.clients[client_hash]
306 if client_thread:
307 bb.event.unregister_UIHhandler(self.clients_ui_ids[client_hash])
308 client_thread.terminateServer()
309 client_thread.join()
310 return True
311 else:
312 return False
313
314 def terminateServer(self):
315 self.quit = True
316
317 def runCommand(self, cmd):
318 return None
319
320 def serve_forever(self, main_server):
321 self.main_server = main_server
322 while not self.quit:
323 self.handle_request()
324 self.server_close()
183 325
184class BitBakeXMLRPCServer(SimpleXMLRPCServer): 326class BitBakeXMLRPCServer(SimpleXMLRPCServer):
185 # remove this when you're done with debugging 327 # remove this when you're done with debugging
@@ -190,13 +332,15 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
190 Constructor 332 Constructor
191 """ 333 """
192 SimpleXMLRPCServer.__init__(self, interface, 334 SimpleXMLRPCServer.__init__(self, interface,
193 requestHandler=SimpleXMLRPCRequestHandler, 335 requestHandler=BitBakeXMLRPCRequestHandler,
194 logRequests=False, allow_none=True) 336 logRequests=False, allow_none=True)
195 self._idlefuns = {} 337 self._idlefuns = {}
196 self.host, self.port = self.socket.getsockname() 338 self.host, self.port = self.socket.getsockname()
339 self.connection_token = None
197 #self.register_introspection_functions() 340 #self.register_introspection_functions()
198 self.commands = BitBakeServerCommands(self) 341 self.commands = BitBakeServerCommands(self)
199 self.autoregister_all_functions(self.commands, "") 342 self.autoregister_all_functions(self.commands, "")
343 self.interface = interface
200 344
201 def addcooker(self, cooker): 345 def addcooker(self, cooker):
202 self.cooker = cooker 346 self.cooker = cooker
@@ -218,8 +362,16 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
218 self._idlefuns[function] = data 362 self._idlefuns[function] = data
219 363
220 def serve_forever(self): 364 def serve_forever(self):
365 # Create and run the event server controller in a separate thread
366 evt_server_ctrl = BitBakeXMLRPCEventServerController((self.host, self.port + 2))
367 self.event_controller_thread = threading.Thread(target = evt_server_ctrl.serve_forever, args = (self,))
368 self.event_controller_thread.start()
369 # Start the actual XMLRPC server
221 bb.cooker.server_main(self.cooker, self._serve_forever) 370 bb.cooker.server_main(self.cooker, self._serve_forever)
222 371
372 def removeClient(self):
373 self.commands.removeClient()
374
223 def _serve_forever(self): 375 def _serve_forever(self):
224 """ 376 """
225 Serve Requests. Overloaded to honor a quit command 377 Serve Requests. Overloaded to honor a quit command
@@ -259,10 +411,15 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
259 retval = function(self, data, True) 411 retval = function(self, data, True)
260 except: 412 except:
261 pass 413 pass
262 414 # Terminate the event server
415 self.event_controller_thread.terminateServer()
416 self.event_controller_thread.join()
263 self.server_close() 417 self.server_close()
264 return 418 return
265 419
420 def set_connection_token(self, token):
421 self.connection_token = token
422
266class BitbakeServerInfo(): 423class BitbakeServerInfo():
267 def __init__(self, host, port): 424 def __init__(self, host, port):
268 self.host = host 425 self.host = host
@@ -321,4 +478,41 @@ class BitBakeServer(object):
321 478
322 def establishConnection(self): 479 def establishConnection(self):
323 self.connection = BitBakeServerConnection(self.serverinfo) 480 self.connection = BitBakeServerConnection(self.serverinfo)
324 return self.connection 481 return self.connection.connect()
482
483 def set_connection_token(self, token):
484 self.connection.transport.set_connection_token(token)
485
486 def endSession(self):
487 self.connection.terminate()
488
489class BitBakeXMLRPCClient(object):
490
491 def __init__(self):
492 pass
493
494 def saveConnectionDetails(self, remote):
495 self.remote = remote
496
497 def establishConnection(self):
498 # The format of "remote" must be "server:port"
499 try:
500 [host, port] = self.remote.split(":")
501 port = int(port)
502 except:
503 return None
504 # We need our IP for the server connection. We get the IP
505 # by trying to connect with the server
506 try:
507 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
508 s.connect((host, port))
509 ip = s.getsockname()[0]
510 s.close()
511 except:
512 return None
513 self.serverinfo = BitbakeServerInfo(host, port)
514 self.connection = BitBakeServerConnection(self.serverinfo, (ip, 0))
515 return self.connection.connect()
516
517 def endSession(self):
518 self.connection.removeClient()