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:
Peter Maydell 2019-03-07 16:16:02 +00:00
commit 6cb4f6db4f
23 changed files with 193 additions and 40 deletions

1
configure vendored
View File

@ -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 \

View File

@ -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

View File

@ -16,12 +16,13 @@ import errno
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 @@ class QEMUMachine(object):
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 @@ class QEMUMachine(object):
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 @@ class QEMUMachine(object):
"""
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"]

View File

@ -13,7 +13,8 @@
import socket
import os
import qemu
from . import QEMUMachine
class QEMUQtestProtocol(object):
@ -79,7 +80,7 @@ class QEMUQtestProtocol(object):
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",

View File

@ -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')

View File

@ -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):

View File

@ -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:

View File

@ -23,6 +23,8 @@ import sys
import subprocess
import json
from graphviz import Digraph
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
from qemu import MonitorResponseError

View File

@ -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()

View File

@ -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
"""

View File

@ -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 @@ class LinuxInitrd(Test):
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")

View File

@ -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')

View File

@ -14,7 +14,6 @@ from avocado_qemu import Test
class Version(Test):
"""
:avocado: enable
:avocado: tags=quick
"""
def test_qmp_human_info_version(self):

View File

@ -11,7 +11,7 @@ Check compatibility of virtio device types
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
"""

View File

@ -13,7 +13,6 @@ from avocado_qemu import Test
class Vnc(Test):
"""
:avocado: enable
:avocado: tags=vnc,quick
"""
def test_no_vnc(self):

View File

@ -24,13 +24,14 @@ import re
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):

View File

@ -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

View File

@ -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

View File

@ -32,8 +32,8 @@ import atexit
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

View File

@ -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

View File

@ -17,7 +17,7 @@ import sys
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