diff options
Diffstat (limited to 'meta/lib/oeqa/utils')
-rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 128 |
1 files changed, 126 insertions, 2 deletions
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index c8d689900d..e976fd0819 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py | |||
@@ -13,6 +13,7 @@ import re | |||
13 | import socket | 13 | import socket |
14 | import select | 14 | import select |
15 | import errno | 15 | import errno |
16 | import threading | ||
16 | 17 | ||
17 | import logging | 18 | import logging |
18 | logger = logging.getLogger("BitBake.QemuRunner") | 19 | logger = logging.getLogger("BitBake.QemuRunner") |
@@ -38,6 +39,7 @@ class QemuRunner: | |||
38 | self.logfile = logfile | 39 | self.logfile = logfile |
39 | self.boottime = boottime | 40 | self.boottime = boottime |
40 | self.logged = False | 41 | self.logged = False |
42 | self.thread = None | ||
41 | 43 | ||
42 | self.runqemutime = 60 | 44 | self.runqemutime = 60 |
43 | 45 | ||
@@ -81,6 +83,7 @@ class QemuRunner: | |||
81 | os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image | 83 | os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image |
82 | 84 | ||
83 | try: | 85 | try: |
86 | threadsock, threadport = self.create_socket() | ||
84 | self.server_socket, self.serverport = self.create_socket() | 87 | self.server_socket, self.serverport = self.create_socket() |
85 | except socket.error, msg: | 88 | except socket.error, msg: |
86 | logger.error("Failed to create listening socket: %s" % msg[1]) | 89 | logger.error("Failed to create listening socket: %s" % msg[1]) |
@@ -89,7 +92,7 @@ class QemuRunner: | |||
89 | # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact | 92 | # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact |
90 | # badly with screensavers. | 93 | # badly with screensavers. |
91 | os.environ["QEMU_DONT_GRAB"] = "1" | 94 | os.environ["QEMU_DONT_GRAB"] = "1" |
92 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport | 95 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:{} -serial tcp:127.0.0.1:{}"'.format(threadport, self.serverport) |
93 | if qemuparams: | 96 | if qemuparams: |
94 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | 97 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' |
95 | 98 | ||
@@ -138,6 +141,18 @@ class QemuRunner: | |||
138 | return False | 141 | return False |
139 | logger.info("Target IP: %s" % self.ip) | 142 | logger.info("Target IP: %s" % self.ip) |
140 | logger.info("Server IP: %s" % self.server_ip) | 143 | logger.info("Server IP: %s" % self.server_ip) |
144 | |||
145 | logger.info("Starting logging thread") | ||
146 | self.thread = LoggingThread(self.log, threadsock, logger) | ||
147 | self.thread.start() | ||
148 | if not self.thread.connection_established.wait(self.boottime): | ||
149 | logger.error("Didn't receive a console connection from qemu. " | ||
150 | "Here is the qemu command line used:\n%s\nand " | ||
151 | "output from runqemu:\n%s" % (cmdline, | ||
152 | getOutput(output))) | ||
153 | self.stop_thread() | ||
154 | return False | ||
155 | |||
141 | logger.info("Waiting at most %d seconds for login banner" % self.boottime) | 156 | logger.info("Waiting at most %d seconds for login banner" % self.boottime) |
142 | endtime = time.time() + self.boottime | 157 | endtime = time.time() + self.boottime |
143 | socklist = [self.server_socket] | 158 | socklist = [self.server_socket] |
@@ -157,7 +172,6 @@ class QemuRunner: | |||
157 | else: | 172 | else: |
158 | data = sock.recv(1024) | 173 | data = sock.recv(1024) |
159 | if data: | 174 | if data: |
160 | self.log(data) | ||
161 | bootlog += data | 175 | bootlog += data |
162 | if re.search(".* login:", bootlog): | 176 | if re.search(".* login:", bootlog): |
163 | self.server_socket = qemusock | 177 | self.server_socket = qemusock |
@@ -214,6 +228,12 @@ class QemuRunner: | |||
214 | self.server_socket = None | 228 | self.server_socket = None |
215 | self.qemupid = None | 229 | self.qemupid = None |
216 | self.ip = None | 230 | self.ip = None |
231 | self.stop_thread() | ||
232 | |||
233 | def stop_thread(self): | ||
234 | if self.thread and self.thread.is_alive(): | ||
235 | self.thread.stop() | ||
236 | self.thread.join() | ||
217 | 237 | ||
218 | def restart(self, qemuparams = None): | 238 | def restart(self, qemuparams = None): |
219 | logger.info("Restarting qemu process") | 239 | logger.info("Restarting qemu process") |
@@ -312,3 +332,107 @@ class QemuRunner: | |||
312 | if (status_cmd == "0"): | 332 | if (status_cmd == "0"): |
313 | status = 1 | 333 | status = 1 |
314 | return (status, str(data)) | 334 | return (status, str(data)) |
335 | |||
336 | # This class is for reading data from a socket and passing it to logfunc | ||
337 | # to be processed. It's completely event driven and has a straightforward | ||
338 | # event loop. The mechanism for stopping the thread is a simple pipe which | ||
339 | # will wake up the poll and allow for tearing everything down. | ||
340 | class LoggingThread(threading.Thread): | ||
341 | def __init__(self, logfunc, sock, logger): | ||
342 | self.connection_established = threading.Event() | ||
343 | self.serversock = sock | ||
344 | self.logfunc = logfunc | ||
345 | self.logger = logger | ||
346 | self.readsock = None | ||
347 | self.running = False | ||
348 | |||
349 | self.errorevents = select.POLLERR | select.POLLHUP | select.POLLNVAL | ||
350 | self.readevents = select.POLLIN | select.POLLPRI | ||
351 | |||
352 | threading.Thread.__init__(self, target=self.threadtarget) | ||
353 | |||
354 | def threadtarget(self): | ||
355 | try: | ||
356 | self.eventloop() | ||
357 | finally: | ||
358 | self.teardown() | ||
359 | |||
360 | def run(self): | ||
361 | self.logger.info("Starting logging thread") | ||
362 | self.readpipe, self.writepipe = os.pipe() | ||
363 | threading.Thread.run(self) | ||
364 | |||
365 | def stop(self): | ||
366 | self.logger.info("Stopping logging thread") | ||
367 | if self.running: | ||
368 | os.write(self.writepipe, "stop") | ||
369 | |||
370 | def teardown(self): | ||
371 | self.close_socket(self.serversock) | ||
372 | |||
373 | if self.readsock is not None: | ||
374 | self.close_socket(self.readsock) | ||
375 | |||
376 | self.close_ignore_error(self.readpipe) | ||
377 | self.close_ignore_error(self.writepipe) | ||
378 | self.running = False | ||
379 | |||
380 | def eventloop(self): | ||
381 | poll = select.poll() | ||
382 | eventmask = self.errorevents | self.readevents | ||
383 | poll.register(self.serversock.fileno()) | ||
384 | poll.register(self.readpipe, eventmask) | ||
385 | |||
386 | breakout = False | ||
387 | self.running = True | ||
388 | self.logger.info("Starting thread event loop") | ||
389 | while not breakout: | ||
390 | events = poll.poll() | ||
391 | for event in events: | ||
392 | # An error occurred, bail out | ||
393 | if event[1] & self.errorevents: | ||
394 | raise Exception(self.stringify_event(event[1])) | ||
395 | |||
396 | # Event to stop the thread | ||
397 | if self.readpipe == event[0]: | ||
398 | self.logger.info("Stop event received") | ||
399 | breakout = True | ||
400 | break | ||
401 | |||
402 | # A connection request was received | ||
403 | elif self.serversock.fileno() == event[0]: | ||
404 | self.logger.info("Connection request received") | ||
405 | self.readsock, _ = self.serversock.accept() | ||
406 | poll.unregister(self.serversock.fileno()) | ||
407 | poll.register(self.readsock.fileno()) | ||
408 | |||
409 | self.logger.info("Setting connection established event") | ||
410 | self.connection_established.set() | ||
411 | |||
412 | # Actual data to be logged | ||
413 | elif self.readsock.fileno() == event[0]: | ||
414 | data = self.readsock.recv(1024) | ||
415 | if not data: | ||
416 | raise Exception("No data on read ready socket") | ||
417 | |||
418 | self.logfunc(data) | ||
419 | |||
420 | def stringify_event(self, event): | ||
421 | val = '' | ||
422 | if select.POLLERR == event: | ||
423 | val = 'POLLER' | ||
424 | elif select.POLLHUP == event: | ||
425 | val = 'POLLHUP' | ||
426 | elif select.POLLNVAL == event: | ||
427 | val = 'POLLNVAL' | ||
428 | return val | ||
429 | |||
430 | def close_socket(self, sock): | ||
431 | sock.shutdown(socket.SHUT_RDWR) | ||
432 | sock.close() | ||
433 | |||
434 | def close_ignore_error(self, fd): | ||
435 | try: | ||
436 | os.close(fd) | ||
437 | except OSError: | ||
438 | pass | ||