tests/acceptance: skip s390x_ccw_vrtio_tcg on GitLab
[qemu.git] / python / qemu / qmp.py
1 """ QEMU Monitor Protocol Python class """
2 # Copyright (C) 2009, 2010 Red Hat Inc.
3 #
4 # Authors:
5 # Luiz Capitulino <lcapitulino@redhat.com>
6 #
7 # This work is licensed under the terms of the GNU GPL, version 2. See
8 # the COPYING file in the top-level directory.
9
10 import json
11 import errno
12 import socket
13 import logging
14 from typing import (
15 Optional,
16 TextIO,
17 Type,
18 )
19 from types import TracebackType
20
21
22 class QMPError(Exception):
23 """
24 QMP base exception
25 """
26
27
28 class QMPConnectError(QMPError):
29 """
30 QMP connection exception
31 """
32
33
34 class QMPCapabilitiesError(QMPError):
35 """
36 QMP negotiate capabilities exception
37 """
38
39
40 class QMPTimeoutError(QMPError):
41 """
42 QMP timeout exception
43 """
44
45
46 class QEMUMonitorProtocol:
47 """
48 Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
49 allow to handle commands and events.
50 """
51
52 #: Logger object for debugging messages
53 logger = logging.getLogger('QMP')
54
55 def __init__(self, address, server=False, nickname=None):
56 """
57 Create a QEMUMonitorProtocol class.
58
59 @param address: QEMU address, can be either a unix socket path (string)
60 or a tuple in the form ( address, port ) for a TCP
61 connection
62 @param server: server mode listens on the socket (bool)
63 @raise OSError on socket connection errors
64 @note No connection is established, this is done by the connect() or
65 accept() methods
66 """
67 self.__events = []
68 self.__address = address
69 self.__sock = self.__get_sock()
70 self.__sockfile: Optional[TextIO] = None
71 self._nickname = nickname
72 if self._nickname:
73 self.logger = logging.getLogger('QMP').getChild(self._nickname)
74 if server:
75 self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
76 self.__sock.bind(self.__address)
77 self.__sock.listen(1)
78
79 def __get_sock(self):
80 if isinstance(self.__address, tuple):
81 family = socket.AF_INET
82 else:
83 family = socket.AF_UNIX
84 return socket.socket(family, socket.SOCK_STREAM)
85
86 def __negotiate_capabilities(self):
87 greeting = self.__json_read()
88 if greeting is None or "QMP" not in greeting:
89 raise QMPConnectError
90 # Greeting seems ok, negotiate capabilities
91 resp = self.cmd('qmp_capabilities')
92 if resp and "return" in resp:
93 return greeting
94 raise QMPCapabilitiesError
95
96 def __json_read(self, only_event=False):
97 assert self.__sockfile is not None
98 while True:
99 data = self.__sockfile.readline()
100 if not data:
101 return None
102 resp = json.loads(data)
103 if 'event' in resp:
104 self.logger.debug("<<< %s", resp)
105 self.__events.append(resp)
106 if not only_event:
107 continue
108 return resp
109
110 def __get_events(self, wait=False):
111 """
112 Check for new events in the stream and cache them in __events.
113
114 @param wait (bool): block until an event is available.
115 @param wait (float): If wait is a float, treat it as a timeout value.
116
117 @raise QMPTimeoutError: If a timeout float is provided and the timeout
118 period elapses.
119 @raise QMPConnectError: If wait is True but no events could be
120 retrieved or if some other error occurred.
121 """
122
123 # Check for new events regardless and pull them into the cache:
124 self.__sock.setblocking(False)
125 try:
126 self.__json_read()
127 except OSError as err:
128 if err.errno == errno.EAGAIN:
129 # No data available
130 pass
131 self.__sock.setblocking(True)
132
133 # Wait for new events, if needed.
134 # if wait is 0.0, this means "no wait" and is also implicitly false.
135 if not self.__events and wait:
136 if isinstance(wait, float):
137 self.__sock.settimeout(wait)
138 try:
139 ret = self.__json_read(only_event=True)
140 except socket.timeout:
141 raise QMPTimeoutError("Timeout waiting for event")
142 except:
143 raise QMPConnectError("Error while reading from socket")
144 if ret is None:
145 raise QMPConnectError("Error while reading from socket")
146 self.__sock.settimeout(None)
147
148 def __enter__(self):
149 # Implement context manager enter function.
150 return self
151
152 def __exit__(self,
153 # pylint: disable=duplicate-code
154 # see https://github.com/PyCQA/pylint/issues/3619
155 exc_type: Optional[Type[BaseException]],
156 exc_val: Optional[BaseException],
157 exc_tb: Optional[TracebackType]) -> None:
158 # Implement context manager exit function.
159 self.close()
160
161 def connect(self, negotiate=True):
162 """
163 Connect to the QMP Monitor and perform capabilities negotiation.
164
165 @return QMP greeting dict, or None if negotiate is false
166 @raise OSError on socket connection errors
167 @raise QMPConnectError if the greeting is not received
168 @raise QMPCapabilitiesError if fails to negotiate capabilities
169 """
170 self.__sock.connect(self.__address)
171 self.__sockfile = self.__sock.makefile(mode='r')
172 if negotiate:
173 return self.__negotiate_capabilities()
174 return None
175
176 def accept(self, timeout=15.0):
177 """
178 Await connection from QMP Monitor and perform capabilities negotiation.
179
180 @param timeout: timeout in seconds (nonnegative float number, or
181 None). The value passed will set the behavior of the
182 underneath QMP socket as described in [1].
183 Default value is set to 15.0.
184 @return QMP greeting dict
185 @raise OSError on socket connection errors
186 @raise QMPConnectError if the greeting is not received
187 @raise QMPCapabilitiesError if fails to negotiate capabilities
188
189 [1]
190 https://docs.python.org/3/library/socket.html#socket.socket.settimeout
191 """
192 self.__sock.settimeout(timeout)
193 self.__sock, _ = self.__sock.accept()
194 self.__sockfile = self.__sock.makefile(mode='r')
195 return self.__negotiate_capabilities()
196
197 def cmd_obj(self, qmp_cmd):
198 """
199 Send a QMP command to the QMP Monitor.
200
201 @param qmp_cmd: QMP command to be sent as a Python dict
202 @return QMP response as a Python dict or None if the connection has
203 been closed
204 """
205 self.logger.debug(">>> %s", qmp_cmd)
206 try:
207 self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
208 except OSError as err:
209 if err.errno == errno.EPIPE:
210 return None
211 raise err
212 resp = self.__json_read()
213 self.logger.debug("<<< %s", resp)
214 return resp
215
216 def cmd(self, name, args=None, cmd_id=None):
217 """
218 Build a QMP command and send it to the QMP Monitor.
219
220 @param name: command name (string)
221 @param args: command arguments (dict)
222 @param cmd_id: command id (dict, list, string or int)
223 """
224 qmp_cmd = {'execute': name}
225 if args:
226 qmp_cmd['arguments'] = args
227 if cmd_id:
228 qmp_cmd['id'] = cmd_id
229 return self.cmd_obj(qmp_cmd)
230
231 def command(self, cmd, **kwds):
232 """
233 Build and send a QMP command to the monitor, report errors if any
234 """
235 ret = self.cmd(cmd, kwds)
236 if "error" in ret:
237 raise Exception(ret['error']['desc'])
238 return ret['return']
239
240 def pull_event(self, wait=False):
241 """
242 Pulls a single event.
243
244 @param wait (bool): block until an event is available.
245 @param wait (float): If wait is a float, treat it as a timeout value.
246
247 @raise QMPTimeoutError: If a timeout float is provided and the timeout
248 period elapses.
249 @raise QMPConnectError: If wait is True but no events could be
250 retrieved or if some other error occurred.
251
252 @return The first available QMP event, or None.
253 """
254 self.__get_events(wait)
255
256 if self.__events:
257 return self.__events.pop(0)
258 return None
259
260 def get_events(self, wait=False):
261 """
262 Get a list of available QMP events.
263
264 @param wait (bool): block until an event is available.
265 @param wait (float): If wait is a float, treat it as a timeout value.
266
267 @raise QMPTimeoutError: If a timeout float is provided and the timeout
268 period elapses.
269 @raise QMPConnectError: If wait is True but no events could be
270 retrieved or if some other error occurred.
271
272 @return The list of available QMP events.
273 """
274 self.__get_events(wait)
275 return self.__events
276
277 def clear_events(self):
278 """
279 Clear current list of pending events.
280 """
281 self.__events = []
282
283 def close(self):
284 """
285 Close the socket and socket file.
286 """
287 if self.__sock:
288 self.__sock.close()
289 if self.__sockfile:
290 self.__sockfile.close()
291
292 def settimeout(self, timeout):
293 """
294 Set the socket timeout.
295
296 @param timeout (float): timeout in seconds, or None.
297 @note This is a wrap around socket.settimeout
298 """
299 self.__sock.settimeout(timeout)
300
301 def get_sock_fd(self):
302 """
303 Get the socket file descriptor.
304
305 @return The file descriptor number.
306 """
307 return self.__sock.fileno()
308
309 def is_scm_available(self):
310 """
311 Check if the socket allows for SCM_RIGHTS.
312
313 @return True if SCM_RIGHTS is available, otherwise False.
314 """
315 return self.__sock.family == socket.AF_UNIX