sync with latest
[sdk/emulator/qemu.git] / QMP / qmp.py
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
11 import json
12 import errno
13 import socket
14
15 class QMPError(Exception):
16     pass
17
18 class QMPConnectError(QMPError):
19     pass
20
21 class QMPCapabilitiesError(QMPError):
22     pass
23
24 class QEMUMonitorProtocol:
25     def __init__(self, address, server=False):
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         @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
36         """
37         self.__events = []
38         self.__address = address
39         self.__sock = self.__get_sock()
40         if server:
41             self.__sock.bind(self.__address)
42             self.__sock.listen(1)
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
51     def __negotiate_capabilities(self):
52         self.__sockfile = self.__sock.makefile()
53         greeting = self.__json_read()
54         if greeting is None or not greeting.has_key('QMP'):
55             raise QMPConnectError
56         # Greeting seems ok, negotiate capabilities
57         resp = self.cmd('qmp_capabilities')
58         if "return" in resp:
59             return greeting
60         raise QMPCapabilitiesError
61
62     def __json_read(self, only_event=False):
63         while True:
64             data = self.__sockfile.readline()
65             if not data:
66                 return
67             resp = json.loads(data)
68             if 'event' in resp:
69                 self.__events.append(resp)
70                 if not only_event:
71                     continue
72             return resp
73
74     error = socket.error
75
76     def connect(self):
77         """
78         Connect to the QMP Monitor and perform capabilities negotiation.
79
80         @return QMP greeting dict
81         @raise socket.error on socket connection errors
82         @raise QMPConnectError if the greeting is not received
83         @raise QMPCapabilitiesError if fails to negotiate capabilities
84         """
85         self.__sock.connect(self.__address)
86         return self.__negotiate_capabilities()
87
88     def accept(self):
89         """
90         Await connection from QMP Monitor and perform capabilities negotiation.
91
92         @return QMP greeting dict
93         @raise socket.error on socket connection errors
94         @raise QMPConnectError if the greeting is not received
95         @raise QMPCapabilitiesError if fails to negotiate capabilities
96         """
97         self.__sock, _ = self.__sock.accept()
98         return self.__negotiate_capabilities()
99
100     def cmd_obj(self, qmp_cmd):
101         """
102         Send a QMP command to the QMP Monitor.
103
104         @param qmp_cmd: QMP command to be sent as a Python dict
105         @return QMP response as a Python dict or None if the connection has
106                 been closed
107         """
108         try:
109             self.__sock.sendall(json.dumps(qmp_cmd))
110         except socket.error, err:
111             if err[0] == errno.EPIPE:
112                 return
113             raise socket.error(err)
114         return self.__json_read()
115
116     def cmd(self, name, args=None, id=None):
117         """
118         Build a QMP command and send it to the QMP Monitor.
119
120         @param name: command name (string)
121         @param args: command arguments (dict)
122         @param id: command id (dict, list, string or int)
123         """
124         qmp_cmd = { 'execute': name }
125         if args:
126             qmp_cmd['arguments'] = args
127         if id:
128             qmp_cmd['id'] = id
129         return self.cmd_obj(qmp_cmd)
130
131     def command(self, cmd, **kwds):
132         ret = self.cmd(cmd, kwds)
133         if ret.has_key('error'):
134             raise Exception(ret['error']['desc'])
135         return ret['return']
136
137     def get_events(self, wait=False):
138         """
139         Get a list of available QMP events.
140
141         @param wait: block until an event is available (bool)
142         """
143         self.__sock.setblocking(0)
144         try:
145             self.__json_read()
146         except socket.error, err:
147             if err[0] == errno.EAGAIN:
148                 # No data available
149                 pass
150         self.__sock.setblocking(1)
151         if not self.__events and wait:
152             self.__json_read(only_event=True)
153         return self.__events
154
155     def clear_events(self):
156         """
157         Clear current list of pending events.
158         """
159         self.__events = []
160
161     def close(self):
162         self.__sock.close()
163         self.__sockfile.close()