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 | ||