From 6acc5c046905183d38e2f44fb0fe69001939d969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:33 +0200 Subject: [PATCH 01/25] scripts/qemugdb: Remove shebang header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These scripts are loaded as plugin by GDB (and they don't have any __main__ entry point). Remove the shebang header. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Alex Bennée Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-2-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- scripts/qemugdb/__init__.py | 3 +-- scripts/qemugdb/aio.py | 3 +-- scripts/qemugdb/coroutine.py | 3 +-- scripts/qemugdb/mtree.py | 4 +--- scripts/qemugdb/tcg.py | 1 - scripts/qemugdb/timers.py | 1 - 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/scripts/qemugdb/__init__.py b/scripts/qemugdb/__init__.py index 969f552b26..da8ff612e5 100644 --- a/scripts/qemugdb/__init__.py +++ b/scripts/qemugdb/__init__.py @@ -1,5 +1,4 @@ -#!/usr/bin/python - +# # GDB debugging support # # Copyright (c) 2015 Linaro Ltd diff --git a/scripts/qemugdb/aio.py b/scripts/qemugdb/aio.py index 2ba00c4444..d7c1ba0c28 100644 --- a/scripts/qemugdb/aio.py +++ b/scripts/qemugdb/aio.py @@ -1,5 +1,4 @@ -#!/usr/bin/python - +# # GDB debugging support: aio/iohandler debug # # Copyright (c) 2015 Red Hat, Inc. diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py index 41e079d0e2..db61389022 100644 --- a/scripts/qemugdb/coroutine.py +++ b/scripts/qemugdb/coroutine.py @@ -1,5 +1,4 @@ -#!/usr/bin/python - +# # GDB debugging support # # Copyright 2012 Red Hat, Inc. and/or its affiliates diff --git a/scripts/qemugdb/mtree.py b/scripts/qemugdb/mtree.py index 3030a60d3f..8fe42c3c12 100644 --- a/scripts/qemugdb/mtree.py +++ b/scripts/qemugdb/mtree.py @@ -1,5 +1,4 @@ -#!/usr/bin/python - +# # GDB debugging support # # Copyright 2012 Red Hat, Inc. and/or its affiliates @@ -84,4 +83,3 @@ class MtreeCommand(gdb.Command): while not isnull(subregion): self.print_item(subregion, addr, level) subregion = subregion['subregions_link']['tqe_next'] - diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py index 18880fc9a7..16c03c06a9 100644 --- a/scripts/qemugdb/tcg.py +++ b/scripts/qemugdb/tcg.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # # GDB debugging support, TCG status diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py index f0e132d27a..46537b27cf 100644 --- a/scripts/qemugdb/timers.py +++ b/scripts/qemugdb/timers.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # GDB debugging support # From 806c200ef47db61078320b58e4aafaaa5a5cdecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:34 +0200 Subject: [PATCH 02/25] scripts/qemu-gdb: Use Python 3 interpreter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-3-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- scripts/qemu-gdb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index f2a305c42e..e0bfa7b5a4 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -1,5 +1,5 @@ -#!/usr/bin/python - +#!/usr/bin/env python3 +# # GDB debugging support # # Copyright 2012 Red Hat, Inc. and/or its affiliates From 06d4c71f4869386abf97f45f61c21e9dc449943f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:35 +0200 Subject: [PATCH 03/25] scripts/qmp: Use Python 3 interpreter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-4-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- scripts/qmp/qom-get | 2 +- scripts/qmp/qom-list | 2 +- scripts/qmp/qom-set | 2 +- scripts/qmp/qom-tree | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 007b4cd442..7c5ede91bb 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 ## # QEMU Object Model test tools # diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 03bda3446b..bb68fd65d4 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 ## # QEMU Object Model test tools # diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index c37fe78b00..19881d85e9 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 ## # QEMU Object Model test tools # diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 1c8acf61e7..fa91147a03 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 ## # QEMU Object Model test tools # From e57a707a82a0ddc07615e048ef72cf8553c3a4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:36 +0200 Subject: [PATCH 04/25] scripts/kvm/vmxcap: Use Python 3 interpreter and add pseudo-main() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: Paolo Bonzini Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-5-philmd@redhat.com> --- scripts/kvm/vmxcap | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 971ed0e721..6fe66d5f57 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # tool for querying VMX capabilities # @@ -275,5 +275,6 @@ controls = [ ), ] -for c in controls: - c.show() +if __name__ == '__main__': + for c in controls: + c.show() From 5aa628045d4cf1c258c92ce7e525bb8d4b2e072d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:37 +0200 Subject: [PATCH 05/25] scripts/modules/module_block: Use Python 3 interpreter & add pseudo-main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-6-philmd@redhat.com> --- scripts/modules/module_block.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/scripts/modules/module_block.py b/scripts/modules/module_block.py index f23191fac1..1109df827d 100644 --- a/scripts/modules/module_block.py +++ b/scripts/modules/module_block.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Module information generator # @@ -80,19 +80,20 @@ def print_bottom(fheader): #endif ''') -# First argument: output file -# All other arguments: modules source files (.c) -output_file = sys.argv[1] -with open(output_file, 'w') as fheader: - print_top(fheader) +if __name__ == '__main__': + # First argument: output file + # All other arguments: modules source files (.c) + output_file = sys.argv[1] + with open(output_file, 'w') as fheader: + print_top(fheader) - for filename in sys.argv[2:]: - if os.path.isfile(filename): - process_file(fheader, filename) - else: - print("File " + filename + " does not exist.", file=sys.stderr) - sys.exit(1) + for filename in sys.argv[2:]: + if os.path.isfile(filename): + process_file(fheader, filename) + else: + print("File " + filename + " does not exist.", file=sys.stderr) + sys.exit(1) - print_bottom(fheader) + print_bottom(fheader) -sys.exit(0) + sys.exit(0) From c7b942d7f84ef54f266921bf7668d43f1f2c7c79 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 28 May 2020 18:21:26 -0400 Subject: [PATCH 06/25] scripts/qmp: Fix shebang and imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's more wrong with these scripts; They are in various stages of disrepair. That's beyond the scope of this current patchset. This just mechanically corrects the imports and the shebangs, as part of ensuring that the python/qemu/lib refactoring didn't break anything needlessly. Signed-off-by: John Snow Message-Id: <20200528222129.23826-2-jsnow@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- scripts/qmp/qmp | 4 +++- scripts/qmp/qom-fuse | 4 +++- scripts/qmp/qom-get | 4 +++- scripts/qmp/qom-list | 4 +++- scripts/qmp/qom-set | 4 +++- scripts/qmp/qom-tree | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 0625fc2aba..8e52e4a54d 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -11,7 +11,9 @@ # See the COPYING file in the top-level directory. 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=[]): if type(rsp) == list: diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index 6bada2c33d..5fa6b3bf64 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -15,7 +15,9 @@ import fuse, stat from fuse import Fuse import os, posix 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) diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 7c5ede91bb..666df71832 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -13,7 +13,9 @@ import sys 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:] socket_path = None diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index bb68fd65d4..5074fd939f 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -13,7 +13,9 @@ import sys 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:] socket_path = None diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 19881d85e9..240a78187f 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -13,7 +13,9 @@ import sys 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:] socket_path = None diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index fa91147a03..25b0781323 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -15,7 +15,9 @@ import sys 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:] socket_path = None From 2d110c11497ac52d5ce9f4b116463cdb8c3f4ad5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 13 May 2020 23:52:30 -0400 Subject: [PATCH 07/25] python: remove more instances of sys.version_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We guarantee 3.5+ everywhere; remove more dead checks. In general, try to avoid using version checks and instead prefer to attempt behavior when possible. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514035230.25756-1-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- scripts/analyze-migration.py | 5 ----- scripts/decodetree.py | 25 +++++++++--------------- scripts/qmp/qmp-shell | 3 --- tests/docker/docker.py | 5 +++-- tests/qemu-iotests/nbd-fault-injector.py | 5 +---- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 96a31d3974..95838cbff3 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -25,11 +25,6 @@ import struct 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): try: os.makedirs(path) diff --git a/scripts/decodetree.py b/scripts/decodetree.py index 46ab917807..f9d204aa36 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -75,13 +75,6 @@ def output(*args): 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(): output('/* This file is autogenerated by scripts/decodetree.py. */\n\n') @@ -428,18 +421,18 @@ def parse_field(lineno, name, toks): width = 0 func = None for t in toks: - if re_fullmatch('!function=' + re_ident, t): + if re.fullmatch('!function=' + re_ident, t): if func: error(lineno, 'duplicate function') func = t.split('=') func = func[1] continue - if re_fullmatch('[0-9]+:s[0-9]+', t): + if re.fullmatch('[0-9]+:s[0-9]+', t): # Signed field extract subtoks = t.split(':s') sign = True - elif re_fullmatch('[0-9]+:[0-9]+', t): + elif re.fullmatch('[0-9]+:[0-9]+', t): # Unsigned field extract subtoks = t.split(':') sign = False @@ -488,11 +481,11 @@ def parse_arguments(lineno, name, toks): flds = [] extern = False for t in toks: - if re_fullmatch('!extern', t): + if re.fullmatch('!extern', t): extern = True anyextern = True continue - if not re_fullmatch(re_ident, t): + if not re.fullmatch(re_ident, t): error(lineno, 'invalid argument set token "{0}"'.format(t)) if t in flds: error(lineno, 'duplicate argument "{0}"'.format(t)) @@ -621,13 +614,13 @@ def parse_generic(lineno, is_format, name, toks): continue # '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('=%') flds = add_field_byname(lineno, flds, fname, iname) continue # '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('=') value = int(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, # required ones, or dont-cares. - if re_fullmatch('[01.-]+', t): + if re.fullmatch('[01.-]+', t): shift = len(t) fms = t.replace('0', '1') fms = fms.replace('.', '0') @@ -652,7 +645,7 @@ def parse_generic(lineno, is_format, name, toks): fixedmask = (fixedmask << shift) | fms undefmask = (undefmask << shift) | ubm # 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(':') sign = False if flen[0] == 's': diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index a01d31de1e..c5eef06f3f 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -77,9 +77,6 @@ import re sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import qmp -if sys.version_info[0] == 2: - input = raw_input - class QMPCompleter(list): def complete(self, text, state): for cmd in self: diff --git a/tests/docker/docker.py b/tests/docker/docker.py index d8268c1111..5a9735db78 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -258,12 +258,13 @@ class Docker(object): return self._do_kill_instances(True) def _output(self, cmd, **kwargs): - if sys.version_info[1] >= 6: + try: return subprocess.check_output(self._command + cmd, stderr=subprocess.STDOUT, encoding='utf-8', **kwargs) - else: + except TypeError: + # 'encoding' argument was added in 3.6+ return subprocess.check_output(self._command + cmd, stderr=subprocess.STDOUT, **kwargs).decode('utf-8') diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index 588d62aebf..78f42c4214 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -47,10 +47,7 @@ import sys import socket import struct import collections -if sys.version_info.major >= 3: - import configparser -else: - import ConfigParser as configparser +import configparser FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB From e0e925a61141552bca7277d06516ad78258423da Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 17 Feb 2020 18:02:42 +0300 Subject: [PATCH 08/25] python/qemu/machine: add kill() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add method to hard-kill vm, without any quit commands. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Andrey Shinkevich Message-Id: <20200217150246.29180-19-vsementsov@virtuozzo.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index b9a98e2c86..d2f531f1b4 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -342,7 +342,7 @@ class QEMUMachine(object): self._load_io_log() self._post_shutdown() - def shutdown(self, has_quit=False): + def shutdown(self, has_quit=False, hard=False): """ Terminate the VM and clean up """ @@ -354,7 +354,9 @@ class QEMUMachine(object): self._console_socket = None if self.is_running(): - if self._qmp: + if hard: + self._popen.kill() + elif self._qmp: try: if not has_quit: self._qmp.cmd('quit') @@ -368,7 +370,8 @@ class QEMUMachine(object): self._post_shutdown() 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' if self._qemu_full_args: command = ' '.join(self._qemu_full_args) @@ -378,6 +381,9 @@ class QEMUMachine(object): self._launched = False + def kill(self): + self.shutdown(hard=True) + def set_qmp_monitor(self, enabled=True): """ Set the QMP monitor. From 053774bdecf60c0500d66a05e02e48ff24ab23cf Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:52 -0400 Subject: [PATCH 09/25] python/qemu/machine: remove logging configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.5 and above do not print a warning when logging is not configured. As a library, it's best practice to leave logging configuration to the client executable. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-22-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index d2f531f1b4..41554de533 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -119,9 +119,6 @@ class QEMUMachine(object): self._console_socket = None self._remove_files = [] - # just in case logging wasn't configured by the main script: - logging.basicConfig() - def __enter__(self): return self From 9b8ccd6d5b81f10436764bf7e334e087f3918d12 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 28 May 2020 18:21:28 -0400 Subject: [PATCH 10/25] python/qemu: delint and add pylintrc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring our these files up to speed with pylint 2.5.0. Add a pylintrc file to formalize which pylint subset we are targeting. The similarity ignore is there to suppress similarity reports across imports, which for typing constants, are going to trigger this report erroneously. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200528222129.23826-4-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine.py | 6 ++--- python/qemu/pylintrc | 58 ++++++++++++++++++++++++++++++++++++++++++ python/qemu/qtest.py | 42 +++++++++++++++++++----------- 3 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 python/qemu/pylintrc diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 41554de533..8e4ecd1837 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -58,7 +58,7 @@ class MonitorResponseError(qmp.QMPError): self.reply = reply -class QEMUMachine(object): +class QEMUMachine: """ A QEMU VM @@ -239,7 +239,7 @@ class QEMUMachine(object): 'chardev=mon,mode=control']) if self._machine is not None: args.extend(['-machine', self._machine]) - for i in range(self._console_index): + for _ in range(self._console_index): args.extend(['-serial', 'null']) if self._console_set: self._console_address = os.path.join(self._sock_dir, @@ -374,7 +374,7 @@ class QEMUMachine(object): command = ' '.join(self._qemu_full_args) else: command = '' - LOG.warning(msg, -exitcode, command) + LOG.warning(msg, -int(exitcode), command) self._launched = False diff --git a/python/qemu/pylintrc b/python/qemu/pylintrc new file mode 100644 index 0000000000..5d6ae7367d --- /dev/null +++ b/python/qemu/pylintrc @@ -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] diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index d24ad04256..53d814c064 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -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. # # Authors: @@ -17,19 +23,21 @@ import os from .machine import QEMUMachine -class QEMUQtestProtocol(object): - def __init__(self, address, server=False): - """ - Create a QEMUQtestProtocol object. +class QEMUQtestProtocol: + """ + QEMUQtestProtocol implements a connection to a qtest socket. - @param address: QEMU address, can be either a unix socket path (string) - or a tuple in the form ( address, port ) for a TCP - connection - @param server: server mode, listens on the socket (bool) - @raise socket.error on socket connection errors - @note No connection is established, this is done by the connect() or - accept() methods - """ + :param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + :param server: server mode, listens on the socket (bool) + :raise socket.error: on socket connection errors + + .. 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._sock = self._get_sock() self._sockfile = None @@ -73,15 +81,19 @@ class QEMUQtestProtocol(object): return resp def close(self): + """Close this socket.""" self._sock.close() self._sockfile.close() def settimeout(self, timeout): + """Set a timeout, in seconds.""" self._sock.settimeout(timeout) 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", socket_scm_helper=None, sock_dir=None): From 8dfac2edb2146d87b25543c70e25723f3d4dbd60 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 28 May 2020 18:21:29 -0400 Subject: [PATCH 11/25] python/qemu: delint; add flake8 config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly, ignore the "no bare except" rule, because flake8 is not contextual and cannot determine if we re-raise. Pylint can, though, so always prefer pylint for that. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200528222129.23826-5-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/.flake8 | 2 ++ python/qemu/accel.py | 9 ++++++--- python/qemu/machine.py | 13 +++++++++---- python/qemu/qmp.py | 4 ++-- 4 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 python/qemu/.flake8 diff --git a/python/qemu/.flake8 b/python/qemu/.flake8 new file mode 100644 index 0000000000..45d8146f3f --- /dev/null +++ b/python/qemu/.flake8 @@ -0,0 +1,2 @@ +[flake8] +extend-ignore = E722 # Pylint handles this, but smarter. \ No newline at end of file diff --git a/python/qemu/accel.py b/python/qemu/accel.py index 36ae85791e..7fabe62920 100644 --- a/python/qemu/accel.py +++ b/python/qemu/accel.py @@ -23,11 +23,12 @@ LOG = logging.getLogger(__name__) # Mapping host architecture to any additional architectures it can # support which often includes its 32 bit cousin. ADDITIONAL_ARCHES = { - "x86_64" : "i386", - "aarch64" : "armhf", - "ppc64le" : "ppc64", + "x86_64": "i386", + "aarch64": "armhf", + "ppc64le": "ppc64", } + def list_accel(qemu_bin): """ List accelerators enabled in the QEMU binary. @@ -47,6 +48,7 @@ def list_accel(qemu_bin): # Skip the first line which is the header. return [acc.strip() for acc in out.splitlines()[1:]] + def kvm_available(target_arch=None, qemu_bin=None): """ 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 True + def tcg_available(qemu_bin): """ Check if TCG is available. diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 8e4ecd1837..187790ce9e 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -29,6 +29,7 @@ from . import qmp LOG = logging.getLogger(__name__) + class QEMUMachineError(Exception): """ Exception called when an error in QEMUMachine happens. @@ -62,7 +63,8 @@ class QEMUMachine: """ 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: ... @@ -185,8 +187,10 @@ class QEMUMachine: fd_param.append(str(fd)) devnull = open(os.path.devnull, 'rb') - proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, close_fds=False) + proc = subprocess.Popen( + fd_param, stdin=devnull, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, close_fds=False + ) output = proc.communicate()[0] if output: LOG.debug(output) @@ -485,7 +489,8 @@ class QEMUMachine: 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. The match criteria are optional and may be None. diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index d6c9b2f4b1..6ae7693965 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -168,8 +168,8 @@ class QEMUMonitorProtocol: @param timeout: timeout in seconds (nonnegative float number, or None). The value passed will set the behavior of the - underneath QMP socket as described in [1]. Default value - is set to 15.0. + underneath QMP socket as described in [1]. + Default value is set to 15.0. @return QMP greeting dict @raise OSError on socket connection errors @raise QMPConnectError if the greeting is not received From 3797dbcbb7bf1dffdd74ef84b5b21ed9c825e171 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:42 -0400 Subject: [PATCH 12/25] python/qemu: remove Python2 style super() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the Python3 style instead. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-12-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine.py | 2 +- python/qemu/qtest.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 187790ce9e..95a20a17f9 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -55,7 +55,7 @@ class MonitorResponseError(qmp.QMPError): desc = reply["error"]["desc"] except KeyError: desc = reply - super(MonitorResponseError, self).__init__(desc) + super().__init__(desc) self.reply = reply diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index 53d814c064..7943487c2b 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -101,29 +101,28 @@ class QEMUQtestMachine(QEMUMachine): name = "qemu-%d" % os.getpid() if sock_dir is None: sock_dir = test_dir - super(QEMUQtestMachine, - self).__init__(binary, args, name=name, test_dir=test_dir, - socket_scm_helper=socket_scm_helper, - sock_dir=sock_dir) + super().__init__(binary, args, name=name, test_dir=test_dir, + socket_scm_helper=socket_scm_helper, + sock_dir=sock_dir) self._qtest = None self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") def _base_args(self): - args = super(QEMUQtestMachine, self)._base_args() + args = super()._base_args() args.extend(['-qtest', 'unix:path=' + self._qtest_path, '-accel', 'qtest']) return args def _pre_launch(self): - super(QEMUQtestMachine, self)._pre_launch() + super()._pre_launch() self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) def _post_launch(self): - super(QEMUQtestMachine, self)._post_launch() + super()._post_launch() self._qtest.accept() def _post_shutdown(self): - super(QEMUQtestMachine, self)._post_shutdown() + super()._post_shutdown() self._remove_if_exists(self._qtest_path) def qtest(self, cmd): From 0add048fbd9992151e4c592977df9cff8558ca60 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:43 -0400 Subject: [PATCH 13/25] python/qemu: fix socket.makefile() typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: A bug in typeshed (https://github.com/python/typeshed/issues/3977) misinterprets the type of makefile(). Work around this by explicitly stating that we are opening a text-mode file. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-13-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/qmp.py | 10 +++++++--- python/qemu/qtest.py | 12 ++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 6ae7693965..73d49050ed 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -11,6 +11,10 @@ import json import errno import socket import logging +from typing import ( + Optional, + TextIO, +) class QMPError(Exception): @@ -61,7 +65,7 @@ class QEMUMonitorProtocol: self.__events = [] self.__address = address self.__sock = self.__get_sock() - self.__sockfile = None + self.__sockfile: Optional[TextIO] = None self._nickname = nickname if self._nickname: self.logger = logging.getLogger('QMP').getChild(self._nickname) @@ -157,7 +161,7 @@ class QEMUMonitorProtocol: @raise QMPCapabilitiesError if fails to negotiate capabilities """ self.__sock.connect(self.__address) - self.__sockfile = self.__sock.makefile() + self.__sockfile = self.__sock.makefile(mode='r') if negotiate: return self.__negotiate_capabilities() return None @@ -180,7 +184,7 @@ class QEMUMonitorProtocol: """ self.__sock.settimeout(timeout) self.__sock, _ = self.__sock.accept() - self.__sockfile = self.__sock.makefile() + self.__sockfile = self.__sock.makefile(mode='r') return self.__negotiate_capabilities() def cmd_obj(self, qmp_cmd): diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index 7943487c2b..4c88590eb0 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -19,6 +19,7 @@ subclass of QEMUMachine, respectively. import socket import os +from typing import Optional, TextIO from .machine import QEMUMachine @@ -40,7 +41,7 @@ class QEMUQtestProtocol: def __init__(self, address, server=False): self._address = address self._sock = self._get_sock() - self._sockfile = None + self._sockfile: Optional[TextIO] = None if server: self._sock.bind(self._address) self._sock.listen(1) @@ -59,7 +60,7 @@ class QEMUQtestProtocol: @raise socket.error on socket connection errors """ self._sock.connect(self._address) - self._sockfile = self._sock.makefile() + self._sockfile = self._sock.makefile(mode='r') def accept(self): """ @@ -68,7 +69,7 @@ class QEMUQtestProtocol: @raise socket.error on socket connection errors """ self._sock, _ = self._sock.accept() - self._sockfile = self._sock.makefile() + self._sockfile = self._sock.makefile(mode='r') def cmd(self, qtest_cmd): """ @@ -76,6 +77,7 @@ class QEMUQtestProtocol: @param qtest_cmd: qtest command text to be sent """ + assert self._sockfile is not None self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) resp = self._sockfile.readline() return resp @@ -83,7 +85,9 @@ class QEMUQtestProtocol: def close(self): """Close this socket.""" self._sock.close() - self._sockfile.close() + if self._sockfile: + self._sockfile.close() + self._sockfile = None def settimeout(self, timeout): """Set a timeout, in seconds.""" From 1dda0404d8afeb0ed45fbeae85e380e1ff57da35 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:44 -0400 Subject: [PATCH 14/25] python/qemu: Adjust traceback typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mypy considers it incorrect to use `bool` to statically return false, because it will assume that it could conceivably return True, and gives different analysis in that case. Use a None return to achieve the same effect, but make mypy happy. Note: Pylint considers function signatures as code that might trip the duplicate-code checker. I'd rather not disable this as it does not trigger often in practice, so I'm disabling it as a one-off and filed a change request; see https://github.com/PyCQA/pylint/issues/3619 Signed-off-by: John Snow Acked-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-14-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine.py | 8 ++++++-- python/qemu/qmp.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 95a20a17f9..041c615052 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -24,6 +24,8 @@ import subprocess import shutil import socket import tempfile +from typing import Optional, Type +from types import TracebackType from . import qmp @@ -124,9 +126,11 @@ class QEMUMachine: def __enter__(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() - return False def add_monitor_null(self): """ diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 73d49050ed..b91c9d5c1c 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -14,7 +14,9 @@ import logging from typing import ( Optional, TextIO, + Type, ) +from types import TracebackType class QMPError(Exception): @@ -146,10 +148,14 @@ class QEMUMonitorProtocol: # Implement context manager enter function. 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. self.close() - return False def connect(self, negotiate=True): """ From 7af67d694e289ab116c7abeca8a5bd752fbd46d7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:45 -0400 Subject: [PATCH 15/25] python/qemu/qmp: use True/False for non/blocking modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type system doesn't want integers. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-15-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/qmp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index b91c9d5c1c..a634c4e26c 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -120,14 +120,14 @@ class QEMUMonitorProtocol: """ # Check for new events regardless and pull them into the cache: - self.__sock.setblocking(0) + self.__sock.setblocking(False) try: self.__json_read() except OSError as err: if err.errno == errno.EAGAIN: # No data available pass - self.__sock.setblocking(1) + self.__sock.setblocking(True) # Wait for new events, if needed. # if wait is 0.0, this means "no wait" and is also implicitly false. From de210ec53c842fa67aa10110a7a351d64f91c487 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:53:51 -0400 Subject: [PATCH 16/25] python/qemu/qmp: assert sockfile is not None MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In truth, if you don't do this, you'll just get a TypeError exception. Now, you'll get an AssertionError. Is this tangibly better? No. Does mypy complain less? Yes. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-21-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/qmp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index a634c4e26c..e64b6b5faa 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -94,6 +94,7 @@ class QEMUMonitorProtocol: raise QMPCapabilitiesError def __json_read(self, only_event=False): + assert self.__sockfile is not None while True: data = self.__sockfile.readline() if not data: From c95dddce4910c9cd5315b942180b03ca4e48a6a6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 14 May 2020 01:54:00 -0400 Subject: [PATCH 17/25] python/qemu/qtest: Check before accessing _qtest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It can be None; so add assertions or exceptions where appropriate to guard the access accordingly. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200514055403.18902-30-jsnow@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/qtest.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index 4c88590eb0..888c8bd2f6 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -121,7 +121,8 @@ class QEMUQtestMachine(QEMUMachine): super()._pre_launch() self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) - def _post_launch(self): + def _post_launch(self) -> None: + assert self._qtest is not None super()._post_launch() self._qtest.accept() @@ -129,6 +130,13 @@ class QEMUQtestMachine(QEMUMachine): super()._post_shutdown() self._remove_if_exists(self._qtest_path) - def qtest(self, cmd): - '''Send a qtest command to guest''' + def qtest(self, cmd: str) -> str: + """ + 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) From d5326a24378dbf228b5ea842945eff34ed9543a0 Mon Sep 17 00:00:00 2001 From: Robert Foley Date: Fri, 29 May 2020 16:34:50 -0400 Subject: [PATCH 18/25] tests/vm: Pass --debug through for vm-boot-ssh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps debug issues that occur during the boot sequence. Signed-off-by: Robert Foley Reviewed-by: Peter Puhov Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20200529203458.1038-5-robert.foley@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- tests/vm/Makefile.include | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 74ab522c55..80f7f6bdee 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -91,6 +91,7 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(call quiet-command, \ $(PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ + $(if $(V)$(DEBUG), --debug) \ --image "$<" \ --interactive \ false, \ From e56c45047bd5bbcfdc36e3f4ed8b439c5d5c989a Mon Sep 17 00:00:00 2001 From: Robert Foley Date: Fri, 29 May 2020 16:34:51 -0400 Subject: [PATCH 19/25] tests/vm: Add ability to select QEMU from current build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a new special variable QEMU_LOCAL=1, which will indicate to take the QEMU binary from the current build. Signed-off-by: Robert Foley Reviewed-by: Peter Puhov Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20200529203458.1038-6-robert.foley@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- tests/vm/Makefile.include | 4 ++++ tests/vm/basevm.py | 28 +++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 80f7f6bdee..a253aba457 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -41,6 +41,7 @@ endif @echo " J=[0..9]* - Override the -jN parameter for make commands" @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" @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_IMG=/path/to/qemu-img - Change path to qemu-img tool" @@ -57,6 +58,7 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ $(PYTHON) $< \ $(if $(V)$(DEBUG), --debug) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ --image "$@" \ --force \ --build-image $@, \ @@ -71,6 +73,7 @@ vm-build-%: $(IMAGES_DIR)/%.img $(if $(DEBUG), --interactive) \ $(if $(J),--jobs $(J)) \ $(if $(V),--verbose) \ + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ --image "$<" \ $(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \ --snapshot \ @@ -92,6 +95,7 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ $(if $(V)$(DEBUG), --debug) \ + $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ --image "$<" \ --interactive \ false, \ diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index a2d4054d72..5a3ce42281 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -61,9 +61,11 @@ class BaseVM(object): # 4 is arbitrary, but greater than 2, # since we found we need to wait more than twice as long. 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._genisoimage = genisoimage + self._build_path = build_path self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", suffix=".tmp", dir=".")) @@ -184,15 +186,15 @@ class BaseVM(object): "-device", "virtio-blk,drive=drive0,bootindex=0"] args += self._data_args + extra_args logging.debug("QEMU args: %s", " ".join(args)) - qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) - guest = QEMUMachine(binary=qemu_bin, args=args) + qemu_path = get_qemu_path(self.arch, self._build_path) + guest = QEMUMachine(binary=qemu_path, args=args) guest.set_machine('pc') guest.set_console() try: guest.launch() except: 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(guest.get_log()) logging.error("QEMU version >= 2.10 is required") @@ -391,6 +393,19 @@ class BaseVM(object): 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 get_default_jobs(): @@ -421,6 +436,9 @@ def parse_args(vmcls): help="build QEMU from source in guest") parser.add_option("--build-target", 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", help="Interactively run command") parser.add_option("--snapshot", "-s", action="store_true", @@ -439,7 +457,7 @@ def main(vmcls): logging.basicConfig(level=(logging.DEBUG if args.debug else logging.WARN)) vm = vmcls(debug=args.debug, vcpus=args.jobs, - genisoimage=args.genisoimage) + genisoimage=args.genisoimage, build_path=args.build_path) if args.build_image: if os.path.exists(args.image) and not args.force: sys.stderr.writelines(["Image file exists: %s\n" % args.image, From 6ee982c9abc42e726f9e783ba67bbb7676a9f9b4 Mon Sep 17 00:00:00 2001 From: Robert Foley Date: Fri, 29 May 2020 16:34:52 -0400 Subject: [PATCH 20/25] tests/vm: allow wait_ssh() to specify command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows for waiting for completion of arbitrary commands. Signed-off-by: Robert Foley Reviewed-by: Peter Puhov Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20200529203458.1038-7-robert.foley@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- tests/vm/basevm.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 5a3ce42281..a80b616a08 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -320,24 +320,24 @@ class BaseVM(object): def print_step(self, 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. if not kvm_available(self.arch): seconds *= self.tcg_ssh_timeout_multiplier starttime = datetime.datetime.now() endtime = starttime + datetime.timedelta(seconds=seconds) - guest_up = False + cmd_success = False while datetime.datetime.now() < endtime: - if wait_root and self.ssh_root("exit 0") == 0: - guest_up = True + if wait_root and self.ssh_root(cmd) == 0: + cmd_success = True break - elif self.ssh("exit 0") == 0: - guest_up = True + elif self.ssh(cmd) == 0: + cmd_success = True break seconds = (endtime - datetime.datetime.now()).total_seconds() logging.debug("%ds before timeout", seconds) time.sleep(1) - if not guest_up: + if not cmd_success: raise Exception("Timeout while waiting for guest ssh") def shutdown(self): From 83389e22c5aa48877b9e4f903ae4ec49a442df2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 May 2020 12:32:38 +0200 Subject: [PATCH 21/25] tests/migration/guestperf: Use Python 3 interpreter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Reviewed-by: Kevin Wolf Message-Id: <20200512103238.7078-7-philmd@redhat.com> --- tests/migration/guestperf-batch.py | 2 +- tests/migration/guestperf-plot.py | 2 +- tests/migration/guestperf.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/migration/guestperf-batch.py b/tests/migration/guestperf-batch.py index cb150ce804..f1e900908d 100755 --- a/tests/migration/guestperf-batch.py +++ b/tests/migration/guestperf-batch.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Migration test batch comparison invokation # diff --git a/tests/migration/guestperf-plot.py b/tests/migration/guestperf-plot.py index d70bb7a557..907151011a 100755 --- a/tests/migration/guestperf-plot.py +++ b/tests/migration/guestperf-plot.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Migration test graph plotting command # diff --git a/tests/migration/guestperf.py b/tests/migration/guestperf.py index 99b027e8ba..ba1c4bc4ca 100755 --- a/tests/migration/guestperf.py +++ b/tests/migration/guestperf.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Migration test direct invokation command # From 2c9120a223e666d7171e965b8f8bbcd72620f566 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 28 May 2020 12:24:04 +0100 Subject: [PATCH 22/25] tests/acceptance/migration.py: Wait for both sides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the source finishes migration the destination will still be receiving the data sent by the source, so it might not have quite finished yet, so won't quite have reached 'completed'. This lead to occasional asserts in the next few checks. After the source has finished, check the destination as well. (We can't just switch to checking the destination, because it doesn't give a status until it has started receiving the migration). Reported-by: Alex Bennée Signed-off-by: Dr. David Alan Gilbert Tested-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20200528112404.121972-1-dgilbert@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/acceptance/migration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py index 0365289cda..792639cb69 100644 --- a/tests/acceptance/migration.py +++ b/tests/acceptance/migration.py @@ -35,6 +35,10 @@ class Migration(Test): timeout=self.timeout, step=0.1, 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(dst_vm.command('query-migrate')['status'], 'completed') self.assertEqual(dst_vm.command('query-status')['status'], 'running') From a5ba86d423c2b071894d86c60487f2317c7ffb60 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Fri, 29 May 2020 10:04:39 +0300 Subject: [PATCH 23/25] tests/acceptance: allow console interaction with specific VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Console interaction in avocado scripts was possible only with single default VM. This patch modifies the function parameters to allow passing a specific VM as a parameter to interact with it. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Willian Rampazzo Reviewed-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé Message-Id: <159073587933.20809.5122618715976660635.stgit@pasha-ThinkPad-X280> Signed-off-by: Philippe Mathieu-Daudé --- tests/acceptance/avocado_qemu/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index 59e7b4f763..77d1c1d9ff 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -69,13 +69,15 @@ def pick_default_qemu_bin(arch=None): 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 - console = test.vm.console_socket.makefile() + if vm is None: + vm = test.vm + console = vm.console_socket.makefile() console_logger = logging.getLogger('console') while True: if send_string: - test.vm.console_socket.sendall(send_string.encode()) + vm.console_socket.sendall(send_string.encode()) if not keep_sending: send_string = None # send only once msg = console.readline().strip() @@ -115,7 +117,8 @@ def interrupt_interactive_console_until_pattern(test, success_message, _console_interaction(test, success_message, failure_message, 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 @@ -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 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, success_message, failure_message=None): From 12121c496fcc609e23033c4a36399b54f98bcd56 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Fri, 29 May 2020 10:04:44 +0300 Subject: [PATCH 24/25] tests/acceptance: refactor boot_linux_console test to allow code reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch splits code in BootLinuxConsole class into two different classes to allow reusing it by record/replay tests. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé Message-Id: <159073588490.20809.13942096070255577558.stgit@pasha-ThinkPad-X280> Signed-off-by: Philippe Mathieu-Daudé --- tests/acceptance/boot_linux_console.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index c6b06a1a13..12725d4529 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -28,19 +28,13 @@ try: except CmdNotFoundError: P7ZIP_AVAILABLE = False -class BootLinuxConsole(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 - +class LinuxKernelTest(Test): 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, - failure_message='Kernel panic - not syncing') + failure_message='Kernel panic - not syncing', + vm=vm) def extract_from_deb(self, deb, path): """ @@ -79,6 +73,13 @@ class BootLinuxConsole(Test): os.chdir(cwd) 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): """ :avocado: tags=arch:x86_64 From 1c80c87c8c2489e4318c93c844aa29bc1d014146 Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Fri, 29 May 2020 10:05:31 +0300 Subject: [PATCH 25/25] tests/acceptance: refactor boot_linux to allow code reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves image downloading functions to the separate class to allow reusing them from record/replay tests. Signed-off-by: Pavel Dovgalyuk Tested-by: Philippe Mathieu-Daudé Message-Id: <159073593167.20809.17582679291556188984.stgit@pasha-ThinkPad-X280> Signed-off-by: Philippe Mathieu-Daudé --- tests/acceptance/boot_linux.py | 49 ++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py index 075a386300..3aa57e88b0 100644 --- a/tests/acceptance/boot_linux.py +++ b/tests/acceptance/boot_linux.py @@ -26,22 +26,8 @@ KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM" TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" -class BootLinux(Test): - """ - 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): +class BootLinuxBase(Test): + def download_boot(self): self.log.debug('Looking for and selecting a qemu-img binary to be ' 'used to create the bootable snapshot image') # If qemu-img has been built, use it, otherwise the system wide one @@ -60,17 +46,17 @@ class BootLinux(Test): if image_arch == 'ppc64': image_arch = 'ppc64le' try: - self.boot = vmimage.get( + boot = vmimage.get( 'fedora', arch=image_arch, version='31', checksum=self.chksum, algorithm='sha256', cache_dir=self.cache_dirs[0], snapshot_dir=self.workdir) - self.vm.add_args('-drive', 'file=%s' % self.boot.path) except: 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') try: cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') @@ -81,9 +67,32 @@ class BootLinux(Test): # QEMU's hard coded usermode router address phone_home_host='10.0.2.2', phone_home_port=self.phone_home_port) - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) except Exception: 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): self.vm.set_console()