mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 01:03:55 -06:00
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 classes151323210
693157969
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl7T3U8ACgkQ4+MsLN6t wN4JnhAAif0Lw06lEl+ZksD0d4YLyBp079BNJEUWvflsivHY9RIn4e+eXdL3Q8m9 roBzlsV71C7Ufbp26LFthcnvLaq0JH7RhBUVUoQAI7XZ46HAr3KRyOBJQP7LCr5N 4Z97y8hqfSdchwpYbxkEbPy58caCRIneqIvg0sp8XuyXpDpVDqP11rXTg4fgqi7i 1+D1yjr+wgaa7Vvf4sYzOw4D5zD2Mh+zMyDFI9d7yajs/4RH9k+iZteV7baLRQ5Q xkC0yqHDGp+uzEF4mk+5VUiZDvDUUxnkuFYKc6mFcahKzhrxLpEsvhnPFZ+vr4ib 1DDmSr6ihf37wBzowHgAkmTwAiGmVEobu/2h93JXJesWw0TKRT74w1ftZKEIY1v4 1Hka38gV0LULOAOjiy+aKNJqpJ/eipds94MvllRLHCgbB4H9VKBd4ts6linn+xsM CUebvUOgiVzH+hYbLJ1EBLFhbsmQ+yvopbQtLIlyFpKTFhdE1dA3vfb9NV0iqfOL fxaP/WaibKEFF5H40H7Ro+H7cT2+hF8MyByBT6q/UzoDURkZxeswqd2ww2VcUw2M X6h3/Hzek8PtZ+md3G6Hb1mJccfBHElrSgXAjrZ0WLOy4ZV7Y+/QrE8ooJwIKGKZ NinXrUocDl8xfRNWjynImzqma5TdaLW5tOmx6yTSK1R3lQh2z7A= =7xHS -----END PGP SIGNATURE----- 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 classes151323210
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>
This commit is contained in:
commit
b73f417aae
35 changed files with 317 additions and 178 deletions
2
python/qemu/.flake8
Normal file
2
python/qemu/.flake8
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[flake8]
|
||||||
|
extend-ignore = E722 # Pylint handles this, but smarter.
|
|
@ -28,6 +28,7 @@ ADDITIONAL_ARCHES = {
|
||||||
"ppc64le": "ppc64",
|
"ppc64le": "ppc64",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def list_accel(qemu_bin):
|
def list_accel(qemu_bin):
|
||||||
"""
|
"""
|
||||||
List accelerators enabled in the QEMU binary.
|
List accelerators enabled in the QEMU binary.
|
||||||
|
@ -47,6 +48,7 @@ def list_accel(qemu_bin):
|
||||||
# Skip the first line which is the header.
|
# Skip the first line which is the header.
|
||||||
return [acc.strip() for acc in out.splitlines()[1:]]
|
return [acc.strip() for acc in out.splitlines()[1:]]
|
||||||
|
|
||||||
|
|
||||||
def kvm_available(target_arch=None, qemu_bin=None):
|
def kvm_available(target_arch=None, qemu_bin=None):
|
||||||
"""
|
"""
|
||||||
Check if KVM is available using the following heuristic:
|
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 False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def tcg_available(qemu_bin):
|
def tcg_available(qemu_bin):
|
||||||
"""
|
"""
|
||||||
Check if TCG is available.
|
Check if TCG is available.
|
||||||
|
|
|
@ -24,11 +24,14 @@ import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from typing import Optional, Type
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
from . import qmp
|
from . import qmp
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class QEMUMachineError(Exception):
|
class QEMUMachineError(Exception):
|
||||||
"""
|
"""
|
||||||
Exception called when an error in QEMUMachine happens.
|
Exception called when an error in QEMUMachine happens.
|
||||||
|
@ -54,15 +57,16 @@ class MonitorResponseError(qmp.QMPError):
|
||||||
desc = reply["error"]["desc"]
|
desc = reply["error"]["desc"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
desc = reply
|
desc = reply
|
||||||
super(MonitorResponseError, self).__init__(desc)
|
super().__init__(desc)
|
||||||
self.reply = reply
|
self.reply = reply
|
||||||
|
|
||||||
|
|
||||||
class QEMUMachine(object):
|
class QEMUMachine:
|
||||||
"""
|
"""
|
||||||
A QEMU VM
|
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:
|
with VM(binary) as vm:
|
||||||
...
|
...
|
||||||
|
@ -119,15 +123,14 @@ class QEMUMachine(object):
|
||||||
self._console_socket = None
|
self._console_socket = None
|
||||||
self._remove_files = []
|
self._remove_files = []
|
||||||
|
|
||||||
# just in case logging wasn't configured by the main script:
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return 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()
|
self.shutdown()
|
||||||
return False
|
|
||||||
|
|
||||||
def add_monitor_null(self):
|
def add_monitor_null(self):
|
||||||
"""
|
"""
|
||||||
|
@ -188,8 +191,10 @@ class QEMUMachine(object):
|
||||||
fd_param.append(str(fd))
|
fd_param.append(str(fd))
|
||||||
|
|
||||||
devnull = open(os.path.devnull, 'rb')
|
devnull = open(os.path.devnull, 'rb')
|
||||||
proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
|
proc = subprocess.Popen(
|
||||||
stderr=subprocess.STDOUT, close_fds=False)
|
fd_param, stdin=devnull, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT, close_fds=False
|
||||||
|
)
|
||||||
output = proc.communicate()[0]
|
output = proc.communicate()[0]
|
||||||
if output:
|
if output:
|
||||||
LOG.debug(output)
|
LOG.debug(output)
|
||||||
|
@ -242,7 +247,7 @@ class QEMUMachine(object):
|
||||||
'chardev=mon,mode=control'])
|
'chardev=mon,mode=control'])
|
||||||
if self._machine is not None:
|
if self._machine is not None:
|
||||||
args.extend(['-machine', self._machine])
|
args.extend(['-machine', self._machine])
|
||||||
for i in range(self._console_index):
|
for _ in range(self._console_index):
|
||||||
args.extend(['-serial', 'null'])
|
args.extend(['-serial', 'null'])
|
||||||
if self._console_set:
|
if self._console_set:
|
||||||
self._console_address = os.path.join(self._sock_dir,
|
self._console_address = os.path.join(self._sock_dir,
|
||||||
|
@ -342,7 +347,7 @@ class QEMUMachine(object):
|
||||||
self._load_io_log()
|
self._load_io_log()
|
||||||
self._post_shutdown()
|
self._post_shutdown()
|
||||||
|
|
||||||
def shutdown(self, has_quit=False):
|
def shutdown(self, has_quit=False, hard=False):
|
||||||
"""
|
"""
|
||||||
Terminate the VM and clean up
|
Terminate the VM and clean up
|
||||||
"""
|
"""
|
||||||
|
@ -354,7 +359,9 @@ class QEMUMachine(object):
|
||||||
self._console_socket = None
|
self._console_socket = None
|
||||||
|
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
if self._qmp:
|
if hard:
|
||||||
|
self._popen.kill()
|
||||||
|
elif self._qmp:
|
||||||
try:
|
try:
|
||||||
if not has_quit:
|
if not has_quit:
|
||||||
self._qmp.cmd('quit')
|
self._qmp.cmd('quit')
|
||||||
|
@ -368,16 +375,20 @@ class QEMUMachine(object):
|
||||||
self._post_shutdown()
|
self._post_shutdown()
|
||||||
|
|
||||||
exitcode = self.exitcode()
|
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'
|
msg = 'qemu received signal %i: %s'
|
||||||
if self._qemu_full_args:
|
if self._qemu_full_args:
|
||||||
command = ' '.join(self._qemu_full_args)
|
command = ' '.join(self._qemu_full_args)
|
||||||
else:
|
else:
|
||||||
command = ''
|
command = ''
|
||||||
LOG.warning(msg, -exitcode, command)
|
LOG.warning(msg, -int(exitcode), command)
|
||||||
|
|
||||||
self._launched = False
|
self._launched = False
|
||||||
|
|
||||||
|
def kill(self):
|
||||||
|
self.shutdown(hard=True)
|
||||||
|
|
||||||
def set_qmp_monitor(self, enabled=True):
|
def set_qmp_monitor(self, enabled=True):
|
||||||
"""
|
"""
|
||||||
Set the QMP monitor.
|
Set the QMP monitor.
|
||||||
|
@ -482,7 +493,8 @@ class QEMUMachine(object):
|
||||||
|
|
||||||
def events_wait(self, events, timeout=60.0):
|
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.
|
events: a sequence of (name, match_criteria) tuples.
|
||||||
The match criteria are optional and may be None.
|
The match criteria are optional and may be None.
|
||||||
|
|
58
python/qemu/pylintrc
Normal file
58
python/qemu/pylintrc
Normal file
|
@ -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]
|
|
@ -11,6 +11,12 @@ import json
|
||||||
import errno
|
import errno
|
||||||
import socket
|
import socket
|
||||||
import logging
|
import logging
|
||||||
|
from typing import (
|
||||||
|
Optional,
|
||||||
|
TextIO,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
|
|
||||||
class QMPError(Exception):
|
class QMPError(Exception):
|
||||||
|
@ -61,7 +67,7 @@ class QEMUMonitorProtocol:
|
||||||
self.__events = []
|
self.__events = []
|
||||||
self.__address = address
|
self.__address = address
|
||||||
self.__sock = self.__get_sock()
|
self.__sock = self.__get_sock()
|
||||||
self.__sockfile = None
|
self.__sockfile: Optional[TextIO] = None
|
||||||
self._nickname = nickname
|
self._nickname = nickname
|
||||||
if self._nickname:
|
if self._nickname:
|
||||||
self.logger = logging.getLogger('QMP').getChild(self._nickname)
|
self.logger = logging.getLogger('QMP').getChild(self._nickname)
|
||||||
|
@ -88,6 +94,7 @@ class QEMUMonitorProtocol:
|
||||||
raise QMPCapabilitiesError
|
raise QMPCapabilitiesError
|
||||||
|
|
||||||
def __json_read(self, only_event=False):
|
def __json_read(self, only_event=False):
|
||||||
|
assert self.__sockfile is not None
|
||||||
while True:
|
while True:
|
||||||
data = self.__sockfile.readline()
|
data = self.__sockfile.readline()
|
||||||
if not data:
|
if not data:
|
||||||
|
@ -114,14 +121,14 @@ class QEMUMonitorProtocol:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Check for new events regardless and pull them into the cache:
|
# Check for new events regardless and pull them into the cache:
|
||||||
self.__sock.setblocking(0)
|
self.__sock.setblocking(False)
|
||||||
try:
|
try:
|
||||||
self.__json_read()
|
self.__json_read()
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EAGAIN:
|
if err.errno == errno.EAGAIN:
|
||||||
# No data available
|
# No data available
|
||||||
pass
|
pass
|
||||||
self.__sock.setblocking(1)
|
self.__sock.setblocking(True)
|
||||||
|
|
||||||
# Wait for new events, if needed.
|
# Wait for new events, if needed.
|
||||||
# if wait is 0.0, this means "no wait" and is also implicitly false.
|
# 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.
|
# Implement context manager enter function.
|
||||||
return self
|
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.
|
# Implement context manager exit function.
|
||||||
self.close()
|
self.close()
|
||||||
return False
|
|
||||||
|
|
||||||
def connect(self, negotiate=True):
|
def connect(self, negotiate=True):
|
||||||
"""
|
"""
|
||||||
|
@ -157,7 +168,7 @@ class QEMUMonitorProtocol:
|
||||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||||
"""
|
"""
|
||||||
self.__sock.connect(self.__address)
|
self.__sock.connect(self.__address)
|
||||||
self.__sockfile = self.__sock.makefile()
|
self.__sockfile = self.__sock.makefile(mode='r')
|
||||||
if negotiate:
|
if negotiate:
|
||||||
return self.__negotiate_capabilities()
|
return self.__negotiate_capabilities()
|
||||||
return None
|
return None
|
||||||
|
@ -168,8 +179,8 @@ class QEMUMonitorProtocol:
|
||||||
|
|
||||||
@param timeout: timeout in seconds (nonnegative float number, or
|
@param timeout: timeout in seconds (nonnegative float number, or
|
||||||
None). The value passed will set the behavior of the
|
None). The value passed will set the behavior of the
|
||||||
underneath QMP socket as described in [1]. Default value
|
underneath QMP socket as described in [1].
|
||||||
is set to 15.0.
|
Default value is set to 15.0.
|
||||||
@return QMP greeting dict
|
@return QMP greeting dict
|
||||||
@raise OSError on socket connection errors
|
@raise OSError on socket connection errors
|
||||||
@raise QMPConnectError if the greeting is not received
|
@raise QMPConnectError if the greeting is not received
|
||||||
|
@ -180,7 +191,7 @@ class QEMUMonitorProtocol:
|
||||||
"""
|
"""
|
||||||
self.__sock.settimeout(timeout)
|
self.__sock.settimeout(timeout)
|
||||||
self.__sock, _ = self.__sock.accept()
|
self.__sock, _ = self.__sock.accept()
|
||||||
self.__sockfile = self.__sock.makefile()
|
self.__sockfile = self.__sock.makefile(mode='r')
|
||||||
return self.__negotiate_capabilities()
|
return self.__negotiate_capabilities()
|
||||||
|
|
||||||
def cmd_obj(self, qmp_cmd):
|
def cmd_obj(self, qmp_cmd):
|
||||||
|
|
|
@ -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.
|
# Copyright (C) 2015 Red Hat Inc.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
|
@ -13,26 +19,29 @@
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import os
|
import os
|
||||||
|
from typing import Optional, TextIO
|
||||||
|
|
||||||
from .machine import QEMUMachine
|
from .machine import QEMUMachine
|
||||||
|
|
||||||
|
|
||||||
class QEMUQtestProtocol(object):
|
class QEMUQtestProtocol:
|
||||||
def __init__(self, address, server=False):
|
|
||||||
"""
|
"""
|
||||||
Create a QEMUQtestProtocol object.
|
QEMUQtestProtocol implements a connection to a qtest socket.
|
||||||
|
|
||||||
@param address: QEMU address, can be either a unix socket path (string)
|
:param address: QEMU address, can be either a unix socket path (string)
|
||||||
or a tuple in the form ( address, port ) for a TCP
|
or a tuple in the form ( address, port ) for a TCP
|
||||||
connection
|
connection
|
||||||
@param server: server mode, listens on the socket (bool)
|
:param server: server mode, listens on the socket (bool)
|
||||||
@raise socket.error on socket connection errors
|
:raise socket.error: on socket connection errors
|
||||||
@note No connection is established, this is done by the connect() or
|
|
||||||
accept() methods
|
.. 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._address = address
|
||||||
self._sock = self._get_sock()
|
self._sock = self._get_sock()
|
||||||
self._sockfile = None
|
self._sockfile: Optional[TextIO] = None
|
||||||
if server:
|
if server:
|
||||||
self._sock.bind(self._address)
|
self._sock.bind(self._address)
|
||||||
self._sock.listen(1)
|
self._sock.listen(1)
|
||||||
|
@ -51,7 +60,7 @@ class QEMUQtestProtocol(object):
|
||||||
@raise socket.error on socket connection errors
|
@raise socket.error on socket connection errors
|
||||||
"""
|
"""
|
||||||
self._sock.connect(self._address)
|
self._sock.connect(self._address)
|
||||||
self._sockfile = self._sock.makefile()
|
self._sockfile = self._sock.makefile(mode='r')
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
"""
|
"""
|
||||||
|
@ -60,7 +69,7 @@ class QEMUQtestProtocol(object):
|
||||||
@raise socket.error on socket connection errors
|
@raise socket.error on socket connection errors
|
||||||
"""
|
"""
|
||||||
self._sock, _ = self._sock.accept()
|
self._sock, _ = self._sock.accept()
|
||||||
self._sockfile = self._sock.makefile()
|
self._sockfile = self._sock.makefile(mode='r')
|
||||||
|
|
||||||
def cmd(self, qtest_cmd):
|
def cmd(self, qtest_cmd):
|
||||||
"""
|
"""
|
||||||
|
@ -68,20 +77,27 @@ class QEMUQtestProtocol(object):
|
||||||
|
|
||||||
@param qtest_cmd: qtest command text to be sent
|
@param qtest_cmd: qtest command text to be sent
|
||||||
"""
|
"""
|
||||||
|
assert self._sockfile is not None
|
||||||
self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
|
self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
|
||||||
resp = self._sockfile.readline()
|
resp = self._sockfile.readline()
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Close this socket."""
|
||||||
self._sock.close()
|
self._sock.close()
|
||||||
|
if self._sockfile:
|
||||||
self._sockfile.close()
|
self._sockfile.close()
|
||||||
|
self._sockfile = None
|
||||||
|
|
||||||
def settimeout(self, timeout):
|
def settimeout(self, timeout):
|
||||||
|
"""Set a timeout, in seconds."""
|
||||||
self._sock.settimeout(timeout)
|
self._sock.settimeout(timeout)
|
||||||
|
|
||||||
|
|
||||||
class QEMUQtestMachine(QEMUMachine):
|
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",
|
def __init__(self, binary, args=None, name=None, test_dir="/var/tmp",
|
||||||
socket_scm_helper=None, sock_dir=None):
|
socket_scm_helper=None, sock_dir=None):
|
||||||
|
@ -89,31 +105,38 @@ class QEMUQtestMachine(QEMUMachine):
|
||||||
name = "qemu-%d" % os.getpid()
|
name = "qemu-%d" % os.getpid()
|
||||||
if sock_dir is None:
|
if sock_dir is None:
|
||||||
sock_dir = test_dir
|
sock_dir = test_dir
|
||||||
super(QEMUQtestMachine,
|
super().__init__(binary, args, name=name, test_dir=test_dir,
|
||||||
self).__init__(binary, args, name=name, test_dir=test_dir,
|
|
||||||
socket_scm_helper=socket_scm_helper,
|
socket_scm_helper=socket_scm_helper,
|
||||||
sock_dir=sock_dir)
|
sock_dir=sock_dir)
|
||||||
self._qtest = None
|
self._qtest = None
|
||||||
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
|
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
|
||||||
|
|
||||||
def _base_args(self):
|
def _base_args(self):
|
||||||
args = super(QEMUQtestMachine, self)._base_args()
|
args = super()._base_args()
|
||||||
args.extend(['-qtest', 'unix:path=' + self._qtest_path,
|
args.extend(['-qtest', 'unix:path=' + self._qtest_path,
|
||||||
'-accel', 'qtest'])
|
'-accel', 'qtest'])
|
||||||
return args
|
return args
|
||||||
|
|
||||||
def _pre_launch(self):
|
def _pre_launch(self):
|
||||||
super(QEMUQtestMachine, self)._pre_launch()
|
super()._pre_launch()
|
||||||
self._qtest = QEMUQtestProtocol(self._qtest_path, server=True)
|
self._qtest = QEMUQtestProtocol(self._qtest_path, server=True)
|
||||||
|
|
||||||
def _post_launch(self):
|
def _post_launch(self) -> None:
|
||||||
super(QEMUQtestMachine, self)._post_launch()
|
assert self._qtest is not None
|
||||||
|
super()._post_launch()
|
||||||
self._qtest.accept()
|
self._qtest.accept()
|
||||||
|
|
||||||
def _post_shutdown(self):
|
def _post_shutdown(self):
|
||||||
super(QEMUQtestMachine, self)._post_shutdown()
|
super()._post_shutdown()
|
||||||
self._remove_if_exists(self._qtest_path)
|
self._remove_if_exists(self._qtest_path)
|
||||||
|
|
||||||
def qtest(self, cmd):
|
def qtest(self, cmd: str) -> str:
|
||||||
'''Send a qtest command to guest'''
|
"""
|
||||||
|
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)
|
return self._qtest.cmd(cmd)
|
||||||
|
|
|
@ -25,11 +25,6 @@ import struct
|
||||||
import sys
|
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):
|
def mkdir_p(path):
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
|
|
|
@ -75,13 +75,6 @@ def output(*args):
|
||||||
output_fd.write(a)
|
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():
|
def output_autogen():
|
||||||
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
|
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
|
||||||
|
|
||||||
|
@ -428,18 +421,18 @@ def parse_field(lineno, name, toks):
|
||||||
width = 0
|
width = 0
|
||||||
func = None
|
func = None
|
||||||
for t in toks:
|
for t in toks:
|
||||||
if re_fullmatch('!function=' + re_ident, t):
|
if re.fullmatch('!function=' + re_ident, t):
|
||||||
if func:
|
if func:
|
||||||
error(lineno, 'duplicate function')
|
error(lineno, 'duplicate function')
|
||||||
func = t.split('=')
|
func = t.split('=')
|
||||||
func = func[1]
|
func = func[1]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if re_fullmatch('[0-9]+:s[0-9]+', t):
|
if re.fullmatch('[0-9]+:s[0-9]+', t):
|
||||||
# Signed field extract
|
# Signed field extract
|
||||||
subtoks = t.split(':s')
|
subtoks = t.split(':s')
|
||||||
sign = True
|
sign = True
|
||||||
elif re_fullmatch('[0-9]+:[0-9]+', t):
|
elif re.fullmatch('[0-9]+:[0-9]+', t):
|
||||||
# Unsigned field extract
|
# Unsigned field extract
|
||||||
subtoks = t.split(':')
|
subtoks = t.split(':')
|
||||||
sign = False
|
sign = False
|
||||||
|
@ -488,11 +481,11 @@ def parse_arguments(lineno, name, toks):
|
||||||
flds = []
|
flds = []
|
||||||
extern = False
|
extern = False
|
||||||
for t in toks:
|
for t in toks:
|
||||||
if re_fullmatch('!extern', t):
|
if re.fullmatch('!extern', t):
|
||||||
extern = True
|
extern = True
|
||||||
anyextern = True
|
anyextern = True
|
||||||
continue
|
continue
|
||||||
if not re_fullmatch(re_ident, t):
|
if not re.fullmatch(re_ident, t):
|
||||||
error(lineno, 'invalid argument set token "{0}"'.format(t))
|
error(lineno, 'invalid argument set token "{0}"'.format(t))
|
||||||
if t in flds:
|
if t in flds:
|
||||||
error(lineno, 'duplicate argument "{0}"'.format(t))
|
error(lineno, 'duplicate argument "{0}"'.format(t))
|
||||||
|
@ -621,13 +614,13 @@ def parse_generic(lineno, is_format, name, toks):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 'Foo=%Bar' imports a field with a different name.
|
# '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('=%')
|
(fname, iname) = t.split('=%')
|
||||||
flds = add_field_byname(lineno, flds, fname, iname)
|
flds = add_field_byname(lineno, flds, fname, iname)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 'Foo=number' sets an argument field to a constant value
|
# '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('=')
|
(fname, value) = t.split('=')
|
||||||
value = int(value)
|
value = int(value)
|
||||||
flds = add_field(lineno, flds, fname, ConstField(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,
|
# Pattern of 0s, 1s, dots and dashes indicate required zeros,
|
||||||
# required ones, or dont-cares.
|
# required ones, or dont-cares.
|
||||||
if re_fullmatch('[01.-]+', t):
|
if re.fullmatch('[01.-]+', t):
|
||||||
shift = len(t)
|
shift = len(t)
|
||||||
fms = t.replace('0', '1')
|
fms = t.replace('0', '1')
|
||||||
fms = fms.replace('.', '0')
|
fms = fms.replace('.', '0')
|
||||||
|
@ -652,7 +645,7 @@ def parse_generic(lineno, is_format, name, toks):
|
||||||
fixedmask = (fixedmask << shift) | fms
|
fixedmask = (fixedmask << shift) | fms
|
||||||
undefmask = (undefmask << shift) | ubm
|
undefmask = (undefmask << shift) | ubm
|
||||||
# Otherwise, fieldname:fieldwidth
|
# 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(':')
|
(fname, flen) = t.split(':')
|
||||||
sign = False
|
sign = False
|
||||||
if flen[0] == 's':
|
if flen[0] == 's':
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# tool for querying VMX capabilities
|
# tool for querying VMX capabilities
|
||||||
#
|
#
|
||||||
|
@ -275,5 +275,6 @@ controls = [
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
for c in controls:
|
for c in controls:
|
||||||
c.show()
|
c.show()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Module information generator
|
# Module information generator
|
||||||
#
|
#
|
||||||
|
@ -80,6 +80,7 @@ def print_bottom(fheader):
|
||||||
#endif
|
#endif
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
# First argument: output file
|
# First argument: output file
|
||||||
# All other arguments: modules source files (.c)
|
# All other arguments: modules source files (.c)
|
||||||
output_file = sys.argv[1]
|
output_file = sys.argv[1]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
# GDB debugging support
|
# GDB debugging support
|
||||||
#
|
#
|
||||||
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/python
|
#
|
||||||
|
|
||||||
# GDB debugging support
|
# GDB debugging support
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015 Linaro Ltd
|
# Copyright (c) 2015 Linaro Ltd
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/python
|
#
|
||||||
|
|
||||||
# GDB debugging support: aio/iohandler debug
|
# GDB debugging support: aio/iohandler debug
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015 Red Hat, Inc.
|
# Copyright (c) 2015 Red Hat, Inc.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/python
|
#
|
||||||
|
|
||||||
# GDB debugging support
|
# GDB debugging support
|
||||||
#
|
#
|
||||||
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/python
|
#
|
||||||
|
|
||||||
# GDB debugging support
|
# GDB debugging support
|
||||||
#
|
#
|
||||||
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
# Copyright 2012 Red Hat, Inc. and/or its affiliates
|
||||||
|
@ -84,4 +83,3 @@ class MtreeCommand(gdb.Command):
|
||||||
while not isnull(subregion):
|
while not isnull(subregion):
|
||||||
self.print_item(subregion, addr, level)
|
self.print_item(subregion, addr, level)
|
||||||
subregion = subregion['subregions_link']['tqe_next']
|
subregion = subregion['subregions_link']['tqe_next']
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# GDB debugging support, TCG status
|
# GDB debugging support, TCG status
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# GDB debugging support
|
# GDB debugging support
|
||||||
#
|
#
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
import sys, os
|
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=[]):
|
def print_response(rsp, prefix=[]):
|
||||||
if type(rsp) == list:
|
if type(rsp) == list:
|
||||||
|
|
|
@ -77,9 +77,6 @@ import re
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||||
from qemu import qmp
|
from qemu import qmp
|
||||||
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
input = raw_input
|
|
||||||
|
|
||||||
class QMPCompleter(list):
|
class QMPCompleter(list):
|
||||||
def complete(self, text, state):
|
def complete(self, text, state):
|
||||||
for cmd in self:
|
for cmd in self:
|
||||||
|
|
|
@ -15,7 +15,9 @@ import fuse, stat
|
||||||
from fuse import Fuse
|
from fuse import Fuse
|
||||||
import os, posix
|
import os, posix
|
||||||
from errno import *
|
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)
|
fuse.fuse_python_api = (0, 2)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
##
|
##
|
||||||
# QEMU Object Model test tools
|
# QEMU Object Model test tools
|
||||||
#
|
#
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
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:]
|
cmd, args = sys.argv[0], sys.argv[1:]
|
||||||
socket_path = None
|
socket_path = None
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
##
|
##
|
||||||
# QEMU Object Model test tools
|
# QEMU Object Model test tools
|
||||||
#
|
#
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
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:]
|
cmd, args = sys.argv[0], sys.argv[1:]
|
||||||
socket_path = None
|
socket_path = None
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
##
|
##
|
||||||
# QEMU Object Model test tools
|
# QEMU Object Model test tools
|
||||||
#
|
#
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
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:]
|
cmd, args = sys.argv[0], sys.argv[1:]
|
||||||
socket_path = None
|
socket_path = None
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
##
|
##
|
||||||
# QEMU Object Model test tools
|
# QEMU Object Model test tools
|
||||||
#
|
#
|
||||||
|
@ -15,7 +15,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
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:]
|
cmd, args = sys.argv[0], sys.argv[1:]
|
||||||
socket_path = None
|
socket_path = None
|
||||||
|
|
|
@ -69,13 +69,15 @@ def pick_default_qemu_bin(arch=None):
|
||||||
|
|
||||||
|
|
||||||
def _console_interaction(test, success_message, failure_message,
|
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
|
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')
|
console_logger = logging.getLogger('console')
|
||||||
while True:
|
while True:
|
||||||
if send_string:
|
if send_string:
|
||||||
test.vm.console_socket.sendall(send_string.encode())
|
vm.console_socket.sendall(send_string.encode())
|
||||||
if not keep_sending:
|
if not keep_sending:
|
||||||
send_string = None # send only once
|
send_string = None # send only once
|
||||||
msg = console.readline().strip()
|
msg = console.readline().strip()
|
||||||
|
@ -115,7 +117,8 @@ def interrupt_interactive_console_until_pattern(test, success_message,
|
||||||
_console_interaction(test, success_message, failure_message,
|
_console_interaction(test, success_message, failure_message,
|
||||||
interrupt_string, True)
|
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
|
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 success_message: if this message appears, test succeeds
|
||||||
:param failure_message: if this message appears, test fails
|
: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,
|
def exec_command_and_wait_for_pattern(test, command,
|
||||||
success_message, failure_message=None):
|
success_message, failure_message=None):
|
||||||
|
|
|
@ -26,22 +26,8 @@ KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM"
|
||||||
TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG"
|
TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG"
|
||||||
|
|
||||||
|
|
||||||
class BootLinux(Test):
|
class BootLinuxBase(Test):
|
||||||
"""
|
def download_boot(self):
|
||||||
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):
|
|
||||||
self.log.debug('Looking for and selecting a qemu-img binary to be '
|
self.log.debug('Looking for and selecting a qemu-img binary to be '
|
||||||
'used to create the bootable snapshot image')
|
'used to create the bootable snapshot image')
|
||||||
# If qemu-img has been built, use it, otherwise the system wide one
|
# If qemu-img has been built, use it, otherwise the system wide one
|
||||||
|
@ -60,17 +46,17 @@ class BootLinux(Test):
|
||||||
if image_arch == 'ppc64':
|
if image_arch == 'ppc64':
|
||||||
image_arch = 'ppc64le'
|
image_arch = 'ppc64le'
|
||||||
try:
|
try:
|
||||||
self.boot = vmimage.get(
|
boot = vmimage.get(
|
||||||
'fedora', arch=image_arch, version='31',
|
'fedora', arch=image_arch, version='31',
|
||||||
checksum=self.chksum,
|
checksum=self.chksum,
|
||||||
algorithm='sha256',
|
algorithm='sha256',
|
||||||
cache_dir=self.cache_dirs[0],
|
cache_dir=self.cache_dirs[0],
|
||||||
snapshot_dir=self.workdir)
|
snapshot_dir=self.workdir)
|
||||||
self.vm.add_args('-drive', 'file=%s' % self.boot.path)
|
|
||||||
except:
|
except:
|
||||||
self.cancel('Failed to download/prepare boot image')
|
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')
|
self.log.info('Preparing cloudinit image')
|
||||||
try:
|
try:
|
||||||
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
|
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
|
||||||
|
@ -81,9 +67,32 @@ class BootLinux(Test):
|
||||||
# QEMU's hard coded usermode router address
|
# QEMU's hard coded usermode router address
|
||||||
phone_home_host='10.0.2.2',
|
phone_home_host='10.0.2.2',
|
||||||
phone_home_port=self.phone_home_port)
|
phone_home_port=self.phone_home_port)
|
||||||
self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.cancel('Failed to prepared cloudinit image')
|
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):
|
def launch_and_wait(self):
|
||||||
self.vm.set_console()
|
self.vm.set_console()
|
||||||
|
|
|
@ -28,19 +28,13 @@ try:
|
||||||
except CmdNotFoundError:
|
except CmdNotFoundError:
|
||||||
P7ZIP_AVAILABLE = False
|
P7ZIP_AVAILABLE = False
|
||||||
|
|
||||||
class BootLinuxConsole(Test):
|
class LinuxKernelTest(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
|
|
||||||
|
|
||||||
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
|
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,
|
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):
|
def extract_from_deb(self, deb, path):
|
||||||
"""
|
"""
|
||||||
|
@ -79,6 +73,13 @@ class BootLinuxConsole(Test):
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
return os.path.normpath(os.path.join(self.workdir, path))
|
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):
|
def test_x86_64_pc(self):
|
||||||
"""
|
"""
|
||||||
:avocado: tags=arch:x86_64
|
:avocado: tags=arch:x86_64
|
||||||
|
|
|
@ -35,6 +35,10 @@ class Migration(Test):
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
step=0.1,
|
step=0.1,
|
||||||
args=(src_vm,))
|
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(src_vm.command('query-migrate')['status'], 'completed')
|
||||||
self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed')
|
self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed')
|
||||||
self.assertEqual(dst_vm.command('query-status')['status'], 'running')
|
self.assertEqual(dst_vm.command('query-status')['status'], 'running')
|
||||||
|
|
|
@ -258,12 +258,13 @@ class Docker(object):
|
||||||
return self._do_kill_instances(True)
|
return self._do_kill_instances(True)
|
||||||
|
|
||||||
def _output(self, cmd, **kwargs):
|
def _output(self, cmd, **kwargs):
|
||||||
if sys.version_info[1] >= 6:
|
try:
|
||||||
return subprocess.check_output(self._command + cmd,
|
return subprocess.check_output(self._command + cmd,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
encoding='utf-8',
|
encoding='utf-8',
|
||||||
**kwargs)
|
**kwargs)
|
||||||
else:
|
except TypeError:
|
||||||
|
# 'encoding' argument was added in 3.6+
|
||||||
return subprocess.check_output(self._command + cmd,
|
return subprocess.check_output(self._command + cmd,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
**kwargs).decode('utf-8')
|
**kwargs).decode('utf-8')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Migration test batch comparison invokation
|
# Migration test batch comparison invokation
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Migration test graph plotting command
|
# Migration test graph plotting command
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Migration test direct invokation command
|
# Migration test direct invokation command
|
||||||
#
|
#
|
||||||
|
|
|
@ -47,10 +47,7 @@ import sys
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import collections
|
import collections
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
import configparser
|
import configparser
|
||||||
else:
|
|
||||||
import ConfigParser as configparser
|
|
||||||
|
|
||||||
FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB
|
FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ endif
|
||||||
@echo " J=[0..9]* - Override the -jN parameter for make commands"
|
@echo " J=[0..9]* - Override the -jN parameter for make commands"
|
||||||
@echo " DEBUG=1 - Enable verbose output on host and interactive debugging"
|
@echo " DEBUG=1 - Enable verbose output on host and interactive debugging"
|
||||||
@echo " V=1 - Enable verbose ouput on host and guest commands"
|
@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=/path/to/qemu - Change path to QEMU binary"
|
||||||
@echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool"
|
@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) $< \
|
$(PYTHON) $< \
|
||||||
$(if $(V)$(DEBUG), --debug) \
|
$(if $(V)$(DEBUG), --debug) \
|
||||||
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
|
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
|
||||||
|
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||||
--image "$@" \
|
--image "$@" \
|
||||||
--force \
|
--force \
|
||||||
--build-image $@, \
|
--build-image $@, \
|
||||||
|
@ -71,6 +73,7 @@ vm-build-%: $(IMAGES_DIR)/%.img
|
||||||
$(if $(DEBUG), --interactive) \
|
$(if $(DEBUG), --interactive) \
|
||||||
$(if $(J),--jobs $(J)) \
|
$(if $(J),--jobs $(J)) \
|
||||||
$(if $(V),--verbose) \
|
$(if $(V),--verbose) \
|
||||||
|
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||||
--image "$<" \
|
--image "$<" \
|
||||||
$(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \
|
$(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \
|
||||||
--snapshot \
|
--snapshot \
|
||||||
|
@ -91,6 +94,8 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||||
$(if $(J),--jobs $(J)) \
|
$(if $(J),--jobs $(J)) \
|
||||||
|
$(if $(V)$(DEBUG), --debug) \
|
||||||
|
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||||
--image "$<" \
|
--image "$<" \
|
||||||
--interactive \
|
--interactive \
|
||||||
false, \
|
false, \
|
||||||
|
|
|
@ -61,9 +61,11 @@ class BaseVM(object):
|
||||||
# 4 is arbitrary, but greater than 2,
|
# 4 is arbitrary, but greater than 2,
|
||||||
# since we found we need to wait more than twice as long.
|
# since we found we need to wait more than twice as long.
|
||||||
tcg_ssh_timeout_multiplier = 4
|
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._guest = None
|
||||||
self._genisoimage = genisoimage
|
self._genisoimage = genisoimage
|
||||||
|
self._build_path = build_path
|
||||||
self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
|
self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
|
||||||
suffix=".tmp",
|
suffix=".tmp",
|
||||||
dir="."))
|
dir="."))
|
||||||
|
@ -184,15 +186,15 @@ class BaseVM(object):
|
||||||
"-device", "virtio-blk,drive=drive0,bootindex=0"]
|
"-device", "virtio-blk,drive=drive0,bootindex=0"]
|
||||||
args += self._data_args + extra_args
|
args += self._data_args + extra_args
|
||||||
logging.debug("QEMU args: %s", " ".join(args))
|
logging.debug("QEMU args: %s", " ".join(args))
|
||||||
qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch)
|
qemu_path = get_qemu_path(self.arch, self._build_path)
|
||||||
guest = QEMUMachine(binary=qemu_bin, args=args)
|
guest = QEMUMachine(binary=qemu_path, args=args)
|
||||||
guest.set_machine('pc')
|
guest.set_machine('pc')
|
||||||
guest.set_console()
|
guest.set_console()
|
||||||
try:
|
try:
|
||||||
guest.launch()
|
guest.launch()
|
||||||
except:
|
except:
|
||||||
logging.error("Failed to launch QEMU, command line:")
|
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("Log:")
|
||||||
logging.error(guest.get_log())
|
logging.error(guest.get_log())
|
||||||
logging.error("QEMU version >= 2.10 is required")
|
logging.error("QEMU version >= 2.10 is required")
|
||||||
|
@ -318,24 +320,24 @@ class BaseVM(object):
|
||||||
def print_step(self, text):
|
def print_step(self, text):
|
||||||
sys.stderr.write("### %s ...\n" % 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.
|
# Allow more time for VM to boot under TCG.
|
||||||
if not kvm_available(self.arch):
|
if not kvm_available(self.arch):
|
||||||
seconds *= self.tcg_ssh_timeout_multiplier
|
seconds *= self.tcg_ssh_timeout_multiplier
|
||||||
starttime = datetime.datetime.now()
|
starttime = datetime.datetime.now()
|
||||||
endtime = starttime + datetime.timedelta(seconds=seconds)
|
endtime = starttime + datetime.timedelta(seconds=seconds)
|
||||||
guest_up = False
|
cmd_success = False
|
||||||
while datetime.datetime.now() < endtime:
|
while datetime.datetime.now() < endtime:
|
||||||
if wait_root and self.ssh_root("exit 0") == 0:
|
if wait_root and self.ssh_root(cmd) == 0:
|
||||||
guest_up = True
|
cmd_success = True
|
||||||
break
|
break
|
||||||
elif self.ssh("exit 0") == 0:
|
elif self.ssh(cmd) == 0:
|
||||||
guest_up = True
|
cmd_success = True
|
||||||
break
|
break
|
||||||
seconds = (endtime - datetime.datetime.now()).total_seconds()
|
seconds = (endtime - datetime.datetime.now()).total_seconds()
|
||||||
logging.debug("%ds before timeout", seconds)
|
logging.debug("%ds before timeout", seconds)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if not guest_up:
|
if not cmd_success:
|
||||||
raise Exception("Timeout while waiting for guest ssh")
|
raise Exception("Timeout while waiting for guest ssh")
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
@ -391,6 +393,19 @@ class BaseVM(object):
|
||||||
|
|
||||||
return os.path.join(cidir, "cloud-init.iso")
|
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 parse_args(vmcls):
|
||||||
|
|
||||||
def get_default_jobs():
|
def get_default_jobs():
|
||||||
|
@ -421,6 +436,9 @@ def parse_args(vmcls):
|
||||||
help="build QEMU from source in guest")
|
help="build QEMU from source in guest")
|
||||||
parser.add_option("--build-target",
|
parser.add_option("--build-target",
|
||||||
help="QEMU build target", default="check")
|
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",
|
parser.add_option("--interactive", "-I", action="store_true",
|
||||||
help="Interactively run command")
|
help="Interactively run command")
|
||||||
parser.add_option("--snapshot", "-s", action="store_true",
|
parser.add_option("--snapshot", "-s", action="store_true",
|
||||||
|
@ -439,7 +457,7 @@ def main(vmcls):
|
||||||
logging.basicConfig(level=(logging.DEBUG if args.debug
|
logging.basicConfig(level=(logging.DEBUG if args.debug
|
||||||
else logging.WARN))
|
else logging.WARN))
|
||||||
vm = vmcls(debug=args.debug, vcpus=args.jobs,
|
vm = vmcls(debug=args.debug, vcpus=args.jobs,
|
||||||
genisoimage=args.genisoimage)
|
genisoimage=args.genisoimage, build_path=args.build_path)
|
||||||
if args.build_image:
|
if args.build_image:
|
||||||
if os.path.exists(args.image) and not args.force:
|
if os.path.exists(args.image) and not args.force:
|
||||||
sys.stderr.writelines(["Image file exists: %s\n" % args.image,
|
sys.stderr.writelines(["Image file exists: %s\n" % args.image,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue