diff options
| -rw-r--r-- | meta/lib/oeqa/utils/oeqemuconsole.py | 45 | ||||
| -rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 123 |
2 files changed, 81 insertions, 87 deletions
diff --git a/meta/lib/oeqa/utils/oeqemuconsole.py b/meta/lib/oeqa/utils/oeqemuconsole.py deleted file mode 100644 index 95a21332de..0000000000 --- a/meta/lib/oeqa/utils/oeqemuconsole.py +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | import socket | ||
| 2 | import time | ||
| 3 | import re | ||
| 4 | from telnetlib import Telnet | ||
| 5 | |||
| 6 | class oeQemuConsole(Telnet): | ||
| 7 | |||
| 8 | """ | ||
| 9 | Override Telnet class to use unix domain sockets, | ||
| 10 | Telnet uses AF_INET for socket, we don't want that. | ||
| 11 | Also, provide a read_all variant with timeout, that | ||
| 12 | returns whatever output there is. | ||
| 13 | """ | ||
| 14 | |||
| 15 | def __init__(self, stream, logfile): | ||
| 16 | |||
| 17 | Telnet.__init__(self, host=None) | ||
| 18 | self.stream = stream | ||
| 19 | self.logfile = logfile | ||
| 20 | self.eof = 0 | ||
| 21 | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 22 | self.sock.connect(stream) | ||
| 23 | |||
| 24 | def log(self, msg): | ||
| 25 | if self.logfile: | ||
| 26 | with open(self.logfile, "a") as f: | ||
| 27 | f.write("%s\n" % msg) | ||
| 28 | |||
| 29 | |||
| 30 | def read_all_timeout(self, match, timeout=200): | ||
| 31 | """Read until EOF or until timeout or until match. | ||
| 32 | """ | ||
| 33 | ret = False | ||
| 34 | self.process_rawq() | ||
| 35 | endtime = time.time() + timeout | ||
| 36 | while not self.eof and time.time() < endtime: | ||
| 37 | self.fill_rawq() | ||
| 38 | self.process_rawq() | ||
| 39 | if re.search(match, self.cookedq): | ||
| 40 | ret = True | ||
| 41 | break | ||
| 42 | buf = self.cookedq | ||
| 43 | self.cookedq = '' | ||
| 44 | self.log(buf) | ||
| 45 | return (ret, buf) | ||
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index d086203c04..20bb1e5e8d 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py | |||
| @@ -6,26 +6,23 @@ | |||
| 6 | # It's used by testimage.bbclass. | 6 | # It's used by testimage.bbclass. |
| 7 | 7 | ||
| 8 | import subprocess | 8 | import subprocess |
| 9 | import optparse | ||
| 10 | import sys | ||
| 11 | import os | 9 | import os |
| 12 | import time | 10 | import time |
| 13 | import signal | 11 | import signal |
| 14 | import re | 12 | import re |
| 13 | import socket | ||
| 14 | import select | ||
| 15 | import bb | 15 | import bb |
| 16 | from oeqa.utils.oeqemuconsole import oeQemuConsole | ||
| 17 | 16 | ||
| 18 | class QemuRunner: | 17 | class QemuRunner: |
| 19 | 18 | ||
| 20 | def __init__(self, machine, rootfs, display = None, tmpdir = None, logfile = None, boottime = 400): | 19 | def __init__(self, machine, rootfs, display = None, tmpdir = None, logfile = None, boottime = 400, runqemutime = 60): |
| 21 | # Popen object | 20 | # Popen object |
| 22 | self.runqemu = None | 21 | self.runqemu = None |
| 23 | 22 | ||
| 24 | self.machine = machine | 23 | self.machine = machine |
| 25 | self.rootfs = rootfs | 24 | self.rootfs = rootfs |
| 26 | 25 | ||
| 27 | self.streampath = '/tmp/qemuconnection.%s' % os.getpid() | ||
| 28 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial unix:%s,server,nowait"' % self.streampath | ||
| 29 | self.qemupid = None | 26 | self.qemupid = None |
| 30 | self.ip = None | 27 | self.ip = None |
| 31 | 28 | ||
| @@ -33,11 +30,30 @@ class QemuRunner: | |||
| 33 | self.tmpdir = tmpdir | 30 | self.tmpdir = tmpdir |
| 34 | self.logfile = logfile | 31 | self.logfile = logfile |
| 35 | self.boottime = boottime | 32 | self.boottime = boottime |
| 33 | self.runqemutime = runqemutime | ||
| 34 | |||
| 35 | self.bootlog = '' | ||
| 36 | self.qemusock = None | ||
| 37 | |||
| 38 | try: | ||
| 39 | self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 40 | self.server_socket.setblocking(0) | ||
| 41 | self.server_socket.bind(("127.0.0.1",0)) | ||
| 42 | self.server_socket.listen(2) | ||
| 43 | self.serverport = self.server_socket.getsockname()[1] | ||
| 44 | bb.note("Created listening socket for qemu serial console on: 127.0.0.1:%s" % self.serverport) | ||
| 45 | except socket.error, msg: | ||
| 46 | self.server_socket.close() | ||
| 47 | bb.fatal("Failed to create listening socket: %s" %msg[1]) | ||
| 48 | |||
| 49 | |||
| 50 | def log(self, msg): | ||
| 51 | if self.logfile: | ||
| 52 | with open(self.logfile, "a") as f: | ||
| 53 | f.write("%s" % msg) | ||
| 36 | 54 | ||
| 37 | def launch(self, qemuparams = None): | 55 | def launch(self, qemuparams = None): |
| 38 | 56 | ||
| 39 | if qemuparams: | ||
| 40 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | ||
| 41 | 57 | ||
| 42 | if self.display: | 58 | if self.display: |
| 43 | os.environ["DISPLAY"] = self.display | 59 | os.environ["DISPLAY"] = self.display |
| @@ -53,49 +69,70 @@ class QemuRunner: | |||
| 53 | else: | 69 | else: |
| 54 | os.environ["OE_TMPDIR"] = self.tmpdir | 70 | os.environ["OE_TMPDIR"] = self.tmpdir |
| 55 | 71 | ||
| 72 | self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport | ||
| 73 | if qemuparams: | ||
| 74 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | ||
| 75 | |||
| 56 | launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams) | 76 | launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams) |
| 57 | self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp) | 77 | self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp) |
| 58 | 78 | ||
| 59 | bb.note("runqemu started, pid is %s" % self.runqemu.pid) | 79 | bb.note("runqemu started, pid is %s" % self.runqemu.pid) |
| 60 | bb.note("waiting at most 60 seconds for qemu pid") | 80 | bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime) |
| 61 | endtime = time.time() + 60 | 81 | endtime = time.time() + self.runqemutime |
| 62 | while not self.is_alive() and time.time() < endtime: | 82 | while not self.is_alive() and time.time() < endtime: |
| 63 | time.sleep(1) | 83 | time.sleep(1) |
| 64 | 84 | ||
| 65 | if self.is_alive(): | 85 | if self.is_alive(): |
| 66 | bb.note("qemu started - qemu procces pid is %s" % self.qemupid) | 86 | bb.note("qemu started - qemu procces pid is %s" % self.qemupid) |
| 67 | 87 | pscmd = 'ps -p %s -fww | grep -o "192\.168\.7\.[0-9]*::" | awk -F":" \'{print $1}\'' % self.qemupid | |
| 68 | console = oeQemuConsole(self.streampath, self.logfile) | 88 | self.ip = subprocess.Popen(pscmd,shell=True,stdout=subprocess.PIPE).communicate()[0].strip() |
| 89 | if not re.search("^((?:[0-9]{1,3}\.){3}[0-9]{1,3})$", self.ip): | ||
| 90 | bb.note("Couldn't get ip from qemu process arguments, I got '%s'" % self.ip) | ||
| 91 | bb.note("Here is the ps output:\n%s" % \ | ||
| 92 | subprocess.Popen("ps -p %s -fww" % self.qemupid,shell=True,stdout=subprocess.PIPE).communicate()[0]) | ||
| 93 | self.kill() | ||
| 94 | return False | ||
| 95 | bb.note("IP found: %s" % self.ip) | ||
| 69 | bb.note("Waiting at most %d seconds for login banner" % self.boottime ) | 96 | bb.note("Waiting at most %d seconds for login banner" % self.boottime ) |
| 70 | (match, text) = console.read_all_timeout("login:", self.boottime) | 97 | endtime = time.time() + self.boottime |
| 71 | 98 | socklist = [self.server_socket] | |
| 72 | if match: | 99 | reachedlogin = False |
| 73 | bb.note("Reached login banner") | 100 | stopread = False |
| 74 | console.write("root\n") | 101 | while time.time() < endtime and not stopread: |
| 75 | (index, match, text) = console.expect([r"(root@[\w-]+:~#)"],10) | 102 | sread, swrite, serror = select.select(socklist, [], [], 0) |
| 76 | if not match: | 103 | for sock in sread: |
| 77 | bb.note("Couldn't get prompt, all I got was:\n%s" % text) | 104 | if sock is self.server_socket: |
| 78 | return False | 105 | self.qemusock, addr = self.server_socket.accept() |
| 79 | console.write("ip addr show `ip route list | sed -n '1p' | awk '{print $5}'` | sed -n '3p' | awk '{ print $2 }' | cut -f 1 -d \"/\"\n") | 106 | self.qemusock.setblocking(0) |
| 80 | (index, match, text) = console.expect([r"((?:[0-9]{1,3}\.){3}[0-9]{1,3})"],10) | 107 | socklist.append(self.qemusock) |
| 81 | console.close() | 108 | socklist.remove(self.server_socket) |
| 82 | if match: | 109 | bb.note("Connection from %s:%s" % addr) |
| 83 | self.ip = match.group(0) | 110 | else: |
| 84 | bb.note("Ip found: %s" % self.ip) | 111 | data = sock.recv(1024) |
| 85 | else: | 112 | if data: |
| 86 | bb.note("Couldn't determine ip, all I got was:\n%s" % text) | 113 | self.log(data) |
| 87 | return False | 114 | self.bootlog += data |
| 88 | else: | 115 | lastlines = "\n".join(self.bootlog.splitlines()[-2:]) |
| 89 | console.close() | 116 | if re.search("login:", lastlines): |
| 117 | stopread = True | ||
| 118 | reachedlogin = True | ||
| 119 | bb.note("Reached login banner") | ||
| 120 | else: | ||
| 121 | socklist.remove(sock) | ||
| 122 | sock.close() | ||
| 123 | stopread = True | ||
| 124 | |||
| 125 | |||
| 126 | if not reachedlogin: | ||
| 90 | bb.note("Target didn't reached login boot in %d seconds" % self.boottime) | 127 | bb.note("Target didn't reached login boot in %d seconds" % self.boottime) |
| 91 | lines = "\n".join(text.splitlines()[-5:]) | 128 | lines = "\n".join(self.bootlog.splitlines()[-5:]) |
| 92 | bb.note("Last 5 lines of text:\n%s" % lines) | 129 | bb.note("Last 5 lines of text:\n%s" % lines) |
| 93 | bb.note("Check full boot log: %s" % self.logfile) | 130 | bb.note("Check full boot log: %s" % self.logfile) |
| 131 | self.kill() | ||
| 94 | return False | 132 | return False |
| 95 | else: | 133 | else: |
| 96 | bb.note("Qemu pid didn't appeared in 30 seconds") | 134 | bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime) |
| 97 | self.runqemu.terminate() | 135 | self.kill() |
| 98 | self.runqemu.kill() | ||
| 99 | bb.note("Output from runqemu: %s " % self.runqemu.stdout.read()) | 136 | bb.note("Output from runqemu: %s " % self.runqemu.stdout.read()) |
| 100 | self.runqemu.stdout.close() | 137 | self.runqemu.stdout.close() |
| 101 | return False | 138 | return False |
| @@ -104,12 +141,15 @@ class QemuRunner: | |||
| 104 | 141 | ||
| 105 | 142 | ||
| 106 | def kill(self): | 143 | def kill(self): |
| 107 | if self.runqemu: | 144 | if self.server_socket: |
| 145 | self.server_socket.close() | ||
| 146 | self.server_socket = None | ||
| 147 | if self.runqemu.pid: | ||
| 108 | os.kill(-self.runqemu.pid,signal.SIGTERM) | 148 | os.kill(-self.runqemu.pid,signal.SIGTERM) |
| 149 | os.kill(-self.runqemu.pid,signal.SIGKILL) | ||
| 150 | self.runqemu.pid = None | ||
| 109 | self.qemupid = None | 151 | self.qemupid = None |
| 110 | self.ip = None | 152 | self.ip = None |
| 111 | if os.path.exists(self.streampath): | ||
| 112 | os.remove(self.streampath) | ||
| 113 | 153 | ||
| 114 | def restart(self, qemuparams = None): | 154 | def restart(self, qemuparams = None): |
| 115 | if self.is_alive(): | 155 | if self.is_alive(): |
| @@ -121,7 +161,7 @@ class QemuRunner: | |||
| 121 | qemu_child = self.find_child(str(self.runqemu.pid)) | 161 | qemu_child = self.find_child(str(self.runqemu.pid)) |
| 122 | if qemu_child: | 162 | if qemu_child: |
| 123 | self.qemupid = qemu_child[0] | 163 | self.qemupid = qemu_child[0] |
| 124 | if os.path.exists("/proc/" + str(self.qemupid)) and os.path.exists(self.streampath): | 164 | if os.path.exists("/proc/" + str(self.qemupid)): |
| 125 | return True | 165 | return True |
| 126 | return False | 166 | return False |
| 127 | 167 | ||
| @@ -145,7 +185,6 @@ class QemuRunner: | |||
| 145 | commands[data[0]] = data[2] | 185 | commands[data[0]] = data[2] |
| 146 | 186 | ||
| 147 | if parent_pid not in pids: | 187 | if parent_pid not in pids: |
| 148 | sys.stderr.write("No children found matching %s\n" % parent_pid) | ||
| 149 | return [] | 188 | return [] |
| 150 | 189 | ||
| 151 | parents = [] | 190 | parents = [] |
| @@ -166,6 +205,6 @@ class QemuRunner: | |||
| 166 | # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx" | 205 | # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx" |
| 167 | basecmd = commands[p].split()[0] | 206 | basecmd = commands[p].split()[0] |
| 168 | basecmd = os.path.basename(basecmd) | 207 | basecmd = os.path.basename(basecmd) |
| 169 | if "qemu-system" in basecmd and "-serial unix" in commands[p]: | 208 | if "qemu-system" in basecmd and "-serial tcp" in commands[p]: |
| 170 | return [int(p),commands[p]] | 209 | return [int(p),commands[p]] |
| 171 | 210 | ||
