diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2013-05-28 16:52:02 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2013-05-30 10:44:00 +0100 |
commit | d0861b7a12113c6626c6206faf3a9389fb8ef5cb (patch) | |
tree | 7b7c9a012c28c71e3ae711e56a611d2084c22d14 | |
parent | 0fc3a1eddfbab5cbf61c028cf8bc0d6b27b6c420 (diff) | |
download | poky-d0861b7a12113c6626c6206faf3a9389fb8ef5cb.tar.gz |
bitbake: bitbake: xmlrpc remote server
Added code in XMLRPC server that creates a stub local server
for a client-only connection and is able to connect to
a remote server, and receive events from the remote server.
Added the option to start a client with a remote server in
bitbake.
Original code by Bogdan Marinescu <bogdan.a.marinescu@intel.com>
(Bitbake rev: 25b2af76104d5aaf6435de8c158e0407512f97ce)
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-x | bitbake/bin/bitbake | 17 | ||||
-rw-r--r-- | bitbake/lib/bb/server/xmlrpc.py | 206 |
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 | |||
35 | import xmlrpclib, sys | 35 | import xmlrpclib, sys |
36 | from bb import daemonize | 36 | from bb import daemonize |
37 | from bb.ui import uievent | 37 | from bb.ui import uievent |
38 | import hashlib, time | ||
39 | import socket | ||
40 | import os, signal | ||
41 | import threading | ||
42 | try: | ||
43 | import cPickle as pickle | ||
44 | except ImportError: | ||
45 | import pickle | ||
38 | 46 | ||
39 | DEBUG = False | 47 | DEBUG = 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. | ||
205 | class 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 | |||
229 | class 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 | |||
274 | class 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 | ||
184 | class BitBakeXMLRPCServer(SimpleXMLRPCServer): | 326 | class 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 | |||
266 | class BitbakeServerInfo(): | 423 | class 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 | |||
489 | class 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() | ||