1beb6b57bSJohn Snow""" 2beb6b57bSJohn SnowQEMU qtest library 3beb6b57bSJohn Snow 4beb6b57bSJohn Snowqtest offers the QEMUQtestProtocol and QEMUQTestMachine classes, which 5beb6b57bSJohn Snowoffer a connection to QEMU's qtest protocol socket, and a qtest-enabled 6beb6b57bSJohn Snowsubclass of QEMUMachine, respectively. 7beb6b57bSJohn Snow""" 8beb6b57bSJohn Snow 9beb6b57bSJohn Snow# Copyright (C) 2015 Red Hat Inc. 10beb6b57bSJohn Snow# 11beb6b57bSJohn Snow# Authors: 12beb6b57bSJohn Snow# Fam Zheng <famz@redhat.com> 13beb6b57bSJohn Snow# 14beb6b57bSJohn Snow# This work is licensed under the terms of the GNU GPL, version 2. See 15beb6b57bSJohn Snow# the COPYING file in the top-level directory. 16beb6b57bSJohn Snow# 17beb6b57bSJohn Snow# Based on qmp.py. 18beb6b57bSJohn Snow# 19beb6b57bSJohn Snow 20beb6b57bSJohn Snowimport os 21beb6b57bSJohn Snowimport socket 22beb6b57bSJohn Snowfrom typing import ( 23beb6b57bSJohn Snow List, 24beb6b57bSJohn Snow Optional, 25beb6b57bSJohn Snow Sequence, 26beb6b57bSJohn Snow TextIO, 27beb6b57bSJohn Snow) 28beb6b57bSJohn Snow 2937094b6dSJohn Snowfrom qemu.qmp import SocketAddrT 30beb6b57bSJohn Snow 31beb6b57bSJohn Snowfrom .machine import QEMUMachine 32beb6b57bSJohn Snow 33beb6b57bSJohn Snow 34beb6b57bSJohn Snowclass QEMUQtestProtocol: 35beb6b57bSJohn Snow """ 36beb6b57bSJohn Snow QEMUQtestProtocol implements a connection to a qtest socket. 37beb6b57bSJohn Snow 38beb6b57bSJohn Snow :param address: QEMU address, can be either a unix socket path (string) 39beb6b57bSJohn Snow or a tuple in the form ( address, port ) for a TCP 40beb6b57bSJohn Snow connection 41beb6b57bSJohn Snow :param server: server mode, listens on the socket (bool) 42beb6b57bSJohn Snow :raise socket.error: on socket connection errors 43beb6b57bSJohn Snow 44beb6b57bSJohn Snow .. note:: 45*af76484eSDongdong Zhang No connection is established by __init__(), this is done 46beb6b57bSJohn Snow by the connect() or accept() methods. 47beb6b57bSJohn Snow """ 48beb6b57bSJohn Snow def __init__(self, address: SocketAddrT, 49beb6b57bSJohn Snow server: bool = False): 50beb6b57bSJohn Snow self._address = address 51beb6b57bSJohn Snow self._sock = self._get_sock() 52beb6b57bSJohn Snow self._sockfile: Optional[TextIO] = None 53beb6b57bSJohn Snow if server: 54beb6b57bSJohn Snow self._sock.bind(self._address) 55beb6b57bSJohn Snow self._sock.listen(1) 56beb6b57bSJohn Snow 57beb6b57bSJohn Snow def _get_sock(self) -> socket.socket: 58beb6b57bSJohn Snow if isinstance(self._address, tuple): 59beb6b57bSJohn Snow family = socket.AF_INET 60beb6b57bSJohn Snow else: 61beb6b57bSJohn Snow family = socket.AF_UNIX 62beb6b57bSJohn Snow return socket.socket(family, socket.SOCK_STREAM) 63beb6b57bSJohn Snow 64beb6b57bSJohn Snow def connect(self) -> None: 65beb6b57bSJohn Snow """ 66beb6b57bSJohn Snow Connect to the qtest socket. 67beb6b57bSJohn Snow 68beb6b57bSJohn Snow @raise socket.error on socket connection errors 69beb6b57bSJohn Snow """ 70beb6b57bSJohn Snow self._sock.connect(self._address) 71beb6b57bSJohn Snow self._sockfile = self._sock.makefile(mode='r') 72beb6b57bSJohn Snow 73beb6b57bSJohn Snow def accept(self) -> None: 74beb6b57bSJohn Snow """ 75beb6b57bSJohn Snow Await connection from QEMU. 76beb6b57bSJohn Snow 77beb6b57bSJohn Snow @raise socket.error on socket connection errors 78beb6b57bSJohn Snow """ 79beb6b57bSJohn Snow self._sock, _ = self._sock.accept() 80beb6b57bSJohn Snow self._sockfile = self._sock.makefile(mode='r') 81beb6b57bSJohn Snow 82beb6b57bSJohn Snow def cmd(self, qtest_cmd: str) -> str: 83beb6b57bSJohn Snow """ 84beb6b57bSJohn Snow Send a qtest command on the wire. 85beb6b57bSJohn Snow 86beb6b57bSJohn Snow @param qtest_cmd: qtest command text to be sent 87beb6b57bSJohn Snow """ 88beb6b57bSJohn Snow assert self._sockfile is not None 89beb6b57bSJohn Snow self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) 90beb6b57bSJohn Snow resp = self._sockfile.readline() 91beb6b57bSJohn Snow return resp 92beb6b57bSJohn Snow 93beb6b57bSJohn Snow def close(self) -> None: 94beb6b57bSJohn Snow """ 95beb6b57bSJohn Snow Close this socket. 96beb6b57bSJohn Snow """ 97beb6b57bSJohn Snow self._sock.close() 98beb6b57bSJohn Snow if self._sockfile: 99beb6b57bSJohn Snow self._sockfile.close() 100beb6b57bSJohn Snow self._sockfile = None 101beb6b57bSJohn Snow 102beb6b57bSJohn Snow def settimeout(self, timeout: Optional[float]) -> None: 103beb6b57bSJohn Snow """Set a timeout, in seconds.""" 104beb6b57bSJohn Snow self._sock.settimeout(timeout) 105beb6b57bSJohn Snow 106beb6b57bSJohn Snow 107beb6b57bSJohn Snowclass QEMUQtestMachine(QEMUMachine): 108beb6b57bSJohn Snow """ 109beb6b57bSJohn Snow A QEMU VM, with a qtest socket available. 110beb6b57bSJohn Snow """ 111beb6b57bSJohn Snow 112beb6b57bSJohn Snow def __init__(self, 113beb6b57bSJohn Snow binary: str, 114beb6b57bSJohn Snow args: Sequence[str] = (), 115804f7695SEmanuele Giuseppe Esposito wrapper: Sequence[str] = (), 116beb6b57bSJohn Snow name: Optional[str] = None, 117beb6b57bSJohn Snow base_temp_dir: str = "/var/tmp", 118e2f948a8SEmanuele Giuseppe Esposito sock_dir: Optional[str] = None, 119e2f948a8SEmanuele Giuseppe Esposito qmp_timer: Optional[float] = None): 12082e6517dSJohn Snow # pylint: disable=too-many-arguments 12182e6517dSJohn Snow 122beb6b57bSJohn Snow if name is None: 123beb6b57bSJohn Snow name = "qemu-%d" % os.getpid() 124beb6b57bSJohn Snow if sock_dir is None: 125beb6b57bSJohn Snow sock_dir = base_temp_dir 126804f7695SEmanuele Giuseppe Esposito super().__init__(binary, args, wrapper=wrapper, name=name, 127804f7695SEmanuele Giuseppe Esposito base_temp_dir=base_temp_dir, 128e2f948a8SEmanuele Giuseppe Esposito sock_dir=sock_dir, qmp_timer=qmp_timer) 129beb6b57bSJohn Snow self._qtest: Optional[QEMUQtestProtocol] = None 130beb6b57bSJohn Snow self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") 131beb6b57bSJohn Snow 132beb6b57bSJohn Snow @property 133beb6b57bSJohn Snow def _base_args(self) -> List[str]: 134beb6b57bSJohn Snow args = super()._base_args 135beb6b57bSJohn Snow args.extend([ 136beb6b57bSJohn Snow '-qtest', f"unix:path={self._qtest_path}", 137beb6b57bSJohn Snow '-accel', 'qtest' 138beb6b57bSJohn Snow ]) 139beb6b57bSJohn Snow return args 140beb6b57bSJohn Snow 141beb6b57bSJohn Snow def _pre_launch(self) -> None: 142beb6b57bSJohn Snow super()._pre_launch() 143beb6b57bSJohn Snow self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) 144beb6b57bSJohn Snow 145beb6b57bSJohn Snow def _post_launch(self) -> None: 146beb6b57bSJohn Snow assert self._qtest is not None 147beb6b57bSJohn Snow super()._post_launch() 148beb6b57bSJohn Snow self._qtest.accept() 149beb6b57bSJohn Snow 150beb6b57bSJohn Snow def _post_shutdown(self) -> None: 151beb6b57bSJohn Snow super()._post_shutdown() 152beb6b57bSJohn Snow self._remove_if_exists(self._qtest_path) 153beb6b57bSJohn Snow 154beb6b57bSJohn Snow def qtest(self, cmd: str) -> str: 155beb6b57bSJohn Snow """ 156beb6b57bSJohn Snow Send a qtest command to the guest. 157beb6b57bSJohn Snow 158beb6b57bSJohn Snow :param cmd: qtest command to send 159beb6b57bSJohn Snow :return: qtest server response 160beb6b57bSJohn Snow """ 161beb6b57bSJohn Snow if self._qtest is None: 162beb6b57bSJohn Snow raise RuntimeError("qtest socket not available") 163beb6b57bSJohn Snow return self._qtest.cmd(cmd) 164