hw/dma: Add SiFive platform DMA controller emulation
[qemu/ar7.git] / python / qemu / qmp.py
blob7935dababbf62b4d025f3ce6c1e010d95c8badd6
1 """ QEMU Monitor Protocol Python class """
2 # Copyright (C) 2009, 2010 Red Hat Inc.
4 # Authors:
5 # Luiz Capitulino <lcapitulino@redhat.com>
7 # This work is licensed under the terms of the GNU GPL, version 2. See
8 # the COPYING file in the top-level directory.
10 import json
11 import errno
12 import socket
13 import logging
14 from typing import (
15 Any,
16 cast,
17 Dict,
18 Optional,
19 TextIO,
20 Type,
21 Tuple,
22 Union,
24 from types import TracebackType
27 # QMPMessage is a QMP Message of any kind.
28 # e.g. {'yee': 'haw'}
30 # QMPReturnValue is the inner value of return values only.
31 # {'return': {}} is the QMPMessage,
32 # {} is the QMPReturnValue.
33 QMPMessage = Dict[str, Any]
34 QMPReturnValue = Dict[str, Any]
36 InternetAddrT = Tuple[str, str]
37 UnixAddrT = str
38 SocketAddrT = Union[InternetAddrT, UnixAddrT]
41 class QMPError(Exception):
42 """
43 QMP base exception
44 """
47 class QMPConnectError(QMPError):
48 """
49 QMP connection exception
50 """
53 class QMPCapabilitiesError(QMPError):
54 """
55 QMP negotiate capabilities exception
56 """
59 class QMPTimeoutError(QMPError):
60 """
61 QMP timeout exception
62 """
65 class QMPProtocolError(QMPError):
66 """
67 QMP protocol error; unexpected response
68 """
71 class QMPResponseError(QMPError):
72 """
73 Represents erroneous QMP monitor reply
74 """
75 def __init__(self, reply: QMPMessage):
76 try:
77 desc = reply['error']['desc']
78 except KeyError:
79 desc = reply
80 super().__init__(desc)
81 self.reply = reply
84 class QEMUMonitorProtocol:
85 """
86 Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
87 allow to handle commands and events.
88 """
90 #: Logger object for debugging messages
91 logger = logging.getLogger('QMP')
93 def __init__(self, address, server=False, nickname=None):
94 """
95 Create a QEMUMonitorProtocol class.
97 @param address: QEMU address, can be either a unix socket path (string)
98 or a tuple in the form ( address, port ) for a TCP
99 connection
100 @param server: server mode listens on the socket (bool)
101 @raise OSError on socket connection errors
102 @note No connection is established, this is done by the connect() or
103 accept() methods
105 self.__events = []
106 self.__address = address
107 self.__sock = self.__get_sock()
108 self.__sockfile: Optional[TextIO] = None
109 self._nickname = nickname
110 if self._nickname:
111 self.logger = logging.getLogger('QMP').getChild(self._nickname)
112 if server:
113 self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
114 self.__sock.bind(self.__address)
115 self.__sock.listen(1)
117 def __get_sock(self):
118 if isinstance(self.__address, tuple):
119 family = socket.AF_INET
120 else:
121 family = socket.AF_UNIX
122 return socket.socket(family, socket.SOCK_STREAM)
124 def __negotiate_capabilities(self):
125 greeting = self.__json_read()
126 if greeting is None or "QMP" not in greeting:
127 raise QMPConnectError
128 # Greeting seems ok, negotiate capabilities
129 resp = self.cmd('qmp_capabilities')
130 if resp and "return" in resp:
131 return greeting
132 raise QMPCapabilitiesError
134 def __json_read(self, only_event=False):
135 assert self.__sockfile is not None
136 while True:
137 data = self.__sockfile.readline()
138 if not data:
139 return None
140 # By definition, any JSON received from QMP is a QMPMessage,
141 # and we are asserting only at static analysis time that it
142 # has a particular shape.
143 resp: QMPMessage = json.loads(data)
144 if 'event' in resp:
145 self.logger.debug("<<< %s", resp)
146 self.__events.append(resp)
147 if not only_event:
148 continue
149 return resp
151 def __get_events(self, wait=False):
153 Check for new events in the stream and cache them in __events.
155 @param wait (bool): block until an event is available.
156 @param wait (float): If wait is a float, treat it as a timeout value.
158 @raise QMPTimeoutError: If a timeout float is provided and the timeout
159 period elapses.
160 @raise QMPConnectError: If wait is True but no events could be
161 retrieved or if some other error occurred.
164 # Check for new events regardless and pull them into the cache:
165 self.__sock.setblocking(False)
166 try:
167 self.__json_read()
168 except OSError as err:
169 if err.errno == errno.EAGAIN:
170 # No data available
171 pass
172 self.__sock.setblocking(True)
174 # Wait for new events, if needed.
175 # if wait is 0.0, this means "no wait" and is also implicitly false.
176 if not self.__events and wait:
177 if isinstance(wait, float):
178 self.__sock.settimeout(wait)
179 try:
180 ret = self.__json_read(only_event=True)
181 except socket.timeout:
182 raise QMPTimeoutError("Timeout waiting for event")
183 except:
184 raise QMPConnectError("Error while reading from socket")
185 if ret is None:
186 raise QMPConnectError("Error while reading from socket")
187 self.__sock.settimeout(None)
189 def __enter__(self):
190 # Implement context manager enter function.
191 return self
193 def __exit__(self,
194 # pylint: disable=duplicate-code
195 # see https://github.com/PyCQA/pylint/issues/3619
196 exc_type: Optional[Type[BaseException]],
197 exc_val: Optional[BaseException],
198 exc_tb: Optional[TracebackType]) -> None:
199 # Implement context manager exit function.
200 self.close()
202 def connect(self, negotiate=True):
204 Connect to the QMP Monitor and perform capabilities negotiation.
206 @return QMP greeting dict, or None if negotiate is false
207 @raise OSError on socket connection errors
208 @raise QMPConnectError if the greeting is not received
209 @raise QMPCapabilitiesError if fails to negotiate capabilities
211 self.__sock.connect(self.__address)
212 self.__sockfile = self.__sock.makefile(mode='r')
213 if negotiate:
214 return self.__negotiate_capabilities()
215 return None
217 def accept(self, timeout=15.0):
219 Await connection from QMP Monitor and perform capabilities negotiation.
221 @param timeout: timeout in seconds (nonnegative float number, or
222 None). The value passed will set the behavior of the
223 underneath QMP socket as described in [1].
224 Default value is set to 15.0.
225 @return QMP greeting dict
226 @raise OSError on socket connection errors
227 @raise QMPConnectError if the greeting is not received
228 @raise QMPCapabilitiesError if fails to negotiate capabilities
231 https://docs.python.org/3/library/socket.html#socket.socket.settimeout
233 self.__sock.settimeout(timeout)
234 self.__sock, _ = self.__sock.accept()
235 self.__sockfile = self.__sock.makefile(mode='r')
236 return self.__negotiate_capabilities()
238 def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
240 Send a QMP command to the QMP Monitor.
242 @param qmp_cmd: QMP command to be sent as a Python dict
243 @return QMP response as a Python dict
245 self.logger.debug(">>> %s", qmp_cmd)
246 self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
247 resp = self.__json_read()
248 if resp is None:
249 raise QMPConnectError("Unexpected empty reply from server")
250 self.logger.debug("<<< %s", resp)
251 return resp
253 def cmd(self, name, args=None, cmd_id=None):
255 Build a QMP command and send it to the QMP Monitor.
257 @param name: command name (string)
258 @param args: command arguments (dict)
259 @param cmd_id: command id (dict, list, string or int)
261 qmp_cmd = {'execute': name}
262 if args:
263 qmp_cmd['arguments'] = args
264 if cmd_id:
265 qmp_cmd['id'] = cmd_id
266 return self.cmd_obj(qmp_cmd)
268 def command(self, cmd, **kwds):
270 Build and send a QMP command to the monitor, report errors if any
272 ret = self.cmd(cmd, kwds)
273 if 'error' in ret:
274 raise QMPResponseError(ret)
275 if 'return' not in ret:
276 raise QMPProtocolError(
277 "'return' key not found in QMP response '{}'".format(str(ret))
279 return cast(QMPReturnValue, ret['return'])
281 def pull_event(self, wait=False):
283 Pulls a single event.
285 @param wait (bool): block until an event is available.
286 @param wait (float): If wait is a float, treat it as a timeout value.
288 @raise QMPTimeoutError: If a timeout float is provided and the timeout
289 period elapses.
290 @raise QMPConnectError: If wait is True but no events could be
291 retrieved or if some other error occurred.
293 @return The first available QMP event, or None.
295 self.__get_events(wait)
297 if self.__events:
298 return self.__events.pop(0)
299 return None
301 def get_events(self, wait=False):
303 Get a list of available QMP events.
305 @param wait (bool): block until an event is available.
306 @param wait (float): If wait is a float, treat it as a timeout value.
308 @raise QMPTimeoutError: If a timeout float is provided and the timeout
309 period elapses.
310 @raise QMPConnectError: If wait is True but no events could be
311 retrieved or if some other error occurred.
313 @return The list of available QMP events.
315 self.__get_events(wait)
316 return self.__events
318 def clear_events(self):
320 Clear current list of pending events.
322 self.__events = []
324 def close(self):
326 Close the socket and socket file.
328 if self.__sock:
329 self.__sock.close()
330 if self.__sockfile:
331 self.__sockfile.close()
333 def settimeout(self, timeout):
335 Set the socket timeout.
337 @param timeout (float): timeout in seconds, or None.
338 @note This is a wrap around socket.settimeout
340 self.__sock.settimeout(timeout)
342 def get_sock_fd(self):
344 Get the socket file descriptor.
346 @return The file descriptor number.
348 return self.__sock.fileno()
350 def is_scm_available(self):
352 Check if the socket allows for SCM_RIGHTS.
354 @return True if SCM_RIGHTS is available, otherwise False.
356 return self.__sock.family == socket.AF_UNIX