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 /bitbake | |
| 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>
Diffstat (limited to 'bitbake')
| -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() | ||
