aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-05-31 21:49:07 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-05-31 21:49:07 +0100
commitb73f417aaeeedee933aa031d6430ecb9ada71ccb (patch)
treecedf9d186a4729e6353837bf0f1ed8139bd8e774
parent4ec2a1f53e8aaa22924614b64dde97321126943e (diff)
parent1c80c87c8c2489e4318c93c844aa29bc1d014146 (diff)
downloadqemu-b73f417aaeeedee933aa031d6430ecb9ada71ccb.zip
qemu-b73f417aaeeedee933aa031d6430ecb9ada71ccb.tar.gz
qemu-b73f417aaeeedee933aa031d6430ecb9ada71ccb.tar.bz2
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>
-rw-r--r--python/qemu/.flake82
-rw-r--r--python/qemu/accel.py9
-rw-r--r--python/qemu/machine.py44
-rw-r--r--python/qemu/pylintrc58
-rw-r--r--python/qemu/qmp.py29
-rw-r--r--python/qemu/qtest.py83
-rwxr-xr-xscripts/analyze-migration.py5
-rwxr-xr-xscripts/decodetree.py25
-rwxr-xr-xscripts/kvm/vmxcap7
-rw-r--r--scripts/modules/module_block.py29
-rw-r--r--scripts/qemu-gdb.py4
-rw-r--r--scripts/qemugdb/__init__.py3
-rw-r--r--scripts/qemugdb/aio.py3
-rw-r--r--scripts/qemugdb/coroutine.py3
-rw-r--r--scripts/qemugdb/mtree.py4
-rw-r--r--scripts/qemugdb/tcg.py1
-rw-r--r--scripts/qemugdb/timers.py1
-rwxr-xr-xscripts/qmp/qmp4
-rwxr-xr-xscripts/qmp/qmp-shell3
-rwxr-xr-xscripts/qmp/qom-fuse4
-rwxr-xr-xscripts/qmp/qom-get6
-rwxr-xr-xscripts/qmp/qom-list6
-rwxr-xr-xscripts/qmp/qom-set6
-rwxr-xr-xscripts/qmp/qom-tree6
-rw-r--r--tests/acceptance/avocado_qemu/__init__.py13
-rw-r--r--tests/acceptance/boot_linux.py49
-rw-r--r--tests/acceptance/boot_linux_console.py21
-rw-r--r--tests/acceptance/migration.py4
-rwxr-xr-xtests/docker/docker.py5
-rwxr-xr-xtests/migration/guestperf-batch.py2
-rwxr-xr-xtests/migration/guestperf-plot.py2
-rwxr-xr-xtests/migration/guestperf.py2
-rwxr-xr-xtests/qemu-iotests/nbd-fault-injector.py5
-rw-r--r--tests/vm/Makefile.include5
-rw-r--r--tests/vm/basevm.py42
35 files changed, 317 insertions, 178 deletions
diff --git a/python/qemu/.flake8 b/python/qemu/.flake8
new file mode 100644
index 0000000..45d8146
--- /dev/null
+++ b/python/qemu/.flake8
@@ -0,0 +1,2 @@
+[flake8]
+extend-ignore = E722 # Pylint handles this, but smarter. \ No newline at end of file
diff --git a/python/qemu/accel.py b/python/qemu/accel.py
index 36ae857..7fabe62 100644
--- a/python/qemu/accel.py
+++ b/python/qemu/accel.py
@@ -23,11 +23,12 @@ LOG = logging.getLogger(__name__)
# Mapping host architecture to any additional architectures it can
# support which often includes its 32 bit cousin.
ADDITIONAL_ARCHES = {
- "x86_64" : "i386",
- "aarch64" : "armhf",
- "ppc64le" : "ppc64",
+ "x86_64": "i386",
+ "aarch64": "armhf",
+ "ppc64le": "ppc64",
}
+
def list_accel(qemu_bin):
"""
List accelerators enabled in the QEMU binary.
@@ -47,6 +48,7 @@ def list_accel(qemu_bin):
# Skip the first line which is the header.
return [acc.strip() for acc in out.splitlines()[1:]]
+
def kvm_available(target_arch=None, qemu_bin=None):
"""
Check if KVM is available using the following heuristic:
@@ -69,6 +71,7 @@ def kvm_available(target_arch=None, qemu_bin=None):
return False
return True
+
def tcg_available(qemu_bin):
"""
Check if TCG is available.
diff --git a/python/qemu/machine.py b/python/qemu/machine.py
index b9a98e2..041c615 100644
--- a/python/qemu/machine.py
+++ b/python/qemu/machine.py
@@ -24,11 +24,14 @@ import subprocess
import shutil
import socket
import tempfile
+from typing import Optional, Type
+from types import TracebackType
from . import qmp
LOG = logging.getLogger(__name__)
+
class QEMUMachineError(Exception):
"""
Exception called when an error in QEMUMachine happens.
@@ -54,15 +57,16 @@ class MonitorResponseError(qmp.QMPError):
desc = reply["error"]["desc"]
except KeyError:
desc = reply
- super(MonitorResponseError, self).__init__(desc)
+ super().__init__(desc)
self.reply = reply
-class QEMUMachine(object):
+class QEMUMachine:
"""
A QEMU VM
- Use this object as a context manager to ensure the QEMU process terminates::
+ Use this object as a context manager to ensure
+ the QEMU process terminates::
with VM(binary) as vm:
...
@@ -119,15 +123,14 @@ class QEMUMachine(object):
self._console_socket = None
self._remove_files = []
- # just in case logging wasn't configured by the main script:
- logging.basicConfig()
-
def __enter__(self):
return self
- def __exit__(self, exc_type, exc_val, exc_tb):
+ def __exit__(self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType]) -> None:
self.shutdown()
- return False
def add_monitor_null(self):
"""
@@ -188,8 +191,10 @@ class QEMUMachine(object):
fd_param.append(str(fd))
devnull = open(os.path.devnull, 'rb')
- proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, close_fds=False)
+ proc = subprocess.Popen(
+ fd_param, stdin=devnull, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, close_fds=False
+ )
output = proc.communicate()[0]
if output:
LOG.debug(output)
@@ -242,7 +247,7 @@ class QEMUMachine(object):
'chardev=mon,mode=control'])
if self._machine is not None:
args.extend(['-machine', self._machine])
- for i in range(self._console_index):
+ for _ in range(self._console_index):
args.extend(['-serial', 'null'])
if self._console_set:
self._console_address = os.path.join(self._sock_dir,
@@ -342,7 +347,7 @@ class QEMUMachine(object):
self._load_io_log()
self._post_shutdown()
- def shutdown(self, has_quit=False):
+ def shutdown(self, has_quit=False, hard=False):
"""
Terminate the VM and clean up
"""
@@ -354,7 +359,9 @@ class QEMUMachine(object):
self._console_socket = None
if self.is_running():
- if self._qmp:
+ if hard:
+ self._popen.kill()
+ elif self._qmp:
try:
if not has_quit:
self._qmp.cmd('quit')
@@ -368,16 +375,20 @@ class QEMUMachine(object):
self._post_shutdown()
exitcode = self.exitcode()
- if exitcode is not None and exitcode < 0:
+ if exitcode is not None and exitcode < 0 and \
+ not (exitcode == -9 and hard):
msg = 'qemu received signal %i: %s'
if self._qemu_full_args:
command = ' '.join(self._qemu_full_args)
else:
command = ''
- LOG.warning(msg, -exitcode, command)
+ LOG.warning(msg, -int(exitcode), command)
self._launched = False
+ def kill(self):
+ self.shutdown(hard=True)
+
def set_qmp_monitor(self, enabled=True):
"""
Set the QMP monitor.
@@ -482,7 +493,8 @@ class QEMUMachine(object):
def events_wait(self, events, timeout=60.0):
"""
- events_wait waits for and returns a named event from QMP with a timeout.
+ events_wait waits for and returns a named event
+ from QMP with a timeout.
events: a sequence of (name, match_criteria) tuples.
The match criteria are optional and may be None.
diff --git a/python/qemu/pylintrc b/python/qemu/pylintrc
new file mode 100644
index 0000000..5d6ae73
--- /dev/null
+++ b/python/qemu/pylintrc
@@ -0,0 +1,58 @@
+[MASTER]
+
+[MESSAGES CONTROL]
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=too-many-arguments,
+ too-many-instance-attributes,
+ too-many-public-methods,
+
+[REPORTS]
+
+[REFACTORING]
+
+[MISCELLANEOUS]
+
+[LOGGING]
+
+[BASIC]
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _,
+ fd,
+
+[VARIABLES]
+
+[STRING]
+
+[SPELLING]
+
+[FORMAT]
+
+[SIMILARITIES]
+
+# Ignore imports when computing similarities.
+ignore-imports=yes
+
+[TYPECHECK]
+
+[CLASSES]
+
+[IMPORTS]
+
+[DESIGN]
+
+[EXCEPTIONS]
diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py
index d6c9b2f..e64b6b5 100644
--- a/python/qemu/qmp.py
+++ b/python/qemu/qmp.py
@@ -11,6 +11,12 @@ import json
import errno
import socket
import logging
+from typing import (
+ Optional,
+ TextIO,
+ Type,
+)
+from types import TracebackType
class QMPError(Exception):
@@ -61,7 +67,7 @@ class QEMUMonitorProtocol:
self.__events = []
self.__address = address
self.__sock = self.__get_sock()
- self.__sockfile = None
+ self.__sockfile: Optional[TextIO] = None
self._nickname = nickname
if self._nickname:
self.logger = logging.getLogger('QMP').getChild(self._nickname)
@@ -88,6 +94,7 @@ class QEMUMonitorProtocol:
raise QMPCapabilitiesError
def __json_read(self, only_event=False):
+ assert self.__sockfile is not None
while True:
data = self.__sockfile.readline()
if not data:
@@ -114,14 +121,14 @@ class QEMUMonitorProtocol:
"""
# Check for new events regardless and pull them into the cache:
- self.__sock.setblocking(0)
+ self.__sock.setblocking(False)
try:
self.__json_read()
except OSError as err:
if err.errno == errno.EAGAIN:
# No data available
pass
- self.__sock.setblocking(1)
+ self.__sock.setblocking(True)
# Wait for new events, if needed.
# if wait is 0.0, this means "no wait" and is also implicitly false.
@@ -142,10 +149,14 @@ class QEMUMonitorProtocol:
# Implement context manager enter function.
return self
- def __exit__(self, exc_type, exc_value, exc_traceback):
+ def __exit__(self,
+ # pylint: disable=duplicate-code
+ # see https://github.com/PyCQA/pylint/issues/3619
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType]) -> None:
# Implement context manager exit function.
self.close()
- return False
def connect(self, negotiate=True):
"""
@@ -157,7 +168,7 @@ class QEMUMonitorProtocol:
@raise QMPCapabilitiesError if fails to negotiate capabilities
"""
self.__sock.connect(self.__address)
- self.__sockfile = self.__sock.makefile()
+ self.__sockfile = self.__sock.makefile(mode='r')
if negotiate:
return self.__negotiate_capabilities()
return None
@@ -168,8 +179,8 @@ class QEMUMonitorProtocol:
@param timeout: timeout in seconds (nonnegative float number, or
None). The value passed will set the behavior of the
- underneath QMP socket as described in [1]. Default value
- is set to 15.0.
+ underneath QMP socket as described in [1].
+ Default value is set to 15.0.
@return QMP greeting dict
@raise OSError on socket connection errors
@raise QMPConnectError if the greeting is not received
@@ -180,7 +191,7 @@ class QEMUMonitorProtocol:
"""
self.__sock.settimeout(timeout)
self.__sock, _ = self.__sock.accept()
- self.__sockfile = self.__sock.makefile()
+ self.__sockfile = self.__sock.makefile(mode='r')
return self.__negotiate_capabilities()
def cmd_obj(self, qmp_cmd):
diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py
index d24ad04..888c8bd 100644
--- a/python/qemu/qtest.py
+++ b/python/qemu/qtest.py
@@ -1,5 +1,11 @@
-# QEMU qtest library
-#
+"""
+QEMU qtest library
+
+qtest offers the QEMUQtestProtocol and QEMUQTestMachine classes, which
+offer a connection to QEMU's qtest protocol socket, and a qtest-enabled
+subclass of QEMUMachine, respectively.
+"""
+
# Copyright (C) 2015 Red Hat Inc.
#
# Authors:
@@ -13,26 +19,29 @@
import socket
import os
+from typing import Optional, TextIO
from .machine import QEMUMachine
-class QEMUQtestProtocol(object):
- def __init__(self, address, server=False):
- """
- Create a QEMUQtestProtocol object.
+class QEMUQtestProtocol:
+ """
+ QEMUQtestProtocol implements a connection to a qtest socket.
- @param address: QEMU address, can be either a unix socket path (string)
- or a tuple in the form ( address, port ) for a TCP
- connection
- @param server: server mode, listens on the socket (bool)
- @raise socket.error on socket connection errors
- @note No connection is established, this is done by the connect() or
- accept() methods
- """
+ :param address: QEMU address, can be either a unix socket path (string)
+ or a tuple in the form ( address, port ) for a TCP
+ connection
+ :param server: server mode, listens on the socket (bool)
+ :raise socket.error: on socket connection errors
+
+ .. note::
+ No conection is estabalished by __init__(), this is done
+ by the connect() or accept() methods.
+ """
+ def __init__(self, address, server=False):
self._address = address
self._sock = self._get_sock()
- self._sockfile = None
+ self._sockfile: Optional[TextIO] = None
if server:
self._sock.bind(self._address)
self._sock.listen(1)
@@ -51,7 +60,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors
"""
self._sock.connect(self._address)
- self._sockfile = self._sock.makefile()
+ self._sockfile = self._sock.makefile(mode='r')
def accept(self):
"""
@@ -60,7 +69,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors
"""
self._sock, _ = self._sock.accept()
- self._sockfile = self._sock.makefile()
+ self._sockfile = self._sock.makefile(mode='r')
def cmd(self, qtest_cmd):
"""
@@ -68,20 +77,27 @@ class QEMUQtestProtocol(object):
@param qtest_cmd: qtest command text to be sent
"""
+ assert self._sockfile is not None
self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
resp = self._sockfile.readline()
return resp
def close(self):
+ """Close this socket."""
self._sock.close()
- self._sockfile.close()
+ if self._sockfile:
+ self._sockfile.close()
+ self._sockfile = None
def settimeout(self, timeout):
+ """Set a timeout, in seconds."""
self._sock.settimeout(timeout)
class QEMUQtestMachine(QEMUMachine):
- '''A QEMU VM'''
+ """
+ A QEMU VM, with a qtest socket available.
+ """
def __init__(self, binary, args=None, name=None, test_dir="/var/tmp",
socket_scm_helper=None, sock_dir=None):
@@ -89,31 +105,38 @@ class QEMUQtestMachine(QEMUMachine):
name = "qemu-%d" % os.getpid()
if sock_dir is None:
sock_dir = test_dir
- super(QEMUQtestMachine,
- self).__init__(binary, args, name=name, test_dir=test_dir,
- socket_scm_helper=socket_scm_helper,
- sock_dir=sock_dir)
+ super().__init__(binary, args, name=name, test_dir=test_dir,
+ socket_scm_helper=socket_scm_helper,
+ sock_dir=sock_dir)
self._qtest = None
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
def _base_args(self):
- args = super(QEMUQtestMachine, self)._base_args()
+ args = super()._base_args()
args.extend(['-qtest', 'unix:path=' + self._qtest_path,
'-accel', 'qtest'])
return args
def _pre_launch(self):
- super(QEMUQtestMachine, self)._pre_launch()
+ super()._pre_launch()
self._qtest = QEMUQtestProtocol(self._qtest_path, server=True)
- def _post_launch(self):
- super(QEMUQtestMachine, self)._post_launch()
+ def _post_launch(self) -> None:
+ assert self._qtest is not None
+ super()._post_launch()
self._qtest.accept()
def _post_shutdown(self):
- super(QEMUQtestMachine, self)._post_shutdown()
+ super()._post_shutdown()
self._remove_if_exists(self._qtest_path)
- def qtest(self, cmd):
- '''Send a qtest command to guest'''
+ def qtest(self, cmd: str) -> str:
+ """
+ Send a qtest command to the guest.
+
+ :param cmd: qtest command to send
+ :return: qtest server response
+ """
+ if self._qtest is None:
+ raise RuntimeError("qtest socket not available")
return self._qtest.cmd(cmd)
diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py
index 96a31d3..95838cb 100755
--- a/scripts/analyze-migration.py
+++ b/scripts/analyze-migration.py
@@ -25,11 +25,6 @@ import struct
import sys
-MIN_PYTHON = (3, 2)
-if sys.version_info < MIN_PYTHON:
- sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON)
-
-
def mkdir_p(path):
try:
os.makedirs(path)
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index 46ab917..f9d204a 100755
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -75,13 +75,6 @@ def output(*args):
output_fd.write(a)
-if sys.version_info >= (3, 4):
- re_fullmatch = re.fullmatch
-else:
- def re_fullmatch(pat, str):
- return re.match('^' + pat + '$', str)
-
-
def output_autogen():
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
@@ -428,18 +421,18 @@ def parse_field(lineno, name, toks):
width = 0
func = None
for t in toks:
- if re_fullmatch('!function=' + re_ident, t):
+ if re.fullmatch('!function=' + re_ident, t):
if func:
error(lineno, 'duplicate function')
func = t.split('=')
func = func[1]
continue
- if re_fullmatch('[0-9]+:s[0-9]+', t):
+ if re.fullmatch('[0-9]+:s[0-9]+', t):
# Signed field extract
subtoks = t.split(':s')
sign = True
- elif re_fullmatch('[0-9]+:[0-9]+', t):
+ elif re.fullmatch('[0-9]+:[0-9]+', t):
# Unsigned field extract
subtoks = t.split(':')
sign = False
@@ -488,11 +481,11 @@ def parse_arguments(lineno, name, toks):
flds = []
extern = False
for t in toks:
- if re_fullmatch('!extern', t):
+ if re.fullmatch('!extern', t):
extern = True
anyextern = True
continue
- if not re_fullmatch(re_ident, t):
+ if not re.fullmatch(re_ident, t):
error(lineno, 'invalid argument set token "{0}"'.format(t))
if t in flds:
error(lineno, 'duplicate argument "{0}"'.format(t))
@@ -621,13 +614,13 @@ def parse_generic(lineno, is_format, name, toks):
continue
# 'Foo=%Bar' imports a field with a different name.
- if re_fullmatch(re_ident + '=%' + re_ident, t):
+ if re.fullmatch(re_ident + '=%' + re_ident, t):
(fname, iname) = t.split('=%')
flds = add_field_byname(lineno, flds, fname, iname)
continue
# 'Foo=number' sets an argument field to a constant value
- if re_fullmatch(re_ident + '=[+-]?[0-9]+', t):
+ if re.fullmatch(re_ident + '=[+-]?[0-9]+', t):
(fname, value) = t.split('=')
value = int(value)
flds = add_field(lineno, flds, fname, ConstField(value))
@@ -635,7 +628,7 @@ def parse_generic(lineno, is_format, name, toks):
# Pattern of 0s, 1s, dots and dashes indicate required zeros,
# required ones, or dont-cares.
- if re_fullmatch('[01.-]+', t):
+ if re.fullmatch('[01.-]+', t):
shift = len(t)
fms = t.replace('0', '1')
fms = fms.replace('.', '0')
@@ -652,7 +645,7 @@ def parse_generic(lineno, is_format, name, toks):
fixedmask = (fixedmask << shift) | fms
undefmask = (undefmask << shift) | ubm
# Otherwise, fieldname:fieldwidth
- elif re_fullmatch(re_ident + ':s?[0-9]+', t):
+ elif re.fullmatch(re_ident + ':s?[0-9]+', t):
(fname, flen) = t.split(':')
sign = False
if flen[0] == 's':
diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap
index 971ed0e..6fe66d5 100755
--- a/scripts/kvm/vmxcap
+++ b/scripts/kvm/vmxcap
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
#
# tool for querying VMX capabilities
#
@@ -275,5 +275,6 @@ controls = [
),
]
-for c in controls:
- c.show()
+if __name__ == '__main__':
+ for c in controls:
+ c.show()
diff --git a/scripts/modules/module_block.py b/scripts/modules/module_block.py
index f23191f..1109df8 100644
--- a/scripts/modules/module_block.py
+++ b/scripts/modules/module_block.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
#
# Module information generator
#
@@ -80,19 +80,20 @@ def print_bottom(fheader):
#endif
''')
-# First argument: output file
-# All other arguments: modules source files (.c)
-output_file = sys.argv[1]
-with open(output_file, 'w') as fheader:
- print_top(fheader)
+if __name__ == '__main__':
+ # First argument: output file
+ # All other arguments: modules source files (.c)
+ output_file = sys.argv[1]
+ with open(output_file, 'w') as fheader:
+ print_top(fheader)
- for filename in sys.argv[2:]:
- if os.path.isfile(filename):
- process_file(fheader, filename)
- else:
- print("File " + filename + " does not exist.", file=sys.stderr)
- sys.exit(1)
+ for filename in sys.argv[2:]:
+ if os.path.isfile(filename):
+ process_file(fheader, filename)
+ else:
+ print("File " + filename + " does not exist.", file=sys.stderr)
+ sys.exit(1)
- print_bottom(fheader)
+ print_bottom(fheader)
-sys.exit(0)
+ sys.exit(0)
diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py
index f2a305c..e0bfa7b 100644
--- a/scripts/qemu-gdb.py
+++ b/scripts/qemu-gdb.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python
-
+#!/usr/bin/env python3
+#
# GDB debugging support
#
# Copyright 2012 Red Hat, Inc. and/or its affiliates
diff --git a/scripts/qemugdb/__init__.py b/scripts/qemugdb/__init__.py
index 969f552..da8ff61 100644
--- a/scripts/qemugdb/__init__.py
+++ b/scripts/qemugdb/__init__.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-
+#
# GDB debugging support
#
# Copyright (c) 2015 Linaro Ltd
diff --git a/scripts/qemugdb/aio.py b/scripts/qemugdb/aio.py
index 2ba00c4..d7c1ba0 100644
--- a/scripts/qemugdb/aio.py
+++ b/scripts/qemugdb/aio.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-
+#
# GDB debugging support: aio/iohandler debug
#
# Copyright (c) 2015 Red Hat, Inc.
diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 41e079d..db61389 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-
+#
# GDB debugging support
#
# Copyright 2012 Red Hat, Inc. and/or its affiliates
diff --git a/scripts/qemugdb/mtree.py b/scripts/qemugdb/mtree.py
index 3030a60..8fe42c3 100644
--- a/scripts/qemugdb/mtree.py
+++ b/scripts/qemugdb/mtree.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-
+#
# GDB debugging support
#
# Copyright 2012 Red Hat, Inc. and/or its affiliates
@@ -84,4 +83,3 @@ class MtreeCommand(gdb.Command):
while not isnull(subregion):
self.print_item(subregion, addr, level)
subregion = subregion['subregions_link']['tqe_next']
-
diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py
index 18880fc..16c03c0 100644
--- a/scripts/qemugdb/tcg.py
+++ b/scripts/qemugdb/tcg.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# GDB debugging support, TCG status
diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py
index f0e132d..46537b2 100644
--- a/scripts/qemugdb/timers.py
+++ b/scripts/qemugdb/timers.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
# -*- coding: utf-8 -*-
# GDB debugging support
#
diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp
index 0625fc2..8e52e4a 100755
--- a/scripts/qmp/qmp
+++ b/scripts/qmp/qmp
@@ -11,7 +11,9 @@
# See the COPYING file in the top-level directory.
import sys, os
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
def print_response(rsp, prefix=[]):
if type(rsp) == list:
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index a01d31d..c5eef06 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -77,9 +77,6 @@ import re
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
-if sys.version_info[0] == 2:
- input = raw_input
-
class QMPCompleter(list):
def complete(self, text, state):
for cmd in self:
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index 6bada2c..5fa6b3b 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -15,7 +15,9 @@ import fuse, stat
from fuse import Fuse
import os, posix
from errno import *
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
fuse.fuse_python_api = (0, 2)
diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get
index 007b4cd..666df71 100755
--- a/scripts/qmp/qom-get
+++ b/scripts/qmp/qom-get
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
##
# QEMU Object Model test tools
#
@@ -13,7 +13,9 @@
import sys
import os
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list
index 03bda34..5074fd9 100755
--- a/scripts/qmp/qom-list
+++ b/scripts/qmp/qom-list
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
##
# QEMU Object Model test tools
#
@@ -13,7 +13,9 @@
import sys
import os
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set
index c37fe78..240a781 100755
--- a/scripts/qmp/qom-set
+++ b/scripts/qmp/qom-set
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
##
# QEMU Object Model test tools
#
@@ -13,7 +13,9 @@
import sys
import os
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree
index 1c8acf6..25b0781 100755
--- a/scripts/qmp/qom-tree
+++ b/scripts/qmp/qom-tree
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
##
# QEMU Object Model test tools
#
@@ -15,7 +15,9 @@
import sys
import os
-from qmp import QEMUMonitorProtocol
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py
index 59e7b4f..77d1c1d 100644
--- a/tests/acceptance/avocado_qemu/__init__.py
+++ b/tests/acceptance/avocado_qemu/__init__.py
@@ -69,13 +69,15 @@ def pick_default_qemu_bin(arch=None):
def _console_interaction(test, success_message, failure_message,
- send_string, keep_sending=False):
+ send_string, keep_sending=False, vm=None):
assert not keep_sending or send_string
- console = test.vm.console_socket.makefile()
+ if vm is None:
+ vm = test.vm
+ console = vm.console_socket.makefile()
console_logger = logging.getLogger('console')
while True:
if send_string:
- test.vm.console_socket.sendall(send_string.encode())
+ vm.console_socket.sendall(send_string.encode())
if not keep_sending:
send_string = None # send only once
msg = console.readline().strip()
@@ -115,7 +117,8 @@ def interrupt_interactive_console_until_pattern(test, success_message,
_console_interaction(test, success_message, failure_message,
interrupt_string, True)
-def wait_for_console_pattern(test, success_message, failure_message=None):
+def wait_for_console_pattern(test, success_message, failure_message=None,
+ vm=None):
"""
Waits for messages to appear on the console, while logging the content
@@ -125,7 +128,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None):
:param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails
"""
- _console_interaction(test, success_message, failure_message, None)
+ _console_interaction(test, success_message, failure_message, None, vm=vm)
def exec_command_and_wait_for_pattern(test, command,
success_message, failure_message=None):
diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py
index 075a386..3aa57e8 100644
--- a/tests/acceptance/boot_linux.py
+++ b/tests/acceptance/boot_linux.py
@@ -26,22 +26,8 @@ KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM"
TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG"
-class BootLinux(Test):
- """
- Boots a Linux system, checking for a successful initialization
- """
-
- timeout = 900
- chksum = None
-
- def setUp(self):
- super(BootLinux, self).setUp()
- self.vm.add_args('-smp', '2')
- self.vm.add_args('-m', '1024')
- self.prepare_boot()
- self.prepare_cloudinit()
-
- def prepare_boot(self):
+class BootLinuxBase(Test):
+ def download_boot(self):
self.log.debug('Looking for and selecting a qemu-img binary to be '
'used to create the bootable snapshot image')
# If qemu-img has been built, use it, otherwise the system wide one
@@ -60,17 +46,17 @@ class BootLinux(Test):
if image_arch == 'ppc64':
image_arch = 'ppc64le'
try:
- self.boot = vmimage.get(
+ boot = vmimage.get(
'fedora', arch=image_arch, version='31',
checksum=self.chksum,
algorithm='sha256',
cache_dir=self.cache_dirs[0],
snapshot_dir=self.workdir)
- self.vm.add_args('-drive', 'file=%s' % self.boot.path)
except:
self.cancel('Failed to download/prepare boot image')
+ return boot.path
- def prepare_cloudinit(self):
+ def download_cloudinit(self):
self.log.info('Preparing cloudinit image')
try:
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
@@ -81,9 +67,32 @@ class BootLinux(Test):
# QEMU's hard coded usermode router address
phone_home_host='10.0.2.2',
phone_home_port=self.phone_home_port)
- self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
except Exception:
self.cancel('Failed to prepared cloudinit image')
+ return cloudinit_iso
+
+class BootLinux(BootLinuxBase):
+ """
+ Boots a Linux system, checking for a successful initialization
+ """
+
+ timeout = 900
+ chksum = None
+
+ def setUp(self):
+ super(BootLinux, self).setUp()
+ self.vm.add_args('-smp', '2')
+ self.vm.add_args('-m', '1024')
+ self.prepare_boot()
+ self.prepare_cloudinit()
+
+ def prepare_boot(self):
+ path = self.download_boot()
+ self.vm.add_args('-drive', 'file=%s' % path)
+
+ def prepare_cloudinit(self):
+ cloudinit_iso = self.download_cloudinit()
+ self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
def launch_and_wait(self):
self.vm.set_console()
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index c6b06a1..12725d4 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -28,19 +28,13 @@ try:
except CmdNotFoundError:
P7ZIP_AVAILABLE = False
-class BootLinuxConsole(Test):
- """
- Boots a Linux kernel and checks that the console is operational and the
- kernel command line is properly passed from QEMU to the kernel
- """
-
- timeout = 90
-
+class LinuxKernelTest(Test):
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
- def wait_for_console_pattern(self, success_message):
+ def wait_for_console_pattern(self, success_message, vm=None):
wait_for_console_pattern(self, success_message,
- failure_message='Kernel panic - not syncing')
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
def extract_from_deb(self, deb, path):
"""
@@ -79,6 +73,13 @@ class BootLinuxConsole(Test):
os.chdir(cwd)
return os.path.normpath(os.path.join(self.workdir, path))
+class BootLinuxConsole(LinuxKernelTest):
+ """
+ Boots a Linux kernel and checks that the console is operational and the
+ kernel command line is properly passed from QEMU to the kernel
+ """
+ timeout = 90
+
def test_x86_64_pc(self):
"""
:avocado: tags=arch:x86_64
diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py
index 0365289..792639c 100644
--- a/tests/acceptance/migration.py
+++ b/tests/acceptance/migration.py
@@ -35,6 +35,10 @@ class Migration(Test):
timeout=self.timeout,
step=0.1,
args=(src_vm,))
+ wait.wait_for(self.migration_finished,
+ timeout=self.timeout,
+ step=0.1,
+ args=(dst_vm,))
self.assertEqual(src_vm.command('query-migrate')['status'], 'completed')
self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed')
self.assertEqual(dst_vm.command('query-status')['status'], 'running')
diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index d8268c1..5a9735d 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -258,12 +258,13 @@ class Docker(object):
return self._do_kill_instances(True)
def _output(self, cmd, **kwargs):
- if sys.version_info[1] >= 6:
+ try:
return subprocess.check_output(self._command + cmd,
stderr=subprocess.STDOUT,
encoding='utf-8',
**kwargs)
- else:
+ except TypeError:
+ # 'encoding' argument was added in 3.6+
return subprocess.check_output(self._command + cmd,
stderr=subprocess.STDOUT,
**kwargs).decode('utf-8')
diff --git a/tests/migration/guestperf-batch.py b/tests/migration/guestperf-batch.py
index cb150ce..f1e9009 100755
--- a/tests/migration/guestperf-batch.py
+++ b/tests/migration/guestperf-batch.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
#
# Migration test batch comparison invokation
#
diff --git a/tests/migration/guestperf-plot.py b/tests/migration/guestperf-plot.py
index d70bb7a..9071510 100755
--- a/tests/migration/guestperf-plot.py
+++ b/tests/migration/guestperf-plot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
#
# Migration test graph plotting command
#
diff --git a/tests/migration/guestperf.py b/tests/migration/guestperf.py
index 99b027e..ba1c4bc 100755
--- a/tests/migration/guestperf.py
+++ b/tests/migration/guestperf.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
#
# Migration test direct invokation command
#
diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py
index 588d62a..78f42c4 100755
--- a/tests/qemu-iotests/nbd-fault-injector.py
+++ b/tests/qemu-iotests/nbd-fault-injector.py
@@ -47,10 +47,7 @@ import sys
import socket
import struct
import collections
-if sys.version_info.major >= 3:
- import configparser
-else:
- import ConfigParser as configparser
+import configparser
FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB
diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include
index 74ab522..a253aba 100644
--- a/tests/vm/Makefile.include
+++ b/tests/vm/Makefile.include
@@ -41,6 +41,7 @@ endif
@echo " J=[0..9]* - Override the -jN parameter for make commands"
@echo " DEBUG=1 - Enable verbose output on host and interactive debugging"
@echo " V=1 - Enable verbose ouput on host and guest commands"
+ @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build."
@echo " QEMU=/path/to/qemu - Change path to QEMU binary"
@echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool"
@@ -57,6 +58,7 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
$(PYTHON) $< \
$(if $(V)$(DEBUG), --debug) \
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
+ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$@" \
--force \
--build-image $@, \
@@ -71,6 +73,7 @@ vm-build-%: $(IMAGES_DIR)/%.img
$(if $(DEBUG), --interactive) \
$(if $(J),--jobs $(J)) \
$(if $(V),--verbose) \
+ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$<" \
$(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \
--snapshot \
@@ -91,6 +94,8 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img
$(call quiet-command, \
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(J),--jobs $(J)) \
+ $(if $(V)$(DEBUG), --debug) \
+ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$<" \
--interactive \
false, \
diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index a2d4054..a80b616 100644
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -61,9 +61,11 @@ class BaseVM(object):
# 4 is arbitrary, but greater than 2,
# since we found we need to wait more than twice as long.
tcg_ssh_timeout_multiplier = 4
- def __init__(self, debug=False, vcpus=None, genisoimage=None):
+ def __init__(self, debug=False, vcpus=None, genisoimage=None,
+ build_path=None):
self._guest = None
self._genisoimage = genisoimage
+ self._build_path = build_path
self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
suffix=".tmp",
dir="."))
@@ -184,15 +186,15 @@ class BaseVM(object):
"-device", "virtio-blk,drive=drive0,bootindex=0"]
args += self._data_args + extra_args
logging.debug("QEMU args: %s", " ".join(args))
- qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch)
- guest = QEMUMachine(binary=qemu_bin, args=args)
+ qemu_path = get_qemu_path(self.arch, self._build_path)
+ guest = QEMUMachine(binary=qemu_path, args=args)
guest.set_machine('pc')
guest.set_console()
try:
guest.launch()
except:
logging.error("Failed to launch QEMU, command line:")
- logging.error(" ".join([qemu_bin] + args))
+ logging.error(" ".join([qemu_path] + args))
logging.error("Log:")
logging.error(guest.get_log())
logging.error("QEMU version >= 2.10 is required")
@@ -318,24 +320,24 @@ class BaseVM(object):
def print_step(self, text):
sys.stderr.write("### %s ...\n" % text)
- def wait_ssh(self, wait_root=False, seconds=300):
+ def wait_ssh(self, wait_root=False, seconds=300, cmd="exit 0"):
# Allow more time for VM to boot under TCG.
if not kvm_available(self.arch):
seconds *= self.tcg_ssh_timeout_multiplier
starttime = datetime.datetime.now()
endtime = starttime + datetime.timedelta(seconds=seconds)
- guest_up = False
+ cmd_success = False
while datetime.datetime.now() < endtime:
- if wait_root and self.ssh_root("exit 0") == 0:
- guest_up = True
+ if wait_root and self.ssh_root(cmd) == 0:
+ cmd_success = True
break
- elif self.ssh("exit 0") == 0:
- guest_up = True
+ elif self.ssh(cmd) == 0:
+ cmd_success = True
break
seconds = (endtime - datetime.datetime.now()).total_seconds()
logging.debug("%ds before timeout", seconds)
time.sleep(1)
- if not guest_up:
+ if not cmd_success:
raise Exception("Timeout while waiting for guest ssh")
def shutdown(self):
@@ -391,6 +393,19 @@ class BaseVM(object):
return os.path.join(cidir, "cloud-init.iso")
+def get_qemu_path(arch, build_path=None):
+ """Fetch the path to the qemu binary."""
+ # If QEMU environment variable set, it takes precedence
+ if "QEMU" in os.environ:
+ qemu_path = os.environ["QEMU"]
+ elif build_path:
+ qemu_path = os.path.join(build_path, arch + "-softmmu")
+ qemu_path = os.path.join(qemu_path, "qemu-system-" + arch)
+ else:
+ # Default is to use system path for qemu.
+ qemu_path = "qemu-system-" + arch
+ return qemu_path
+
def parse_args(vmcls):
def get_default_jobs():
@@ -421,6 +436,9 @@ def parse_args(vmcls):
help="build QEMU from source in guest")
parser.add_option("--build-target",
help="QEMU build target", default="check")
+ parser.add_option("--build-path", default=None,
+ help="Path of build directory, "\
+ "for using build tree QEMU binary. ")
parser.add_option("--interactive", "-I", action="store_true",
help="Interactively run command")
parser.add_option("--snapshot", "-s", action="store_true",
@@ -439,7 +457,7 @@ def main(vmcls):
logging.basicConfig(level=(logging.DEBUG if args.debug
else logging.WARN))
vm = vmcls(debug=args.debug, vcpus=args.jobs,
- genisoimage=args.genisoimage)
+ genisoimage=args.genisoimage, build_path=args.build_path)
if args.build_image:
if os.path.exists(args.image) and not args.force:
sys.stderr.writelines(["Image file exists: %s\n" % args.image,