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