diff options
Diffstat (limited to 'meta/lib')
-rw-r--r-- | meta/lib/oeqa/core/target/qemu.py | 6 | ||||
-rw-r--r-- | meta/lib/oeqa/core/target/ssh.py | 17 | ||||
-rw-r--r-- | meta/lib/oeqa/targetcontrol.py | 3 | ||||
-rw-r--r-- | meta/lib/oeqa/utils/dump.py | 32 | ||||
-rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 70 |
5 files changed, 122 insertions, 6 deletions
diff --git a/meta/lib/oeqa/core/target/qemu.py b/meta/lib/oeqa/core/target/qemu.py index 792efca1f8..4a5df4a9a8 100644 --- a/meta/lib/oeqa/core/target/qemu.py +++ b/meta/lib/oeqa/core/target/qemu.py @@ -12,6 +12,7 @@ from collections import defaultdict from .ssh import OESSHTarget from oeqa.utils.qemurunner import QemuRunner +from oeqa.utils.dump import MonitorDumper from oeqa.utils.dump import TargetDumper supported_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic'] @@ -43,6 +44,11 @@ class OEQemuTarget(OESSHTarget): dump_host_cmds=dump_host_cmds, logger=logger, serial_ports=serial_ports, boot_patterns = boot_patterns, use_ovmf=ovmf, tmpfsdir=tmpfsdir) + dump_monitor_cmds = kwargs.get("testimage_dump_monitor") + self.monitor_dumper = MonitorDumper(dump_monitor_cmds, dump_dir, self.runner) + if self.monitor_dumper: + self.monitor_dumper.create_dir("qmp") + dump_target_cmds = kwargs.get("testimage_dump_target") self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner) self.target_dumper.create_dir("qemu") diff --git a/meta/lib/oeqa/core/target/ssh.py b/meta/lib/oeqa/core/target/ssh.py index 461448dbc5..923a223b25 100644 --- a/meta/lib/oeqa/core/target/ssh.py +++ b/meta/lib/oeqa/core/target/ssh.py @@ -43,6 +43,7 @@ class OESSHTarget(OETarget): if port: self.ssh = self.ssh + [ '-p', port ] self.scp = self.scp + [ '-P', port ] + self._monitor_dumper = None def start(self, **kwargs): pass @@ -50,6 +51,15 @@ class OESSHTarget(OETarget): def stop(self, **kwargs): pass + @property + def monitor_dumper(self): + return self._monitor_dumper + + @monitor_dumper.setter + def monitor_dumper(self, dumper): + self._monitor_dumper = dumper + self.monitor_dumper.dump_monitor() + def _run(self, command, timeout=None, ignore_status=True): """ Runs command in target using SSHProcess. @@ -87,9 +97,14 @@ class OESSHTarget(OETarget): processTimeout = self.timeout status, output = self._run(sshCmd, processTimeout, True) - self.logger.debug('Command: %s\nOutput: %s\n' % (command, output)) + self.logger.debug('Command: %s\nStatus: %d Output: %s\n' % (command, status, output)) if (status == 255) and (('No route to host') in output): + if self.monitor_dumper: + self.monitor_dumper.dump_monitor() + if status == 255: self.target_dumper.dump_target() + if self.monitor_dumper: + self.monitor_dumper.dump_monitor() return (status, output) def copyTo(self, localSrc, remoteDst): diff --git a/meta/lib/oeqa/targetcontrol.py b/meta/lib/oeqa/targetcontrol.py index 12057f855a..005ebaa7f3 100644 --- a/meta/lib/oeqa/targetcontrol.py +++ b/meta/lib/oeqa/targetcontrol.py @@ -17,6 +17,7 @@ from oeqa.utils.sshcontrol import SSHControl from oeqa.utils.qemurunner import QemuRunner from oeqa.utils.qemutinyrunner import QemuTinyRunner from oeqa.utils.dump import TargetDumper +from oeqa.utils.dump import MonitorDumper from oeqa.controllers.testtargetloader import TestTargetLoader from abc import ABCMeta, abstractmethod @@ -108,6 +109,7 @@ class QemuTarget(BaseTarget): self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime) dump_target_cmds = d.getVar("testimage_dump_target") dump_host_cmds = d.getVar("testimage_dump_host") + dump_monitor_cmds = d.getVar("testimage_dump_monitor") dump_dir = d.getVar("TESTIMAGE_DUMP_DIR") if not dump_dir: dump_dir = os.path.join(d.getVar('LOG_DIR'), 'runtime-hostdump') @@ -149,6 +151,7 @@ class QemuTarget(BaseTarget): serial_ports = len(d.getVar("SERIAL_CONSOLES").split())) self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner) + self.monitor_dumper = MonitorDumper(dump_monitor_cmds, dump_dir, self.runner) def deploy(self): bb.utils.mkdirhier(self.testdir) diff --git a/meta/lib/oeqa/utils/dump.py b/meta/lib/oeqa/utils/dump.py index 09a44329e0..843e19fe8a 100644 --- a/meta/lib/oeqa/utils/dump.py +++ b/meta/lib/oeqa/utils/dump.py @@ -4,6 +4,7 @@ import os import sys +import json import errno import datetime import itertools @@ -51,6 +52,8 @@ class BaseDumper(object): prefix = "host" elif isinstance(self, TargetDumper): prefix = "target" + elif isinstance(self, MonitorDumper): + prefix = "qmp" else: prefix = "unknown" for i in itertools.count(): @@ -58,9 +61,12 @@ class BaseDumper(object): fullname = os.path.join(self.dump_dir, filename) if not os.path.exists(fullname): break - with open(fullname, 'w') as dump_file: - dump_file.write(output) - + if isinstance(self, MonitorDumper): + with open(fullname, 'w') as json_file: + json.dump(output, json_file, indent=4) + else: + with open(fullname, 'w') as dump_file: + dump_file.write(output) class HostDumper(BaseDumper): """ Class to get dumps from the host running the tests """ @@ -96,3 +102,23 @@ class TargetDumper(BaseDumper): except: print("Tried to dump info from target but " "serial console failed") + print("Failed CMD: %s" % (cmd)) + +class MonitorDumper(BaseDumper): + """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner """ + + def __init__(self, cmds, parent_dir, runner): + super(MonitorDumper, self).__init__(cmds, parent_dir) + self.runner = runner + + def dump_monitor(self, dump_dir=""): + if self.runner is None: + return + if dump_dir: + self.dump_dir = dump_dir + for cmd in self.cmds: + try: + output = self.runner.run_monitor(cmd) + self._write_dump(cmd, output) + except: + print("Failed to dump QMP CMD: %s" % (cmd)) diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index 278904ba0b..f6e1007288 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py @@ -20,8 +20,10 @@ import string import threading import codecs import logging +import tempfile from oeqa.utils.dump import HostDumper from collections import defaultdict +import importlib # Get Unicode non printable control chars control_range = list(range(0,32))+list(range(127,160)) @@ -172,6 +174,21 @@ class QemuRunner: return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, env=env) def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None): + # use logfile to determine the recipe-sysroot-native path and + # then add in the site-packages path components and add that + # to the python sys.path so qmp.py can be found. + python_path = os.path.dirname(os.path.dirname(self.logfile)) + python_path += "/recipe-sysroot-native/usr/lib/python3.9/site-packages" + sys.path.append(python_path) + importlib.invalidate_caches() + try: + qmp = importlib.import_module("qmp") + except: + self.logger.error("qemurunner: qmp.py missing, please ensure it's installed") + return False + qmp_port = self.tmpdir + "/." + next(tempfile._get_candidate_names()) + qmp_param = ' -S -qmp unix:%s,server,wait' % (qmp_port) + try: if self.serial_ports >= 2: self.threadsock, threadport = self.create_socket() @@ -188,7 +205,8 @@ class QemuRunner: # and analyze descendents in order to determine it. if os.path.exists(self.qemu_pidfile): os.remove(self.qemu_pidfile) - self.qemuparams = 'bootparams="{0}" qemuparams="-pidfile {1}"'.format(bootparams, self.qemu_pidfile) + self.qemuparams = 'bootparams="{0}" qemuparams="-pidfile {1} {2}"'.format(bootparams, self.qemu_pidfile, qmp_param) + if qemuparams: self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' @@ -242,6 +260,7 @@ class QemuRunner: while not self.is_alive() and time.time() < endtime: if self.runqemu.poll(): if self.runqemu_exited: + self.logger.warning("runqemu during is_alive() test") return False if self.runqemu.returncode: # No point waiting any longer @@ -253,9 +272,51 @@ class QemuRunner: time.sleep(0.5) if self.runqemu_exited: + self.logger.warning("runqemu after timeout") + return False + + if self.runqemu.returncode: + self.logger.warning('runqemu exited with code %d' % self.runqemu.returncode) return False if not self.is_alive(): + self.logger.warning('is_alive() failed later') + return False + + # Create the client socket for the QEMU Monitor Control Socket + # This will allow us to read status from Qemu if the the process + # is still alive + self.logger.debug("QMP Initializing to %s" % (qmp_port)) + try: + self.qmp = qmp.QEMUMonitorProtocol(qmp_port) + except OSError as msg: + self.logger.warning("Failed to initialize qemu monitor socket: %s File: %s" % (msg, msg.filename)) + return False + + self.logger.debug("QMP Connecting to %s" % (qmp_port)) + if not os.path.exists(qmp_port) and self.is_alive(): + self.logger.debug("QMP Port does not exist waiting for it to be created") + endtime = time.time() + self.runqemutime + while not os.path.exists(qmp_port) and self.is_alive() and time.time() < endtime: + self.logger.warning("QMP port does not exist yet!") + time.sleep(0.5) + if not os.path.exists(qmp_port) and self.is_alive(): + self.logger.warning("QMP Port still does not exist but QEMU is alive") + return False + + try: + self.qmp.connect() + except OSError as msg: + self.logger.warning("Failed to connect qemu monitor socket: %s File: %s" % (msg, msg.filename)) + return False + except qmp.QMPConnectError as msg: + self.logger.warning("Failed to communicate with qemu monitor: %s" % (msg)) + return False + + # Release the qemu porcess to continue running + self.run_monitor('cont') + + if not self.is_alive(): self.logger.error("Qemu pid didn't appear in %s seconds (%s)" % (self.runqemutime, time.strftime("%D %H:%M:%S"))) @@ -380,7 +441,6 @@ class QemuRunner: sock.close() stopread = True - if not reachedlogin: if time.time() >= endtime: self.logger.warning("Target didn't reach login banner in %d seconds (%s)" % @@ -441,6 +501,9 @@ class QemuRunner: self.runqemu.stdout.close() self.runqemu_exited = True + if hasattr(self, 'qmp') and self.qmp: + self.qmp.close() + self.qmp = None if hasattr(self, 'server_socket') and self.server_socket: self.server_socket.close() self.server_socket = None @@ -499,6 +562,9 @@ class QemuRunner: return True return False + def run_monitor(self, command, timeout=60): + return self.qmp.cmd(command) + def run_serial(self, command, raw=False, timeout=60): # We assume target system have echo to get command status if not raw: |