diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2013-06-17 12:11:51 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2013-06-17 16:09:10 +0100 |
commit | 3ea9d647ec35c4d30dcaa5b58e9471b775a4716c (patch) | |
tree | e3e5022ce0e34320bd555942c418a3cb02282651 /bitbake | |
parent | 194b395f85dbed216b7dd51e66f8b567955307f2 (diff) | |
download | poky-3ea9d647ec35c4d30dcaa5b58e9471b775a4716c.tar.gz |
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>
Diffstat (limited to 'bitbake')
-rwxr-xr-x | bitbake/bin/bitbake | 8 | ||||
-rw-r--r-- | bitbake/lib/bb/command.py | 7 | ||||
-rw-r--r-- | bitbake/lib/bb/server/xmlrpc.py | 26 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 29 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/uievent.py | 1 |
5 files changed, 49 insertions, 22 deletions
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): | |||
197 | parser.add_option("", "--remote-server", help = "Connect to the specified server", | 197 | parser.add_option("", "--remote-server", help = "Connect to the specified server", |
198 | action = "store", dest = "remote_server", default = False) | 198 | action = "store", dest = "remote_server", default = False) |
199 | 199 | ||
200 | parser.add_option("", "--observe-only", help = "Connect to a server as an observing-only client", | ||
201 | action = "store_true", dest = "observe_only", default = False) | ||
202 | |||
200 | options, targets = parser.parse_args(sys.argv) | 203 | options, targets = parser.parse_args(sys.argv) |
201 | return options, targets[1:] | 204 | return options, targets[1:] |
202 | 205 | ||
@@ -269,6 +272,9 @@ def main(): | |||
269 | if configParams.remote_server and configParams.servertype != "xmlrpc": | 272 | if configParams.remote_server and configParams.servertype != "xmlrpc": |
270 | sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n") | 273 | sys.exit("FATAL: If '--remote-server' is defined, we must set the servertype as 'xmlrpc'.\n") |
271 | 274 | ||
275 | if configParams.observe_only and (not configParams.remote_server or configParams.bind): | ||
276 | sys.exit("FATAL: '--observe-only' can only be used by UI clients connecting to a server.\n") | ||
277 | |||
272 | if "BBDEBUG" in os.environ: | 278 | if "BBDEBUG" in os.environ: |
273 | level = int(os.environ["BBDEBUG"]) | 279 | level = int(os.environ["BBDEBUG"]) |
274 | if level > configuration.debug: | 280 | if level > configuration.debug: |
@@ -295,7 +301,7 @@ def main(): | |||
295 | server = start_server(servermodule, configParams, configuration) | 301 | server = start_server(servermodule, configParams, configuration) |
296 | else: | 302 | else: |
297 | # we start a stub server that is actually a XMLRPClient to | 303 | # we start a stub server that is actually a XMLRPClient to |
298 | server = servermodule.BitBakeXMLRPCClient() | 304 | server = servermodule.BitBakeXMLRPCClient(configParams.observe_only) |
299 | server.saveConnectionDetails(configParams.remote_server) | 305 | server.saveConnectionDetails(configParams.remote_server) |
300 | 306 | ||
301 | logger.removeHandler(handler) | 307 | 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: | |||
59 | # FIXME Add lock for this | 59 | # FIXME Add lock for this |
60 | self.currentAsyncCommand = None | 60 | self.currentAsyncCommand = None |
61 | 61 | ||
62 | def runCommand(self, commandline): | 62 | def runCommand(self, commandline, ro_only = False): |
63 | command = commandline.pop(0) | 63 | command = commandline.pop(0) |
64 | if hasattr(CommandsSync, command): | 64 | if hasattr(CommandsSync, command): |
65 | # Can run synchronous commands straight away | 65 | # Can run synchronous commands straight away |
66 | command_method = getattr(self.cmds_sync, command) | 66 | command_method = getattr(self.cmds_sync, command) |
67 | if ro_only: | ||
68 | if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'): | ||
69 | return None, "Not able to execute not readonly commands in readonly mode" | ||
67 | try: | 70 | try: |
68 | result = command_method(self, commandline) | 71 | result = command_method(self, commandline) |
69 | except CommandError as exc: | 72 | except CommandError as exc: |
@@ -153,6 +156,7 @@ class CommandsSync: | |||
153 | expand = params[1] | 156 | expand = params[1] |
154 | 157 | ||
155 | return command.cooker.data.getVar(varname, expand) | 158 | return command.cooker.data.getVar(varname, expand) |
159 | getVariable.readonly = True | ||
156 | 160 | ||
157 | def setVariable(self, command, params): | 161 | def setVariable(self, command, params): |
158 | """ | 162 | """ |
@@ -200,6 +204,7 @@ class CommandsSync: | |||
200 | Get the CPU count on the bitbake server | 204 | Get the CPU count on the bitbake server |
201 | """ | 205 | """ |
202 | return bb.utils.cpu_count() | 206 | return bb.utils.cpu_count() |
207 | getCpuCount.readonly = True | ||
203 | 208 | ||
204 | def matchFile(self, command, params): | 209 | def matchFile(self, command, params): |
205 | fMatch = params[0] | 210 | 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(): | |||
93 | """ | 93 | """ |
94 | Run a cooker command on the server | 94 | Run a cooker command on the server |
95 | """ | 95 | """ |
96 | return self.cooker.command.runCommand(command) | 96 | return self.cooker.command.runCommand(command, self.server.readonly) |
97 | 97 | ||
98 | def terminateServer(self): | 98 | def terminateServer(self): |
99 | """ | 99 | """ |
@@ -124,7 +124,7 @@ class BitBakeServerCommands(): | |||
124 | # ("service unavailable") is returned to the client. | 124 | # ("service unavailable") is returned to the client. |
125 | class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): | 125 | class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): |
126 | def __init__(self, request, client_address, server): | 126 | def __init__(self, request, client_address, server): |
127 | self.connection_token = server.connection_token | 127 | self.server = server |
128 | SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server) | 128 | SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server) |
129 | 129 | ||
130 | def do_POST(self): | 130 | def do_POST(self): |
@@ -132,9 +132,13 @@ class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): | |||
132 | remote_token = self.headers["Bitbake-token"] | 132 | remote_token = self.headers["Bitbake-token"] |
133 | except: | 133 | except: |
134 | remote_token = None | 134 | remote_token = None |
135 | if remote_token != self.connection_token: | 135 | if remote_token != self.server.connection_token and remote_token != "observer": |
136 | self.report_503() | 136 | self.report_503() |
137 | else: | 137 | else: |
138 | if remote_token == "observer": | ||
139 | self.server.readonly = True | ||
140 | else: | ||
141 | self.server.readonly = False | ||
138 | SimpleXMLRPCRequestHandler.do_POST(self) | 142 | SimpleXMLRPCRequestHandler.do_POST(self) |
139 | 143 | ||
140 | def report_503(self): | 144 | def report_503(self): |
@@ -283,13 +287,17 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer): | |||
283 | self.connection_token = token | 287 | self.connection_token = token |
284 | 288 | ||
285 | class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection): | 289 | class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection): |
286 | def __init__(self, serverImpl, clientinfo=("localhost", 0)): | 290 | def __init__(self, serverImpl, clientinfo=("localhost", 0), observer_only = False): |
287 | self.connection, self.transport = _create_server(serverImpl.host, serverImpl.port) | 291 | self.connection, self.transport = _create_server(serverImpl.host, serverImpl.port) |
288 | self.clientinfo = clientinfo | 292 | self.clientinfo = clientinfo |
289 | self.serverImpl = serverImpl | 293 | self.serverImpl = serverImpl |
294 | self.observer_only = observer_only | ||
290 | 295 | ||
291 | def connect(self): | 296 | def connect(self): |
292 | token = self.connection.addClient() | 297 | if not self.observer_only: |
298 | token = self.connection.addClient() | ||
299 | else: | ||
300 | token = "observer" | ||
293 | if token is None: | 301 | if token is None: |
294 | return None | 302 | return None |
295 | self.transport.set_connection_token(token) | 303 | self.transport.set_connection_token(token) |
@@ -299,7 +307,8 @@ class BitBakeXMLRPCServerConnection(BitBakeBaseServerConnection): | |||
299 | return self | 307 | return self |
300 | 308 | ||
301 | def removeClient(self): | 309 | def removeClient(self): |
302 | self.connection.removeClient() | 310 | if not self.observer_only: |
311 | self.connection.removeClient() | ||
303 | 312 | ||
304 | def terminate(self): | 313 | def terminate(self): |
305 | # Don't wait for server indefinitely | 314 | # Don't wait for server indefinitely |
@@ -331,7 +340,8 @@ class BitBakeServer(BitBakeBaseServer): | |||
331 | 340 | ||
332 | class BitBakeXMLRPCClient(BitBakeBaseServer): | 341 | class BitBakeXMLRPCClient(BitBakeBaseServer): |
333 | 342 | ||
334 | def __init__(self): | 343 | def __init__(self, observer_only = False): |
344 | self.observer_only = observer_only | ||
335 | pass | 345 | pass |
336 | 346 | ||
337 | def saveConnectionDetails(self, remote): | 347 | def saveConnectionDetails(self, remote): |
@@ -354,7 +364,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer): | |||
354 | except: | 364 | except: |
355 | return None | 365 | return None |
356 | self.serverImpl = XMLRPCProxyServer(host, port) | 366 | self.serverImpl = XMLRPCProxyServer(host, port) |
357 | self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0)) | 367 | self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only) |
358 | return self.connection.connect() | 368 | return self.connection.connect() |
359 | 369 | ||
360 | def endSession(self): | 370 | 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): | |||
216 | fd = sys.stdin.fileno() | 216 | fd = sys.stdin.fileno() |
217 | self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup) | 217 | self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup) |
218 | 218 | ||
219 | def main(server, eventHandler, params, tf = TerminalFilter): | 219 | def _log_settings_from_server(server): |
220 | |||
221 | # Get values of variables which control our output | 220 | # Get values of variables which control our output |
222 | includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"]) | 221 | includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"]) |
223 | if error: | 222 | if error: |
224 | logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error) | 223 | logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error) |
225 | return 1 | 224 | raise BaseException(error) |
226 | loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) | 225 | loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) |
227 | if error: | 226 | if error: |
228 | logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error) | 227 | logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error) |
229 | return 1 | 228 | raise BaseException(error) |
230 | consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"]) | 229 | consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"]) |
231 | if error: | 230 | if error: |
232 | logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error) | 231 | logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error) |
233 | return 1 | 232 | raise BaseException(error) |
233 | return includelogs, loglines, consolelogfile | ||
234 | |||
235 | def main(server, eventHandler, params, tf = TerminalFilter): | ||
236 | |||
237 | includelogs, loglines, consolelogfile = _log_settings_from_server(server) | ||
234 | 238 | ||
235 | if sys.stdin.isatty() and sys.stdout.isatty(): | 239 | if sys.stdin.isatty() and sys.stdout.isatty(): |
236 | log_exec_tty = True | 240 | log_exec_tty = True |
@@ -254,7 +258,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
254 | consolelog.setFormatter(conlogformat) | 258 | consolelog.setFormatter(conlogformat) |
255 | logger.addHandler(consolelog) | 259 | logger.addHandler(consolelog) |
256 | 260 | ||
257 | try: | 261 | if not params.observe_only: |
258 | params.updateFromServer(server) | 262 | params.updateFromServer(server) |
259 | cmdline = params.parseActions() | 263 | cmdline = params.parseActions() |
260 | if not cmdline: | 264 | if not cmdline: |
@@ -271,9 +275,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
271 | elif ret != True: | 275 | elif ret != True: |
272 | logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) | 276 | logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) |
273 | return 1 | 277 | return 1 |
274 | except xmlrpclib.Fault as x: | 278 | |
275 | logger.error("XMLRPC Fault getting commandline:\n %s" % x) | ||
276 | return 1 | ||
277 | 279 | ||
278 | parseprogress = None | 280 | parseprogress = None |
279 | cacheprogress = None | 281 | cacheprogress = None |
@@ -320,7 +322,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
320 | elif event.levelno == format.WARNING: | 322 | elif event.levelno == format.WARNING: |
321 | warnings = warnings + 1 | 323 | warnings = warnings + 1 |
322 | # For "normal" logging conditions, don't show note logs from tasks | 324 | # For "normal" logging conditions, don't show note logs from tasks |
323 | # but do show them if the user has changed the default log level to | 325 | # but do show them if the user has changed the default log level to |
324 | # include verbose/debug messages | 326 | # include verbose/debug messages |
325 | if event.taskpid != 0 and event.levelno <= format.NOTE: | 327 | if event.taskpid != 0 and event.levelno <= format.NOTE: |
326 | continue | 328 | continue |
@@ -469,12 +471,15 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
469 | pass | 471 | pass |
470 | except KeyboardInterrupt: | 472 | except KeyboardInterrupt: |
471 | termfilter.clearFooter() | 473 | termfilter.clearFooter() |
472 | if main.shutdown == 1: | 474 | if params.observe_only: |
475 | print("\nKeyboard Interrupt, exiting observer...") | ||
476 | main.shutdown = 2 | ||
477 | if not params.observe_only and main.shutdown == 1: | ||
473 | print("\nSecond Keyboard Interrupt, stopping...\n") | 478 | print("\nSecond Keyboard Interrupt, stopping...\n") |
474 | _, error = server.runCommand(["stateStop"]) | 479 | _, error = server.runCommand(["stateStop"]) |
475 | if error: | 480 | if error: |
476 | logger.error("Unable to cleanly stop: %s" % error) | 481 | logger.error("Unable to cleanly stop: %s" % error) |
477 | if main.shutdown == 0: | 482 | if not params.observe_only and main.shutdown == 0: |
478 | print("\nKeyboard Interrupt, closing down...\n") | 483 | print("\nKeyboard Interrupt, closing down...\n") |
479 | interrupted = True | 484 | interrupted = True |
480 | _, error = server.runCommand(["stateShutdown"]) | 485 | _, 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: | |||
84 | 84 | ||
85 | def startCallbackHandler(self): | 85 | def startCallbackHandler(self): |
86 | 86 | ||
87 | self.server.timeout = 1 | ||
87 | while not self.server.quit: | 88 | while not self.server.quit: |
88 | self.server.handle_request() | 89 | self.server.handle_request() |
89 | self.server.server_close() | 90 | self.server.server_close() |