diff options
Diffstat (limited to 'python/qemu')
| -rw-r--r-- | python/qemu/.flake8 | 2 | ||||
| -rw-r--r-- | python/qemu/.isort.cfg | 7 | ||||
| -rw-r--r-- | python/qemu/README.rst | 8 | ||||
| -rw-r--r-- | python/qemu/__init__.py | 11 | ||||
| -rw-r--r-- | python/qemu/machine/README.rst | 9 | ||||
| -rw-r--r-- | python/qemu/machine/__init__.py | 36 | ||||
| -rw-r--r-- | python/qemu/machine/console_socket.py (renamed from python/qemu/console_socket.py) | 11 | ||||
| -rw-r--r-- | python/qemu/machine/machine.py (renamed from python/qemu/machine.py) | 68 | ||||
| -rw-r--r-- | python/qemu/machine/qtest.py (renamed from python/qemu/qtest.py) | 9 | ||||
| -rw-r--r-- | python/qemu/pylintrc | 58 | ||||
| -rw-r--r-- | python/qemu/qmp/README.rst | 9 | ||||
| -rw-r--r-- | python/qemu/qmp/__init__.py (renamed from python/qemu/qmp.py) | 12 | ||||
| -rw-r--r-- | python/qemu/utils/README.rst | 7 | ||||
| -rw-r--r-- | python/qemu/utils/__init__.py | 45 | ||||
| -rw-r--r-- | python/qemu/utils/accel.py (renamed from python/qemu/accel.py) | 0 |
15 files changed, 181 insertions, 111 deletions
diff --git a/python/qemu/.flake8 b/python/qemu/.flake8 deleted file mode 100644 index 45d8146f3f..0000000000 --- a/python/qemu/.flake8 +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -extend-ignore = E722 # Pylint handles this, but smarter. \ No newline at end of file diff --git a/python/qemu/.isort.cfg b/python/qemu/.isort.cfg deleted file mode 100644 index 6d0fd6cc0d..0000000000 --- a/python/qemu/.isort.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[settings] -force_grid_wrap=4 -force_sort_within_sections=True -include_trailing_comma=True -line_length=72 -lines_after_imports=2 -multi_line_output=3 \ No newline at end of file diff --git a/python/qemu/README.rst b/python/qemu/README.rst new file mode 100644 index 0000000000..d04943f526 --- /dev/null +++ b/python/qemu/README.rst @@ -0,0 +1,8 @@ +QEMU Python Namespace +===================== + +This directory serves as the root of a `Python PEP 420 implicit +namespace package <https://www.python.org/dev/peps/pep-0420/>`_. + +Each directory below is assumed to be an installable Python package that +is available under the ``qemu.<package>`` namespace. diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py deleted file mode 100644 index 4ca06c34a4..0000000000 --- a/python/qemu/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# QEMU library -# -# Copyright (C) 2015-2016 Red Hat Inc. -# Copyright (C) 2012 IBM Corp. -# -# Authors: -# Fam Zheng <famz@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. -# diff --git a/python/qemu/machine/README.rst b/python/qemu/machine/README.rst new file mode 100644 index 0000000000..ac2b4fffb4 --- /dev/null +++ b/python/qemu/machine/README.rst @@ -0,0 +1,9 @@ +qemu.machine package +==================== + +This package provides core utilities used for testing and debugging +QEMU. It is used by the iotests, vm tests, acceptance tests, and several +other utilities in the ./scripts directory. It is not a fully-fledged +SDK and it is subject to change at any time. + +See the documentation in ``__init__.py`` for more information. diff --git a/python/qemu/machine/__init__.py b/python/qemu/machine/__init__.py new file mode 100644 index 0000000000..728f27adbe --- /dev/null +++ b/python/qemu/machine/__init__.py @@ -0,0 +1,36 @@ +""" +QEMU development and testing library. + +This library provides a few high-level classes for driving QEMU from a +test suite, not intended for production use. + +- QEMUMachine: Configure and Boot a QEMU VM + - QEMUQtestMachine: VM class, with a qtest socket. + +- QEMUQtestProtocol: Connect to, send/receive qtest messages. +""" + +# Copyright (C) 2020-2021 John Snow for Red Hat Inc. +# Copyright (C) 2015-2016 Red Hat Inc. +# Copyright (C) 2012 IBM Corp. +# +# Authors: +# John Snow <jsnow@redhat.com> +# Fam Zheng <fam@euphon.net> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# + +# pylint: disable=import-error +# see: https://github.com/PyCQA/pylint/issues/3624 +# see: https://github.com/PyCQA/pylint/issues/3651 +from .machine import QEMUMachine +from .qtest import QEMUQtestMachine, QEMUQtestProtocol + + +__all__ = ( + 'QEMUMachine', + 'QEMUQtestProtocol', + 'QEMUQtestMachine', +) diff --git a/python/qemu/console_socket.py b/python/qemu/machine/console_socket.py index ac21130e44..8c4ff598ad 100644 --- a/python/qemu/console_socket.py +++ b/python/qemu/machine/console_socket.py @@ -39,6 +39,7 @@ class ConsoleSocket(socket.socket): self.connect(address) self._logfile = None if file: + # pylint: disable=consider-using-with self._logfile = open(file, "bw") self._open = True self._drain_thread = None @@ -46,11 +47,11 @@ class ConsoleSocket(socket.socket): self._drain_thread = self._thread_start() def __repr__(self) -> str: - s = super().__repr__() - s = s.rstrip(">") - s = "%s, logfile=%s, drain_thread=%s>" % (s, self._logfile, - self._drain_thread) - return s + tmp = super().__repr__() + tmp = tmp.rstrip(">") + tmp = "%s, logfile=%s, drain_thread=%s>" % (tmp, self._logfile, + self._drain_thread) + return tmp def _drain_fn(self) -> None: """Drains the socket and runs while the socket is open.""" diff --git a/python/qemu/machine.py b/python/qemu/machine/machine.py index 6e44bda337..b62435528e 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine/machine.py @@ -38,8 +38,14 @@ from typing import ( Type, ) -from . import console_socket, qmp -from .qmp import QMPMessage, QMPReturnValue, SocketAddrT +from qemu.qmp import ( # pylint: disable=import-error + QEMUMonitorProtocol, + QMPMessage, + QMPReturnValue, + SocketAddrT, +) + +from . import console_socket LOG = logging.getLogger(__name__) @@ -84,7 +90,7 @@ class QEMUMachine: args: Sequence[str] = (), wrapper: Sequence[str] = (), name: Optional[str] = None, - test_dir: str = "/var/tmp", + base_temp_dir: str = "/var/tmp", monitor_address: Optional[SocketAddrT] = None, socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None, @@ -97,10 +103,10 @@ class QEMUMachine: @param args: list of extra arguments @param wrapper: list of arguments used as prefix to qemu binary @param name: prefix for socket and log file names (default: qemu-PID) - @param test_dir: where to create socket and log file + @param base_temp_dir: default location where temp files are created @param monitor_address: address for QMP monitor @param socket_scm_helper: helper program, required for send_fd_scm() - @param sock_dir: where to create socket (overrides test_dir for sock) + @param sock_dir: where to create socket (defaults to base_temp_dir) @param drain_console: (optional) True to drain console socket to buffer @param console_log: (optional) path to console log file @note: Qemu process is not started until launch() is used. @@ -112,8 +118,8 @@ class QEMUMachine: self._wrapper = wrapper self._name = name or "qemu-%d" % os.getpid() - self._test_dir = test_dir - self._sock_dir = sock_dir or self._test_dir + self._base_temp_dir = base_temp_dir + self._sock_dir = sock_dir or self._base_temp_dir self._socket_scm_helper = socket_scm_helper if monitor_address is not None: @@ -139,7 +145,7 @@ class QEMUMachine: self._events: List[QMPMessage] = [] self._iolog: Optional[str] = None self._qmp_set = True # Enable QMP monitor by default. - self._qmp_connection: Optional[qmp.QEMUMonitorProtocol] = None + self._qmp_connection: Optional[QEMUMonitorProtocol] = None self._qemu_full_args: Tuple[str, ...] = () self._temp_dir: Optional[str] = None self._launched = False @@ -223,14 +229,16 @@ class QEMUMachine: assert fd is not None 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.run( + fd_param, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + close_fds=False, ) - output = proc.communicate()[0] - if output: - LOG.debug(output) + if proc.stdout: + LOG.debug(proc.stdout) return proc.returncode @@ -303,10 +311,7 @@ class QEMUMachine: return args def _pre_launch(self) -> None: - self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-", - dir=self._test_dir) - self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") - self._qemu_log_file = open(self._qemu_log_path, 'wb') + self._qemu_log_path = os.path.join(self.temp_dir, self._name + ".log") if self._console_set: self._remove_files.append(self._console_address) @@ -315,12 +320,17 @@ class QEMUMachine: if self._remove_monitor_sockfile: assert isinstance(self._monitor_address, str) self._remove_files.append(self._monitor_address) - self._qmp_connection = qmp.QEMUMonitorProtocol( + self._qmp_connection = QEMUMonitorProtocol( self._monitor_address, server=True, nickname=self._name ) + # NOTE: Make sure any opened resources are *definitely* freed in + # _post_shutdown()! + # pylint: disable=consider-using-with + self._qemu_log_file = open(self._qemu_log_path, 'wb') + def _post_launch(self) -> None: if self._qmp_connection: self._qmp.accept() @@ -393,7 +403,6 @@ class QEMUMachine: """ Launch the VM and establish a QMP connection """ - devnull = open(os.path.devnull, 'rb') self._pre_launch() self._qemu_full_args = tuple( chain(self._wrapper, @@ -402,8 +411,11 @@ class QEMUMachine: self._args) ) LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) + + # Cleaning up of this subprocess is guaranteed by _do_shutdown. + # pylint: disable=consider-using-with self._popen = subprocess.Popen(self._qemu_full_args, - stdin=devnull, + stdin=subprocess.DEVNULL, stdout=self._qemu_log_file, stderr=subprocess.STDOUT, shell=False, @@ -535,7 +547,7 @@ class QEMUMachine: self._qmp_set = enabled @property - def _qmp(self) -> qmp.QEMUMonitorProtocol: + def _qmp(self) -> QEMUMonitorProtocol: if self._qmp_connection is None: raise QEMUMachineError("Attempt to access QMP with no connection") return self._qmp_connection @@ -744,3 +756,13 @@ class QEMUMachine: file=self._console_log_path, drain=self._drain_console) return self._console_socket + + @property + def temp_dir(self) -> str: + """ + Returns a temporary directory to be used for this machine + """ + if self._temp_dir is None: + self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-", + dir=self._base_temp_dir) + return self._temp_dir diff --git a/python/qemu/qtest.py b/python/qemu/machine/qtest.py index 39a0cf62fe..93700684d1 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/machine/qtest.py @@ -26,8 +26,9 @@ from typing import ( TextIO, ) +from qemu.qmp import SocketAddrT # pylint: disable=import-error + from .machine import QEMUMachine -from .qmp import SocketAddrT class QEMUQtestProtocol: @@ -112,14 +113,14 @@ class QEMUQtestMachine(QEMUMachine): binary: str, args: Sequence[str] = (), name: Optional[str] = None, - test_dir: str = "/var/tmp", + base_temp_dir: str = "/var/tmp", socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None): if name is None: name = "qemu-%d" % os.getpid() if sock_dir is None: - sock_dir = test_dir - super().__init__(binary, args, name=name, test_dir=test_dir, + sock_dir = base_temp_dir + super().__init__(binary, args, name=name, base_temp_dir=base_temp_dir, socket_scm_helper=socket_scm_helper, sock_dir=sock_dir) self._qtest: Optional[QEMUQtestProtocol] = None diff --git a/python/qemu/pylintrc b/python/qemu/pylintrc deleted file mode 100644 index 3f69205000..0000000000 --- a/python/qemu/pylintrc +++ /dev/null @@ -1,58 +0,0 @@ -[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, - c, -[VARIABLES] - -[STRING] - -[SPELLING] - -[FORMAT] - -[SIMILARITIES] - -# Ignore imports when computing similarities. -ignore-imports=yes - -[TYPECHECK] - -[CLASSES] - -[IMPORTS] - -[DESIGN] - -[EXCEPTIONS] diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst new file mode 100644 index 0000000000..c21951491c --- /dev/null +++ b/python/qemu/qmp/README.rst @@ -0,0 +1,9 @@ +qemu.qmp package +================ + +This package provides a library used for connecting to and communicating +with QMP servers. It is used extensively by iotests, vm tests, +acceptance tests, and other utilities in the ./scripts directory. It is +not a fully-fledged SDK and is subject to change at any time. + +See the documentation in ``__init__.py`` for more information. diff --git a/python/qemu/qmp.py b/python/qemu/qmp/__init__.py index 2cd4d43036..9606248a3d 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp/__init__.py @@ -1,4 +1,14 @@ -""" QEMU Monitor Protocol Python class """ +""" +QEMU Monitor Protocol (QMP) development library & tooling. + +This package provides a fairly low-level class for communicating to QMP +protocol servers, as implemented by QEMU, the QEMU Guest Agent, and the +QEMU Storage Daemon. This library is not intended for production use. + +`QEMUMonitorProtocol` is the primary class of interest, and all errors +raised derive from `QMPError`. +""" + # Copyright (C) 2009, 2010 Red Hat Inc. # # Authors: diff --git a/python/qemu/utils/README.rst b/python/qemu/utils/README.rst new file mode 100644 index 0000000000..975fbf4d7d --- /dev/null +++ b/python/qemu/utils/README.rst @@ -0,0 +1,7 @@ +qemu.utils package +================== + +This package provides miscellaneous utilities used for testing and +debugging QEMU. It is used primarily by the vm and acceptance tests. + +See the documentation in ``__init__.py`` for more information. diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py new file mode 100644 index 0000000000..7f1a5138c4 --- /dev/null +++ b/python/qemu/utils/__init__.py @@ -0,0 +1,45 @@ +""" +QEMU development and testing utilities + +This package provides a small handful of utilities for performing +various tasks not directly related to the launching of a VM. +""" + +# Copyright (C) 2021 Red Hat Inc. +# +# Authors: +# John Snow <jsnow@redhat.com> +# Cleber Rosa <crosa@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# + +import re +from typing import Optional + +# pylint: disable=import-error +from .accel import kvm_available, list_accel, tcg_available + + +__all__ = ( + 'get_info_usernet_hostfwd_port', + 'kvm_available', + 'list_accel', + 'tcg_available', +) + + +def get_info_usernet_hostfwd_port(info_usernet_output: str) -> Optional[int]: + """ + Returns the port given to the hostfwd parameter via info usernet + + :param info_usernet_output: output generated by hmp command "info usernet" + :return: the port number allocated by the hostfwd option + """ + for line in info_usernet_output.split('\r\n'): + regex = r'TCP.HOST_FORWARD.*127\.0\.0\.1\s+(\d+)\s+10\.' + match = re.search(regex, line) + if match is not None: + return int(match[1]) + return None diff --git a/python/qemu/accel.py b/python/qemu/utils/accel.py index 297933df2a..297933df2a 100644 --- a/python/qemu/accel.py +++ b/python/qemu/utils/accel.py |