]>
Commit | Line | Data |
---|---|---|
cedebdac LC |
1 | # QEMU Monitor Protocol Python class |
2 | # | |
1d00a07d | 3 | # Copyright (C) 2009, 2010 Red Hat Inc. |
cedebdac LC |
4 | # |
5 | # Authors: | |
6 | # Luiz Capitulino <[email protected]> | |
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 | ||
1d00a07d LC |
11 | import json |
12 | import errno | |
13 | import socket | |
cedebdac LC |
14 | |
15 | class QMPError(Exception): | |
16 | pass | |
17 | ||
18 | class QMPConnectError(QMPError): | |
19 | pass | |
20 | ||
1d00a07d LC |
21 | class QMPCapabilitiesError(QMPError): |
22 | pass | |
23 | ||
cedebdac | 24 | class QEMUMonitorProtocol: |
37628f11 | 25 | def __init__(self, address, server=False): |
1d00a07d LC |
26 | """ |
27 | Create a QEMUMonitorProtocol class. | |
28 | ||
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 | |
37628f11 SH |
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 | |
1d00a07d LC |
36 | """ |
37 | self.__events = [] | |
38 | self.__address = address | |
39 | self.__sock = self.__get_sock() | |
37628f11 SH |
40 | if server: |
41 | self.__sock.bind(self.__address) | |
42 | self.__sock.listen(1) | |
1d00a07d LC |
43 | |
44 | def __get_sock(self): | |
45 | if isinstance(self.__address, tuple): | |
46 | family = socket.AF_INET | |
47 | else: | |
48 | family = socket.AF_UNIX | |
49 | return socket.socket(family, socket.SOCK_STREAM) | |
50 | ||
37628f11 | 51 | def __negotiate_capabilities(self): |
37628f11 SH |
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 | ||
91b8eddf | 61 | def __json_read(self, only_event=False): |
1d00a07d LC |
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) | |
91b8eddf SH |
69 | if not only_event: |
70 | continue | |
1d00a07d LC |
71 | return resp |
72 | ||
73 | error = socket.error | |
74 | ||
e9d17b68 | 75 | def connect(self, negotiate=True): |
1d00a07d LC |
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) | |
e9d17b68 RO |
85 | self.__sockfile = self.__sock.makefile() |
86 | if negotiate: | |
87 | return self.__negotiate_capabilities() | |
37628f11 SH |
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() | |
b3d0380e | 99 | self.__sockfile = self.__sock.makefile() |
37628f11 | 100 | return self.__negotiate_capabilities() |
cedebdac | 101 | |
1d00a07d LC |
102 | def cmd_obj(self, qmp_cmd): |
103 | """ | |
104 | Send a QMP command to the QMP Monitor. | |
cedebdac | 105 | |
1d00a07d LC |
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) | |
cedebdac LC |
116 | return self.__json_read() |
117 | ||
1d00a07d LC |
118 | def cmd(self, name, args=None, id=None): |
119 | """ | |
120 | Build a QMP command and send it to the QMP Monitor. | |
cedebdac | 121 | |
1d00a07d LC |
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 | ||
9f68f7fb AL |
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 | ||
9eb80ead PB |
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 | ||
91b8eddf | 159 | def get_events(self, wait=False): |
1d00a07d LC |
160 | """ |
161 | Get a list of available QMP events. | |
91b8eddf SH |
162 | |
163 | @param wait: block until an event is available (bool) | |
1d00a07d LC |
164 | """ |
165 | self.__sock.setblocking(0) | |
cedebdac | 166 | try: |
1d00a07d LC |
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) | |
91b8eddf SH |
173 | if not self.__events and wait: |
174 | self.__json_read(only_event=True) | |
1d00a07d LC |
175 | return self.__events |
176 | ||
177 | def clear_events(self): | |
178 | """ | |
179 | Clear current list of pending events. | |
180 | """ | |
181 | self.__events = [] | |
182 | ||
183 | def close(self): | |
184 | self.__sock.close() | |
185 | self.__sockfile.close() | |
e37b350a RO |
186 | |
187 | timeout = socket.timeout | |
188 | ||
189 | def settimeout(self, timeout): | |
190 | self.__sock.settimeout(timeout) |