Loading src/balloon.py +27 −3 Original line number Diff line number Diff line Loading @@ -213,9 +213,20 @@ async def _get_host_qemu_guest_mem_rss(qmp: QMPClient, qemu_pid: int) -> Optiona # QMP helpers using qemu.qmp # ========================================================== async def qmp_wait_connected(sock_path: str, interval: int = 5) -> QMPClient: """Create and connect a QMPClient, retrying until successful.""" def _is_process_alive(pid: int) -> bool: """Check if a process with the given PID is still running.""" try: os.kill(pid, 0) return True except (ProcessLookupError, OSError): return False async def qmp_wait_connected(sock_path: str, interval: int = 5, qemu_pid: int = -1) -> QMPClient: """Create and connect a QMPClient, retrying until successful or QEMU exits.""" while True: if qemu_pid > 0 and not _is_process_alive(qemu_pid): raise ConnectionError("QEMU process (pid %d) is no longer running" % qemu_pid) qmp = QMPClient("balloon-monitor") try: await qmp.connect(sock_path) Loading Loading @@ -367,7 +378,7 @@ class BalloonMonitor: ) async def _qmp_connect(self) -> None: self.qmp = await qmp_wait_connected(self.args.qmp_sock, self.args.interval) self.qmp = await qmp_wait_connected(self.args.qmp_sock, self.args.interval, self.qemu_pid) self.event_task = asyncio.create_task(self._qmp_event_listener()) self._record_balloon(await qmp_get_actual_balloon(self.qmp)) Loading @@ -389,6 +400,10 @@ class BalloonMonitor: log.debug("QMP event listener stopped: %s", e) async def _qmp_reconnect(self) -> None: if self.qemu_pid > 0 and not _is_process_alive(self.qemu_pid): log.info("QEMU process (pid %d) has exited, shutting down.", self.qemu_pid) self._stop.set() return if self.qmp: try: await self.qmp.disconnect() Loading Loading @@ -759,6 +774,9 @@ def main() -> None: datefmt="%Y-%m-%d %H:%M:%S", ) # Suppress noisy qemu.qmp internal messages (e.g. "end-of-file") unless debug is requested logging.getLogger("qemu.qmp").setLevel(logging.WARNING) if args.debug is not None: targets = {t.strip() for t in args.debug.split(",")} if "all" in targets: Loading @@ -778,3 +796,9 @@ if __name__ == "__main__": except KeyboardInterrupt: log.debug("Monitor stopped by user.") sys.exit(0) except (ConnectionError, BrokenPipeError, OSError) as e: log.info("Monitor exiting: %s", e) sys.exit(0) except Exception as e: log.error("Monitor terminated unexpectedly: %s", e, exc_info=e) sys.exit(1) Loading
src/balloon.py +27 −3 Original line number Diff line number Diff line Loading @@ -213,9 +213,20 @@ async def _get_host_qemu_guest_mem_rss(qmp: QMPClient, qemu_pid: int) -> Optiona # QMP helpers using qemu.qmp # ========================================================== async def qmp_wait_connected(sock_path: str, interval: int = 5) -> QMPClient: """Create and connect a QMPClient, retrying until successful.""" def _is_process_alive(pid: int) -> bool: """Check if a process with the given PID is still running.""" try: os.kill(pid, 0) return True except (ProcessLookupError, OSError): return False async def qmp_wait_connected(sock_path: str, interval: int = 5, qemu_pid: int = -1) -> QMPClient: """Create and connect a QMPClient, retrying until successful or QEMU exits.""" while True: if qemu_pid > 0 and not _is_process_alive(qemu_pid): raise ConnectionError("QEMU process (pid %d) is no longer running" % qemu_pid) qmp = QMPClient("balloon-monitor") try: await qmp.connect(sock_path) Loading Loading @@ -367,7 +378,7 @@ class BalloonMonitor: ) async def _qmp_connect(self) -> None: self.qmp = await qmp_wait_connected(self.args.qmp_sock, self.args.interval) self.qmp = await qmp_wait_connected(self.args.qmp_sock, self.args.interval, self.qemu_pid) self.event_task = asyncio.create_task(self._qmp_event_listener()) self._record_balloon(await qmp_get_actual_balloon(self.qmp)) Loading @@ -389,6 +400,10 @@ class BalloonMonitor: log.debug("QMP event listener stopped: %s", e) async def _qmp_reconnect(self) -> None: if self.qemu_pid > 0 and not _is_process_alive(self.qemu_pid): log.info("QEMU process (pid %d) has exited, shutting down.", self.qemu_pid) self._stop.set() return if self.qmp: try: await self.qmp.disconnect() Loading Loading @@ -759,6 +774,9 @@ def main() -> None: datefmt="%Y-%m-%d %H:%M:%S", ) # Suppress noisy qemu.qmp internal messages (e.g. "end-of-file") unless debug is requested logging.getLogger("qemu.qmp").setLevel(logging.WARNING) if args.debug is not None: targets = {t.strip() for t in args.debug.split(",")} if "all" in targets: Loading @@ -778,3 +796,9 @@ if __name__ == "__main__": except KeyboardInterrupt: log.debug("Monitor stopped by user.") sys.exit(0) except (ConnectionError, BrokenPipeError, OSError) as e: log.info("Monitor exiting: %s", e) sys.exit(0) except Exception as e: log.error("Monitor terminated unexpectedly: %s", e, exc_info=e) sys.exit(1)