qemu/scripts/qmp/qmp.py
<<
>>
Prefs
   1# QEMU Monitor Protocol Python class
   2#
   3# Copyright (C) 2009, 2010 Red Hat Inc.
   4#
   5# Authors:
   6#  Luiz Capitulino <lcapitulino@redhat.com>
   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
  11import json
  12import errno
  13import socket
  14
  15class QMPError(Exception):
  16    pass
  17
  18class QMPConnectError(QMPError):
  19    pass
  20
  21class QMPCapabilitiesError(QMPError):
  22    pass
  23
  24class QMPTimeoutError(QMPError):
  25    pass
  26
  27class QEMUMonitorProtocol:
  28    def __init__(self, address, server=False):
  29        """
  30        Create a QEMUMonitorProtocol class.
  31
  32        @param address: QEMU address, can be either a unix socket path (string)
  33                        or a tuple in the form ( address, port ) for a TCP
  34                        connection
  35        @param server: server mode listens on the socket (bool)
  36        @raise socket.error on socket connection errors
  37        @note No connection is established, this is done by the connect() or
  38              accept() methods
  39        """
  40        self.__events = []
  41        self.__address = address
  42        self.__sock = self.__get_sock()
  43        if server:
  44            self.__sock.bind(self.__address)
  45            self.__sock.listen(1)
  46
  47    def __get_sock(self):
  48        if isinstance(self.__address, tuple):
  49            family = socket.AF_INET
  50        else:
  51            family = socket.AF_UNIX
  52        return socket.socket(family, socket.SOCK_STREAM)
  53
  54    def __negotiate_capabilities(self):
  55        greeting = self.__json_read()
  56        if greeting is None or not greeting.has_key('QMP'):
  57            raise QMPConnectError
  58        # Greeting seems ok, negotiate capabilities
  59        resp = self.cmd('qmp_capabilities')
  60        if "return" in resp:
  61            return greeting
  62        raise QMPCapabilitiesError
  63
  64    def __json_read(self, only_event=False):
  65        while True:
  66            data = self.__sockfile.readline()
  67            if not data:
  68                return
  69            resp = json.loads(data)
  70            if 'event' in resp:
  71                self.__events.append(resp)
  72                if not only_event:
  73                    continue
  74            return resp
  75
  76    error = socket.error
  77
  78    def __get_events(self, wait=False):
  79        """
  80        Check for new events in the stream and cache them in __events.
  81
  82        @param wait (bool): block until an event is available.
  83        @param wait (float): If wait is a float, treat it as a timeout value.
  84
  85        @raise QMPTimeoutError: If a timeout float is provided and the timeout
  86                                period elapses.
  87        @raise QMPConnectError: If wait is True but no events could be retrieved
  88                                or if some other error occurred.
  89        """
  90
  91        # Check for new events regardless and pull them into the cache:
  92        self.__sock.setblocking(0)
  93        try:
  94            self.__json_read()
  95        except socket.error as err:
  96            if err[0] == errno.EAGAIN:
  97                # No data available
  98                pass
  99        self.__sock.setblocking(1)
 100
 101        # Wait for new events, if needed.
 102        # if wait is 0.0, this means "no wait" and is also implicitly false.
 103        if not self.__events and wait:
 104            if isinstance(wait, float):
 105                self.__sock.settimeout(wait)
 106            try:
 107                ret = self.__json_read(only_event=True)
 108            except socket.timeout:
 109                raise QMPTimeoutError("Timeout waiting for event")
 110            except:
 111                raise QMPConnectError("Error while reading from socket")
 112            if ret is None:
 113                raise QMPConnectError("Error while reading from socket")
 114            self.__sock.settimeout(None)
 115
 116    def connect(self, negotiate=True):
 117        """
 118        Connect to the QMP Monitor and perform capabilities negotiation.
 119
 120        @return QMP greeting dict
 121        @raise socket.error on socket connection errors
 122        @raise QMPConnectError if the greeting is not received
 123        @raise QMPCapabilitiesError if fails to negotiate capabilities
 124        """
 125        self.__sock.connect(self.__address)
 126        self.__sockfile = self.__sock.makefile()
 127        if negotiate:
 128            return self.__negotiate_capabilities()
 129
 130    def accept(self):
 131        """
 132        Await connection from QMP Monitor and perform capabilities negotiation.
 133
 134        @return QMP greeting dict
 135        @raise socket.error on socket connection errors
 136        @raise QMPConnectError if the greeting is not received
 137        @raise QMPCapabilitiesError if fails to negotiate capabilities
 138        """
 139        self.__sock, _ = self.__sock.accept()
 140        self.__sockfile = self.__sock.makefile()
 141        return self.__negotiate_capabilities()
 142
 143    def cmd_obj(self, qmp_cmd):
 144        """
 145        Send a QMP command to the QMP Monitor.
 146
 147        @param qmp_cmd: QMP command to be sent as a Python dict
 148        @return QMP response as a Python dict or None if the connection has
 149                been closed
 150        """
 151        try:
 152            self.__sock.sendall(json.dumps(qmp_cmd))
 153        except socket.error as err:
 154            if err[0] == errno.EPIPE:
 155                return
 156            raise socket.error(err)
 157        return self.__json_read()
 158
 159    def cmd(self, name, args=None, id=None):
 160        """
 161        Build a QMP command and send it to the QMP Monitor.
 162
 163        @param name: command name (string)
 164        @param args: command arguments (dict)
 165        @param id: command id (dict, list, string or int)
 166        """
 167        qmp_cmd = { 'execute': name }
 168        if args:
 169            qmp_cmd['arguments'] = args
 170        if id:
 171            qmp_cmd['id'] = id
 172        return self.cmd_obj(qmp_cmd)
 173
 174    def command(self, cmd, **kwds):
 175        ret = self.cmd(cmd, kwds)
 176        if ret.has_key('error'):
 177            raise Exception(ret['error']['desc'])
 178        return ret['return']
 179
 180    def pull_event(self, wait=False):
 181        """
 182        Get and delete the first available QMP event.
 183
 184        @param wait (bool): block until an event is available.
 185        @param wait (float): If wait is a float, treat it as a timeout value.
 186
 187        @raise QMPTimeoutError: If a timeout float is provided and the timeout
 188                                period elapses.
 189        @raise QMPConnectError: If wait is True but no events could be retrieved
 190                                or if some other error occurred.
 191
 192        @return The first available QMP event, or None.
 193        """
 194        self.__get_events(wait)
 195
 196        if self.__events:
 197            return self.__events.pop(0)
 198        return None
 199
 200    def get_events(self, wait=False):
 201        """
 202        Get a list of available QMP events.
 203
 204        @param wait (bool): block until an event is available.
 205        @param wait (float): If wait is a float, treat it as a timeout value.
 206
 207        @raise QMPTimeoutError: If a timeout float is provided and the timeout
 208                                period elapses.
 209        @raise QMPConnectError: If wait is True but no events could be retrieved
 210                                or if some other error occurred.
 211
 212        @return The list of available QMP events.
 213        """
 214        self.__get_events(wait)
 215        return self.__events
 216
 217    def clear_events(self):
 218        """
 219        Clear current list of pending events.
 220        """
 221        self.__events = []
 222
 223    def close(self):
 224        self.__sock.close()
 225        self.__sockfile.close()
 226
 227    timeout = socket.timeout
 228
 229    def settimeout(self, timeout):
 230        self.__sock.settimeout(timeout)
 231
 232    def get_sock_fd(self):
 233        return self.__sock.fileno()
 234
 235    def is_scm_available(self):
 236        return self.__sock.family == socket.AF_UNIX
 237