]>
git.proxmox.com Git - mirror_qemu.git/blob - python/qemu/qmp.py
1 """ QEMU Monitor Protocol Python class """
2 # Copyright (C) 2009, 2010 Red Hat Inc.
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.
24 from types
import TracebackType
27 # QMPMessage is a QMP Message of any kind.
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]
38 SocketAddrT
= Union
[InternetAddrT
, UnixAddrT
]
41 class QMPError(Exception):
47 class QMPConnectError(QMPError
):
49 QMP connection exception
53 class QMPCapabilitiesError(QMPError
):
55 QMP negotiate capabilities exception
59 class QMPTimeoutError(QMPError
):
65 class QMPProtocolError(QMPError
):
67 QMP protocol error; unexpected response
71 class QMPResponseError(QMPError
):
73 Represents erroneous QMP monitor reply
75 def __init__(self
, reply
: QMPMessage
):
77 desc
= reply
['error']['desc']
80 super().__init
__(desc
)
84 class QEMUMonitorProtocol
:
86 Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
87 allow to handle commands and events.
90 #: Logger object for debugging messages
91 logger
= logging
.getLogger('QMP')
93 def __init__(self
, address
, server
=False, nickname
=None):
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
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
106 self
.__address
= address
107 self
.__sock
= self
.__get
_sock
()
108 self
.__sockfile
: Optional
[TextIO
] = None
109 self
._nickname
= nickname
111 self
.logger
= logging
.getLogger('QMP').getChild(self
._nickname
)
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
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
:
132 raise QMPCapabilitiesError
134 def __json_read(self
, only_event
=False):
135 assert self
.__sockfile
is not None
137 data
= self
.__sockfile
.readline()
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
)
145 self
.logger
.debug("<<< %s", resp
)
146 self
.__events
.append(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
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)
168 except OSError as err
:
169 if err
.errno
== errno
.EAGAIN
:
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
)
180 ret
= self
.__json
_read
(only_event
=True)
181 except socket
.timeout
:
182 raise QMPTimeoutError("Timeout waiting for event")
184 raise QMPConnectError("Error while reading from socket")
186 raise QMPConnectError("Error while reading from socket")
187 self
.__sock
.settimeout(None)
190 # Implement context manager enter function.
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.
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')
214 return self
.__negotiate
_capabilities
()
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
()
249 raise QMPConnectError("Unexpected empty reply from server")
250 self
.logger
.debug("<<< %s", 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
}
263 qmp_cmd
['arguments'] = args
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
)
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
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
)
298 return self
.__events
.pop(0)
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
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
)
318 def clear_events(self
):
320 Clear current list of pending events.
326 Close the socket and socket file.
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