|  | # 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, server=False): | 
|  | """ | 
|  | 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 | 
|  | @param server: server mode listens on the socket (bool) | 
|  | @raise socket.error on socket connection errors | 
|  | @note No connection is established, this is done by the connect() or | 
|  | accept() methods | 
|  | """ | 
|  | self.__events = [] | 
|  | self.__address = address | 
|  | self.__sock = self.__get_sock() | 
|  | if server: | 
|  | self.__sock.bind(self.__address) | 
|  | self.__sock.listen(1) | 
|  |  | 
|  | 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 __negotiate_capabilities(self): | 
|  | 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 __json_read(self, only_event=False): | 
|  | while True: | 
|  | data = self.__sockfile.readline() | 
|  | if not data: | 
|  | return | 
|  | resp = json.loads(data) | 
|  | if 'event' in resp: | 
|  | self.__events.append(resp) | 
|  | if not only_event: | 
|  | continue | 
|  | return resp | 
|  |  | 
|  | error = socket.error | 
|  |  | 
|  | def connect(self, negotiate=True): | 
|  | """ | 
|  | 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) | 
|  | self.__sockfile = self.__sock.makefile() | 
|  | if negotiate: | 
|  | return self.__negotiate_capabilities() | 
|  |  | 
|  | def accept(self): | 
|  | """ | 
|  | Await connection from 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, _ = self.__sock.accept() | 
|  | self.__sockfile = self.__sock.makefile() | 
|  | return self.__negotiate_capabilities() | 
|  |  | 
|  | 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 command(self, cmd, **kwds): | 
|  | ret = self.cmd(cmd, kwds) | 
|  | if ret.has_key('error'): | 
|  | raise Exception(ret['error']['desc']) | 
|  | return ret['return'] | 
|  |  | 
|  | def pull_event(self, wait=False): | 
|  | """ | 
|  | Get and delete the first available QMP event. | 
|  |  | 
|  | @param wait: block until an event is available (bool) | 
|  | """ | 
|  | 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) | 
|  | if not self.__events and wait: | 
|  | self.__json_read(only_event=True) | 
|  | event = self.__events[0] | 
|  | del self.__events[0] | 
|  | return event | 
|  |  | 
|  | def get_events(self, wait=False): | 
|  | """ | 
|  | Get a list of available QMP events. | 
|  |  | 
|  | @param wait: block until an event is available (bool) | 
|  | """ | 
|  | 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) | 
|  | if not self.__events and wait: | 
|  | self.__json_read(only_event=True) | 
|  | return self.__events | 
|  |  | 
|  | def clear_events(self): | 
|  | """ | 
|  | Clear current list of pending events. | 
|  | """ | 
|  | self.__events = [] | 
|  |  | 
|  | def close(self): | 
|  | self.__sock.close() | 
|  | self.__sockfile.close() | 
|  |  | 
|  | timeout = socket.timeout | 
|  |  | 
|  | def settimeout(self, timeout): | 
|  | self.__sock.settimeout(timeout) |