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