qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

Merge remote-tracking branch 'remotes/philmd-gitlab/tags/python-next-20200531' into staging

Python queue:

* migration acceptance test fix
* introduce pylintrc & flake8 config
* various cleanups (Python3, style)
* vm-test can set QEMU_LOCAL=1 to use locally built binaries
* refactored BootLinuxBase & LinuxKernelTest acceptance classes

https://gitlab.com/philmd/qemu/pipelines/151323210
https://travis-ci.org/github/philmd/qemu/builds/693157969

# gpg: Signature made Sun 31 May 2020 17:37:35 BST
# gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/python-next-20200531: (25 commits)
tests/acceptance: refactor boot_linux to allow code reuse
tests/acceptance: refactor boot_linux_console test to allow code reuse
tests/acceptance: allow console interaction with specific VMs
tests/acceptance/migration.py: Wait for both sides
tests/migration/guestperf: Use Python 3 interpreter
tests/vm: allow wait_ssh() to specify command
tests/vm: Add ability to select QEMU from current build
tests/vm: Pass --debug through for vm-boot-ssh
python/qemu/qtest: Check before accessing _qtest
python/qemu/qmp: assert sockfile is not None
python/qemu/qmp: use True/False for non/blocking modes
python/qemu: Adjust traceback typing
python/qemu: fix socket.makefile() typing
python/qemu: remove Python2 style super() calls
python/qemu: delint; add flake8 config
python/qemu: delint and add pylintrc
python/qemu/machine: remove logging configuration
python/qemu/machine: add kill() method
python: remove more instances of sys.version_info
scripts/qmp: Fix shebang and imports
...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

+317 -178
+2
python/qemu/.flake8
··· 1 + [flake8] 2 + extend-ignore = E722 # Pylint handles this, but smarter.
+6 -3
python/qemu/accel.py
··· 23 23 # Mapping host architecture to any additional architectures it can 24 24 # support which often includes its 32 bit cousin. 25 25 ADDITIONAL_ARCHES = { 26 - "x86_64" : "i386", 27 - "aarch64" : "armhf", 28 - "ppc64le" : "ppc64", 26 + "x86_64": "i386", 27 + "aarch64": "armhf", 28 + "ppc64le": "ppc64", 29 29 } 30 + 30 31 31 32 def list_accel(qemu_bin): 32 33 """ ··· 47 48 # Skip the first line which is the header. 48 49 return [acc.strip() for acc in out.splitlines()[1:]] 49 50 51 + 50 52 def kvm_available(target_arch=None, qemu_bin=None): 51 53 """ 52 54 Check if KVM is available using the following heuristic: ··· 68 70 if qemu_bin and "kvm" not in list_accel(qemu_bin): 69 71 return False 70 72 return True 73 + 71 74 72 75 def tcg_available(qemu_bin): 73 76 """
+28 -16
python/qemu/machine.py
··· 24 24 import shutil 25 25 import socket 26 26 import tempfile 27 + from typing import Optional, Type 28 + from types import TracebackType 27 29 28 30 from . import qmp 29 31 30 32 LOG = logging.getLogger(__name__) 33 + 31 34 32 35 class QEMUMachineError(Exception): 33 36 """ ··· 54 57 desc = reply["error"]["desc"] 55 58 except KeyError: 56 59 desc = reply 57 - super(MonitorResponseError, self).__init__(desc) 60 + super().__init__(desc) 58 61 self.reply = reply 59 62 60 63 61 - class QEMUMachine(object): 64 + class QEMUMachine: 62 65 """ 63 66 A QEMU VM 64 67 65 - Use this object as a context manager to ensure the QEMU process terminates:: 68 + Use this object as a context manager to ensure 69 + the QEMU process terminates:: 66 70 67 71 with VM(binary) as vm: 68 72 ... ··· 119 123 self._console_socket = None 120 124 self._remove_files = [] 121 125 122 - # just in case logging wasn't configured by the main script: 123 - logging.basicConfig() 124 - 125 126 def __enter__(self): 126 127 return self 127 128 128 - def __exit__(self, exc_type, exc_val, exc_tb): 129 + def __exit__(self, 130 + exc_type: Optional[Type[BaseException]], 131 + exc_val: Optional[BaseException], 132 + exc_tb: Optional[TracebackType]) -> None: 129 133 self.shutdown() 130 - return False 131 134 132 135 def add_monitor_null(self): 133 136 """ ··· 188 191 fd_param.append(str(fd)) 189 192 190 193 devnull = open(os.path.devnull, 'rb') 191 - proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, 192 - stderr=subprocess.STDOUT, close_fds=False) 194 + proc = subprocess.Popen( 195 + fd_param, stdin=devnull, stdout=subprocess.PIPE, 196 + stderr=subprocess.STDOUT, close_fds=False 197 + ) 193 198 output = proc.communicate()[0] 194 199 if output: 195 200 LOG.debug(output) ··· 242 247 'chardev=mon,mode=control']) 243 248 if self._machine is not None: 244 249 args.extend(['-machine', self._machine]) 245 - for i in range(self._console_index): 250 + for _ in range(self._console_index): 246 251 args.extend(['-serial', 'null']) 247 252 if self._console_set: 248 253 self._console_address = os.path.join(self._sock_dir, ··· 342 347 self._load_io_log() 343 348 self._post_shutdown() 344 349 345 - def shutdown(self, has_quit=False): 350 + def shutdown(self, has_quit=False, hard=False): 346 351 """ 347 352 Terminate the VM and clean up 348 353 """ ··· 354 359 self._console_socket = None 355 360 356 361 if self.is_running(): 357 - if self._qmp: 362 + if hard: 363 + self._popen.kill() 364 + elif self._qmp: 358 365 try: 359 366 if not has_quit: 360 367 self._qmp.cmd('quit') ··· 368 375 self._post_shutdown() 369 376 370 377 exitcode = self.exitcode() 371 - if exitcode is not None and exitcode < 0: 378 + if exitcode is not None and exitcode < 0 and \ 379 + not (exitcode == -9 and hard): 372 380 msg = 'qemu received signal %i: %s' 373 381 if self._qemu_full_args: 374 382 command = ' '.join(self._qemu_full_args) 375 383 else: 376 384 command = '' 377 - LOG.warning(msg, -exitcode, command) 385 + LOG.warning(msg, -int(exitcode), command) 378 386 379 387 self._launched = False 388 + 389 + def kill(self): 390 + self.shutdown(hard=True) 380 391 381 392 def set_qmp_monitor(self, enabled=True): 382 393 """ ··· 482 493 483 494 def events_wait(self, events, timeout=60.0): 484 495 """ 485 - events_wait waits for and returns a named event from QMP with a timeout. 496 + events_wait waits for and returns a named event 497 + from QMP with a timeout. 486 498 487 499 events: a sequence of (name, match_criteria) tuples. 488 500 The match criteria are optional and may be None.
+58
python/qemu/pylintrc
··· 1 + [MASTER] 2 + 3 + [MESSAGES CONTROL] 4 + 5 + # Disable the message, report, category or checker with the given id(s). You 6 + # can either give multiple identifiers separated by comma (,) or put this 7 + # option multiple times (only on the command line, not in the configuration 8 + # file where it should appear only once). You can also use "--disable=all" to 9 + # disable everything first and then reenable specific checks. For example, if 10 + # you want to run only the similarities checker, you can use "--disable=all 11 + # --enable=similarities". If you want to run only the classes checker, but have 12 + # no Warning level messages displayed, use "--disable=all --enable=classes 13 + # --disable=W". 14 + disable=too-many-arguments, 15 + too-many-instance-attributes, 16 + too-many-public-methods, 17 + 18 + [REPORTS] 19 + 20 + [REFACTORING] 21 + 22 + [MISCELLANEOUS] 23 + 24 + [LOGGING] 25 + 26 + [BASIC] 27 + 28 + # Good variable names which should always be accepted, separated by a comma. 29 + good-names=i, 30 + j, 31 + k, 32 + ex, 33 + Run, 34 + _, 35 + fd, 36 + 37 + [VARIABLES] 38 + 39 + [STRING] 40 + 41 + [SPELLING] 42 + 43 + [FORMAT] 44 + 45 + [SIMILARITIES] 46 + 47 + # Ignore imports when computing similarities. 48 + ignore-imports=yes 49 + 50 + [TYPECHECK] 51 + 52 + [CLASSES] 53 + 54 + [IMPORTS] 55 + 56 + [DESIGN] 57 + 58 + [EXCEPTIONS]
+20 -9
python/qemu/qmp.py
··· 11 11 import errno 12 12 import socket 13 13 import logging 14 + from typing import ( 15 + Optional, 16 + TextIO, 17 + Type, 18 + ) 19 + from types import TracebackType 14 20 15 21 16 22 class QMPError(Exception): ··· 61 67 self.__events = [] 62 68 self.__address = address 63 69 self.__sock = self.__get_sock() 64 - self.__sockfile = None 70 + self.__sockfile: Optional[TextIO] = None 65 71 self._nickname = nickname 66 72 if self._nickname: 67 73 self.logger = logging.getLogger('QMP').getChild(self._nickname) ··· 88 94 raise QMPCapabilitiesError 89 95 90 96 def __json_read(self, only_event=False): 97 + assert self.__sockfile is not None 91 98 while True: 92 99 data = self.__sockfile.readline() 93 100 if not data: ··· 114 121 """ 115 122 116 123 # Check for new events regardless and pull them into the cache: 117 - self.__sock.setblocking(0) 124 + self.__sock.setblocking(False) 118 125 try: 119 126 self.__json_read() 120 127 except OSError as err: 121 128 if err.errno == errno.EAGAIN: 122 129 # No data available 123 130 pass 124 - self.__sock.setblocking(1) 131 + self.__sock.setblocking(True) 125 132 126 133 # Wait for new events, if needed. 127 134 # if wait is 0.0, this means "no wait" and is also implicitly false. ··· 142 149 # Implement context manager enter function. 143 150 return self 144 151 145 - def __exit__(self, exc_type, exc_value, exc_traceback): 152 + def __exit__(self, 153 + # pylint: disable=duplicate-code 154 + # see https://github.com/PyCQA/pylint/issues/3619 155 + exc_type: Optional[Type[BaseException]], 156 + exc_val: Optional[BaseException], 157 + exc_tb: Optional[TracebackType]) -> None: 146 158 # Implement context manager exit function. 147 159 self.close() 148 - return False 149 160 150 161 def connect(self, negotiate=True): 151 162 """ ··· 157 168 @raise QMPCapabilitiesError if fails to negotiate capabilities 158 169 """ 159 170 self.__sock.connect(self.__address) 160 - self.__sockfile = self.__sock.makefile() 171 + self.__sockfile = self.__sock.makefile(mode='r') 161 172 if negotiate: 162 173 return self.__negotiate_capabilities() 163 174 return None ··· 168 179 169 180 @param timeout: timeout in seconds (nonnegative float number, or 170 181 None). The value passed will set the behavior of the 171 - underneath QMP socket as described in [1]. Default value 172 - is set to 15.0. 182 + underneath QMP socket as described in [1]. 183 + Default value is set to 15.0. 173 184 @return QMP greeting dict 174 185 @raise OSError on socket connection errors 175 186 @raise QMPConnectError if the greeting is not received ··· 180 191 """ 181 192 self.__sock.settimeout(timeout) 182 193 self.__sock, _ = self.__sock.accept() 183 - self.__sockfile = self.__sock.makefile() 194 + self.__sockfile = self.__sock.makefile(mode='r') 184 195 return self.__negotiate_capabilities() 185 196 186 197 def cmd_obj(self, qmp_cmd):
+53 -30
python/qemu/qtest.py
··· 1 - # QEMU qtest library 2 - # 1 + """ 2 + QEMU qtest library 3 + 4 + qtest offers the QEMUQtestProtocol and QEMUQTestMachine classes, which 5 + offer a connection to QEMU's qtest protocol socket, and a qtest-enabled 6 + subclass of QEMUMachine, respectively. 7 + """ 8 + 3 9 # Copyright (C) 2015 Red Hat Inc. 4 10 # 5 11 # Authors: ··· 13 19 14 20 import socket 15 21 import os 22 + from typing import Optional, TextIO 16 23 17 24 from .machine import QEMUMachine 18 25 19 26 20 - class QEMUQtestProtocol(object): 21 - def __init__(self, address, server=False): 22 - """ 23 - Create a QEMUQtestProtocol object. 27 + class QEMUQtestProtocol: 28 + """ 29 + QEMUQtestProtocol implements a connection to a qtest socket. 24 30 25 - @param address: QEMU address, can be either a unix socket path (string) 26 - or a tuple in the form ( address, port ) for a TCP 27 - connection 28 - @param server: server mode, listens on the socket (bool) 29 - @raise socket.error on socket connection errors 30 - @note No connection is established, this is done by the connect() or 31 - accept() methods 32 - """ 31 + :param address: QEMU address, can be either a unix socket path (string) 32 + or a tuple in the form ( address, port ) for a TCP 33 + connection 34 + :param server: server mode, listens on the socket (bool) 35 + :raise socket.error: on socket connection errors 36 + 37 + .. note:: 38 + No conection is estabalished by __init__(), this is done 39 + by the connect() or accept() methods. 40 + """ 41 + def __init__(self, address, server=False): 33 42 self._address = address 34 43 self._sock = self._get_sock() 35 - self._sockfile = None 44 + self._sockfile: Optional[TextIO] = None 36 45 if server: 37 46 self._sock.bind(self._address) 38 47 self._sock.listen(1) ··· 51 60 @raise socket.error on socket connection errors 52 61 """ 53 62 self._sock.connect(self._address) 54 - self._sockfile = self._sock.makefile() 63 + self._sockfile = self._sock.makefile(mode='r') 55 64 56 65 def accept(self): 57 66 """ ··· 60 69 @raise socket.error on socket connection errors 61 70 """ 62 71 self._sock, _ = self._sock.accept() 63 - self._sockfile = self._sock.makefile() 72 + self._sockfile = self._sock.makefile(mode='r') 64 73 65 74 def cmd(self, qtest_cmd): 66 75 """ ··· 68 77 69 78 @param qtest_cmd: qtest command text to be sent 70 79 """ 80 + assert self._sockfile is not None 71 81 self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) 72 82 resp = self._sockfile.readline() 73 83 return resp 74 84 75 85 def close(self): 86 + """Close this socket.""" 76 87 self._sock.close() 77 - self._sockfile.close() 88 + if self._sockfile: 89 + self._sockfile.close() 90 + self._sockfile = None 78 91 79 92 def settimeout(self, timeout): 93 + """Set a timeout, in seconds.""" 80 94 self._sock.settimeout(timeout) 81 95 82 96 83 97 class QEMUQtestMachine(QEMUMachine): 84 - '''A QEMU VM''' 98 + """ 99 + A QEMU VM, with a qtest socket available. 100 + """ 85 101 86 102 def __init__(self, binary, args=None, name=None, test_dir="/var/tmp", 87 103 socket_scm_helper=None, sock_dir=None): ··· 89 105 name = "qemu-%d" % os.getpid() 90 106 if sock_dir is None: 91 107 sock_dir = test_dir 92 - super(QEMUQtestMachine, 93 - self).__init__(binary, args, name=name, test_dir=test_dir, 94 - socket_scm_helper=socket_scm_helper, 95 - sock_dir=sock_dir) 108 + super().__init__(binary, args, name=name, test_dir=test_dir, 109 + socket_scm_helper=socket_scm_helper, 110 + sock_dir=sock_dir) 96 111 self._qtest = None 97 112 self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") 98 113 99 114 def _base_args(self): 100 - args = super(QEMUQtestMachine, self)._base_args() 115 + args = super()._base_args() 101 116 args.extend(['-qtest', 'unix:path=' + self._qtest_path, 102 117 '-accel', 'qtest']) 103 118 return args 104 119 105 120 def _pre_launch(self): 106 - super(QEMUQtestMachine, self)._pre_launch() 121 + super()._pre_launch() 107 122 self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) 108 123 109 - def _post_launch(self): 110 - super(QEMUQtestMachine, self)._post_launch() 124 + def _post_launch(self) -> None: 125 + assert self._qtest is not None 126 + super()._post_launch() 111 127 self._qtest.accept() 112 128 113 129 def _post_shutdown(self): 114 - super(QEMUQtestMachine, self)._post_shutdown() 130 + super()._post_shutdown() 115 131 self._remove_if_exists(self._qtest_path) 116 132 117 - def qtest(self, cmd): 118 - '''Send a qtest command to guest''' 133 + def qtest(self, cmd: str) -> str: 134 + """ 135 + Send a qtest command to the guest. 136 + 137 + :param cmd: qtest command to send 138 + :return: qtest server response 139 + """ 140 + if self._qtest is None: 141 + raise RuntimeError("qtest socket not available") 119 142 return self._qtest.cmd(cmd)
-5
scripts/analyze-migration.py
··· 25 25 import sys 26 26 27 27 28 - MIN_PYTHON = (3, 2) 29 - if sys.version_info < MIN_PYTHON: 30 - sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON) 31 - 32 - 33 28 def mkdir_p(path): 34 29 try: 35 30 os.makedirs(path)
+9 -16
scripts/decodetree.py
··· 75 75 output_fd.write(a) 76 76 77 77 78 - if sys.version_info >= (3, 4): 79 - re_fullmatch = re.fullmatch 80 - else: 81 - def re_fullmatch(pat, str): 82 - return re.match('^' + pat + '$', str) 83 - 84 - 85 78 def output_autogen(): 86 79 output('/* This file is autogenerated by scripts/decodetree.py. */\n\n') 87 80 ··· 428 421 width = 0 429 422 func = None 430 423 for t in toks: 431 - if re_fullmatch('!function=' + re_ident, t): 424 + if re.fullmatch('!function=' + re_ident, t): 432 425 if func: 433 426 error(lineno, 'duplicate function') 434 427 func = t.split('=') 435 428 func = func[1] 436 429 continue 437 430 438 - if re_fullmatch('[0-9]+:s[0-9]+', t): 431 + if re.fullmatch('[0-9]+:s[0-9]+', t): 439 432 # Signed field extract 440 433 subtoks = t.split(':s') 441 434 sign = True 442 - elif re_fullmatch('[0-9]+:[0-9]+', t): 435 + elif re.fullmatch('[0-9]+:[0-9]+', t): 443 436 # Unsigned field extract 444 437 subtoks = t.split(':') 445 438 sign = False ··· 488 481 flds = [] 489 482 extern = False 490 483 for t in toks: 491 - if re_fullmatch('!extern', t): 484 + if re.fullmatch('!extern', t): 492 485 extern = True 493 486 anyextern = True 494 487 continue 495 - if not re_fullmatch(re_ident, t): 488 + if not re.fullmatch(re_ident, t): 496 489 error(lineno, 'invalid argument set token "{0}"'.format(t)) 497 490 if t in flds: 498 491 error(lineno, 'duplicate argument "{0}"'.format(t)) ··· 621 614 continue 622 615 623 616 # 'Foo=%Bar' imports a field with a different name. 624 - if re_fullmatch(re_ident + '=%' + re_ident, t): 617 + if re.fullmatch(re_ident + '=%' + re_ident, t): 625 618 (fname, iname) = t.split('=%') 626 619 flds = add_field_byname(lineno, flds, fname, iname) 627 620 continue 628 621 629 622 # 'Foo=number' sets an argument field to a constant value 630 - if re_fullmatch(re_ident + '=[+-]?[0-9]+', t): 623 + if re.fullmatch(re_ident + '=[+-]?[0-9]+', t): 631 624 (fname, value) = t.split('=') 632 625 value = int(value) 633 626 flds = add_field(lineno, flds, fname, ConstField(value)) ··· 635 628 636 629 # Pattern of 0s, 1s, dots and dashes indicate required zeros, 637 630 # required ones, or dont-cares. 638 - if re_fullmatch('[01.-]+', t): 631 + if re.fullmatch('[01.-]+', t): 639 632 shift = len(t) 640 633 fms = t.replace('0', '1') 641 634 fms = fms.replace('.', '0') ··· 652 645 fixedmask = (fixedmask << shift) | fms 653 646 undefmask = (undefmask << shift) | ubm 654 647 # Otherwise, fieldname:fieldwidth 655 - elif re_fullmatch(re_ident + ':s?[0-9]+', t): 648 + elif re.fullmatch(re_ident + ':s?[0-9]+', t): 656 649 (fname, flen) = t.split(':') 657 650 sign = False 658 651 if flen[0] == 's':
+4 -3
scripts/kvm/vmxcap
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 # 3 3 # tool for querying VMX capabilities 4 4 # ··· 275 275 ), 276 276 ] 277 277 278 - for c in controls: 279 - c.show() 278 + if __name__ == '__main__': 279 + for c in controls: 280 + c.show()
+15 -14
scripts/modules/module_block.py
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 # 3 3 # Module information generator 4 4 # ··· 80 80 #endif 81 81 ''') 82 82 83 - # First argument: output file 84 - # All other arguments: modules source files (.c) 85 - output_file = sys.argv[1] 86 - with open(output_file, 'w') as fheader: 87 - print_top(fheader) 83 + if __name__ == '__main__': 84 + # First argument: output file 85 + # All other arguments: modules source files (.c) 86 + output_file = sys.argv[1] 87 + with open(output_file, 'w') as fheader: 88 + print_top(fheader) 88 89 89 - for filename in sys.argv[2:]: 90 - if os.path.isfile(filename): 91 - process_file(fheader, filename) 92 - else: 93 - print("File " + filename + " does not exist.", file=sys.stderr) 94 - sys.exit(1) 90 + for filename in sys.argv[2:]: 91 + if os.path.isfile(filename): 92 + process_file(fheader, filename) 93 + else: 94 + print("File " + filename + " does not exist.", file=sys.stderr) 95 + sys.exit(1) 95 96 96 - print_bottom(fheader) 97 + print_bottom(fheader) 97 98 98 - sys.exit(0) 99 + sys.exit(0)
+2 -2
scripts/qemu-gdb.py
··· 1 - #!/usr/bin/python 2 - 1 + #!/usr/bin/env python3 2 + # 3 3 # GDB debugging support 4 4 # 5 5 # Copyright 2012 Red Hat, Inc. and/or its affiliates
+1 -2
scripts/qemugdb/__init__.py
··· 1 - #!/usr/bin/python 2 - 1 + # 3 2 # GDB debugging support 4 3 # 5 4 # Copyright (c) 2015 Linaro Ltd
+1 -2
scripts/qemugdb/aio.py
··· 1 - #!/usr/bin/python 2 - 1 + # 3 2 # GDB debugging support: aio/iohandler debug 4 3 # 5 4 # Copyright (c) 2015 Red Hat, Inc.
+1 -2
scripts/qemugdb/coroutine.py
··· 1 - #!/usr/bin/python 2 - 1 + # 3 2 # GDB debugging support 4 3 # 5 4 # Copyright 2012 Red Hat, Inc. and/or its affiliates
+1 -3
scripts/qemugdb/mtree.py
··· 1 - #!/usr/bin/python 2 - 1 + # 3 2 # GDB debugging support 4 3 # 5 4 # Copyright 2012 Red Hat, Inc. and/or its affiliates ··· 84 83 while not isnull(subregion): 85 84 self.print_item(subregion, addr, level) 86 85 subregion = subregion['subregions_link']['tqe_next'] 87 -
-1
scripts/qemugdb/tcg.py
··· 1 - #!/usr/bin/python 2 1 # -*- coding: utf-8 -*- 3 2 # 4 3 # GDB debugging support, TCG status
-1
scripts/qemugdb/timers.py
··· 1 - #!/usr/bin/python 2 1 # -*- coding: utf-8 -*- 3 2 # GDB debugging support 4 3 #
+3 -1
scripts/qmp/qmp
··· 11 11 # See the COPYING file in the top-level directory. 12 12 13 13 import sys, os 14 - from qmp import QEMUMonitorProtocol 14 + 15 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 16 + from qemu.qmp import QEMUMonitorProtocol 15 17 16 18 def print_response(rsp, prefix=[]): 17 19 if type(rsp) == list:
-3
scripts/qmp/qmp-shell
··· 77 77 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 78 78 from qemu import qmp 79 79 80 - if sys.version_info[0] == 2: 81 - input = raw_input 82 - 83 80 class QMPCompleter(list): 84 81 def complete(self, text, state): 85 82 for cmd in self:
+3 -1
scripts/qmp/qom-fuse
··· 15 15 from fuse import Fuse 16 16 import os, posix 17 17 from errno import * 18 - from qmp import QEMUMonitorProtocol 18 + 19 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 20 + from qemu.qmp import QEMUMonitorProtocol 19 21 20 22 fuse.fuse_python_api = (0, 2) 21 23
+4 -2
scripts/qmp/qom-get
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 ## 3 3 # QEMU Object Model test tools 4 4 # ··· 13 13 14 14 import sys 15 15 import os 16 - from qmp import QEMUMonitorProtocol 16 + 17 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 18 + from qemu.qmp import QEMUMonitorProtocol 17 19 18 20 cmd, args = sys.argv[0], sys.argv[1:] 19 21 socket_path = None
+4 -2
scripts/qmp/qom-list
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 ## 3 3 # QEMU Object Model test tools 4 4 # ··· 13 13 14 14 import sys 15 15 import os 16 - from qmp import QEMUMonitorProtocol 16 + 17 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 18 + from qemu.qmp import QEMUMonitorProtocol 17 19 18 20 cmd, args = sys.argv[0], sys.argv[1:] 19 21 socket_path = None
+4 -2
scripts/qmp/qom-set
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 ## 3 3 # QEMU Object Model test tools 4 4 # ··· 13 13 14 14 import sys 15 15 import os 16 - from qmp import QEMUMonitorProtocol 16 + 17 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 18 + from qemu.qmp import QEMUMonitorProtocol 17 19 18 20 cmd, args = sys.argv[0], sys.argv[1:] 19 21 socket_path = None
+4 -2
scripts/qmp/qom-tree
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 ## 3 3 # QEMU Object Model test tools 4 4 # ··· 15 15 16 16 import sys 17 17 import os 18 - from qmp import QEMUMonitorProtocol 18 + 19 + sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 20 + from qemu.qmp import QEMUMonitorProtocol 19 21 20 22 cmd, args = sys.argv[0], sys.argv[1:] 21 23 socket_path = None
+8 -5
tests/acceptance/avocado_qemu/__init__.py
··· 69 69 70 70 71 71 def _console_interaction(test, success_message, failure_message, 72 - send_string, keep_sending=False): 72 + send_string, keep_sending=False, vm=None): 73 73 assert not keep_sending or send_string 74 - console = test.vm.console_socket.makefile() 74 + if vm is None: 75 + vm = test.vm 76 + console = vm.console_socket.makefile() 75 77 console_logger = logging.getLogger('console') 76 78 while True: 77 79 if send_string: 78 - test.vm.console_socket.sendall(send_string.encode()) 80 + vm.console_socket.sendall(send_string.encode()) 79 81 if not keep_sending: 80 82 send_string = None # send only once 81 83 msg = console.readline().strip() ··· 115 117 _console_interaction(test, success_message, failure_message, 116 118 interrupt_string, True) 117 119 118 - def wait_for_console_pattern(test, success_message, failure_message=None): 120 + def wait_for_console_pattern(test, success_message, failure_message=None, 121 + vm=None): 119 122 """ 120 123 Waits for messages to appear on the console, while logging the content 121 124 ··· 125 128 :param success_message: if this message appears, test succeeds 126 129 :param failure_message: if this message appears, test fails 127 130 """ 128 - _console_interaction(test, success_message, failure_message, None) 131 + _console_interaction(test, success_message, failure_message, None, vm=vm) 129 132 130 133 def exec_command_and_wait_for_pattern(test, command, 131 134 success_message, failure_message=None):
+29 -20
tests/acceptance/boot_linux.py
··· 26 26 TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" 27 27 28 28 29 - class BootLinux(Test): 30 - """ 31 - Boots a Linux system, checking for a successful initialization 32 - """ 33 - 34 - timeout = 900 35 - chksum = None 36 - 37 - def setUp(self): 38 - super(BootLinux, self).setUp() 39 - self.vm.add_args('-smp', '2') 40 - self.vm.add_args('-m', '1024') 41 - self.prepare_boot() 42 - self.prepare_cloudinit() 43 - 44 - def prepare_boot(self): 29 + class BootLinuxBase(Test): 30 + def download_boot(self): 45 31 self.log.debug('Looking for and selecting a qemu-img binary to be ' 46 32 'used to create the bootable snapshot image') 47 33 # If qemu-img has been built, use it, otherwise the system wide one ··· 60 46 if image_arch == 'ppc64': 61 47 image_arch = 'ppc64le' 62 48 try: 63 - self.boot = vmimage.get( 49 + boot = vmimage.get( 64 50 'fedora', arch=image_arch, version='31', 65 51 checksum=self.chksum, 66 52 algorithm='sha256', 67 53 cache_dir=self.cache_dirs[0], 68 54 snapshot_dir=self.workdir) 69 - self.vm.add_args('-drive', 'file=%s' % self.boot.path) 70 55 except: 71 56 self.cancel('Failed to download/prepare boot image') 57 + return boot.path 72 58 73 - def prepare_cloudinit(self): 59 + def download_cloudinit(self): 74 60 self.log.info('Preparing cloudinit image') 75 61 try: 76 62 cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') ··· 81 67 # QEMU's hard coded usermode router address 82 68 phone_home_host='10.0.2.2', 83 69 phone_home_port=self.phone_home_port) 84 - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) 85 70 except Exception: 86 71 self.cancel('Failed to prepared cloudinit image') 72 + return cloudinit_iso 73 + 74 + class BootLinux(BootLinuxBase): 75 + """ 76 + Boots a Linux system, checking for a successful initialization 77 + """ 78 + 79 + timeout = 900 80 + chksum = None 81 + 82 + def setUp(self): 83 + super(BootLinux, self).setUp() 84 + self.vm.add_args('-smp', '2') 85 + self.vm.add_args('-m', '1024') 86 + self.prepare_boot() 87 + self.prepare_cloudinit() 88 + 89 + def prepare_boot(self): 90 + path = self.download_boot() 91 + self.vm.add_args('-drive', 'file=%s' % path) 92 + 93 + def prepare_cloudinit(self): 94 + cloudinit_iso = self.download_cloudinit() 95 + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) 87 96 88 97 def launch_and_wait(self): 89 98 self.vm.set_console()
+11 -10
tests/acceptance/boot_linux_console.py
··· 28 28 except CmdNotFoundError: 29 29 P7ZIP_AVAILABLE = False 30 30 31 - class BootLinuxConsole(Test): 32 - """ 33 - Boots a Linux kernel and checks that the console is operational and the 34 - kernel command line is properly passed from QEMU to the kernel 35 - """ 36 - 37 - timeout = 90 38 - 31 + class LinuxKernelTest(Test): 39 32 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' 40 33 41 - def wait_for_console_pattern(self, success_message): 34 + def wait_for_console_pattern(self, success_message, vm=None): 42 35 wait_for_console_pattern(self, success_message, 43 - failure_message='Kernel panic - not syncing') 36 + failure_message='Kernel panic - not syncing', 37 + vm=vm) 44 38 45 39 def extract_from_deb(self, deb, path): 46 40 """ ··· 78 72 process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) 79 73 os.chdir(cwd) 80 74 return os.path.normpath(os.path.join(self.workdir, path)) 75 + 76 + class BootLinuxConsole(LinuxKernelTest): 77 + """ 78 + Boots a Linux kernel and checks that the console is operational and the 79 + kernel command line is properly passed from QEMU to the kernel 80 + """ 81 + timeout = 90 81 82 82 83 def test_x86_64_pc(self): 83 84 """
+4
tests/acceptance/migration.py
··· 35 35 timeout=self.timeout, 36 36 step=0.1, 37 37 args=(src_vm,)) 38 + wait.wait_for(self.migration_finished, 39 + timeout=self.timeout, 40 + step=0.1, 41 + args=(dst_vm,)) 38 42 self.assertEqual(src_vm.command('query-migrate')['status'], 'completed') 39 43 self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed') 40 44 self.assertEqual(dst_vm.command('query-status')['status'], 'running')
+3 -2
tests/docker/docker.py
··· 258 258 return self._do_kill_instances(True) 259 259 260 260 def _output(self, cmd, **kwargs): 261 - if sys.version_info[1] >= 6: 261 + try: 262 262 return subprocess.check_output(self._command + cmd, 263 263 stderr=subprocess.STDOUT, 264 264 encoding='utf-8', 265 265 **kwargs) 266 - else: 266 + except TypeError: 267 + # 'encoding' argument was added in 3.6+ 267 268 return subprocess.check_output(self._command + cmd, 268 269 stderr=subprocess.STDOUT, 269 270 **kwargs).decode('utf-8')
+1 -1
tests/migration/guestperf-batch.py
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 # 3 3 # Migration test batch comparison invokation 4 4 #
+1 -1
tests/migration/guestperf-plot.py
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 # 3 3 # Migration test graph plotting command 4 4 #
+1 -1
tests/migration/guestperf.py
··· 1 - #!/usr/bin/python 1 + #!/usr/bin/env python3 2 2 # 3 3 # Migration test direct invokation command 4 4 #
+1 -4
tests/qemu-iotests/nbd-fault-injector.py
··· 47 47 import socket 48 48 import struct 49 49 import collections 50 - if sys.version_info.major >= 3: 51 - import configparser 52 - else: 53 - import ConfigParser as configparser 50 + import configparser 54 51 55 52 FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB 56 53
+5
tests/vm/Makefile.include
··· 41 41 @echo " J=[0..9]* - Override the -jN parameter for make commands" 42 42 @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" 43 43 @echo " V=1 - Enable verbose ouput on host and guest commands" 44 + @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." 44 45 @echo " QEMU=/path/to/qemu - Change path to QEMU binary" 45 46 @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" 46 47 ··· 57 58 $(PYTHON) $< \ 58 59 $(if $(V)$(DEBUG), --debug) \ 59 60 $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ 61 + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ 60 62 --image "$@" \ 61 63 --force \ 62 64 --build-image $@, \ ··· 71 73 $(if $(DEBUG), --interactive) \ 72 74 $(if $(J),--jobs $(J)) \ 73 75 $(if $(V),--verbose) \ 76 + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ 74 77 --image "$<" \ 75 78 $(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \ 76 79 --snapshot \ ··· 91 94 $(call quiet-command, \ 92 95 $(PYTHON) $(SRC_PATH)/tests/vm/$* \ 93 96 $(if $(J),--jobs $(J)) \ 97 + $(if $(V)$(DEBUG), --debug) \ 98 + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ 94 99 --image "$<" \ 95 100 --interactive \ 96 101 false, \
+30 -12
tests/vm/basevm.py
··· 61 61 # 4 is arbitrary, but greater than 2, 62 62 # since we found we need to wait more than twice as long. 63 63 tcg_ssh_timeout_multiplier = 4 64 - def __init__(self, debug=False, vcpus=None, genisoimage=None): 64 + def __init__(self, debug=False, vcpus=None, genisoimage=None, 65 + build_path=None): 65 66 self._guest = None 66 67 self._genisoimage = genisoimage 68 + self._build_path = build_path 67 69 self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", 68 70 suffix=".tmp", 69 71 dir=".")) ··· 184 186 "-device", "virtio-blk,drive=drive0,bootindex=0"] 185 187 args += self._data_args + extra_args 186 188 logging.debug("QEMU args: %s", " ".join(args)) 187 - qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) 188 - guest = QEMUMachine(binary=qemu_bin, args=args) 189 + qemu_path = get_qemu_path(self.arch, self._build_path) 190 + guest = QEMUMachine(binary=qemu_path, args=args) 189 191 guest.set_machine('pc') 190 192 guest.set_console() 191 193 try: 192 194 guest.launch() 193 195 except: 194 196 logging.error("Failed to launch QEMU, command line:") 195 - logging.error(" ".join([qemu_bin] + args)) 197 + logging.error(" ".join([qemu_path] + args)) 196 198 logging.error("Log:") 197 199 logging.error(guest.get_log()) 198 200 logging.error("QEMU version >= 2.10 is required") ··· 318 320 def print_step(self, text): 319 321 sys.stderr.write("### %s ...\n" % text) 320 322 321 - def wait_ssh(self, wait_root=False, seconds=300): 323 + def wait_ssh(self, wait_root=False, seconds=300, cmd="exit 0"): 322 324 # Allow more time for VM to boot under TCG. 323 325 if not kvm_available(self.arch): 324 326 seconds *= self.tcg_ssh_timeout_multiplier 325 327 starttime = datetime.datetime.now() 326 328 endtime = starttime + datetime.timedelta(seconds=seconds) 327 - guest_up = False 329 + cmd_success = False 328 330 while datetime.datetime.now() < endtime: 329 - if wait_root and self.ssh_root("exit 0") == 0: 330 - guest_up = True 331 + if wait_root and self.ssh_root(cmd) == 0: 332 + cmd_success = True 331 333 break 332 - elif self.ssh("exit 0") == 0: 333 - guest_up = True 334 + elif self.ssh(cmd) == 0: 335 + cmd_success = True 334 336 break 335 337 seconds = (endtime - datetime.datetime.now()).total_seconds() 336 338 logging.debug("%ds before timeout", seconds) 337 339 time.sleep(1) 338 - if not guest_up: 340 + if not cmd_success: 339 341 raise Exception("Timeout while waiting for guest ssh") 340 342 341 343 def shutdown(self): ··· 391 393 392 394 return os.path.join(cidir, "cloud-init.iso") 393 395 396 + def get_qemu_path(arch, build_path=None): 397 + """Fetch the path to the qemu binary.""" 398 + # If QEMU environment variable set, it takes precedence 399 + if "QEMU" in os.environ: 400 + qemu_path = os.environ["QEMU"] 401 + elif build_path: 402 + qemu_path = os.path.join(build_path, arch + "-softmmu") 403 + qemu_path = os.path.join(qemu_path, "qemu-system-" + arch) 404 + else: 405 + # Default is to use system path for qemu. 406 + qemu_path = "qemu-system-" + arch 407 + return qemu_path 408 + 394 409 def parse_args(vmcls): 395 410 396 411 def get_default_jobs(): ··· 421 436 help="build QEMU from source in guest") 422 437 parser.add_option("--build-target", 423 438 help="QEMU build target", default="check") 439 + parser.add_option("--build-path", default=None, 440 + help="Path of build directory, "\ 441 + "for using build tree QEMU binary. ") 424 442 parser.add_option("--interactive", "-I", action="store_true", 425 443 help="Interactively run command") 426 444 parser.add_option("--snapshot", "-s", action="store_true", ··· 439 457 logging.basicConfig(level=(logging.DEBUG if args.debug 440 458 else logging.WARN)) 441 459 vm = vmcls(debug=args.debug, vcpus=args.jobs, 442 - genisoimage=args.genisoimage) 460 + genisoimage=args.genisoimage, build_path=args.build_path) 443 461 if args.build_image: 444 462 if os.path.exists(args.image) and not args.force: 445 463 sys.stderr.writelines(["Image file exists: %s\n" % args.image,