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