SHA256
1
0
forked from pool/qemu
qemu/python-introduce-qmp-shell-wrap-convenie.patch
2022-05-27 12:52:53 +00:00

168 lines
5.6 KiB
Diff

From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
Date: Fri, 28 Jan 2022 16:11:56 +0000
Subject: python: introduce qmp-shell-wrap convenience tool
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Git-commit: 439125293cc9cfb684eb4db23db04199f5f435a2
With the current 'qmp-shell' tool developers must first spawn QEMU with
a suitable -qmp arg and then spawn qmp-shell in a separate terminal
pointing to the right socket.
With 'qmp-shell-wrap' developers can ignore QMP sockets entirely and
just pass the QEMU command and arguments they want. The program will
listen on a UNIX socket and tell QEMU to connect QMP to that.
For example, this:
# qmp-shell-wrap -- qemu-system-x86_64 -display none
Is roughly equivalent of running:
# qemu-system-x86_64 -display none -qmp qmp-shell-1234 &
# qmp-shell qmp-shell-1234
Except that 'qmp-shell-wrap' switches the socket peers around so that
it is the UNIX socket server and QEMU is the socket client. This makes
QEMU reliably go away when qmp-shell-wrap exits, closing the server
socket.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20220128161157.36261-2-berrange@redhat.com
[Edited for rebase. --js]
Signed-off-by: John Snow <jsnow@redhat.com>
Signed-off-by: Li Zhang <lizhang@suse.de>
---
python/qemu/aqmp/qmp_shell.py | 65 ++++++++++++++++++++++++++++++++---
python/setup.cfg | 1 +
scripts/qmp/qmp-shell-wrap | 11 ++++++
3 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/python/qemu/aqmp/qmp_shell.py b/python/qemu/aqmp/qmp_shell.py
index d11bf54b00e5d56616ae57be0006..c60df787fcd50bf8a0109e5f5cd3 100644
--- a/python/qemu/aqmp/qmp_shell.py
+++ b/python/qemu/aqmp/qmp_shell.py
@@ -86,6 +86,7 @@ import logging
import os
import re
import readline
+from subprocess import Popen
import sys
from typing import (
Iterator,
@@ -167,8 +168,10 @@ class QMPShell(QEMUMonitorProtocol):
:param verbose: Echo outgoing QMP messages to console.
"""
def __init__(self, address: SocketAddrT,
- pretty: bool = False, verbose: bool = False):
- super().__init__(address)
+ pretty: bool = False,
+ verbose: bool = False,
+ server: bool = False):
+ super().__init__(address, server=server)
self._greeting: Optional[QMPMessage] = None
self._completer = QMPCompleter()
self._transmode = False
@@ -409,8 +412,10 @@ class HMPShell(QMPShell):
:param verbose: Echo outgoing QMP messages to console.
"""
def __init__(self, address: SocketAddrT,
- pretty: bool = False, verbose: bool = False):
- super().__init__(address, pretty, verbose)
+ pretty: bool = False,
+ verbose: bool = False,
+ server: bool = False):
+ super().__init__(address, pretty, verbose, server)
self._cpu_index = 0
def _cmd_completion(self) -> None:
@@ -533,5 +538,57 @@ def main() -> None:
pass
+def main_wrap() -> None:
+ """
+ qmp-shell-wrap entry point: parse command line arguments and
+ start the REPL.
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-H', '--hmp', action='store_true',
+ help='Use HMP interface')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Verbose (echo commands sent and received)')
+ parser.add_argument('-p', '--pretty', action='store_true',
+ help='Pretty-print JSON')
+
+ parser.add_argument('command', nargs=argparse.REMAINDER,
+ help='QEMU command line to invoke')
+
+ args = parser.parse_args()
+
+ cmd = args.command
+ if len(cmd) != 0 and cmd[0] == '--':
+ cmd = cmd[1:]
+ if len(cmd) == 0:
+ cmd = ["qemu-system-x86_64"]
+
+ sockpath = "qmp-shell-wrap-%d" % os.getpid()
+ cmd += ["-qmp", "unix:%s" % sockpath]
+
+ shell_class = HMPShell if args.hmp else QMPShell
+
+ try:
+ address = shell_class.parse_address(sockpath)
+ except QMPBadPortError:
+ parser.error(f"Bad port number: {sockpath}")
+ return # pycharm doesn't know error() is noreturn
+
+ try:
+ with shell_class(address, args.pretty, args.verbose, True) as qemu:
+ with Popen(cmd):
+
+ try:
+ qemu.accept()
+ except ConnectError as err:
+ if isinstance(err.exc, OSError):
+ die(f"Couldn't connect to {args.qmp_server}: {err!s}")
+ die(str(err))
+
+ for _ in qemu.repl():
+ pass
+ finally:
+ os.unlink(sockpath)
+
+
if __name__ == '__main__':
main()
diff --git a/python/setup.cfg b/python/setup.cfg
index 0063c757b78638ef651a362af338..bec54e8b0d663191e2b7afbfa350 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -68,6 +68,7 @@ console_scripts =
qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
qemu-ga-client = qemu.utils.qemu_ga_client:main
qmp-shell = qemu.aqmp.qmp_shell:main
+ qmp-shell-wrap = qemu.aqmp.qmp_shell:main_wrap
aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]
[flake8]
diff --git a/scripts/qmp/qmp-shell-wrap b/scripts/qmp/qmp-shell-wrap
new file mode 100755
index 0000000000000000000000000000000000000000..9e94da114f5f87588639f6b2cc636391e80c3864
--- /dev/null
+++ b/scripts/qmp/qmp-shell-wrap
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import qmp_shell
+
+
+if __name__ == '__main__':
+ qmp_shell.main_wrap()