Statistics
| Branch: | Revision:

root / scripts / qmp / qmp.py @ 48645123

History | View | Annotate | Download (6 kB)

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

    
11
import json
12
import errno
13
import socket
14

    
15
class QMPError(Exception):
16
    pass
17

    
18
class QMPConnectError(QMPError):
19
    pass
20

    
21
class QMPCapabilitiesError(QMPError):
22
    pass
23

    
24
class QEMUMonitorProtocol:
25
    def __init__(self, address, server=False):
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
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
36
        """
37
        self.__events = []
38
        self.__address = address
39
        self.__sock = self.__get_sock()
40
        if server:
41
            self.__sock.bind(self.__address)
42
            self.__sock.listen(1)
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)
50

    
51
    def __negotiate_capabilities(self):
52
        greeting = self.__json_read()
53
        if greeting is None or not greeting.has_key('QMP'):
54
            raise QMPConnectError
55
        # Greeting seems ok, negotiate capabilities
56
        resp = self.cmd('qmp_capabilities')
57
        if "return" in resp:
58
            return greeting
59
        raise QMPCapabilitiesError
60

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

    
73
    error = socket.error
74

    
75
    def connect(self, negotiate=True):
76
        """
77
        Connect to the QMP Monitor and perform capabilities negotiation.
78

79
        @return QMP greeting dict
80
        @raise socket.error on socket connection errors
81
        @raise QMPConnectError if the greeting is not received
82
        @raise QMPCapabilitiesError if fails to negotiate capabilities
83
        """
84
        self.__sock.connect(self.__address)
85
        self.__sockfile = self.__sock.makefile()
86
        if negotiate:
87
            return self.__negotiate_capabilities()
88

    
89
    def accept(self):
90
        """
91
        Await connection from QMP Monitor and perform capabilities negotiation.
92

93
        @return QMP greeting dict
94
        @raise socket.error on socket connection errors
95
        @raise QMPConnectError if the greeting is not received
96
        @raise QMPCapabilitiesError if fails to negotiate capabilities
97
        """
98
        self.__sock, _ = self.__sock.accept()
99
        self.__sockfile = self.__sock.makefile()
100
        return self.__negotiate_capabilities()
101

    
102
    def cmd_obj(self, qmp_cmd):
103
        """
104
        Send a QMP command to the QMP Monitor.
105

106
        @param qmp_cmd: QMP command to be sent as a Python dict
107
        @return QMP response as a Python dict or None if the connection has
108
                been closed
109
        """
110
        try:
111
            self.__sock.sendall(json.dumps(qmp_cmd))
112
        except socket.error, err:
113
            if err[0] == errno.EPIPE:
114
                return
115
            raise socket.error(err)
116
        return self.__json_read()
117

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

122
        @param name: command name (string)
123
        @param args: command arguments (dict)
124
        @param id: command id (dict, list, string or int)
125
        """
126
        qmp_cmd = { 'execute': name }
127
        if args:
128
            qmp_cmd['arguments'] = args
129
        if id:
130
            qmp_cmd['id'] = id
131
        return self.cmd_obj(qmp_cmd)
132

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

    
139
    def pull_event(self, wait=False):
140
        """
141
        Get and delete the first available QMP event.
142

143
        @param wait: block until an event is available (bool)
144
        """
145
        self.__sock.setblocking(0)
146
        try:
147
            self.__json_read()
148
        except socket.error, err:
149
            if err[0] == errno.EAGAIN:
150
                # No data available
151
                pass
152
        self.__sock.setblocking(1)
153
        if not self.__events and wait:
154
            self.__json_read(only_event=True)
155
        event = self.__events[0]
156
        del self.__events[0]
157
        return event
158

    
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)
166
        try:
167
            self.__json_read()
168
        except socket.error, err:
169
            if err[0] == errno.EAGAIN:
170
                # No data available
171
                pass
172
        self.__sock.setblocking(1)
173
        if not self.__events and wait:
174
            ret = self.__json_read(only_event=True)
175
            if ret == None:
176
                # We are in blocking mode, if don't get anything, something
177
                # went wrong
178
                raise QMPConnectError("Error while reading from socket")
179

    
180
        return self.__events
181

    
182
    def clear_events(self):
183
        """
184
        Clear current list of pending events.
185
        """
186
        self.__events = []
187

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

    
192
    timeout = socket.timeout
193

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

    
197
    def get_sock_fd(self):
198
        return self.__sock.fileno()
199

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