|
|
1.1 root 1: # QEMU Monitor Protocol Python class
2: #
1.1.1.3 root 3: # Copyright (C) 2009, 2010 Red Hat Inc.
1.1 root 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:
1.1.1.3 root 11: import json
12: import errno
13: import socket
1.1 root 14:
15: class QMPError(Exception):
16: pass
17:
18: class QMPConnectError(QMPError):
19: pass
20:
1.1.1.3 root 21: class QMPCapabilitiesError(QMPError):
22: pass
23:
1.1 root 24: class QEMUMonitorProtocol:
1.1.1.4 root 25: def __init__(self, address, server=False):
1.1.1.3 root 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
1.1.1.4 root 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
1.1.1.3 root 36: """
37: self.__events = []
38: self.__address = address
39: self.__sock = self.__get_sock()
1.1.1.4 root 40: if server:
41: self.__sock.bind(self.__address)
42: self.__sock.listen(1)
1.1.1.3 root 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)
1.1 root 50:
1.1.1.4 root 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):
1.1.1.3 root 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)
1.1.1.4 root 70: if not only_event:
71: continue
1.1.1.3 root 72: return resp
1.1 root 73:
1.1.1.3 root 74: error = socket.error
1.1 root 75:
1.1.1.3 root 76: def connect(self):
77: """
78: Connect to the QMP Monitor and perform capabilities negotiation.
1.1 root 79:
1.1.1.3 root 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)
1.1.1.4 root 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()
1.1.1.3 root 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()
1.1 root 115:
1.1.1.3 root 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:
1.1.1.5 ! root 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:
1.1.1.4 root 137: def get_events(self, wait=False):
1.1.1.3 root 138: """
139: Get a list of available QMP events.
1.1.1.4 root 140:
141: @param wait: block until an event is available (bool)
1.1.1.3 root 142: """
143: self.__sock.setblocking(0)
1.1 root 144: try:
1.1.1.3 root 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)
1.1.1.4 root 151: if not self.__events and wait:
152: self.__json_read(only_event=True)
1.1.1.3 root 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()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.