summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/utils/qemurunner.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/utils/qemurunner.py')
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py128
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
13import socket 13import socket
14import select 14import select
15import errno 15import errno
16import threading
16 17
17import logging 18import logging
18logger = logging.getLogger("BitBake.QemuRunner") 19logger = 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.
340class 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