qmp.py 5.8 KB
Newer Older
L
Luiz Capitulino 已提交
1
# QEMU Monitor Protocol Python class
L
Luiz Capitulino 已提交
2
#
3
# Copyright (C) 2009, 2010 Red Hat Inc.
L
Luiz Capitulino 已提交
4 5 6 7 8 9 10
#
# 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.

11 12 13
import json
import errno
import socket
L
Luiz Capitulino 已提交
14 15 16 17 18 19 20

class QMPError(Exception):
    pass

class QMPConnectError(QMPError):
    pass

21 22 23
class QMPCapabilitiesError(QMPError):
    pass

L
Luiz Capitulino 已提交
24
class QEMUMonitorProtocol:
25
    def __init__(self, address, server=False):
26 27 28 29 30 31
        """
        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
32 33 34 35
        @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
36 37 38 39
        """
        self.__events = []
        self.__address = address
        self.__sock = self.__get_sock()
40 41 42
        if server:
            self.__sock.bind(self.__address)
            self.__sock.listen(1)
43 44 45 46 47 48 49 50

    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)

51 52 53 54 55 56 57 58 59 60
    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

61
    def __json_read(self, only_event=False):
62 63 64 65 66 67 68
        while True:
            data = self.__sockfile.readline()
            if not data:
                return
            resp = json.loads(data)
            if 'event' in resp:
                self.__events.append(resp)
69 70
                if not only_event:
                    continue
71 72 73 74
            return resp

    error = socket.error

75
    def connect(self, negotiate=True):
76 77 78 79 80 81 82 83 84
        """
        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)
85 86 87
        self.__sockfile = self.__sock.makefile()
        if negotiate:
            return self.__negotiate_capabilities()
88 89 90 91 92 93 94 95 96 97 98

    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()
J
Jeff Cody 已提交
99
        self.__sockfile = self.__sock.makefile()
100
        return self.__negotiate_capabilities()
L
Luiz Capitulino 已提交
101

102 103 104
    def cmd_obj(self, qmp_cmd):
        """
        Send a QMP command to the QMP Monitor.
L
Luiz Capitulino 已提交
105

106 107 108 109 110 111 112 113 114 115
        @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)
L
Luiz Capitulino 已提交
116 117
        return self.__json_read()

118 119 120
    def cmd(self, name, args=None, id=None):
        """
        Build a QMP command and send it to the QMP Monitor.
L
Luiz Capitulino 已提交
121

122 123 124 125 126 127 128 129 130 131 132
        @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)

A
Anthony Liguori 已提交
133 134 135 136 137 138
    def command(self, cmd, **kwds):
        ret = self.cmd(cmd, kwds)
        if ret.has_key('error'):
            raise Exception(ret['error']['desc'])
        return ret['return']

P
Paolo Bonzini 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    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

159
    def get_events(self, wait=False):
160 161
        """
        Get a list of available QMP events.
162 163

        @param wait: block until an event is available (bool)
164 165
        """
        self.__sock.setblocking(0)
L
Luiz Capitulino 已提交
166
        try:
167 168 169 170 171 172
            self.__json_read()
        except socket.error, err:
            if err[0] == errno.EAGAIN:
                # No data available
                pass
        self.__sock.setblocking(1)
173 174
        if not self.__events and wait:
            self.__json_read(only_event=True)
175 176 177 178 179 180 181 182 183 184 185
        return self.__events

    def clear_events(self):
        """
        Clear current list of pending events.
        """
        self.__events = []

    def close(self):
        self.__sock.close()
        self.__sockfile.close()
186 187 188 189 190

    timeout = socket.timeout

    def settimeout(self, timeout):
        self.__sock.settimeout(timeout)
191 192 193 194 195 196

    def get_sock_fd(self):
        return self.__sock.fileno()

    def is_scm_available(self):
        return self.__sock.family == socket.AF_UNIX