mirror of https://gitee.com/openkylin/qemu.git
Python queue, 2019-02-22
Python: * introduce "python" directory with module namespace * log QEMU launch command line on qemu.QEMUMachine Acceptance Tests: * initrd 4GiB+ test * migration test * multi vm support in test class * bump Avocado version and drop "🥑 enable" -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJccE9jAAoJEGV+jTOl8gnzb1cP/j99kGbgfQJA4CftO9eRXdIm FKms4Z42n7KPus+/DphgfOXGYaHzPcqJQNguQYHuPlWaM3DWNU0rcFfAi/QdcZC1 3iYMyQwiRubjnCMN0Ab4k+GhpCPW6fea6GTzyvqha4jNRhCIhx7v54GTDfxWESQp nqW40gAONGSG98DdFgubxg1YYqt7zlI9EVogGixe1gO9SVDkMEe7uH8tPCl9mt2m VjN7AeP/NTDmidiwu+2LwSpDC0UmpDAsFnxGI6rDcNx8NOnjSHkSHmtxNJ8j2uZz 9P0ncGui+LfivdQh/yiBgrjTWXEXAx/oHKQCz7r8uJ8f60eYLFtjTHm//2G7lG48 luLSnNKq/niM4k/vNhBQr0ByqoHHlpmqAjbmYqw7wdvImBbkXN2Gh9kjNs55S8VZ Z7wTceC0G7pyM3LCdFnikyCXKoRxLZ3AXQ3YXFN0PgX/IsyHVuBWBGPFkPkLwcRa JW3DEmwx/oeTg2MKp7iA3dGTUIarbsjp+R04erMznlLvE+NgmB8ENY8T+qZ6c+NM ZNyp1MH2nuTJsYxY3CkVKwPUqNSoaTLkMxvoZW5rKQdtvNinCYZpaeHuBchaHJed E63r0+1n9vAMH3PHDrypW5qjcjSDBOHS+8ajhr0jr2r+6grLQKYEP8q+PwubUaMq BsS5jOb8gLGC8ESfZxx/ =dwff -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cleber/tags/python-next-pull-request' into staging Python queue, 2019-02-22 Python: * introduce "python" directory with module namespace * log QEMU launch command line on qemu.QEMUMachine Acceptance Tests: * initrd 4GiB+ test * migration test * multi vm support in test class * bump Avocado version and drop "🥑 enable" # gpg: Signature made Fri 22 Feb 2019 19:37:07 GMT # gpg: using RSA key 657E8D33A5F209F3 # gpg: Good signature from "Cleber Rosa <crosa@redhat.com>" [marginal] # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 7ABB 96EB 8B46 B94D 5E0F E9BB 657E 8D33 A5F2 09F3 * remotes/cleber/tags/python-next-pull-request: Acceptance tests: expect boot to extract 2GiB+ initrd with linux-v4.16 Acceptance tests: use linux-3.6 and set vm memory to 4GiB tests.acceptance: adds simple migration test tests.acceptance: adds multi vm capability for acceptance tests scripts/qemu.py: log QEMU launch command line Introduce a Python module structure Acceptance tests: drop usage of "🥑 enable" Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
6cb4f6db4f
|
@ -7674,6 +7674,7 @@ LINKS="$LINKS pc-bios/qemu-icon.bmp"
|
|||
LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit
|
||||
LINKS="$LINKS tests/acceptance tests/data"
|
||||
LINKS="$LINKS tests/qemu-iotests/check"
|
||||
LINKS="$LINKS python"
|
||||
for bios_file in \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.lid \
|
||||
|
|
|
@ -600,7 +600,6 @@ the ``avocado_qemu.Test`` class. Here's a simple usage example:
|
|||
|
||||
class Version(Test):
|
||||
"""
|
||||
:avocado: enable
|
||||
:avocado: tags=quick
|
||||
"""
|
||||
def test_qmp_human_info_version(self):
|
||||
|
@ -634,7 +633,46 @@ instance, available at ``self.vm``. Because many tests will tweak the
|
|||
QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``)
|
||||
is left to the test writer.
|
||||
|
||||
At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine
|
||||
The base test class has also support for tests with more than one
|
||||
QEMUMachine. The way to get machines is through the ``self.get_vm()``
|
||||
method which will return a QEMUMachine instance. The ``self.get_vm()``
|
||||
method accepts arguments that will be passed to the QEMUMachine creation
|
||||
and also an optional `name` attribute so you can identify a specific
|
||||
machine and get it more than once through the tests methods. A simple
|
||||
and hypothetical example follows:
|
||||
|
||||
.. code::
|
||||
|
||||
from avocado_qemu import Test
|
||||
|
||||
|
||||
class MultipleMachines(Test):
|
||||
"""
|
||||
:avocado: enable
|
||||
"""
|
||||
def test_multiple_machines(self):
|
||||
first_machine = self.get_vm()
|
||||
second_machine = self.get_vm()
|
||||
self.get_vm(name='third_machine').launch()
|
||||
|
||||
first_machine.launch()
|
||||
second_machine.launch()
|
||||
|
||||
first_res = first_machine.command(
|
||||
'human-monitor-command',
|
||||
command_line='info version')
|
||||
|
||||
second_res = second_machine.command(
|
||||
'human-monitor-command',
|
||||
command_line='info version')
|
||||
|
||||
third_res = self.get_vm(name='third_machine').command(
|
||||
'human-monitor-command',
|
||||
command_line='info version')
|
||||
|
||||
self.assertEquals(first_res, second_res, third_res)
|
||||
|
||||
At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines
|
||||
shutdown.
|
||||
|
||||
QEMUMachine
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import qmp.qmp
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import tempfile
|
||||
|
||||
from . import qmp
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -66,7 +67,7 @@ class QEMUMachineAddDeviceError(QEMUMachineError):
|
|||
failures reported by the QEMU binary itself.
|
||||
"""
|
||||
|
||||
class MonitorResponseError(qmp.qmp.QMPError):
|
||||
class MonitorResponseError(qmp.QMPError):
|
||||
"""
|
||||
Represents erroneous QMP monitor reply
|
||||
"""
|
||||
|
@ -266,8 +267,8 @@ def _pre_launch(self):
|
|||
self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
|
||||
self._qemu_log_file = open(self._qemu_log_path, 'wb')
|
||||
|
||||
self._qmp = qmp.qmp.QEMUMonitorProtocol(self._vm_monitor,
|
||||
server=True)
|
||||
self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
|
||||
server=True)
|
||||
|
||||
def _post_launch(self):
|
||||
self._qmp.accept()
|
||||
|
@ -319,6 +320,7 @@ def _launch(self):
|
|||
self._pre_launch()
|
||||
self._qemu_full_args = (self._wrapper + [self._binary] +
|
||||
self._base_args() + self._args)
|
||||
LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
|
||||
self._popen = subprocess.Popen(self._qemu_full_args,
|
||||
stdin=devnull,
|
||||
stdout=self._qemu_log_file,
|
||||
|
@ -383,7 +385,7 @@ def command(self, cmd, conv_keys=True, **args):
|
|||
"""
|
||||
reply = self.qmp(cmd, conv_keys, **args)
|
||||
if reply is None:
|
||||
raise qmp.qmp.QMPError("Monitor is closed")
|
||||
raise qmp.QMPError("Monitor is closed")
|
||||
if "error" in reply:
|
||||
raise MonitorResponseError(reply)
|
||||
return reply["return"]
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
import socket
|
||||
import os
|
||||
import qemu
|
||||
|
||||
from . import QEMUMachine
|
||||
|
||||
|
||||
class QEMUQtestProtocol(object):
|
||||
|
@ -79,7 +80,7 @@ def settimeout(self, timeout):
|
|||
self._sock.settimeout(timeout)
|
||||
|
||||
|
||||
class QEMUQtestMachine(qemu.QEMUMachine):
|
||||
class QEMUQtestMachine(QEMUMachine):
|
||||
'''A QEMU VM'''
|
||||
|
||||
def __init__(self, binary, args=None, name=None, test_dir="/var/tmp",
|
|
@ -25,6 +25,7 @@ check for crashes and unexpected errors.
|
|||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import logging
|
||||
|
@ -34,6 +35,7 @@ import random
|
|||
import argparse
|
||||
from itertools import chain
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
|
||||
from qemu import QEMUMachine
|
||||
|
||||
logger = logging.getLogger('device-crash-test')
|
||||
|
|
|
@ -37,10 +37,13 @@
|
|||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import base64
|
||||
import random
|
||||
|
||||
import qmp
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import qmp
|
||||
|
||||
|
||||
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
# sent to QEMU, which is useful for debugging and documentation generation.
|
||||
|
||||
from __future__ import print_function
|
||||
import qmp
|
||||
import json
|
||||
import ast
|
||||
import readline
|
||||
|
@ -76,6 +75,9 @@ import errno
|
|||
import atexit
|
||||
import shlex
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import qmp
|
||||
|
||||
class QMPCompleter(list):
|
||||
def complete(self, text, state):
|
||||
for cmd in self:
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
import subprocess
|
||||
import json
|
||||
from graphviz import Digraph
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
|
||||
from qemu import MonitorResponseError
|
||||
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import avocado
|
||||
|
||||
SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR))
|
||||
sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts'))
|
||||
SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..')
|
||||
sys.path.append(os.path.join(SRC_ROOT_DIR, 'python'))
|
||||
|
||||
from qemu import QEMUMachine
|
||||
|
||||
|
@ -42,13 +42,29 @@ def pick_default_qemu_bin():
|
|||
|
||||
class Test(avocado.Test):
|
||||
def setUp(self):
|
||||
self.vm = None
|
||||
self._vms = {}
|
||||
self.qemu_bin = self.params.get('qemu_bin',
|
||||
default=pick_default_qemu_bin())
|
||||
if self.qemu_bin is None:
|
||||
self.cancel("No QEMU binary defined or found in the source tree")
|
||||
self.vm = QEMUMachine(self.qemu_bin)
|
||||
|
||||
def _new_vm(self, *args):
|
||||
vm = QEMUMachine(self.qemu_bin)
|
||||
if args:
|
||||
vm.add_args(*args)
|
||||
return vm
|
||||
|
||||
@property
|
||||
def vm(self):
|
||||
return self.get_vm(name='default')
|
||||
|
||||
def get_vm(self, *args, name=None):
|
||||
if not name:
|
||||
name = str(uuid.uuid4())
|
||||
if self._vms.get(name) is None:
|
||||
self._vms[name] = self._new_vm(*args)
|
||||
return self._vms[name]
|
||||
|
||||
def tearDown(self):
|
||||
if self.vm is not None:
|
||||
self.vm.shutdown()
|
||||
for vm in self._vms.values():
|
||||
vm.shutdown()
|
||||
|
|
|
@ -18,7 +18,6 @@ class BootLinuxConsole(Test):
|
|||
Boots a x86_64 Linux kernel and checks that the console is operational
|
||||
and the kernel command line is properly passed from QEMU to the kernel
|
||||
|
||||
:avocado: enable
|
||||
:avocado: tags=x86_64
|
||||
"""
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
import logging
|
||||
import tempfile
|
||||
from avocado.utils.process import run
|
||||
|
||||
|
@ -18,20 +19,21 @@ class LinuxInitrd(Test):
|
|||
"""
|
||||
Checks QEMU evaluates correctly the initrd file passed as -initrd option.
|
||||
|
||||
:avocado: enable
|
||||
:avocado: tags=x86_64
|
||||
"""
|
||||
|
||||
timeout = 60
|
||||
timeout = 300
|
||||
|
||||
def test_with_2gib_file_should_exit_error_msg(self):
|
||||
def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self):
|
||||
"""
|
||||
Pretends to boot QEMU with an initrd file with size of 2GiB
|
||||
and expect it exits with error message.
|
||||
Fedora-18 shipped with linux-3.6 which have not supported xloadflags
|
||||
cannot support more than 2GiB initrd.
|
||||
"""
|
||||
kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/'
|
||||
'Everything/x86_64/os/images/pxeboot/vmlinuz')
|
||||
kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a'
|
||||
kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li'
|
||||
'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz')
|
||||
kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21'
|
||||
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||
max_size = 2 * (1024 ** 3) - 1
|
||||
|
||||
|
@ -39,10 +41,44 @@ def test_with_2gib_file_should_exit_error_msg(self):
|
|||
initrd.seek(max_size)
|
||||
initrd.write(b'\0')
|
||||
initrd.flush()
|
||||
cmd = "%s -kernel %s -initrd %s" % (self.qemu_bin, kernel_path,
|
||||
initrd.name)
|
||||
cmd = "%s -kernel %s -initrd %s -m 4096" % (
|
||||
self.qemu_bin, kernel_path, initrd.name)
|
||||
res = run(cmd, ignore_status=True)
|
||||
self.assertEqual(res.exit_status, 1)
|
||||
expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % (
|
||||
max_size + 1)
|
||||
self.assertRegex(res.stderr_text, expected_msg)
|
||||
|
||||
def test_with_2gib_file_should_work_with_linux_v4_16(self):
|
||||
"""
|
||||
QEMU has supported up to 4 GiB initrd for recent kernel
|
||||
Expect guest can reach 'Unpacking initramfs...'
|
||||
"""
|
||||
kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/'
|
||||
'Everything/x86_64/os/images/pxeboot/vmlinuz')
|
||||
kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a'
|
||||
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||
max_size = 2 * (1024 ** 3) + 1
|
||||
|
||||
with tempfile.NamedTemporaryFile() as initrd:
|
||||
initrd.seek(max_size)
|
||||
initrd.write(b'\0')
|
||||
initrd.flush()
|
||||
|
||||
self.vm.set_machine('pc')
|
||||
self.vm.set_console()
|
||||
kernel_command_line = 'console=ttyS0'
|
||||
self.vm.add_args('-kernel', kernel_path,
|
||||
'-append', kernel_command_line,
|
||||
'-initrd', initrd.name,
|
||||
'-m', '5120')
|
||||
self.vm.launch()
|
||||
console = self.vm.console_socket.makefile()
|
||||
console_logger = logging.getLogger('console')
|
||||
while True:
|
||||
msg = console.readline()
|
||||
console_logger.debug(msg.strip())
|
||||
if 'Unpacking initramfs...' in msg:
|
||||
break
|
||||
if 'Kernel panic - not syncing' in msg:
|
||||
self.fail("Kernel panic reached")
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# Migration test
|
||||
#
|
||||
# Copyright (c) 2019 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Cleber Rosa <crosa@redhat.com>
|
||||
# Caio Carrara <ccarrara@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
|
||||
from avocado_qemu import Test
|
||||
|
||||
from avocado.utils import network
|
||||
from avocado.utils import wait
|
||||
|
||||
|
||||
class Migration(Test):
|
||||
"""
|
||||
:avocado: enable
|
||||
"""
|
||||
|
||||
timeout = 10
|
||||
|
||||
@staticmethod
|
||||
def migration_finished(vm):
|
||||
return vm.command('query-migrate')['status'] in ('completed', 'failed')
|
||||
|
||||
def _get_free_port(self):
|
||||
port = network.find_free_port()
|
||||
if port is None:
|
||||
self.cancel('Failed to find a free port')
|
||||
return port
|
||||
|
||||
|
||||
def test_migration_with_tcp_localhost(self):
|
||||
source_vm = self.get_vm()
|
||||
dest_uri = 'tcp:localhost:%u' % self._get_free_port()
|
||||
dest_vm = self.get_vm('-incoming', dest_uri)
|
||||
dest_vm.launch()
|
||||
source_vm.launch()
|
||||
source_vm.qmp('migrate', uri=dest_uri)
|
||||
wait.wait_for(
|
||||
self.migration_finished,
|
||||
timeout=self.timeout,
|
||||
step=0.1,
|
||||
args=(source_vm,)
|
||||
)
|
||||
self.assertEqual(dest_vm.command('query-migrate')['status'], 'completed')
|
||||
self.assertEqual(source_vm.command('query-migrate')['status'], 'completed')
|
||||
self.assertEqual(dest_vm.command('query-status')['status'], 'running')
|
||||
self.assertEqual(source_vm.command('query-status')['status'], 'postmigrate')
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
class Version(Test):
|
||||
"""
|
||||
:avocado: enable
|
||||
:avocado: tags=quick
|
||||
"""
|
||||
def test_qmp_human_info_version(self):
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts"))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import QEMUMachine
|
||||
from avocado_qemu import Test
|
||||
|
||||
|
@ -61,7 +61,6 @@ class VirtioVersionCheck(Test):
|
|||
same device tree created by `disable-modern` and
|
||||
`disable-legacy`.
|
||||
|
||||
:avocado: enable
|
||||
:avocado: tags=x86_64
|
||||
"""
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
class Vnc(Test):
|
||||
"""
|
||||
:avocado: enable
|
||||
:avocado: tags=vnc,quick
|
||||
"""
|
||||
def test_no_vnc(self):
|
||||
|
|
|
@ -24,13 +24,14 @@
|
|||
import sys
|
||||
import time
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'scripts'))
|
||||
import qemu
|
||||
import qmp.qmp
|
||||
from guestperf.progress import Progress, ProgressStats
|
||||
from guestperf.report import Report
|
||||
from guestperf.timings import TimingRecord, Timings
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__),
|
||||
'..', '..', '..', 'python'))
|
||||
import qemu
|
||||
|
||||
|
||||
class Engine(object):
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import os
|
|||
import iotests
|
||||
from iotests import qemu_img_create, qemu_io, file_path, log
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
|
||||
from qemu import QEMUMachine
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import os
|
|||
import iotests
|
||||
from iotests import log
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
|
||||
from qemu import QEMUMachine
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
import io
|
||||
from collections import OrderedDict
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
|
||||
import qtest
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import qtest
|
||||
|
||||
|
||||
# This will not work if arguments contain spaces but is necessary if we
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Add Python module requirements, one per line, to be installed
|
||||
# in the tests/venv Python virtual environment. For more info,
|
||||
# refer to: https://pip.pypa.io/en/stable/user_guide/#id1
|
||||
avocado-framework==65.0
|
||||
avocado-framework==68.0
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import logging
|
||||
import time
|
||||
import datetime
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts"))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu import QEMUMachine, kvm_available
|
||||
import subprocess
|
||||
import hashlib
|
||||
|
|
Loading…
Reference in New Issue