Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 1 | # QEMU Monitor Protocol Python class |
Luiz Capitulino | 22f3946 | 2013-09-10 16:39:23 -0400 | [diff] [blame] | 2 | # |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 3 | # Copyright (C) 2009, 2010 Red Hat Inc. |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 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 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 11 | import json |
| 12 | import errno |
| 13 | import socket |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 14 | |
| 15 | class QMPError(Exception): |
| 16 | pass |
| 17 | |
| 18 | class QMPConnectError(QMPError): |
| 19 | pass |
| 20 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 21 | class QMPCapabilitiesError(QMPError): |
| 22 | pass |
| 23 | |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 24 | class QEMUMonitorProtocol: |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 25 | def __init__(self, address, server=False): |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 26 | """ |
| 27 | Create a QEMUMonitorProtocol class. |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 28 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 29 | @param address: QEMU address, can be either a unix socket path (string) |
| 30 | or a tuple in the form ( address, port ) for a TCP |
| 31 | connection |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 32 | @param server: server mode listens on the socket (bool) |
| 33 | @raise socket.error on socket connection errors |
| 34 | @note No connection is established, this is done by the connect() or |
| 35 | accept() methods |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 36 | """ |
| 37 | self.__events = [] |
| 38 | self.__address = address |
| 39 | self.__sock = self.__get_sock() |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 40 | if server: |
| 41 | self.__sock.bind(self.__address) |
| 42 | self.__sock.listen(1) |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 43 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 44 | def __get_sock(self): |
| 45 | if isinstance(self.__address, tuple): |
| 46 | family = socket.AF_INET |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 47 | else: |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 48 | family = socket.AF_UNIX |
| 49 | return socket.socket(family, socket.SOCK_STREAM) |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 50 | |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 51 | def __negotiate_capabilities(self): |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 52 | greeting = self.__json_read() |
| 53 | if greeting is None or not greeting.has_key('QMP'): |
| 54 | raise QMPConnectError |
| 55 | # Greeting seems ok, negotiate capabilities |
| 56 | resp = self.cmd('qmp_capabilities') |
| 57 | if "return" in resp: |
| 58 | return greeting |
| 59 | raise QMPCapabilitiesError |
| 60 | |
Stefan Hajnoczi | 91b8edd | 2011-05-25 19:48:00 +0100 | [diff] [blame] | 61 | def __json_read(self, only_event=False): |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 62 | while True: |
| 63 | data = self.__sockfile.readline() |
| 64 | if not data: |
| 65 | return |
| 66 | resp = json.loads(data) |
| 67 | if 'event' in resp: |
| 68 | self.__events.append(resp) |
Stefan Hajnoczi | 91b8edd | 2011-05-25 19:48:00 +0100 | [diff] [blame] | 69 | if not only_event: |
| 70 | continue |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 71 | return resp |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 72 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 73 | error = socket.error |
| 74 | |
Ryota Ozaki | e9d17b6 | 2012-09-14 21:44:20 +0900 | [diff] [blame] | 75 | def connect(self, negotiate=True): |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 76 | """ |
| 77 | Connect to the QMP Monitor and perform capabilities negotiation. |
| 78 | |
| 79 | @return QMP greeting dict |
| 80 | @raise socket.error on socket connection errors |
| 81 | @raise QMPConnectError if the greeting is not received |
| 82 | @raise QMPCapabilitiesError if fails to negotiate capabilities |
| 83 | """ |
| 84 | self.__sock.connect(self.__address) |
Ryota Ozaki | e9d17b6 | 2012-09-14 21:44:20 +0900 | [diff] [blame] | 85 | self.__sockfile = self.__sock.makefile() |
| 86 | if negotiate: |
| 87 | return self.__negotiate_capabilities() |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 88 | |
| 89 | def accept(self): |
| 90 | """ |
| 91 | Await connection from QMP Monitor and perform capabilities negotiation. |
| 92 | |
| 93 | @return QMP greeting dict |
| 94 | @raise socket.error on socket connection errors |
| 95 | @raise QMPConnectError if the greeting is not received |
| 96 | @raise QMPCapabilitiesError if fails to negotiate capabilities |
| 97 | """ |
| 98 | self.__sock, _ = self.__sock.accept() |
Jeff Cody | b3d0380 | 2012-10-15 16:58:02 -0400 | [diff] [blame] | 99 | self.__sockfile = self.__sock.makefile() |
Stefan Hajnoczi | 37628f1 | 2011-05-25 19:48:01 +0100 | [diff] [blame] | 100 | return self.__negotiate_capabilities() |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 101 | |
| 102 | def cmd_obj(self, qmp_cmd): |
| 103 | """ |
| 104 | Send a QMP command to the QMP Monitor. |
| 105 | |
| 106 | @param qmp_cmd: QMP command to be sent as a Python dict |
| 107 | @return QMP response as a Python dict or None if the connection has |
| 108 | been closed |
| 109 | """ |
| 110 | try: |
| 111 | self.__sock.sendall(json.dumps(qmp_cmd)) |
| 112 | except socket.error, err: |
| 113 | if err[0] == errno.EPIPE: |
| 114 | return |
| 115 | raise socket.error(err) |
| 116 | return self.__json_read() |
| 117 | |
| 118 | def cmd(self, name, args=None, id=None): |
| 119 | """ |
| 120 | Build a QMP command and send it to the QMP Monitor. |
| 121 | |
| 122 | @param name: command name (string) |
| 123 | @param args: command arguments (dict) |
| 124 | @param id: command id (dict, list, string or int) |
| 125 | """ |
| 126 | qmp_cmd = { 'execute': name } |
| 127 | if args: |
| 128 | qmp_cmd['arguments'] = args |
| 129 | if id: |
| 130 | qmp_cmd['id'] = id |
| 131 | return self.cmd_obj(qmp_cmd) |
| 132 | |
Anthony Liguori | 9f68f7f | 2012-02-20 16:28:29 -0600 | [diff] [blame] | 133 | def command(self, cmd, **kwds): |
| 134 | ret = self.cmd(cmd, kwds) |
| 135 | if ret.has_key('error'): |
| 136 | raise Exception(ret['error']['desc']) |
| 137 | return ret['return'] |
| 138 | |
Paolo Bonzini | 9eb80ea | 2012-10-18 16:49:29 +0200 | [diff] [blame] | 139 | def pull_event(self, wait=False): |
| 140 | """ |
| 141 | Get and delete the first available QMP event. |
| 142 | |
| 143 | @param wait: block until an event is available (bool) |
| 144 | """ |
| 145 | self.__sock.setblocking(0) |
| 146 | try: |
| 147 | self.__json_read() |
| 148 | except socket.error, err: |
| 149 | if err[0] == errno.EAGAIN: |
| 150 | # No data available |
| 151 | pass |
| 152 | self.__sock.setblocking(1) |
| 153 | if not self.__events and wait: |
| 154 | self.__json_read(only_event=True) |
| 155 | event = self.__events[0] |
| 156 | del self.__events[0] |
| 157 | return event |
| 158 | |
Stefan Hajnoczi | 91b8edd | 2011-05-25 19:48:00 +0100 | [diff] [blame] | 159 | def get_events(self, wait=False): |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 160 | """ |
| 161 | Get a list of available QMP events. |
Stefan Hajnoczi | 91b8edd | 2011-05-25 19:48:00 +0100 | [diff] [blame] | 162 | |
| 163 | @param wait: block until an event is available (bool) |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 164 | """ |
| 165 | self.__sock.setblocking(0) |
| 166 | try: |
| 167 | self.__json_read() |
| 168 | except socket.error, err: |
| 169 | if err[0] == errno.EAGAIN: |
| 170 | # No data available |
| 171 | pass |
| 172 | self.__sock.setblocking(1) |
Stefan Hajnoczi | 91b8edd | 2011-05-25 19:48:00 +0100 | [diff] [blame] | 173 | if not self.__events and wait: |
Fam Zheng | 4864512 | 2014-02-24 13:45:01 +0800 | [diff] [blame] | 174 | ret = self.__json_read(only_event=True) |
| 175 | if ret == None: |
| 176 | # We are in blocking mode, if don't get anything, something |
| 177 | # went wrong |
| 178 | raise QMPConnectError("Error while reading from socket") |
| 179 | |
Luiz Capitulino | 1d00a07 | 2010-10-27 17:43:34 -0200 | [diff] [blame] | 180 | return self.__events |
| 181 | |
| 182 | def clear_events(self): |
| 183 | """ |
| 184 | Clear current list of pending events. |
| 185 | """ |
| 186 | self.__events = [] |
| 187 | |
| 188 | def close(self): |
| 189 | self.__sock.close() |
| 190 | self.__sockfile.close() |
Ryota Ozaki | e37b350 | 2012-09-14 21:44:21 +0900 | [diff] [blame] | 191 | |
| 192 | timeout = socket.timeout |
| 193 | |
| 194 | def settimeout(self, timeout): |
| 195 | self.__sock.settimeout(timeout) |
Wenchao Xia | 30b005d | 2013-09-06 11:24:33 +0800 | [diff] [blame] | 196 | |
| 197 | def get_sock_fd(self): |
| 198 | return self.__sock.fileno() |
| 199 | |
| 200 | def is_scm_available(self): |
| 201 | return self.__sock.family == socket.AF_UNIX |