Revision 1d00a07d

b/QMP/qmp.py
1 1
# QEMU Monitor Protocol Python class
2 2
# 
3
# Copyright (C) 2009 Red Hat Inc.
3
# Copyright (C) 2009, 2010 Red Hat Inc.
4 4
#
5 5
# Authors:
6 6
#  Luiz Capitulino <lcapitulino@redhat.com>
......
8 8
# This work is licensed under the terms of the GNU GPL, version 2.  See
9 9
# the COPYING file in the top-level directory.
10 10

  
11
import socket, json
11
import json
12
import errno
13
import socket
12 14

  
13 15
class QMPError(Exception):
14 16
    pass
......
16 18
class QMPConnectError(QMPError):
17 19
    pass
18 20

  
21
class QMPCapabilitiesError(QMPError):
22
    pass
23

  
19 24
class QEMUMonitorProtocol:
25
    def __init__(self, address):
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
        @note No connection is established, this is done by the connect() method
33
        """
34
        self.__events = []
35
        self.__address = address
36
        self.__sock = self.__get_sock()
37
        self.__sockfile = self.__sock.makefile()
38

  
39
    def __get_sock(self):
40
        if isinstance(self.__address, tuple):
41
            family = socket.AF_INET
42
        else:
43
            family = socket.AF_UNIX
44
        return socket.socket(family, socket.SOCK_STREAM)
45

  
46
    def __json_read(self):
47
        while True:
48
            data = self.__sockfile.readline()
49
            if not data:
50
                return
51
            resp = json.loads(data)
52
            if 'event' in resp:
53
                self.__events.append(resp)
54
                continue
55
            return resp
56

  
57
    error = socket.error
58

  
20 59
    def connect(self):
21
        self.sock.connect(self.filename)
22
        data = self.__json_read()
23
        if data == None:
24
            raise QMPConnectError
25
        if not data.has_key('QMP'):
60
        """
61
        Connect to the QMP Monitor and perform capabilities negotiation.
62

  
63
        @return QMP greeting dict
64
        @raise socket.error on socket connection errors
65
        @raise QMPConnectError if the greeting is not received
66
        @raise QMPCapabilitiesError if fails to negotiate capabilities
67
        """
68
        self.__sock.connect(self.__address)
69
        greeting = self.__json_read()
70
        if greeting is None or not greeting.has_key('QMP'):
26 71
            raise QMPConnectError
27
        return data['QMP']['capabilities']
72
        # Greeting seems ok, negotiate capabilities
73
        resp = self.cmd('qmp_capabilities')
74
        if "return" in resp:
75
            return greeting
76
        raise QMPCapabilitiesError
28 77

  
29
    def close(self):
30
        self.sock.close()
78
    def cmd_obj(self, qmp_cmd):
79
        """
80
        Send a QMP command to the QMP Monitor.
31 81

  
32
    def send_raw(self, line):
33
        self.sock.send(str(line))
82
        @param qmp_cmd: QMP command to be sent as a Python dict
83
        @return QMP response as a Python dict or None if the connection has
84
                been closed
85
        """
86
        try:
87
            self.__sock.sendall(json.dumps(qmp_cmd))
88
        except socket.error, err:
89
            if err[0] == errno.EPIPE:
90
                return
91
            raise socket.error(err)
34 92
        return self.__json_read()
35 93

  
36
    def send(self, cmdline):
37
        cmd = self.__build_cmd(cmdline)
38
        self.__json_send(cmd)
39
        resp = self.__json_read()
40
        if resp == None:
41
            return
42
        elif resp.has_key('error'):
43
            return resp['error']
44
        else:
45
            return resp['return']
46

  
47
    def __build_cmd(self, cmdline):
48
        cmdargs = cmdline.split()
49
        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
50
        for arg in cmdargs[1:]:
51
            opt = arg.split('=')
52
            try:
53
                value = int(opt[1])
54
            except ValueError:
55
                value = opt[1]
56
            qmpcmd['arguments'][opt[0]] = value
57
        return qmpcmd
58

  
59
    def __json_send(self, cmd):
60
        # XXX: We have to send any additional char, otherwise
61
        # the Server won't read our input
62
        self.sock.send(json.dumps(cmd) + ' ')
94
    def cmd(self, name, args=None, id=None):
95
        """
96
        Build a QMP command and send it to the QMP Monitor.
63 97

  
64
    def __json_read(self):
98
        @param name: command name (string)
99
        @param args: command arguments (dict)
100
        @param id: command id (dict, list, string or int)
101
        """
102
        qmp_cmd = { 'execute': name }
103
        if args:
104
            qmp_cmd['arguments'] = args
105
        if id:
106
            qmp_cmd['id'] = id
107
        return self.cmd_obj(qmp_cmd)
108

  
109
    def get_events(self):
110
        """
111
        Get a list of available QMP events.
112
        """
113
        self.__sock.setblocking(0)
65 114
        try:
66
            while True:
67
                line = json.loads(self.sockfile.readline())
68
                if not 'event' in line:
69
                    return line
70
        except ValueError:
71
            return
72

  
73
    def __init__(self, filename):
74
        self.filename = filename
75
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
76
        self.sockfile = self.sock.makefile()
115
            self.__json_read()
116
        except socket.error, err:
117
            if err[0] == errno.EAGAIN:
118
                # No data available
119
                pass
120
        self.__sock.setblocking(1)
121
        return self.__events
122

  
123
    def clear_events(self):
124
        """
125
        Clear current list of pending events.
126
        """
127
        self.__events = []
128

  
129
    def close(self):
130
        self.__sock.close()
131
        self.__sockfile.close()

Also available in: Unified diff