diff options
Diffstat (limited to 'meta/lib/oeqa/utils')
| -rw-r--r-- | meta/lib/oeqa/utils/dump.py | 32 | ||||
| -rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 70 |
2 files changed, 97 insertions, 5 deletions
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 @@ | |||
| 4 | 4 | ||
| 5 | import os | 5 | import os |
| 6 | import sys | 6 | import sys |
| 7 | import json | ||
| 7 | import errno | 8 | import errno |
| 8 | import datetime | 9 | import datetime |
| 9 | import itertools | 10 | import itertools |
| @@ -51,6 +52,8 @@ class BaseDumper(object): | |||
| 51 | prefix = "host" | 52 | prefix = "host" |
| 52 | elif isinstance(self, TargetDumper): | 53 | elif isinstance(self, TargetDumper): |
| 53 | prefix = "target" | 54 | prefix = "target" |
| 55 | elif isinstance(self, MonitorDumper): | ||
| 56 | prefix = "qmp" | ||
| 54 | else: | 57 | else: |
| 55 | prefix = "unknown" | 58 | prefix = "unknown" |
| 56 | for i in itertools.count(): | 59 | for i in itertools.count(): |
| @@ -58,9 +61,12 @@ class BaseDumper(object): | |||
| 58 | fullname = os.path.join(self.dump_dir, filename) | 61 | fullname = os.path.join(self.dump_dir, filename) |
| 59 | if not os.path.exists(fullname): | 62 | if not os.path.exists(fullname): |
| 60 | break | 63 | break |
| 61 | with open(fullname, 'w') as dump_file: | 64 | if isinstance(self, MonitorDumper): |
| 62 | dump_file.write(output) | 65 | with open(fullname, 'w') as json_file: |
| 63 | 66 | json.dump(output, json_file, indent=4) | |
| 67 | else: | ||
| 68 | with open(fullname, 'w') as dump_file: | ||
| 69 | dump_file.write(output) | ||
| 64 | 70 | ||
| 65 | class HostDumper(BaseDumper): | 71 | class HostDumper(BaseDumper): |
| 66 | """ Class to get dumps from the host running the tests """ | 72 | """ Class to get dumps from the host running the tests """ |
| @@ -96,3 +102,23 @@ class TargetDumper(BaseDumper): | |||
| 96 | except: | 102 | except: |
| 97 | print("Tried to dump info from target but " | 103 | print("Tried to dump info from target but " |
| 98 | "serial console failed") | 104 | "serial console failed") |
| 105 | print("Failed CMD: %s" % (cmd)) | ||
| 106 | |||
| 107 | class MonitorDumper(BaseDumper): | ||
| 108 | """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner """ | ||
| 109 | |||
| 110 | def __init__(self, cmds, parent_dir, runner): | ||
| 111 | super(MonitorDumper, self).__init__(cmds, parent_dir) | ||
| 112 | self.runner = runner | ||
| 113 | |||
| 114 | def dump_monitor(self, dump_dir=""): | ||
| 115 | if self.runner is None: | ||
| 116 | return | ||
| 117 | if dump_dir: | ||
| 118 | self.dump_dir = dump_dir | ||
| 119 | for cmd in self.cmds: | ||
| 120 | try: | ||
| 121 | output = self.runner.run_monitor(cmd) | ||
| 122 | self._write_dump(cmd, output) | ||
| 123 | except: | ||
| 124 | 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 | |||
| 20 | import threading | 20 | import threading |
| 21 | import codecs | 21 | import codecs |
| 22 | import logging | 22 | import logging |
| 23 | import tempfile | ||
| 23 | from oeqa.utils.dump import HostDumper | 24 | from oeqa.utils.dump import HostDumper |
| 24 | from collections import defaultdict | 25 | from collections import defaultdict |
| 26 | import importlib | ||
| 25 | 27 | ||
| 26 | # Get Unicode non printable control chars | 28 | # Get Unicode non printable control chars |
| 27 | control_range = list(range(0,32))+list(range(127,160)) | 29 | control_range = list(range(0,32))+list(range(127,160)) |
| @@ -172,6 +174,21 @@ class QemuRunner: | |||
| 172 | return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, env=env) | 174 | return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, env=env) |
| 173 | 175 | ||
| 174 | def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None): | 176 | def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None): |
| 177 | # use logfile to determine the recipe-sysroot-native path and | ||
| 178 | # then add in the site-packages path components and add that | ||
| 179 | # to the python sys.path so qmp.py can be found. | ||
| 180 | python_path = os.path.dirname(os.path.dirname(self.logfile)) | ||
| 181 | python_path += "/recipe-sysroot-native/usr/lib/python3.9/site-packages" | ||
| 182 | sys.path.append(python_path) | ||
| 183 | importlib.invalidate_caches() | ||
| 184 | try: | ||
| 185 | qmp = importlib.import_module("qmp") | ||
| 186 | except: | ||
| 187 | self.logger.error("qemurunner: qmp.py missing, please ensure it's installed") | ||
| 188 | return False | ||
| 189 | qmp_port = self.tmpdir + "/." + next(tempfile._get_candidate_names()) | ||
| 190 | qmp_param = ' -S -qmp unix:%s,server,wait' % (qmp_port) | ||
| 191 | |||
| 175 | try: | 192 | try: |
| 176 | if self.serial_ports >= 2: | 193 | if self.serial_ports >= 2: |
| 177 | self.threadsock, threadport = self.create_socket() | 194 | self.threadsock, threadport = self.create_socket() |
| @@ -188,7 +205,8 @@ class QemuRunner: | |||
| 188 | # and analyze descendents in order to determine it. | 205 | # and analyze descendents in order to determine it. |
| 189 | if os.path.exists(self.qemu_pidfile): | 206 | if os.path.exists(self.qemu_pidfile): |
| 190 | os.remove(self.qemu_pidfile) | 207 | os.remove(self.qemu_pidfile) |
| 191 | self.qemuparams = 'bootparams="{0}" qemuparams="-pidfile {1}"'.format(bootparams, self.qemu_pidfile) | 208 | self.qemuparams = 'bootparams="{0}" qemuparams="-pidfile {1} {2}"'.format(bootparams, self.qemu_pidfile, qmp_param) |
| 209 | |||
| 192 | if qemuparams: | 210 | if qemuparams: |
| 193 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | 211 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' |
| 194 | 212 | ||
| @@ -242,6 +260,7 @@ class QemuRunner: | |||
| 242 | while not self.is_alive() and time.time() < endtime: | 260 | while not self.is_alive() and time.time() < endtime: |
| 243 | if self.runqemu.poll(): | 261 | if self.runqemu.poll(): |
| 244 | if self.runqemu_exited: | 262 | if self.runqemu_exited: |
| 263 | self.logger.warning("runqemu during is_alive() test") | ||
| 245 | return False | 264 | return False |
| 246 | if self.runqemu.returncode: | 265 | if self.runqemu.returncode: |
| 247 | # No point waiting any longer | 266 | # No point waiting any longer |
| @@ -253,9 +272,51 @@ class QemuRunner: | |||
| 253 | time.sleep(0.5) | 272 | time.sleep(0.5) |
| 254 | 273 | ||
| 255 | if self.runqemu_exited: | 274 | if self.runqemu_exited: |
| 275 | self.logger.warning("runqemu after timeout") | ||
| 276 | return False | ||
| 277 | |||
| 278 | if self.runqemu.returncode: | ||
| 279 | self.logger.warning('runqemu exited with code %d' % self.runqemu.returncode) | ||
| 256 | return False | 280 | return False |
| 257 | 281 | ||
| 258 | if not self.is_alive(): | 282 | if not self.is_alive(): |
| 283 | self.logger.warning('is_alive() failed later') | ||
| 284 | return False | ||
| 285 | |||
| 286 | # Create the client socket for the QEMU Monitor Control Socket | ||
| 287 | # This will allow us to read status from Qemu if the the process | ||
| 288 | # is still alive | ||
| 289 | self.logger.debug("QMP Initializing to %s" % (qmp_port)) | ||
| 290 | try: | ||
| 291 | self.qmp = qmp.QEMUMonitorProtocol(qmp_port) | ||
| 292 | except OSError as msg: | ||
| 293 | self.logger.warning("Failed to initialize qemu monitor socket: %s File: %s" % (msg, msg.filename)) | ||
| 294 | return False | ||
| 295 | |||
| 296 | self.logger.debug("QMP Connecting to %s" % (qmp_port)) | ||
| 297 | if not os.path.exists(qmp_port) and self.is_alive(): | ||
| 298 | self.logger.debug("QMP Port does not exist waiting for it to be created") | ||
| 299 | endtime = time.time() + self.runqemutime | ||
| 300 | while not os.path.exists(qmp_port) and self.is_alive() and time.time() < endtime: | ||
| 301 | self.logger.warning("QMP port does not exist yet!") | ||
| 302 | time.sleep(0.5) | ||
| 303 | if not os.path.exists(qmp_port) and self.is_alive(): | ||
| 304 | self.logger.warning("QMP Port still does not exist but QEMU is alive") | ||
| 305 | return False | ||
| 306 | |||
| 307 | try: | ||
| 308 | self.qmp.connect() | ||
| 309 | except OSError as msg: | ||
| 310 | self.logger.warning("Failed to connect qemu monitor socket: %s File: %s" % (msg, msg.filename)) | ||
| 311 | return False | ||
| 312 | except qmp.QMPConnectError as msg: | ||
| 313 | self.logger.warning("Failed to communicate with qemu monitor: %s" % (msg)) | ||
| 314 | return False | ||
| 315 | |||
| 316 | # Release the qemu porcess to continue running | ||
| 317 | self.run_monitor('cont') | ||
| 318 | |||
| 319 | if not self.is_alive(): | ||
| 259 | self.logger.error("Qemu pid didn't appear in %s seconds (%s)" % | 320 | self.logger.error("Qemu pid didn't appear in %s seconds (%s)" % |
| 260 | (self.runqemutime, time.strftime("%D %H:%M:%S"))) | 321 | (self.runqemutime, time.strftime("%D %H:%M:%S"))) |
| 261 | 322 | ||
| @@ -380,7 +441,6 @@ class QemuRunner: | |||
| 380 | sock.close() | 441 | sock.close() |
| 381 | stopread = True | 442 | stopread = True |
| 382 | 443 | ||
| 383 | |||
| 384 | if not reachedlogin: | 444 | if not reachedlogin: |
| 385 | if time.time() >= endtime: | 445 | if time.time() >= endtime: |
| 386 | self.logger.warning("Target didn't reach login banner in %d seconds (%s)" % | 446 | self.logger.warning("Target didn't reach login banner in %d seconds (%s)" % |
| @@ -441,6 +501,9 @@ class QemuRunner: | |||
| 441 | self.runqemu.stdout.close() | 501 | self.runqemu.stdout.close() |
| 442 | self.runqemu_exited = True | 502 | self.runqemu_exited = True |
| 443 | 503 | ||
| 504 | if hasattr(self, 'qmp') and self.qmp: | ||
| 505 | self.qmp.close() | ||
| 506 | self.qmp = None | ||
| 444 | if hasattr(self, 'server_socket') and self.server_socket: | 507 | if hasattr(self, 'server_socket') and self.server_socket: |
| 445 | self.server_socket.close() | 508 | self.server_socket.close() |
| 446 | self.server_socket = None | 509 | self.server_socket = None |
| @@ -499,6 +562,9 @@ class QemuRunner: | |||
| 499 | return True | 562 | return True |
| 500 | return False | 563 | return False |
| 501 | 564 | ||
| 565 | def run_monitor(self, command, timeout=60): | ||
| 566 | return self.qmp.cmd(command) | ||
| 567 | |||
| 502 | def run_serial(self, command, raw=False, timeout=60): | 568 | def run_serial(self, command, raw=False, timeout=60): |
| 503 | # We assume target system have echo to get command status | 569 | # We assume target system have echo to get command status |
| 504 | if not raw: | 570 | if not raw: |
