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 <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
194b395f85
commit
3ea9d647ec
|
@ -197,6 +197,9 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
|
||||||
parser.add_option("", "--remote-server", help = "Connect to the specified server",
|
parser.add_option("", "--remote-server", help = "Connect to the specified server",
|
||||||
action = "store", dest = "remote_server", default = False)
|
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)
|
options, targets = parser.parse_args(sys.argv)
|
||||||
return options, targets[1:]
|
return options, targets[1:]
|
||||||
|
|
||||||
|
@ -269,6 +272,9 @@ def main():
|
||||||
if configParams.remote_server and configParams.servertype != "xmlrpc":
|
if configParams.remote_server and configParams.servertype != "xmlrpc":
|
||||||
sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n")
|
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:
|
if "BBDEBUG" in os.environ:
|
||||||
level = int(os.environ["BBDEBUG"])
|
level = int(os.environ["BBDEBUG"])
|
||||||
if level > configuration.debug:
|
if level > configuration.debug:
|
||||||
|
@ -295,7 +301,7 @@ def main():
|
||||||
server = start_server(servermodule, configParams, configuration)
|
server = start_server(servermodule, configParams, configuration)
|
||||||
else:
|
else:
|
||||||
# we start a stub server that is actually a XMLRPClient to
|
# 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)
|
server.saveConnectionDetails(configParams.remote_server)
|
||||||
|
|
||||||
logger.removeHandler(handler)
|
logger.removeHandler(handler)
|
||||||
|
|
|
@ -59,11 +59,14 @@ class Command:
|
||||||
# FIXME Add lock for this
|
# FIXME Add lock for this
|
||||||
self.currentAsyncCommand = None
|
self.currentAsyncCommand = None
|
||||||
|
|
||||||
def runCommand(self, commandline):
|
def runCommand(self, commandline, ro_only = False):
|
||||||
command = commandline.pop(0)
|
command = commandline.pop(0)
|
||||||
if hasattr(CommandsSync, command):
|
if hasattr(CommandsSync, command):
|
||||||
# Can run synchronous commands straight away
|
# Can run synchronous commands straight away
|
||||||
command_method = getattr(self.cmds_sync, command)
|
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:
|
try:
|
||||||
result = command_method(self, commandline)
|
result = command_method(self, commandline)
|
||||||
except CommandError as exc:
|
except CommandError as exc:
|
||||||
|
@ -153,6 +156,7 @@ class CommandsSync:
|
||||||
expand = params[1]
|
expand = params[1]
|
||||||
|
|
||||||
return command.cooker.data.getVar(varname, expand)
|
return command.cooker.data.getVar(varname, expand)
|
||||||
|
getVariable.readonly = True
|
||||||
|
|
||||||
def setVariable(self, command, params):
|
def setVariable(self, command, params):
|
||||||
"""
|
"""
|
||||||
|
@ -200,6 +204,7 @@ class CommandsSync:
|
||||||
Get the CPU count on the bitbake server
|
Get the CPU count on the bitbake server
|
||||||
"""
|
"""
|
||||||
return bb.utils.cpu_count()
|
return bb.utils.cpu_count()
|
||||||
|
getCpuCount.readonly = True
|
||||||
|
|
||||||
def matchFile(self, command, params):
|
def matchFile(self, command, params):
|
||||||
fMatch = params[0]
|
fMatch = params[0]
|
||||||
|
|
|
@ -93,7 +93,7 @@ class BitBakeServerCommands():
|
||||||
"""
|
"""
|
||||||
Run a cooker command on the server
|
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):
|
def terminateServer(self):
|
||||||
"""
|
"""
|
||||||
|
@ -124,7 +124,7 @@ class BitBakeServerCommands():
|
||||||
# ("service unavailable") is returned to the client.
|
# ("service unavailable") is returned to the client.
|
||||||
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
def __init__(self, request, client_address, server):
|
def __init__(self, request, client_address, server):
|
||||||
self.connection_token = server.connection_token
|
self.server = server
|
||||||
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
|
@ -132,9 +132,13 @@ class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
remote_token = self.headers["Bitbake-token"]
|
remote_token = self.headers["Bitbake-token"]
|
||||||
except:
|
except:
|
||||||
remote_token = None
|
remote_token = None
|
||||||
if remote_token != self.connection_token:
|
if remote_token != self.server.connection_token and remote_token != "observer":
|
||||||
self.report_503()
|
self.report_503()
|
||||||
else:
|
else:
|
||||||
|
if remote_token == "observer":
|
||||||
|
self.server.readonly = True
|
||||||
|
else:
|
||||||
|
self.server.readonly = False
|
||||||
SimpleXMLRPCRequestHandler.do_POST(self)
|
SimpleXMLRPCRequestHandler.do_POST(self)
|
||||||
|
|
||||||
def report_503(self):
|
def report_503(self):
|
||||||
|
@ -283,13 +287,17 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
|
||||||
self.connection_token = token
|
self.connection_token = token
|
||||||
|
|
||||||
class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
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.connection, self.transport = _create_server(serverImpl.host, serverImpl.port)
|
||||||
self.clientinfo = clientinfo
|
self.clientinfo = clientinfo
|
||||||
self.serverImpl = serverImpl
|
self.serverImpl = serverImpl
|
||||||
|
self.observer_only = observer_only
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
token = self.connection.addClient()
|
if not self.observer_only:
|
||||||
|
token = self.connection.addClient()
|
||||||
|
else:
|
||||||
|
token = "observer"
|
||||||
if token is None:
|
if token is None:
|
||||||
return None
|
return None
|
||||||
self.transport.set_connection_token(token)
|
self.transport.set_connection_token(token)
|
||||||
|
@ -299,7 +307,8 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def removeClient(self):
|
def removeClient(self):
|
||||||
self.connection.removeClient()
|
if not self.observer_only:
|
||||||
|
self.connection.removeClient()
|
||||||
|
|
||||||
def terminate(self):
|
def terminate(self):
|
||||||
# Don't wait for server indefinitely
|
# Don't wait for server indefinitely
|
||||||
|
@ -331,7 +340,8 @@ class BitBakeServer(BitBakeBaseServer):
|
||||||
|
|
||||||
class BitBakeXMLRPCClient(BitBakeBaseServer):
|
class BitBakeXMLRPCClient(BitBakeBaseServer):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, observer_only = False):
|
||||||
|
self.observer_only = observer_only
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def saveConnectionDetails(self, remote):
|
def saveConnectionDetails(self, remote):
|
||||||
|
@ -354,7 +364,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
self.serverImpl = XMLRPCProxyServer(host, port)
|
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()
|
return self.connection.connect()
|
||||||
|
|
||||||
def endSession(self):
|
def endSession(self):
|
||||||
|
|
|
@ -216,21 +216,25 @@ class TerminalFilter(object):
|
||||||
fd = sys.stdin.fileno()
|
fd = sys.stdin.fileno()
|
||||||
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
|
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
|
# Get values of variables which control our output
|
||||||
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
|
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
|
||||||
if error:
|
if error:
|
||||||
logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % 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"])
|
loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
|
||||||
if error:
|
if error:
|
||||||
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % 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"])
|
consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
|
||||||
if error:
|
if error:
|
||||||
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % 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():
|
if sys.stdin.isatty() and sys.stdout.isatty():
|
||||||
log_exec_tty = True
|
log_exec_tty = True
|
||||||
|
@ -254,7 +258,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||||
consolelog.setFormatter(conlogformat)
|
consolelog.setFormatter(conlogformat)
|
||||||
logger.addHandler(consolelog)
|
logger.addHandler(consolelog)
|
||||||
|
|
||||||
try:
|
if not params.observe_only:
|
||||||
params.updateFromServer(server)
|
params.updateFromServer(server)
|
||||||
cmdline = params.parseActions()
|
cmdline = params.parseActions()
|
||||||
if not cmdline:
|
if not cmdline:
|
||||||
|
@ -271,9 +275,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||||
elif ret != True:
|
elif ret != True:
|
||||||
logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
|
logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
|
||||||
return 1
|
return 1
|
||||||
except xmlrpclib.Fault as x:
|
|
||||||
logger.error("XMLRPC Fault getting commandline:\n %s" % x)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
parseprogress = None
|
parseprogress = None
|
||||||
cacheprogress = None
|
cacheprogress = None
|
||||||
|
@ -320,7 +322,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||||
elif event.levelno == format.WARNING:
|
elif event.levelno == format.WARNING:
|
||||||
warnings = warnings + 1
|
warnings = warnings + 1
|
||||||
# For "normal" logging conditions, don't show note logs from tasks
|
# 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
|
# include verbose/debug messages
|
||||||
if event.taskpid != 0 and event.levelno <= format.NOTE:
|
if event.taskpid != 0 and event.levelno <= format.NOTE:
|
||||||
continue
|
continue
|
||||||
|
@ -469,12 +471,15 @@ def main(server, eventHandler, params, tf = TerminalFilter):
|
||||||
pass
|
pass
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
termfilter.clearFooter()
|
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")
|
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||||
_, error = server.runCommand(["stateStop"])
|
_, error = server.runCommand(["stateStop"])
|
||||||
if error:
|
if error:
|
||||||
logger.error("Unable to cleanly stop: %s" % 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")
|
print("\nKeyboard Interrupt, closing down...\n")
|
||||||
interrupted = True
|
interrupted = True
|
||||||
_, error = server.runCommand(["stateShutdown"])
|
_, error = server.runCommand(["stateShutdown"])
|
||||||
|
|
|
@ -84,6 +84,7 @@ class BBUIEventQueue:
|
||||||
|
|
||||||
def startCallbackHandler(self):
|
def startCallbackHandler(self):
|
||||||
|
|
||||||
|
self.server.timeout = 1
|
||||||
while not self.server.quit:
|
while not self.server.quit:
|
||||||
self.server.handle_request()
|
self.server.handle_request()
|
||||||
self.server.server_close()
|
self.server.server_close()
|
||||||
|
|
Loading…
Reference in New Issue