diff options
Diffstat (limited to 'QMP/qmp.py')
-rw-r--r-- | QMP/qmp.py | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/QMP/qmp.py b/QMP/qmp.py new file mode 100644 index 0000000..14ce8b0 --- /dev/null +++ b/QMP/qmp.py @@ -0,0 +1,131 @@ +# QEMU Monitor Protocol Python class +# +# Copyright (C) 2009, 2010 Red Hat Inc. +# +# Authors: +# Luiz Capitulino <lcapitulino@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. + +import json +import errno +import socket + +class QMPError(Exception): + pass + +class QMPConnectError(QMPError): + pass + +class QMPCapabilitiesError(QMPError): + pass + +class QEMUMonitorProtocol: + def __init__(self, address): + """ + Create a QEMUMonitorProtocol class. + + @param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + @note No connection is established, this is done by the connect() method + """ + self.__events = [] + self.__address = address + self.__sock = self.__get_sock() + self.__sockfile = self.__sock.makefile() + + def __get_sock(self): + if isinstance(self.__address, tuple): + family = socket.AF_INET + else: + family = socket.AF_UNIX + return socket.socket(family, socket.SOCK_STREAM) + + def __json_read(self): + while True: + data = self.__sockfile.readline() + if not data: + return + resp = json.loads(data) + if 'event' in resp: + self.__events.append(resp) + continue + return resp + + error = socket.error + + def connect(self): + """ + Connect to the QMP Monitor and perform capabilities negotiation. + + @return QMP greeting dict + @raise socket.error on socket connection errors + @raise QMPConnectError if the greeting is not received + @raise QMPCapabilitiesError if fails to negotiate capabilities + """ + self.__sock.connect(self.__address) + greeting = self.__json_read() + if greeting is None or not greeting.has_key('QMP'): + raise QMPConnectError + # Greeting seems ok, negotiate capabilities + resp = self.cmd('qmp_capabilities') + if "return" in resp: + return greeting + raise QMPCapabilitiesError + + def cmd_obj(self, qmp_cmd): + """ + Send a QMP command to the QMP Monitor. + + @param qmp_cmd: QMP command to be sent as a Python dict + @return QMP response as a Python dict or None if the connection has + been closed + """ + try: + self.__sock.sendall(json.dumps(qmp_cmd)) + except socket.error, err: + if err[0] == errno.EPIPE: + return + raise socket.error(err) + return self.__json_read() + + def cmd(self, name, args=None, id=None): + """ + Build a QMP command and send it to the QMP Monitor. + + @param name: command name (string) + @param args: command arguments (dict) + @param id: command id (dict, list, string or int) + """ + qmp_cmd = { 'execute': name } + if args: + qmp_cmd['arguments'] = args + if id: + qmp_cmd['id'] = id + return self.cmd_obj(qmp_cmd) + + def get_events(self): + """ + Get a list of available QMP events. + """ + self.__sock.setblocking(0) + try: + self.__json_read() + except socket.error, err: + if err[0] == errno.EAGAIN: + # No data available + pass + self.__sock.setblocking(1) + return self.__events + + def clear_events(self): + """ + Clear current list of pending events. + """ + self.__events = [] + + def close(self): + self.__sock.close() + self.__sockfile.close() |