]>
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 <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 | ||
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: |
1d00a07d LC |
25 | def __init__(self, address): |
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 | |
32 | @note No connection is established, this is done by the connect() method | |
33 | """ | |
34 | self.__events = [] | |
35 | self.__address = address | |
36 | self.__sock = self.__get_sock() | |
37 | self.__sockfile = self.__sock.makefile() | |
38 | ||
39 | def __get_sock(self): | |
40 | if isinstance(self.__address, tuple): | |
41 | family = socket.AF_INET | |
42 | else: | |
43 | family = socket.AF_UNIX | |
44 | return socket.socket(family, socket.SOCK_STREAM) | |
45 | ||
46 | def __json_read(self): | |
47 | while True: | |
48 | data = self.__sockfile.readline() | |
49 | if not data: | |
50 | return | |
51 | resp = json.loads(data) | |
52 | if 'event' in resp: | |
53 | self.__events.append(resp) | |
54 | continue | |
55 | return resp | |
56 | ||
57 | error = socket.error | |
58 | ||
cedebdac | 59 | def connect(self): |
1d00a07d LC |
60 | """ |
61 | Connect to the QMP Monitor and perform capabilities negotiation. | |
62 | ||
63 | @return QMP greeting dict | |
64 | @raise socket.error on socket connection errors | |
65 | @raise QMPConnectError if the greeting is not received | |
66 | @raise QMPCapabilitiesError if fails to negotiate capabilities | |
67 | """ | |
68 | self.__sock.connect(self.__address) | |
69 | greeting = self.__json_read() | |
70 | if greeting is None or not greeting.has_key('QMP'): | |
cedebdac | 71 | raise QMPConnectError |
1d00a07d LC |
72 | # Greeting seems ok, negotiate capabilities |
73 | resp = self.cmd('qmp_capabilities') | |
74 | if "return" in resp: | |
75 | return greeting | |
76 | raise QMPCapabilitiesError | |
cedebdac | 77 | |
1d00a07d LC |
78 | def cmd_obj(self, qmp_cmd): |
79 | """ | |
80 | Send a QMP command to the QMP Monitor. | |
cedebdac | 81 | |
1d00a07d LC |
82 | @param qmp_cmd: QMP command to be sent as a Python dict |
83 | @return QMP response as a Python dict or None if the connection has | |
84 | been closed | |
85 | """ | |
86 | try: | |
87 | self.__sock.sendall(json.dumps(qmp_cmd)) | |
88 | except socket.error, err: | |
89 | if err[0] == errno.EPIPE: | |
90 | return | |
91 | raise socket.error(err) | |
cedebdac LC |
92 | return self.__json_read() |
93 | ||
1d00a07d LC |
94 | def cmd(self, name, args=None, id=None): |
95 | """ | |
96 | Build a QMP command and send it to the QMP Monitor. | |
cedebdac | 97 | |
1d00a07d LC |
98 | @param name: command name (string) |
99 | @param args: command arguments (dict) | |
100 | @param id: command id (dict, list, string or int) | |
101 | """ | |
102 | qmp_cmd = { 'execute': name } | |
103 | if args: | |
104 | qmp_cmd['arguments'] = args | |
105 | if id: | |
106 | qmp_cmd['id'] = id | |
107 | return self.cmd_obj(qmp_cmd) | |
108 | ||
109 | def get_events(self): | |
110 | """ | |
111 | Get a list of available QMP events. | |
112 | """ | |
113 | self.__sock.setblocking(0) | |
cedebdac | 114 | try: |
1d00a07d LC |
115 | self.__json_read() |
116 | except socket.error, err: | |
117 | if err[0] == errno.EAGAIN: | |
118 | # No data available | |
119 | pass | |
120 | self.__sock.setblocking(1) | |
121 | return self.__events | |
122 | ||
123 | def clear_events(self): | |
124 | """ | |
125 | Clear current list of pending events. | |
126 | """ | |
127 | self.__events = [] | |
128 | ||
129 | def close(self): | |
130 | self.__sock.close() | |
131 | self.__sockfile.close() |