Em Fri, 9 Aug 2024 00:41:37 +0200 Mauro Carvalho Chehab <mchehab+hua...@kernel.org> escreveu:
> > You should be able to use e.g. > > > > legacy.py's QEMUMonitorProtocol class for synchronous connections, e.g. > > > > from qemu.qmp.legacy import QEMUMonitorProtocol > > > > qmp = QEMUMonitorProtocol((host, port)) > > qmp.connect(negotiate=True) > > That sounds interesting! I give it a try. I applied the enclosed patch at the end of my patch series, but somehow it is not working. For whatever reason, connect() is raising a StateError apparently due to Runstate.CONNECTING. I tried both as declaring (see enclosed patch): class qmp(QEMUMonitorProtocol) and using: - super().__init__(self.host, self.port) + self.qmp_monitor = QEMUMonitorProtocol(self.host, self.port) On both cases, it keeps waiting forever for a connection. Regards, Mauro --- diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py index e9e9388bcb8b..62ca267cdc87 100644 --- a/scripts/qmp_helper.py +++ b/scripts/qmp_helper.py @@ -9,9 +9,23 @@ import socket import sys +from os import path + +try: + qemu_dir = path.abspath(path.dirname(path.dirname(__file__))) + sys.path.append(path.join(qemu_dir, 'python')) + + from qemu.qmp.legacy import QEMUMonitorProtocol + from qemu.qmp.protocol import StateError + +except ModuleNotFoundError as exc: + print(f"Module '{exc.name}' not found.") + print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir") + sys.exit(1) + from base64 import b64encode -class qmp: +class qmp(QEMUMonitorProtocol): """ Opens a connection and send/receive QMP commands. """ @@ -21,22 +35,20 @@ def send_cmd(self, command, may_open=False,return_error=True): if may_open: self._connect() - elif not self.socket: - return None + elif not self.connected: + return False if isinstance(command, dict): data = json.dumps(command).encode("utf-8") else: data = command.encode("utf-8") - self.socket.sendall(data) - data = self.socket.recv(1024) try: - obj = json.loads(data.decode("utf-8")) - except json.JSONDecodeError as e: - print(f"Invalid QMP answer: {e}") - self._close() - return None + obj = self.cmd_obj(command) + except Exception as e: + print("Failed to inject error: {e}.") + + print(obj) if "return" in obj: if isinstance(obj.get("return"), dict): @@ -46,86 +58,47 @@ def send_cmd(self, command, may_open=False,return_error=True): else: return obj["return"] - elif isinstance(obj.get("error"), dict): - error = obj["error"] - if return_error: - print(f'{error["class"]}: {error["desc"]}') - else: - print(json.dumps(obj)) - return None def _close(self): """Shutdown and close the socket, if opened""" - if not self.socket: + if not self.connected: return - self.socket.shutdown(socket.SHUT_WR) - while 1: - data = self.socket.recv(1024) - if data == b"": - break - try: - obj = json.loads(data.decode("utf-8")) - except json.JSONDecodeError as e: - print(f"Invalid QMP answer: {e}") - self.socket.close() - self.socket = None - return - - if isinstance(obj.get("return"), dict): - print(json.dumps(obj["return"])) - if isinstance(obj.get("error"), dict): - error = obj["error"] - print(f'{error["class"]}: {error["desc"]}') - else: - print(json.dumps(obj)) - - self.socket.close() - self.socket = None + self.close() + self.connected = False def _connect(self): """Connect to a QMP TCP/IP port, if not connected yet""" - if self.socket: + if self.connected: return True - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - self.socket.connect((self.host, self.port)) - except ConnectionRefusedError: - sys.exit(f"Can't connect to QMP host {self.host}:{self.port}") - - data = self.socket.recv(1024) - try: - obj = json.loads(data.decode("utf-8")) - except json.JSONDecodeError as e: - print(f"Invalid QMP answer: {e}") - self._close() - return False - - if "QMP" not in obj: - print(f"Invalid QMP answer: {data.decode('utf-8')}") - self._close() - return False + is_connecting = True + while is_connecting: + try: + ret = self.connect(negotiate=True) + self.accept() + is_connecting = False + except ConnectionError: + sys.exit(f"Can't connect to QMP host {self.host}:{self.port}") + return False + except StateError as e: + print(f"StateError: {e}") - result = self.send_cmd('{ "execute": "qmp_capabilities" }') - if not result: - self._close() - return False + self.connected = True return True def __init__(self, host, port, debug=False): """Initialize variables used by the QMP send logic""" - self.socket = None + self.connected = False self.host = host self.port = port self.debug = debug - def __del__(self): - self._close() + super().__init__(self.host, self.port) # # Socket QMP send command @@ -168,8 +141,12 @@ def send_cper(self, guid, data): self._connect() - if self.send_cmd(command): - print("Error injected.") + try: + self.cmd_obj(command) + except Exception as e: + print("Failed to inject error: {e}.") + + print("Error injected.") def search_qom(self, path, prop, regex): """ @@ -180,8 +157,9 @@ def search_qom(self, path, prop, regex): ... """ - found = [] + self._connect() + found = [] i = 0 while 1: dev = f"{path}[{i}]" @@ -192,7 +170,11 @@ def search_qom(self, path, prop, regex): 'property': prop } } - ret = self.send_cmd(cmd, may_open=True, return_error=False) + try: + ret = self.cmd_obj(cmd) + except Exception as e: + print("Failed to inject error: {e}.") + if not ret: break Thanks, Mauro