summaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
authorDaniel P. Berrangé2022-01-28 17:11:56 +0100
committerJohn Snow2022-02-23 23:07:26 +0100
commit439125293cc9cfb684eb4db23db04199f5f435a2 (patch)
tree28d4356bed0cf6a1f3fa700ce273a8964d4b0023 /python
parentMerge remote-tracking branch 'remotes/lvivier-gitlab/tags/trivial-branch-for-... (diff)
downloadqemu-439125293cc9cfb684eb4db23db04199f5f435a2.tar.gz
qemu-439125293cc9cfb684eb4db23db04199f5f435a2.tar.xz
qemu-439125293cc9cfb684eb4db23db04199f5f435a2.zip
python: introduce qmp-shell-wrap convenience tool
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>
Diffstat (limited to 'python')
-rw-r--r--python/qemu/aqmp/qmp_shell.py65
-rw-r--r--python/setup.cfg1
2 files changed, 62 insertions, 4 deletions
diff --git a/python/qemu/aqmp/qmp_shell.py b/python/qemu/aqmp/qmp_shell.py
index d11bf54b00..c60df787fc 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 18aea2bab3..0959603238 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]