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>
This commit is contained in:
parent
0fc3a1eddf
commit
d0861b7a12
|
@ -190,8 +190,13 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||||
|
|
||||||
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
|
parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to",
|
||||||
action = "store", dest = "bind", default = False)
|
action = "store", dest = "bind", default = False)
|
||||||
|
|
||||||
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
|
parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",
|
||||||
action = "store_true", dest = "nosetscene", default = False)
|
action = "store_true", dest = "nosetscene", default = False)
|
||||||
|
|
||||||
|
parser.add_option("", "--remote-server", help = "Connect to the specified server",
|
||||||
|
action = "store", dest = "remote_server", default = False)
|
||||||
|
|
||||||
options, targets = parser.parse_args(sys.argv)
|
options, targets = parser.parse_args(sys.argv)
|
||||||
return options, targets[1:]
|
return options, targets[1:]
|
||||||
|
|
||||||
|
@ -260,6 +265,9 @@ def main():
|
||||||
if configParams.bind and configParams.servertype != "xmlrpc":
|
if configParams.bind and configParams.servertype != "xmlrpc":
|
||||||
sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n")
|
sys.exit("FATAL: If '-B' or '--bind' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||||
|
|
||||||
|
if configParams.remote_server and configParams.servertype != "xmlrpc":
|
||||||
|
sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n")
|
||||||
|
|
||||||
if "BBDEBUG" in os.environ:
|
if "BBDEBUG" in os.environ:
|
||||||
level = int(os.environ["BBDEBUG"])
|
level = int(os.environ["BBDEBUG"])
|
||||||
if level > configuration.debug:
|
if level > configuration.debug:
|
||||||
|
@ -281,8 +289,13 @@ def main():
|
||||||
else:
|
else:
|
||||||
configuration.extra_caches = getattr(ui_module, "extraCaches", [])
|
configuration.extra_caches = getattr(ui_module, "extraCaches", [])
|
||||||
|
|
||||||
# we start a server with a given configuration
|
if not configParams.remote_server:
|
||||||
server = start_server(servermodule, configParams, configuration)
|
# we start a server with a given configuration
|
||||||
|
server = start_server(servermodule, configParams, configuration)
|
||||||
|
else:
|
||||||
|
# we start a stub server that is actually a XMLRPClient to
|
||||||
|
server = servermodule.BitBakeXMLRPCClient()
|
||||||
|
server.saveConnectionDetails(configParams.remote_server)
|
||||||
|
|
||||||
logger.removeHandler(handler)
|
logger.removeHandler(handler)
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,14 @@ import bb
|
||||||
import xmlrpclib, sys
|
import xmlrpclib, sys
|
||||||
from bb import daemonize
|
from bb import daemonize
|
||||||
from bb.ui import uievent
|
from bb.ui import uievent
|
||||||
|
import hashlib, time
|
||||||
|
import socket
|
||||||
|
import os, signal
|
||||||
|
import threading
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
|
@ -175,11 +183,145 @@ class BitBakeServerCommands():
|
||||||
print("Server (cooker) exiting")
|
print("Server (cooker) exiting")
|
||||||
return
|
return
|
||||||
|
|
||||||
def ping(self):
|
def addClient(self):
|
||||||
|
if self.has_client:
|
||||||
|
return None
|
||||||
|
token = hashlib.md5(str(time.time())).hexdigest()
|
||||||
|
self.server.set_connection_token(token)
|
||||||
|
self.has_client = True
|
||||||
|
return token
|
||||||
|
|
||||||
|
def removeClient(self):
|
||||||
|
if self.has_client:
|
||||||
|
self.server.set_connection_token(None)
|
||||||
|
self.has_client = False
|
||||||
|
|
||||||
|
# This request handler checks if the request has a "Bitbake-token" header
|
||||||
|
# field (this comes from the client side) and compares it with its internal
|
||||||
|
# "Bitbake-token" field (this comes from the server). If the two are not
|
||||||
|
# equal, it is assumed that a client is trying to connect to the server
|
||||||
|
# while another client is connected to the server. In this case, a 503 error
|
||||||
|
# ("service unavailable") is returned to the client.
|
||||||
|
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
|
def __init__(self, request, client_address, server):
|
||||||
|
self.connection_token = server.connection_token
|
||||||
|
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
try:
|
||||||
|
remote_token = self.headers["Bitbake-token"]
|
||||||
|
except:
|
||||||
|
remote_token = None
|
||||||
|
if remote_token != self.connection_token:
|
||||||
|
self.report_503()
|
||||||
|
else:
|
||||||
|
SimpleXMLRPCRequestHandler.do_POST(self)
|
||||||
|
|
||||||
|
def report_503(self):
|
||||||
|
self.send_response(503)
|
||||||
|
response = 'No more client allowed'
|
||||||
|
self.send_header("Content-type", "text/plain")
|
||||||
|
self.send_header("Content-length", str(len(response)))
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(response)
|
||||||
|
|
||||||
|
|
||||||
|
class BitBakeUIEventServer(threading.Thread):
|
||||||
|
class EventAdapter():
|
||||||
"""
|
"""
|
||||||
Dummy method which can be used to check the server is still alive
|
Adapter to wrap our event queue since the caller (bb.event) expects to
|
||||||
|
call a send() method, but our actual queue only has put()
|
||||||
"""
|
"""
|
||||||
return True
|
def __init__(self, notify):
|
||||||
|
self.queue = []
|
||||||
|
self.notify = notify
|
||||||
|
self.qlock = threading.Lock()
|
||||||
|
|
||||||
|
def send(self, event):
|
||||||
|
self.qlock.acquire()
|
||||||
|
self.queue.append(event)
|
||||||
|
self.qlock.release()
|
||||||
|
self.notify.set()
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.qlock.acquire()
|
||||||
|
if len(self.queue) == 0:
|
||||||
|
self.qlock.release()
|
||||||
|
return None
|
||||||
|
e = self.queue.pop(0)
|
||||||
|
if len(self.queue) == 0:
|
||||||
|
self.notify.clear()
|
||||||
|
self.qlock.release()
|
||||||
|
return e
|
||||||
|
|
||||||
|
def __init__(self, connection):
|
||||||
|
self.connection = connection
|
||||||
|
self.notify = threading.Event()
|
||||||
|
self.event = BitBakeUIEventServer.EventAdapter(self.notify)
|
||||||
|
self.quit = False
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
|
def terminateServer(self):
|
||||||
|
self.quit = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while not self.quit:
|
||||||
|
self.notify.wait(0.1)
|
||||||
|
evt = self.event.get()
|
||||||
|
if evt:
|
||||||
|
self.connection.event.sendpickle(pickle.dumps(evt))
|
||||||
|
|
||||||
|
class BitBakeXMLRPCEventServerController(SimpleXMLRPCServer):
|
||||||
|
def __init__(self, interface):
|
||||||
|
SimpleXMLRPCServer.__init__(self, interface, logRequests=False, allow_none=True)
|
||||||
|
self.register_function(self.registerEventHandler, "registerEventHandler")
|
||||||
|
self.register_function(self.unregisterEventHandler, "unregisterEventHandler")
|
||||||
|
self.register_function(self.terminateServer, "terminateServer")
|
||||||
|
#self.register_function(self.runCommand, "runCommand")
|
||||||
|
self.quit = False
|
||||||
|
self.clients = {}
|
||||||
|
self.client_ui_ids = {}
|
||||||
|
|
||||||
|
def registerEventHandler(self, host, port):
|
||||||
|
"""
|
||||||
|
Register a remote UI Event Handler
|
||||||
|
"""
|
||||||
|
print "registering handler %s:%s" % (host,port)
|
||||||
|
connection = xmlrpclib.ServerProxy("http://%s:%d/" % (host, port), allow_none=True)
|
||||||
|
client_hash = "%s:%d" % (host, port)
|
||||||
|
if self.clients.has_key(client_hash):
|
||||||
|
return None
|
||||||
|
client_ui_server = BitBakeUIEventServer(connection)
|
||||||
|
self.client_ui_ids[client_hash] = bb.event.register_UIHhandler(client_ui_server)
|
||||||
|
client_ui_server.start()
|
||||||
|
self.clients[client_hash] = client_ui_server
|
||||||
|
return client_hash
|
||||||
|
|
||||||
|
def unregisterEventHandler(self, client_hash):
|
||||||
|
"""
|
||||||
|
Unregister a remote UI Event Handler
|
||||||
|
"""
|
||||||
|
print "unregistering handler %s:%s" % (host,port)
|
||||||
|
client_thread = self.clients[client_hash]
|
||||||
|
if client_thread:
|
||||||
|
bb.event.unregister_UIHhandler(self.clients_ui_ids[client_hash])
|
||||||
|
client_thread.terminateServer()
|
||||||
|
client_thread.join()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def terminateServer(self):
|
||||||
|
self.quit = True
|
||||||
|
|
||||||
|
def runCommand(self, cmd):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def serve_forever(self, main_server):
|
||||||
|
self.main_server = main_server
|
||||||
|
while not self.quit:
|
||||||
|
self.handle_request()
|
||||||
|
self.server_close()
|
||||||
|
|
||||||
class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||||
# remove this when you're done with debugging
|
# remove this when you're done with debugging
|
||||||
|
@ -190,13 +332,15 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
SimpleXMLRPCServer.__init__(self, interface,
|
SimpleXMLRPCServer.__init__(self, interface,
|
||||||
requestHandler=SimpleXMLRPCRequestHandler,
|
requestHandler=BitBakeXMLRPCRequestHandler,
|
||||||
logRequests=False, allow_none=True)
|
logRequests=False, allow_none=True)
|
||||||
self._idlefuns = {}
|
self._idlefuns = {}
|
||||||
self.host, self.port = self.socket.getsockname()
|
self.host, self.port = self.socket.getsockname()
|
||||||
|
self.connection_token = None
|
||||||
#self.register_introspection_functions()
|
#self.register_introspection_functions()
|
||||||
self.commands = BitBakeServerCommands(self)
|
self.commands = BitBakeServerCommands(self)
|
||||||
self.autoregister_all_functions(self.commands, "")
|
self.autoregister_all_functions(self.commands, "")
|
||||||
|
self.interface = interface
|
||||||
|
|
||||||
def addcooker(self, cooker):
|
def addcooker(self, cooker):
|
||||||
self.cooker = cooker
|
self.cooker = cooker
|
||||||
|
@ -218,8 +362,16 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||||
self._idlefuns[function] = data
|
self._idlefuns[function] = data
|
||||||
|
|
||||||
def serve_forever(self):
|
def serve_forever(self):
|
||||||
|
# Create and run the event server controller in a separate thread
|
||||||
|
evt_server_ctrl = BitBakeXMLRPCEventServerController((self.host, self.port + 2))
|
||||||
|
self.event_controller_thread = threading.Thread(target = evt_server_ctrl.serve_forever, args = (self,))
|
||||||
|
self.event_controller_thread.start()
|
||||||
|
# Start the actual XMLRPC server
|
||||||
bb.cooker.server_main(self.cooker, self._serve_forever)
|
bb.cooker.server_main(self.cooker, self._serve_forever)
|
||||||
|
|
||||||
|
def removeClient(self):
|
||||||
|
self.commands.removeClient()
|
||||||
|
|
||||||
def _serve_forever(self):
|
def _serve_forever(self):
|
||||||
"""
|
"""
|
||||||
Serve Requests. Overloaded to honor a quit command
|
Serve Requests. Overloaded to honor a quit command
|
||||||
|
@ -259,10 +411,15 @@ class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||||||
retval = function(self, data, True)
|
retval = function(self, data, True)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
# Terminate the event server
|
||||||
|
self.event_controller_thread.terminateServer()
|
||||||
|
self.event_controller_thread.join()
|
||||||
self.server_close()
|
self.server_close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def set_connection_token(self, token):
|
||||||
|
self.connection_token = token
|
||||||
|
|
||||||
class BitbakeServerInfo():
|
class BitbakeServerInfo():
|
||||||
def __init__(self, host, port):
|
def __init__(self, host, port):
|
||||||
self.host = host
|
self.host = host
|
||||||
|
@ -321,4 +478,41 @@ class BitBakeServer(object):
|
||||||
|
|
||||||
def establishConnection(self):
|
def establishConnection(self):
|
||||||
self.connection = BitBakeServerConnection(self.serverinfo)
|
self.connection = BitBakeServerConnection(self.serverinfo)
|
||||||
return self.connection
|
return self.connection.connect()
|
||||||
|
|
||||||
|
def set_connection_token(self, token):
|
||||||
|
self.connection.transport.set_connection_token(token)
|
||||||
|
|
||||||
|
def endSession(self):
|
||||||
|
self.connection.terminate()
|
||||||
|
|
||||||
|
class BitBakeXMLRPCClient(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def saveConnectionDetails(self, remote):
|
||||||
|
self.remote = remote
|
||||||
|
|
||||||
|
def establishConnection(self):
|
||||||
|
# The format of "remote" must be "server:port"
|
||||||
|
try:
|
||||||
|
[host, port] = self.remote.split(":")
|
||||||
|
port = int(port)
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
# We need our IP for the server connection. We get the IP
|
||||||
|
# by trying to connect with the server
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect((host, port))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
self.serverinfo = BitbakeServerInfo(host, port)
|
||||||
|
self.connection = BitBakeServerConnection(self.serverinfo, (ip, 0))
|
||||||
|
return self.connection.connect()
|
||||||
|
|
||||||
|
def endSession(self):
|
||||||
|
self.connection.removeClient()
|
||||||
|
|
Loading…
Reference in New Issue