On Thu, Mar 17, 2022 at 07:49:25PM -0400, John Snow wrote:
> qemu_img_json() is a new helper built on top of qemu_img() that tries to
> pull a valid JSON document out of the stdout stream.
>
> In the event that the return code is negative (the program crashed), or
> the code is greater than zero and did not produce valid JSON output, the
> VerboseProcessError raised by qemu_img() is re-raised.
>
> In the event that the return code is zero but we can't parse valid JSON,
> allow the JSON deserialization error to be raised.
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
> tests/qemu-iotests/iotests.py | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 7057db0686..9d23066701 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -276,6 +276,38 @@ def ordered_qmp(qmsg, conv_keys=True):
> def qemu_img_create(*args: str) -> subprocess.CompletedProcess[str]:
> return qemu_img('create', *args)
>
> +def qemu_img_json(*args: str) -> Any:
> + """
> + Run qemu-img and return its output as deserialized JSON.
> +
> + :raise CalledProcessError:
> + When qemu-img crashes, or returns a non-zero exit code without
> + producing a valid JSON document to stdout.
> + :raise JSONDecoderError:
> + When qemu-img returns 0, but failed to produce a valid JSON document.
> +
> + :return: A deserialized JSON object; probably a dict[str, Any].
Interesting choice to type the function as '-> Any', but document that
we expect a more specific '-> dict[str, Any]' for our known usage of
qemu-img. But it makes sense to me (in case a future qemu-img
--output=json produces something that is JSON but not a dict).
> + """
> + try:
> + res = qemu_img(*args, combine_stdio=False)
> + except subprocess.CalledProcessError as exc:
> + # Terminated due to signal. Don't bother.
> + if exc.returncode < 0:
> + raise
> +
> + # Commands like 'check' can return failure (exit codes 2 and 3)
> + # to indicate command completion, but with errors found. For
> + # multi-command flexibility, ignore the exact error codes and
> + # *try* to load JSON.
> + try:
> + return json.loads(exc.stdout)
> + except json.JSONDecodeError:
> + # Nope. This thing is toast. Raise the /process/ error.
> + pass
> + raise
> +
> + return json.loads(res.stdout)
The comments were very helpful.
Reviewed-by: Eric Blake <eblake@redhat.com>
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org