summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa
diff options
context:
space:
mode:
authorStefan Stanacar <stefanx.stanacar@intel.com>2013-08-13 17:47:12 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-08-13 23:05:59 +0100
commit0ba78c1162bb125850a0ee504ca6fbe5bf21247f (patch)
tree7624e31a6b04e6cc154013b787660c6abdaeb0ed /meta/lib/oeqa
parentbc4b98bd4ce8fcf2bedc09d172893aa770f68df0 (diff)
downloadpoky-0ba78c1162bb125850a0ee504ca6fbe5bf21247f.tar.gz
oeqa/utils/qemurunner: get ip old fashioned way and use tcp serial console
The way we read data from the serial console was unreliable and blocking (AutoBuilder seems to hit that often), so change the serial console type from unix socket to tcp and reverse the connection - don't let qemu act as server (wait for a connection). So now the serial console is used to save the boot log and make sure that we reached the login prompt. Until a better way is found this should solve some of the AutoBuilder failures (one being YB#4904). Also we need to use the same method as the old qemuimagetest to get the ip (from the qemu process arguments), because that it's more reliable. The first version used here was to log into the target and use the output of "ip addr show eth0" but then systemd decides that it should rename interfaces, so that was changed to get the ip of the interface that has the default gw, but if there is no default gw we'll get the loopback ip and we end up trying to ssh into the host machine (some recent AutoBuilder runs showed that). Changed in V2: - use -ww for ps, as output might get truncated (From OE-Core rev: 55e78185110937b7e2b143cf1020426d8df58b72) Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa')
-rw-r--r--meta/lib/oeqa/utils/oeqemuconsole.py45
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py123
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 @@
1import socket
2import time
3import re
4from telnetlib import Telnet
5
6class 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
8import subprocess 8import subprocess
9import optparse
10import sys
11import os 9import os
12import time 10import time
13import signal 11import signal
14import re 12import re
13import socket
14import select
15import bb 15import bb
16from oeqa.utils.oeqemuconsole import oeQemuConsole
17 16
18class QemuRunner: 17class 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