From nobody Wed Feb 11 00:59:23 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1645459355942751.9907666200215; Mon, 21 Feb 2022 08:02:35 -0800 (PST) Received: from localhost ([::1]:44686 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nMB90-0002Tp-Io for importer@patchew.org; Mon, 21 Feb 2022 11:02:34 -0500 Received: from eggs.gnu.org ([209.51.188.92]:55228) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nMB2K-0006eW-9K for qemu-devel@nongnu.org; Mon, 21 Feb 2022 10:55:40 -0500 Received: from beetle.greensocs.com ([5.135.226.135]:35576) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nMB2H-0002BZ-Gz for qemu-devel@nongnu.org; Mon, 21 Feb 2022 10:55:39 -0500 Received: from crumble.bar.greensocs.com (unknown [172.17.10.6]) by beetle.greensocs.com (Postfix) with ESMTPS id 1C56D21CCE; Mon, 21 Feb 2022 15:55:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=greensocs.com; s=mail; t=1645458935; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=adKF6/kzoZwx7qMvkl5SLyG2oeu0edQPfJy1BbduMxk=; b=CN1F5PC0yF7P87NhZuM/8+v5oCp/lta5TOIMQoRCsxgon+aW0Knvqja62tWg+CBxQVw76G 2aB5iabEPmmLmNBKIjGUYhzy/EZivUsN0dlaTZ04x60MpR2/1NtyLMY36CtUk5wQWn/xCD 10AxQ4R4OXbNLGr004348GJFMlpGgp8= From: Damien Hedde To: qemu-devel@nongnu.org Subject: [PATCH 4/5] python: qmp_shell: add -e/--exit-on-error option Date: Mon, 21 Feb 2022 16:55:18 +0100 Message-Id: <20220221155519.2367-5-damien.hedde@greensocs.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220221155519.2367-1-damien.hedde@greensocs.com> References: <20220221155519.2367-1-damien.hedde@greensocs.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=5.135.226.135; envelope-from=damien.hedde@greensocs.com; helo=beetle.greensocs.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Damien Hedde , Eduardo Habkost , John Snow , Cleber Rosa Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1645459358187100002 Content-Type: text/plain; charset="utf-8" This option makes qmp_shell exit (with error code 1) as soon as one of the following error occurs: + command parsing error + disconnection + command failure (response is an error) _execute_cmd() method now returns None or the response so that read_exec_command() can do the last check. This is meant to be used in combination with an input file redirection. It allows to store a list of commands into a file and try to run them by qmp_shell and easily see if it failed or not. Signed-off-by: Damien Hedde --- python/qemu/aqmp/qmp_shell.py | 39 +++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/python/qemu/aqmp/qmp_shell.py b/python/qemu/aqmp/qmp_shell.py index cce7732ba2..dd38ef8a13 100644 --- a/python/qemu/aqmp/qmp_shell.py +++ b/python/qemu/aqmp/qmp_shell.py @@ -11,7 +11,7 @@ """ Low-level QEMU shell on top of QMP. =20 -usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server +usage: qmp-shell [-h] [-H] [-N] [-v] [-p] [-e] qmp_server =20 positional arguments: qmp_server < UNIX socket path | TCP address:port > @@ -23,6 +23,8 @@ Skip negotiate (for qemu-ga) -v, --verbose Verbose (echo commands sent and received) -p, --pretty Pretty-print JSON + -e, --exit-on-error Exit when an error occurs (command parsing, + disconnection and command failure) =20 =20 Start QEMU with: @@ -177,9 +179,11 @@ class QMPShell(QEMUMonitorProtocol): :param address: Address of the QMP server. :param pretty: Pretty-print QMP messages. :param verbose: Echo outgoing QMP messages to console. + :param raise_error: Don't continue after an error occured """ def __init__(self, address: SocketAddrT, - pretty: bool =3D False, verbose: bool =3D False): + pretty: bool =3D False, verbose: bool =3D False, + raise_error: bool =3D False): super().__init__(address) self._greeting: Optional[QMPMessage] =3D None self._completer =3D QMPCompleter() @@ -189,6 +193,7 @@ def __init__(self, address: SocketAddrT, '.qmp-shell_history') self.pretty =3D pretty self.verbose =3D verbose + self.raise_error =3D raise_error =20 def close(self) -> None: # Hook into context manager of parent to save shell history. @@ -343,19 +348,19 @@ def _print_parse_error(self, err: QMPShellParseError)= -> None: file=3Dsys.stderr ) =20 - def _execute_cmd(self, cmdline: str) -> bool: + def _execute_cmd(self, cmdline: str) -> Optional[QMPMessage]: qmpcmd =3D self._build_cmd(cmdline) =20 # For transaction mode, we may have just cached the action: if qmpcmd is None: - return True + return None if self.verbose: self._print(qmpcmd) resp =3D self.cmd_obj(qmpcmd) if resp is None: raise QMPShellConnectError('Disconnected') self._print(resp) - return True + return resp =20 def connect(self, negotiate: bool =3D True) -> None: self._greeting =3D super().connect(negotiate) @@ -401,8 +406,13 @@ def read_exec_command(self) -> bool: print(event) return True =20 + if self.raise_error: + resp =3D self._execute_cmd(cmdline) + if resp and 'error' in resp: + raise QMPShellError(f"Command failed: {resp['error']}") + return True try: - return self._execute_cmd(cmdline) + self._execute_cmd(cmdline) except QMPShellParseError as err: self._print_parse_error(err) except QMPShellConnectError as err: @@ -477,7 +487,7 @@ def _cmd_passthrough(self, cmdline: str, def _print_parse_error(self, err: QMPShellParseError) -> None: print(f"{err!s}") =20 - def _execute_cmd(self, cmdline: str) -> bool: + def _execute_cmd(self, cmdline: str) -> Optional[QMPMessage]: if cmdline.split()[0] =3D=3D "cpu": # trap the cpu command, it requires special setting try: @@ -498,7 +508,7 @@ def _execute_cmd(self, cmdline: str) -> bool: else: # Error print('%s: %s' % (resp['error']['class'], resp['error']['desc'= ])) - return True + return resp =20 def show_banner(self, msg: str =3D 'Welcome to the HMP shell!') -> Non= e: QMPShell.show_banner(self, msg) @@ -523,6 +533,9 @@ def main() -> None: help=3D'Verbose (echo commands sent and received)') parser.add_argument('-p', '--pretty', action=3D'store_true', help=3D'Pretty-print JSON') + parser.add_argument('-e', '--exit-on-error', action=3D'store_true', + help=3D'Exit when an error occurs (command parsing= ,' + ' disconnection and command failure)') =20 default_server =3D os.environ.get('QMP_SOCKET') parser.add_argument('qmp_server', action=3D'store', @@ -541,7 +554,8 @@ def main() -> None: parser.error(f"Bad port number: {args.qmp_server}") return # pycharm doesn't know error() is noreturn =20 - with shell_class(address, args.pretty, args.verbose) as qemu: + with shell_class(address, args.pretty, args.verbose, + args.exit_on_error) as qemu: try: qemu.connect(negotiate=3Dnot args.skip_negotiation) except ConnectError as err: @@ -549,8 +563,11 @@ def main() -> None: die(f"Couldn't connect to {args.qmp_server}: {err!s}") die(str(err)) =20 - for _ in qemu.repl(): - pass + try: + for _ in qemu.repl(): + pass + except QMPShellError as err: + die(str(err)) =20 =20 if __name__ =3D=3D '__main__': --=20 2.35.1