You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
9.4KB

  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. import json
  10. import errno
  11. import socket
  12. import logging
  13. class QMPError(Exception):
  14. """
  15. QMP base exception
  16. """
  17. class QMPConnectError(QMPError):
  18. """
  19. QMP connection exception
  20. """
  21. class QMPCapabilitiesError(QMPError):
  22. """
  23. QMP negotiate capabilities exception
  24. """
  25. class QMPTimeoutError(QMPError):
  26. """
  27. QMP timeout exception
  28. """
  29. class QEMUMonitorProtocol:
  30. """
  31. Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
  32. allow to handle commands and events.
  33. """
  34. #: Logger object for debugging messages
  35. logger = logging.getLogger('QMP')
  36. def __init__(self, address, server=False):
  37. """
  38. Create a QEMUMonitorProtocol class.
  39. @param address: QEMU address, can be either a unix socket path (string)
  40. or a tuple in the form ( address, port ) for a TCP
  41. connection
  42. @param server: server mode listens on the socket (bool)
  43. @raise OSError on socket connection errors
  44. @note No connection is established, this is done by the connect() or
  45. accept() methods
  46. """
  47. self.__events = []
  48. self.__address = address
  49. self.__sock = self.__get_sock()
  50. self.__sockfile = None
  51. if server:
  52. self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  53. self.__sock.bind(self.__address)
  54. self.__sock.listen(1)
  55. def __get_sock(self):
  56. if isinstance(self.__address, tuple):
  57. family = socket.AF_INET
  58. else:
  59. family = socket.AF_UNIX
  60. return socket.socket(family, socket.SOCK_STREAM)
  61. def __negotiate_capabilities(self):
  62. greeting = self.__json_read()
  63. if greeting is None or "QMP" not in greeting:
  64. raise QMPConnectError
  65. # Greeting seems ok, negotiate capabilities
  66. resp = self.cmd('qmp_capabilities')
  67. if resp and "return" in resp:
  68. return greeting
  69. raise QMPCapabilitiesError
  70. def __json_read(self, only_event=False):
  71. while True:
  72. data = self.__sockfile.readline()
  73. if not data:
  74. return None
  75. resp = json.loads(data)
  76. if 'event' in resp:
  77. self.logger.debug("<<< %s", resp)
  78. self.__events.append(resp)
  79. if not only_event:
  80. continue
  81. return resp
  82. def __get_events(self, wait=False):
  83. """
  84. Check for new events in the stream and cache them in __events.
  85. @param wait (bool): block until an event is available.
  86. @param wait (float): If wait is a float, treat it as a timeout value.
  87. @raise QMPTimeoutError: If a timeout float is provided and the timeout
  88. period elapses.
  89. @raise QMPConnectError: If wait is True but no events could be
  90. retrieved or if some other error occurred.
  91. """
  92. # Check for new events regardless and pull them into the cache:
  93. self.__sock.setblocking(0)
  94. try:
  95. self.__json_read()
  96. except OSError as err:
  97. if err.errno == errno.EAGAIN:
  98. # No data available
  99. pass
  100. self.__sock.setblocking(1)
  101. # Wait for new events, if needed.
  102. # if wait is 0.0, this means "no wait" and is also implicitly false.
  103. if not self.__events and wait:
  104. if isinstance(wait, float):
  105. self.__sock.settimeout(wait)
  106. try:
  107. ret = self.__json_read(only_event=True)
  108. except socket.timeout:
  109. raise QMPTimeoutError("Timeout waiting for event")
  110. except:
  111. raise QMPConnectError("Error while reading from socket")
  112. if ret is None:
  113. raise QMPConnectError("Error while reading from socket")
  114. self.__sock.settimeout(None)
  115. def __enter__(self):
  116. # Implement context manager enter function.
  117. return self
  118. def __exit__(self, exc_type, exc_value, exc_traceback):
  119. # Implement context manager exit function.
  120. self.close()
  121. return False
  122. def connect(self, negotiate=True):
  123. """
  124. Connect to the QMP Monitor and perform capabilities negotiation.
  125. @return QMP greeting dict, or None if negotiate is false
  126. @raise OSError on socket connection errors
  127. @raise QMPConnectError if the greeting is not received
  128. @raise QMPCapabilitiesError if fails to negotiate capabilities
  129. """
  130. self.__sock.connect(self.__address)
  131. self.__sockfile = self.__sock.makefile()
  132. if negotiate:
  133. return self.__negotiate_capabilities()
  134. return None
  135. def accept(self, timeout=15.0):
  136. """
  137. Await connection from QMP Monitor and perform capabilities negotiation.
  138. @param timeout: timeout in seconds (nonnegative float number, or
  139. None). The value passed will set the behavior of the
  140. underneath QMP socket as described in [1]. Default value
  141. is set to 15.0.
  142. @return QMP greeting dict
  143. @raise OSError on socket connection errors
  144. @raise QMPConnectError if the greeting is not received
  145. @raise QMPCapabilitiesError if fails to negotiate capabilities
  146. [1]
  147. https://docs.python.org/3/library/socket.html#socket.socket.settimeout
  148. """
  149. self.__sock.settimeout(timeout)
  150. self.__sock, _ = self.__sock.accept()
  151. self.__sockfile = self.__sock.makefile()
  152. return self.__negotiate_capabilities()
  153. def cmd_obj(self, qmp_cmd):
  154. """
  155. Send a QMP command to the QMP Monitor.
  156. @param qmp_cmd: QMP command to be sent as a Python dict
  157. @return QMP response as a Python dict or None if the connection has
  158. been closed
  159. """
  160. self.logger.debug(">>> %s", qmp_cmd)
  161. try:
  162. self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
  163. except OSError as err:
  164. if err.errno == errno.EPIPE:
  165. return None
  166. raise err
  167. resp = self.__json_read()
  168. self.logger.debug("<<< %s", resp)
  169. return resp
  170. def cmd(self, name, args=None, cmd_id=None):
  171. """
  172. Build a QMP command and send it to the QMP Monitor.
  173. @param name: command name (string)
  174. @param args: command arguments (dict)
  175. @param cmd_id: command id (dict, list, string or int)
  176. """
  177. qmp_cmd = {'execute': name}
  178. if args:
  179. qmp_cmd['arguments'] = args
  180. if cmd_id:
  181. qmp_cmd['id'] = cmd_id
  182. return self.cmd_obj(qmp_cmd)
  183. def command(self, cmd, **kwds):
  184. """
  185. Build and send a QMP command to the monitor, report errors if any
  186. """
  187. ret = self.cmd(cmd, kwds)
  188. if "error" in ret:
  189. raise Exception(ret['error']['desc'])
  190. return ret['return']
  191. def pull_event(self, wait=False):
  192. """
  193. Pulls a single event.
  194. @param wait (bool): block until an event is available.
  195. @param wait (float): If wait is a float, treat it as a timeout value.
  196. @raise QMPTimeoutError: If a timeout float is provided and the timeout
  197. period elapses.
  198. @raise QMPConnectError: If wait is True but no events could be
  199. retrieved or if some other error occurred.
  200. @return The first available QMP event, or None.
  201. """
  202. self.__get_events(wait)
  203. if self.__events:
  204. return self.__events.pop(0)
  205. return None
  206. def get_events(self, wait=False):
  207. """
  208. Get a list of available QMP events.
  209. @param wait (bool): block until an event is available.
  210. @param wait (float): If wait is a float, treat it as a timeout value.
  211. @raise QMPTimeoutError: If a timeout float is provided and the timeout
  212. period elapses.
  213. @raise QMPConnectError: If wait is True but no events could be
  214. retrieved or if some other error occurred.
  215. @return The list of available QMP events.
  216. """
  217. self.__get_events(wait)
  218. return self.__events
  219. def clear_events(self):
  220. """
  221. Clear current list of pending events.
  222. """
  223. self.__events = []
  224. def close(self):
  225. """
  226. Close the socket and socket file.
  227. """
  228. if self.__sock:
  229. self.__sock.close()
  230. if self.__sockfile:
  231. self.__sockfile.close()
  232. def settimeout(self, timeout):
  233. """
  234. Set the socket timeout.
  235. @param timeout (float): timeout in seconds, or None.
  236. @note This is a wrap around socket.settimeout
  237. """
  238. self.__sock.settimeout(timeout)
  239. def get_sock_fd(self):
  240. """
  241. Get the socket file descriptor.
  242. @return The file descriptor number.
  243. """
  244. return self.__sock.fileno()
  245. def is_scm_available(self):
  246. """
  247. Check if the socket allows for SCM_RIGHTS.
  248. @return True if SCM_RIGHTS is available, otherwise False.
  249. """
  250. return self.__sock.family == socket.AF_UNIX