Commit b73f417a authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 4ec2a1f5 1c80c87c
Loading
Loading
Loading
Loading

python/qemu/.flake8

0 → 100644
+2 −0
Original line number Diff line number Diff line
[flake8]
extend-ignore = E722  # Pylint handles this, but smarter.
 No newline at end of file
+6 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ ADDITIONAL_ARCHES = {
    "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.
+28 −16
Original line number Diff line number Diff line
@@ -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.

python/qemu/pylintrc

0 → 100644
+58 −0
Original line number Diff line number Diff line
[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]
+20 −9
Original line number Diff line number Diff line
@@ -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):
Loading