From 3ea9d647ec35c4d30dcaa5b58e9471b775a4716c Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Mon, 17 Jun 2013 12:11:51 +0100 Subject: bitbake: knotty, xmlrpc: add observer-only mode I add an observer only mode for the knotty UI and the XMLRPC server that will allow the UI to register a callback with a server in order to receive events. The observer-UI is able to send read-only commands to the server, and also is able to register as an event handler. Read-only commands are the commands that do not change the state of the server and have been marked as such in the command module. The observer can switch to a full client if it calls addClient at any time, and the server has no other client running. (Bitbake rev: 4de9ee21f1fa4d04937cc7430fb1fc8b7a8f61e2) Signed-off-by: Alexandru DAMIAN Signed-off-by: Richard Purdie --- bitbake/bin/bitbake | 8 +++++++- bitbake/lib/bb/command.py | 7 ++++++- bitbake/lib/bb/server/xmlrpc.py | 26 ++++++++++++++++++-------- bitbake/lib/bb/ui/knotty.py | 29 +++++++++++++++++------------ bitbake/lib/bb/ui/uievent.py | 1 + 5 files changed, 49 insertions(+), 22 deletions(-) (limited to 'bitbake') diff --git a/bitbake/bin/bitbake b/bitbake/bin/bitbake index 1fac9feb24..e77266b4ba 100755 --- a/bitbake/bin/bitbake +++ b/bitbake/bin/bitbake @@ -197,6 +197,9 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters): parser.add_option("", "--remote-server", help = "Connect to the specified server", action = "store", dest = "remote_server", default = False) + parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client", + action = "store_true", dest = "observe_only", default = False) + options, targets = parser.parse_args(sys.argv) return options, targets[1:] @@ -269,6 +272,9 @@ def main(): 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 configParams.observe_only and (not configParams.remote_server or configParams.bind): + sys.exit("FATAL: '--observe-only' can only be used by UI clients connecting to a server.\n") + if "BBDEBUG" in os.environ: level = int(os.environ["BBDEBUG"]) if level > configuration.debug: @@ -295,7 +301,7 @@ def main(): server = start_server(servermodule, configParams, configuration) else: # we start a stub server that is actually a XMLRPClient to - server = servermodule.BitBakeXMLRPCClient() + server = servermodule.BitBakeXMLRPCClient(configParams.observe_only) server.saveConnectionDetails(configParams.remote_server) logger.removeHandler(handler) diff --git a/bitbake/lib/bb/command.py b/bitbake/lib/bb/command.py index ab6950111f..5f696c2aee 100644 --- a/bitbake/lib/bb/command.py +++ b/bitbake/lib/bb/command.py @@ -59,11 +59,14 @@ class Command: # FIXME Add lock for this self.currentAsyncCommand = None - def runCommand(self, commandline): + def runCommand(self, commandline, ro_only = False): command = commandline.pop(0) if hasattr(CommandsSync, command): # Can run synchronous commands straight away command_method = getattr(self.cmds_sync, command) + if ro_only: + if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'): + return None, "Not able to execute not readonly commands in readonly mode" try: result = command_method(self, commandline) except CommandError as exc: @@ -153,6 +156,7 @@ class CommandsSync: expand = params[1] return command.cooker.data.getVar(varname, expand) + getVariable.readonly = True def setVariable(self, command, params): """ @@ -200,6 +204,7 @@ class CommandsSync: Get the CPU count on the bitbake server """ return bb.utils.cpu_count() + getCpuCount.readonly = True def matchFile(self, command, params): fMatch = params[0] diff --git a/bitbake/lib/bb/server/xmlrpc.py b/bitbake/lib/bb/server/xmlrpc.py index 5045e55ae2..026415efd5 100644 --- a/bitbake/lib/bb/server/xmlrpc.py +++ b/bitbake/lib/bb/server/xmlrpc.py @@ -93,7 +93,7 @@ class BitBakeServerCommands(): """ Run a cooker command on the server """ - return self.cooker.command.runCommand(command) + return self.cooker.command.runCommand(command, self.server.readonly) def terminateServer(self): """ @@ -124,7 +124,7 @@ class BitBakeServerCommands(): # ("service unavailable") is returned to the client. class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def __init__(self, request, client_address, server): - self.connection_token = server.connection_token + self.server = server SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server) def do_POST(self): @@ -132,9 +132,13 @@ class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): remote_token = self.headers["Bitbake-token"] except: remote_token = None - if remote_token != self.connection_token: + if remote_token != self.server.connection_token and remote_token != "observer": self.report_503() else: + if remote_token == "observer": + self.server.readonly = True + else: + self.server.readonly = False SimpleXMLRPCRequestHandler.do_POST(self) def report_503(self): @@ -283,13 +287,17 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer): self.connection_token = token class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection): - def __init__(self, serverImpl, clientinfo=("localhost", 0)): + def __init__(self, serverImpl, clientinfo=("localhost", 0), observer_only = False): self.connection, self.transport = _create_server(serverImpl.host, serverImpl.port) self.clientinfo = clientinfo self.serverImpl = serverImpl + self.observer_only = observer_only def connect(self): - token = self.connection.addClient() + if not self.observer_only: + token = self.connection.addClient() + else: + token = "observer" if token is None: return None self.transport.set_connection_token(token) @@ -299,7 +307,8 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection): return self def removeClient(self): - self.connection.removeClient() + if not self.observer_only: + self.connection.removeClient() def terminate(self): # Don't wait for server indefinitely @@ -331,7 +340,8 @@ class BitBakeServer(BitBakeBaseServer): class BitBakeXMLRPCClient(BitBakeBaseServer): - def __init__(self): + def __init__(self, observer_only = False): + self.observer_only = observer_only pass def saveConnectionDetails(self, remote): @@ -354,7 +364,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer): except: return None self.serverImpl = XMLRPCProxyServer(host, port) - self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0)) + self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only) return self.connection.connect() def endSession(self): diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py index 389c3cc64d..c6a1d3f98a 100644 --- a/bitbake/lib/bb/ui/knotty.py +++ b/bitbake/lib/bb/ui/knotty.py @@ -216,21 +216,25 @@ class TerminalFilter(object): fd = sys.stdin.fileno() self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup) -def main(server, eventHandler, params, tf = TerminalFilter): - +def _log_settings_from_server(server): # Get values of variables which control our output includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"]) if error: logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error) - return 1 + raise BaseException(error) loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) if error: logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error) - return 1 + raise BaseException(error) consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"]) if error: logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error) - return 1 + raise BaseException(error) + return includelogs, loglines, consolelogfile + +def main(server, eventHandler, params, tf = TerminalFilter): + + includelogs, loglines, consolelogfile = _log_settings_from_server(server) if sys.stdin.isatty() and sys.stdout.isatty(): log_exec_tty = True @@ -254,7 +258,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): consolelog.setFormatter(conlogformat) logger.addHandler(consolelog) - try: + if not params.observe_only: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: @@ -271,9 +275,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): elif ret != True: logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) return 1 - except xmlrpclib.Fault as x: - logger.error("XMLRPC Fault getting commandline:\n %s" % x) - return 1 + parseprogress = None cacheprogress = None @@ -320,7 +322,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): elif event.levelno == format.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks - # but do show them if the user has changed the default log level to + # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= format.NOTE: continue @@ -469,12 +471,15 @@ def main(server, eventHandler, params, tf = TerminalFilter): pass except KeyboardInterrupt: termfilter.clearFooter() - if main.shutdown == 1: + if params.observe_only: + print("\nKeyboard Interrupt, exiting observer...") + main.shutdown = 2 + if not params.observe_only and main.shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateStop"]) if error: logger.error("Unable to cleanly stop: %s" % error) - if main.shutdown == 0: + if not params.observe_only and main.shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") interrupted = True _, error = server.runCommand(["stateShutdown"]) diff --git a/bitbake/lib/bb/ui/uievent.py b/bitbake/lib/bb/ui/uievent.py index 0b9a836d0f..038029fcfa 100644 --- a/bitbake/lib/bb/ui/uievent.py +++ b/bitbake/lib/bb/ui/uievent.py @@ -84,6 +84,7 @@ class BBUIEventQueue: def startCallbackHandler(self): + self.server.timeout = 1 while not self.server.quit: self.server.handle_request() self.server.server_close() -- cgit v1.2.3-54-g00ecf