iotests: make qemu_img raise on non-zero rc by default

re-write qemu_img() as a function that will by default raise a
VerboseProcessException (extended from CalledProcessException) on
non-zero return codes. This will produce a stack trace that will show
the command line arguments and return code from the failed process run.

Users that want something more flexible (there appears to be only one)
can use check=False and manage the return themselves. However, when the
return code is negative, the Exception will be raised no matter what.
This is done under the belief that there's no legitimate reason, even in
negative tests, to see a crash from qemu-img.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
Message-Id: <20220321201618.903471-5-jsnow@redhat.com>
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
This commit is contained in:
John Snow 2022-03-21 16:16:04 -04:00 committed by Hanna Reitz
parent fc272d3ce0
commit 2882ccf86a
2 changed files with 55 additions and 10 deletions

View File

@ -241,11 +241,13 @@ def compare_images(image, reference, baseimg=None, expected_match=True):
expected_ret = 0 if expected_match else 1
if baseimg:
qemu_img("rebase", "-u", "-b", baseimg, '-F', iotests.imgfmt, image)
ret = qemu_img("compare", image, reference)
sub = qemu_img("compare", image, reference, check=False)
log('qemu_img compare "{:s}" "{:s}" ==> {:s}, {:s}'.format(
image, reference,
"Identical" if ret == 0 else "Mismatch",
"OK!" if ret == expected_ret else "ERROR!"),
"Identical" if sub.returncode == 0 else "Mismatch",
"OK!" if sub.returncode == expected_ret else "ERROR!"),
filters=[iotests.filter_testfiles])
def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):

View File

@ -37,9 +37,10 @@ import unittest
from contextlib import contextmanager
from qemu.aqmp.legacy import QEMUMonitorProtocol
from qemu.machine import qtest
from qemu.qmp import QMPMessage
from qemu.aqmp.legacy import QEMUMonitorProtocol
from qemu.utils import VerboseProcessError
# Use this logger for logging messages directly from the iotests module
logger = logging.getLogger('qemu.iotests')
@ -215,9 +216,50 @@ def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]:
return qemu_tool_pipe_and_status('qemu-img', full_args,
drop_successful_output=is_create)
def qemu_img(*args: str) -> int:
'''Run qemu-img and return the exit code'''
return qemu_img_pipe_and_status(*args)[1]
def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
) -> 'subprocess.CompletedProcess[str]':
"""
Run qemu_img and return the status code and console output.
This function always prepends QEMU_IMG_OPTIONS and may further alter
the args for 'create' commands.
:param args: command-line arguments to qemu-img.
:param check: Enforce a return code of zero.
:param combine_stdio: set to False to keep stdout/stderr separated.
:raise VerboseProcessError:
When the return code is negative, or on any non-zero exit code
when 'check=True' was provided (the default). This exception has
'stdout', 'stderr', and 'returncode' properties that may be
inspected to show greater detail. If this exception is not
handled, the command-line, return code, and all console output
will be included at the bottom of the stack trace.
:return:
a CompletedProcess. This object has args, returncode, and stdout
properties. If streams are not combined, it will also have a
stderr property.
"""
full_args = qemu_img_args + qemu_img_create_prepare_args(list(args))
subp = subprocess.run(
full_args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT if combine_stdio else subprocess.PIPE,
universal_newlines=True,
check=False
)
if check and subp.returncode or (subp.returncode < 0):
raise VerboseProcessError(
subp.returncode, full_args,
output=subp.stdout,
stderr=subp.stderr,
)
return subp
def ordered_qmp(qmsg, conv_keys=True):
# Dictionaries are not ordered prior to 3.6, therefore:
@ -232,7 +274,7 @@ def ordered_qmp(qmsg, conv_keys=True):
return od
return qmsg
def qemu_img_create(*args):
def qemu_img_create(*args: str) -> 'subprocess.CompletedProcess[str]':
return qemu_img('create', *args)
def qemu_img_measure(*args):
@ -467,8 +509,9 @@ def qemu_nbd_popen(*args):
def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt):
'''Return True if two image files are identical'''
return qemu_img('compare', '-f', fmt1,
'-F', fmt2, img1, img2) == 0
res = qemu_img('compare', '-f', fmt1,
'-F', fmt2, img1, img2, check=False)
return res.returncode == 0
def create_image(name, size):
'''Create a fully-allocated raw image with sector markers'''