Skip to content

Commit 5fbcec4

Browse files
added qmp.py (from qemu projecy)
1 parent 0b56897 commit 5fbcec4

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

lib/qmp.py

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# QEMU Monitor Protocol Python class
2+
#
3+
# Copyright (C) 2009, 2010 Red Hat Inc.
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+
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+
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+
61+
def __json_read(self, only_event=False):
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)
69+
if not only_event:
70+
continue
71+
return resp
72+
73+
error = socket.error
74+
75+
def connect(self, negotiate=True):
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)
85+
self.__sockfile = self.__sock.makefile()
86+
if negotiate:
87+
return self.__negotiate_capabilities()
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()
99+
return self.__negotiate_capabilities()
100+
101+
def cmd_obj(self, qmp_cmd):
102+
"""
103+
Send a QMP command to the QMP Monitor.
104+
105+
@param qmp_cmd: QMP command to be sent as a Python dict
106+
@return QMP response as a Python dict or None if the connection has
107+
been closed
108+
"""
109+
try:
110+
self.__sock.sendall(json.dumps(qmp_cmd))
111+
except socket.error, err:
112+
if err[0] == errno.EPIPE:
113+
return
114+
raise socket.error(err)
115+
return self.__json_read()
116+
117+
def cmd(self, name, args=None, id=None):
118+
"""
119+
Build a QMP command and send it to the QMP Monitor.
120+
121+
@param name: command name (string)
122+
@param args: command arguments (dict)
123+
@param id: command id (dict, list, string or int)
124+
"""
125+
qmp_cmd = { 'execute': name }
126+
if args:
127+
qmp_cmd['arguments'] = args
128+
if id:
129+
qmp_cmd['id'] = id
130+
return self.cmd_obj(qmp_cmd)
131+
132+
def command(self, cmd, **kwds):
133+
ret = self.cmd(cmd, kwds)
134+
if ret.has_key('error'):
135+
raise Exception(ret['error']['desc'])
136+
return ret['return']
137+
138+
def get_events(self, wait=False):
139+
"""
140+
Get a list of available QMP events.
141+
142+
@param wait: block until an event is available (bool)
143+
"""
144+
self.__sock.setblocking(0)
145+
try:
146+
self.__json_read()
147+
except socket.error, err:
148+
if err[0] == errno.EAGAIN:
149+
# No data available
150+
pass
151+
self.__sock.setblocking(1)
152+
if not self.__events and wait:
153+
self.__json_read(only_event=True)
154+
return self.__events
155+
156+
def clear_events(self):
157+
"""
158+
Clear current list of pending events.
159+
"""
160+
self.__events = []
161+
162+
def close(self):
163+
self.__sock.close()
164+
self.__sockfile.close()
165+
166+
timeout = socket.timeout
167+
168+
def settimeout(self, timeout):
169+
self.__sock.settimeout(timeout)

0 commit comments

Comments
 (0)