summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuro Bystricky <juro.bystricky@intel.com>2017-09-06 17:33:58 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2018-03-04 11:11:58 +0000
commit8100e7c44e77b3f039a21c6c4c5f025b9733c6ab (patch)
tree306aa24654fd21160475a7f122ba572a9294f787
parent50750e15727f783c17df2b3377ba21880afe6a44 (diff)
downloadpoky-8100e7c44e77b3f039a21c6c4c5f025b9733c6ab.tar.gz
qemurunner.py: refactor searching for QEMU PID
Ask QEMU to store the QEMU process PID in a file, this way we don't have to parse running processes and analyze descendents. This is done via QEMU command line argument "-pidfile". [YOCTO #12001] (From OE-Core rev: 67612dcd2a8a1aa1d683dddb0bd2f592886ff020) (From OE-Core rev: 9fe7aa1824666d3f7ceb085bef59026e245cb752) Signed-off-by: Juro Bystricky <juro.bystricky@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> Signed-off-by: Armin Kuster <akuster808@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py303
1 files changed, 136 insertions, 167 deletions
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
index 3148d7ec22..59886b10ed 100644
--- a/meta/lib/oeqa/utils/qemurunner.py
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -53,6 +53,7 @@ class QemuRunner:
53 self.use_kvm = use_kvm 53 self.use_kvm = use_kvm
54 54
55 self.runqemutime = 120 55 self.runqemutime = 120
56 self.qemu_pidfile = 'pidfile_'+str(os.getpid())
56 self.host_dumper = HostDumper(dump_host_cmds, dump_dir) 57 self.host_dumper = HostDumper(dump_host_cmds, dump_dir)
57 58
58 self.logger = logging.getLogger("BitBake.QemuRunner") 59 self.logger = logging.getLogger("BitBake.QemuRunner")
@@ -142,7 +143,11 @@ class QemuRunner:
142 if extra_bootparams: 143 if extra_bootparams:
143 bootparams = bootparams + ' ' + extra_bootparams 144 bootparams = bootparams + ' ' + extra_bootparams
144 145
145 self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport) 146 # Ask QEMU to store the QEMU process PID in file, this way we don't have to parse running processes
147 # and analyze descendents in order to determine it.
148 if os.path.exists(self.qemu_pidfile):
149 os.remove(self.qemu_pidfile)
150 self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1} -pidfile {2}"'.format(bootparams, threadport, self.qemu_pidfile)
146 if qemuparams: 151 if qemuparams:
147 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' 152 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
148 153
@@ -198,125 +203,14 @@ class QemuRunner:
198 self.stop() 203 self.stop()
199 self.logger.info("Output from runqemu:\n%s" % self.getOutput(output)) 204 self.logger.info("Output from runqemu:\n%s" % self.getOutput(output))
200 return False 205 return False
201 time.sleep(1) 206 time.sleep(0.5)
202 207
203 out = self.getOutput(output) 208 if not self.is_alive():
204 netconf = False # network configuration is not required by default
205 if self.is_alive():
206 self.logger.info("qemu started in %s seconds - qemu procces pid is %s" % (time.time() - (endtime - self.runqemutime), self.qemupid))
207 if get_ip:
208 cmdline = ''
209 with open('/proc/%s/cmdline' % self.qemupid) as p:
210 cmdline = p.read()
211 # It is needed to sanitize the data received
212 # because is possible to have control characters
213 cmdline = re_control_char.sub('', cmdline)
214 try:
215 ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
216 self.ip = ips[0]
217 self.server_ip = ips[1]
218 self.logger.info("qemu cmdline used:\n{}".format(cmdline))
219 except (IndexError, ValueError):
220 # Try to get network configuration from runqemu output
221 match = re.match('.*Network configuration: ([0-9.]+)::([0-9.]+):([0-9.]+)$.*',
222 out, re.MULTILINE|re.DOTALL)
223 if match:
224 self.ip, self.server_ip, self.netmask = match.groups()
225 # network configuration is required as we couldn't get it
226 # from the runqemu command line, so qemu doesn't run kernel
227 # and guest networking is not configured
228 netconf = True
229 else:
230 self.logger.error("Couldn't get ip from qemu command line and runqemu output! "
231 "Here is the qemu command line used:\n%s\n"
232 "and output from runqemu:\n%s" % (cmdline, out))
233 self._dump_host()
234 self.stop()
235 return False
236
237 self.logger.info("Target IP: %s" % self.ip)
238 self.logger.info("Server IP: %s" % self.server_ip)
239
240 self.thread = LoggingThread(self.log, threadsock, self.logger)
241 self.thread.start()
242 if not self.thread.connection_established.wait(self.boottime):
243 self.logger.error("Didn't receive a console connection from qemu. "
244 "Here is the qemu command line used:\n%s\nand "
245 "output from runqemu:\n%s" % (cmdline, out))
246 self.stop_thread()
247 return False
248
249 self.logger.info("Output from runqemu:\n%s", out)
250 self.logger.info("Waiting at most %d seconds for login banner" % self.boottime)
251 endtime = time.time() + self.boottime
252 socklist = [self.server_socket]
253 reachedlogin = False
254 stopread = False
255 qemusock = None
256 bootlog = ''
257 data = b''
258 while time.time() < endtime and not stopread:
259 try:
260 sread, swrite, serror = select.select(socklist, [], [], 5)
261 except InterruptedError:
262 continue
263 for sock in sread:
264 if sock is self.server_socket:
265 qemusock, addr = self.server_socket.accept()
266 qemusock.setblocking(0)
267 socklist.append(qemusock)
268 socklist.remove(self.server_socket)
269 self.logger.info("Connection from %s:%s" % addr)
270 else:
271 data = data + sock.recv(1024)
272 if data:
273 try:
274 data = data.decode("utf-8", errors="surrogateescape")
275 bootlog += data
276 data = b''
277 if re.search(".* login:", bootlog):
278 self.server_socket = qemusock
279 stopread = True
280 reachedlogin = True
281 self.logger.info("Reached login banner")
282 except UnicodeDecodeError:
283 continue
284 else:
285 socklist.remove(sock)
286 sock.close()
287 stopread = True
288
289 if not reachedlogin:
290 self.logger.info("Target didn't reached login boot in %d seconds" % self.boottime)
291 lines = "\n".join(bootlog.splitlines()[-25:])
292 self.logger.info("Last 25 lines of text:\n%s" % lines)
293 self.logger.info("Check full boot log: %s" % self.logfile)
294 self._dump_host()
295 self.stop()
296 return False
297
298 # If we are not able to login the tests can continue
299 try:
300 (status, output) = self.run_serial("root\n", raw=True)
301 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
302 self.logged = True
303 self.logger.info("Logged as root in serial console")
304 if netconf:
305 # configure guest networking
306 cmd = "ifconfig eth0 %s netmask %s up\n" % (self.ip, self.netmask)
307 output = self.run_serial(cmd, raw=True)[1]
308 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
309 self.logger.info("configured ip address %s", self.ip)
310 else:
311 self.logger.info("Couldn't configure guest networking")
312 else:
313 self.logger.info("Couldn't login into serial console"
314 " as root using blank password")
315 except:
316 self.logger.info("Serial console failed while trying to login")
317
318 else:
319 self.logger.error("Qemu pid didn't appear in %s seconds" % self.runqemutime) 209 self.logger.error("Qemu pid didn't appear in %s seconds" % self.runqemutime)
210 # Dump all processes to help us to figure out what is going on...
211 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command '], stdout=subprocess.PIPE).communicate()[0]
212 processes = ps.decode("utf-8")
213 self.logger.info("Running processes:\n%s" % processes)
320 self._dump_host() 214 self._dump_host()
321 self.stop() 215 self.stop()
322 op = self.getOutput(output) 216 op = self.getOutput(output)
@@ -326,7 +220,121 @@ class QemuRunner:
326 self.logger.error("No output from runqemu.\n") 220 self.logger.error("No output from runqemu.\n")
327 return False 221 return False
328 222
329 return self.is_alive() 223 # We are alive: qemu is running
224 out = self.getOutput(output)
225 netconf = False # network configuration is not required by default
226 self.logger.info("qemu started in %s seconds - qemu procces pid is %s" % (time.time() - (endtime - self.runqemutime), self.qemupid))
227 if get_ip:
228 cmdline = ''
229 with open('/proc/%s/cmdline' % self.qemupid) as p:
230 cmdline = p.read()
231 # It is needed to sanitize the data received
232 # because is possible to have control characters
233 cmdline = re_control_char.sub(' ', cmdline)
234 try:
235 ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
236 self.ip = ips[0]
237 self.server_ip = ips[1]
238 self.logger.info("qemu cmdline used:\n{}".format(cmdline))
239 except (IndexError, ValueError):
240 # Try to get network configuration from runqemu output
241 match = re.match('.*Network configuration: ([0-9.]+)::([0-9.]+):([0-9.]+)$.*',
242 out, re.MULTILINE|re.DOTALL)
243 if match:
244 self.ip, self.server_ip, self.netmask = match.groups()
245 # network configuration is required as we couldn't get it
246 # from the runqemu command line, so qemu doesn't run kernel
247 # and guest networking is not configured
248 netconf = True
249 else:
250 self.logger.error("Couldn't get ip from qemu command line and runqemu output! "
251 "Here is the qemu command line used:\n%s\n"
252 "and output from runqemu:\n%s" % (cmdline, out))
253 self._dump_host()
254 self.stop()
255 return False
256
257 self.logger.info("Target IP: %s" % self.ip)
258 self.logger.info("Server IP: %s" % self.server_ip)
259
260 self.thread = LoggingThread(self.log, threadsock, self.logger)
261 self.thread.start()
262 if not self.thread.connection_established.wait(self.boottime):
263 self.logger.error("Didn't receive a console connection from qemu. "
264 "Here is the qemu command line used:\n%s\nand "
265 "output from runqemu:\n%s" % (cmdline, out))
266 self.stop_thread()
267 return False
268
269 self.logger.info("Output from runqemu:\n%s", out)
270 self.logger.info("Waiting at most %d seconds for login banner" % self.boottime)
271 endtime = time.time() + self.boottime
272 socklist = [self.server_socket]
273 reachedlogin = False
274 stopread = False
275 qemusock = None
276 bootlog = ''
277 data = b''
278 while time.time() < endtime and not stopread:
279 try:
280 sread, swrite, serror = select.select(socklist, [], [], 5)
281 except InterruptedError:
282 continue
283 for sock in sread:
284 if sock is self.server_socket:
285 qemusock, addr = self.server_socket.accept()
286 qemusock.setblocking(0)
287 socklist.append(qemusock)
288 socklist.remove(self.server_socket)
289 self.logger.info("Connection from %s:%s" % addr)
290 else:
291 data = data + sock.recv(1024)
292 if data:
293 try:
294 data = data.decode("utf-8", errors="surrogateescape")
295 bootlog += data
296 data = b''
297 if re.search(".* login:", bootlog):
298 self.server_socket = qemusock
299 stopread = True
300 reachedlogin = True
301 self.logger.info("Reached login banner")
302 except UnicodeDecodeError:
303 continue
304 else:
305 socklist.remove(sock)
306 sock.close()
307 stopread = True
308
309 if not reachedlogin:
310 self.logger.info("Target didn't reached login boot in %d seconds" % self.boottime)
311 lines = "\n".join(bootlog.splitlines()[-25:])
312 self.logger.info("Last 25 lines of text:\n%s" % lines)
313 self.logger.info("Check full boot log: %s" % self.logfile)
314 self._dump_host()
315 self.stop()
316 return False
317
318 # If we are not able to login the tests can continue
319 try:
320 (status, output) = self.run_serial("root\n", raw=True)
321 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
322 self.logged = True
323 self.logger.info("Logged as root in serial console")
324 if netconf:
325 # configure guest networking
326 cmd = "ifconfig eth0 %s netmask %s up\n" % (self.ip, self.netmask)
327 output = self.run_serial(cmd, raw=True)[1]
328 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
329 self.logger.info("configured ip address %s", self.ip)
330 else:
331 self.logger.info("Couldn't configure guest networking")
332 else:
333 self.logger.info("Couldn't login into serial console"
334 " as root using blank password")
335 except:
336 self.logger.info("Serial console failed while trying to login")
337 return True
330 338
331 def stop(self): 339 def stop(self):
332 self.stop_thread() 340 self.stop_thread()
@@ -354,6 +362,8 @@ class QemuRunner:
354 self.server_socket = None 362 self.server_socket = None
355 self.qemupid = None 363 self.qemupid = None
356 self.ip = None 364 self.ip = None
365 if os.path.exists(self.qemu_pidfile):
366 os.remove(self.qemu_pidfile)
357 367
358 def stop_qemu_system(self): 368 def stop_qemu_system(self):
359 if self.qemupid: 369 if self.qemupid:
@@ -379,56 +389,15 @@ class QemuRunner:
379 def is_alive(self): 389 def is_alive(self):
380 if not self.runqemu: 390 if not self.runqemu:
381 return False 391 return False
382 qemu_child = self.find_child(str(self.runqemu.pid)) 392 if os.path.isfile(self.qemu_pidfile):
383 if qemu_child: 393 f = open(self.qemu_pidfile, 'r')
384 self.qemupid = qemu_child[0] 394 qemu_pid = f.read()
385 if os.path.exists("/proc/" + str(self.qemupid)): 395 f.close()
386 return True 396 #logger.info("qemu_pid: %s" % qemu_pid)
397 self.qemupid = int(qemu_pid)
398 return True
387 return False 399 return False
388 400
389 def find_child(self,parent_pid):
390 #
391 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
392 #
393 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
394 processes = ps.decode("utf-8").split('\n')
395 nfields = len(processes[0].split()) - 1
396 pids = {}
397 commands = {}
398 for row in processes[1:]:
399 data = row.split(None, nfields)
400 if len(data) != 3:
401 continue
402 if data[1] not in pids:
403 pids[data[1]] = []
404
405 pids[data[1]].append(data[0])
406 commands[data[0]] = data[2]
407
408 if parent_pid not in pids:
409 return []
410
411 parents = []
412 newparents = pids[parent_pid]
413 while newparents:
414 next = []
415 for p in newparents:
416 if p in pids:
417 for n in pids[p]:
418 if n not in parents and n not in next:
419 next.append(n)
420 if p not in parents:
421 parents.append(p)
422 newparents = next
423 #print("Children matching %s:" % str(parents))
424 for p in parents:
425 # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
426 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
427 basecmd = commands[p].split()[0]
428 basecmd = os.path.basename(basecmd)
429 if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
430 return [int(p),commands[p]]
431
432 def run_serial(self, command, raw=False, timeout=5): 401 def run_serial(self, command, raw=False, timeout=5):
433 # We assume target system have echo to get command status 402 # We assume target system have echo to get command status
434 if not raw: 403 if not raw: