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.py237
1 files changed, 237 insertions, 0 deletions
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
new file mode 100644
index 0000000..f1a7e24
--- /dev/null
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -0,0 +1,237 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module provides a class for starting qemu images using runqemu.
6# It's used by testimage.bbclass.
7
8import subprocess
9import os
10import time
11import signal
12import re
13import socket
14import select
15import bb
16
17class QemuRunner:
18
19 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime):
20
21 # Popen object for runqemu
22 self.runqemu = None
23 # pid of the qemu process that runqemu will start
24 self.qemupid = None
25 # target ip - from the command line
26 self.ip = None
27 # host ip - where qemu is running
28 self.server_ip = None
29
30 self.machine = machine
31 self.rootfs = rootfs
32 self.display = display
33 self.tmpdir = tmpdir
34 self.deploy_dir_image = deploy_dir_image
35 self.logfile = logfile
36 self.boottime = boottime
37
38 self.runqemutime = 60
39
40 self.create_socket()
41
42
43 def create_socket(self):
44
45 self.bootlog = ''
46 self.qemusock = None
47
48 try:
49 self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
50 self.server_socket.setblocking(0)
51 self.server_socket.bind(("127.0.0.1",0))
52 self.server_socket.listen(2)
53 self.serverport = self.server_socket.getsockname()[1]
54 bb.note("Created listening socket for qemu serial console on: 127.0.0.1:%s" % self.serverport)
55 except socket.error, msg:
56 self.server_socket.close()
57 bb.fatal("Failed to create listening socket: %s" %msg[1])
58
59
60 def log(self, msg):
61 if self.logfile:
62 with open(self.logfile, "a") as f:
63 f.write("%s" % msg)
64
65 def start(self, qemuparams = None):
66
67 if self.display:
68 os.environ["DISPLAY"] = self.display
69 else:
70 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
71 return False
72 if not os.path.exists(self.rootfs):
73 bb.error("Invalid rootfs %s" % self.rootfs)
74 return False
75 if not os.path.exists(self.tmpdir):
76 bb.error("Invalid TMPDIR path %s" % self.tmpdir)
77 return False
78 else:
79 os.environ["OE_TMPDIR"] = self.tmpdir
80 if not os.path.exists(self.deploy_dir_image):
81 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
82 return False
83 else:
84 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
85
86 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
87 # badly with screensavers.
88 os.environ["QEMU_DONT_GRAB"] = "1"
89 self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport
90 if qemuparams:
91 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
92
93 launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams)
94 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
95
96 bb.note("runqemu started, pid is %s" % self.runqemu.pid)
97 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
98 endtime = time.time() + self.runqemutime
99 while not self.is_alive() and time.time() < endtime:
100 time.sleep(1)
101
102 if self.is_alive():
103 bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
104 cmdline = ''
105 with open('/proc/%s/cmdline' % self.qemupid) as p:
106 cmdline = p.read()
107 ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
108 if not ips or len(ips) != 3:
109 bb.note("Couldn't get ip from qemu process arguments! Here is the qemu command line used: %s" % cmdline)
110 self.stop()
111 return False
112 else:
113 self.ip = ips[0]
114 self.server_ip = ips[1]
115 bb.note("Target IP: %s" % self.ip)
116 bb.note("Server IP: %s" % self.server_ip)
117 bb.note("Waiting at most %d seconds for login banner" % self.boottime )
118 endtime = time.time() + self.boottime
119 socklist = [self.server_socket]
120 reachedlogin = False
121 stopread = False
122 while time.time() < endtime and not stopread:
123 sread, swrite, serror = select.select(socklist, [], [], 5)
124 for sock in sread:
125 if sock is self.server_socket:
126 self.qemusock, addr = self.server_socket.accept()
127 self.qemusock.setblocking(0)
128 socklist.append(self.qemusock)
129 socklist.remove(self.server_socket)
130 bb.note("Connection from %s:%s" % addr)
131 else:
132 data = sock.recv(1024)
133 if data:
134 self.log(data)
135 self.bootlog += data
136 if re.search("qemu.* login:", self.bootlog):
137 stopread = True
138 reachedlogin = True
139 bb.note("Reached login banner")
140 else:
141 socklist.remove(sock)
142 sock.close()
143 stopread = True
144
145 if not reachedlogin:
146 bb.note("Target didn't reached login boot in %d seconds" % self.boottime)
147 lines = "\n".join(self.bootlog.splitlines()[-5:])
148 bb.note("Last 5 lines of text:\n%s" % lines)
149 bb.note("Check full boot log: %s" % self.logfile)
150 self.stop()
151 return False
152 else:
153 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
154 output = self.runqemu.stdout
155 self.stop()
156 bb.note("Output from runqemu:\n%s" % output.read())
157 return False
158
159 return self.is_alive()
160
161 def stop(self):
162
163 if self.runqemu:
164 bb.note("Sending SIGTERM to runqemu")
165 os.killpg(self.runqemu.pid, signal.SIGTERM)
166 endtime = time.time() + self.runqemutime
167 while self.runqemu.poll() is None and time.time() < endtime:
168 time.sleep(1)
169 if self.runqemu.poll() is None:
170 bb.note("Sending SIGKILL to runqemu")
171 os.killpg(self.runqemu.pid, signal.SIGKILL)
172 self.runqemu = None
173 if self.server_socket:
174 self.server_socket.close()
175 self.server_socket = None
176 self.qemupid = None
177 self.ip = None
178
179 def restart(self, qemuparams = None):
180 bb.note("Restarting qemu process")
181 if self.runqemu.poll() is None:
182 self.stop()
183 self.create_socket()
184 if self.start(qemuparams):
185 return True
186 return False
187
188 def is_alive(self):
189 qemu_child = self.find_child(str(self.runqemu.pid))
190 if qemu_child:
191 self.qemupid = qemu_child[0]
192 if os.path.exists("/proc/" + str(self.qemupid)):
193 return True
194 return False
195
196 def find_child(self,parent_pid):
197 #
198 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
199 #
200 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
201 processes = ps.split('\n')
202 nfields = len(processes[0].split()) - 1
203 pids = {}
204 commands = {}
205 for row in processes[1:]:
206 data = row.split(None, nfields)
207 if len(data) != 3:
208 continue
209 if data[1] not in pids:
210 pids[data[1]] = []
211
212 pids[data[1]].append(data[0])
213 commands[data[0]] = data[2]
214
215 if parent_pid not in pids:
216 return []
217
218 parents = []
219 newparents = pids[parent_pid]
220 while newparents:
221 next = []
222 for p in newparents:
223 if p in pids:
224 for n in pids[p]:
225 if n not in parents and n not in next:
226 next.append(n)
227 if p not in parents:
228 parents.append(p)
229 newparents = next
230 #print "Children matching %s:" % str(parents)
231 for p in parents:
232 # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
233 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
234 basecmd = commands[p].split()[0]
235 basecmd = os.path.basename(basecmd)
236 if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
237 return [int(p),commands[p]]