Statistics
| Branch: | Revision:

root / QMP / qmp-shell @ 9bed0d0d

History | View | Annotate | Download (4.9 kB)

1
#!/usr/bin/python
2
#
3
# Low-level QEMU shell on top of QMP.
4
#
5
# Copyright (C) 2009, 2010 Red Hat Inc.
6
#
7
# Authors:
8
#  Luiz Capitulino <lcapitulino@redhat.com>
9
#
10
# This work is licensed under the terms of the GNU GPL, version 2.  See
11
# the COPYING file in the top-level directory.
12
#
13
# Usage:
14
#
15
# Start QEMU with:
16
#
17
# # qemu [...] -qmp unix:./qmp-sock,server
18
#
19
# Run the shell:
20
#
21
# $ qmp-shell ./qmp-sock
22
#
23
# Commands have the following format:
24
#
25
#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
26
#
27
# For example:
28
#
29
# (QEMU) device_add driver=e1000 id=net1
30
# {u'return': {}}
31
# (QEMU)
32

    
33
import qmp
34
import readline
35
import sys
36

    
37
class QMPCompleter(list):
38
    def complete(self, text, state):
39
        for cmd in self:
40
            if cmd.startswith(text):
41
                if not state:
42
                    return cmd
43
                else:
44
                    state -= 1
45

    
46
class QMPShellError(Exception):
47
    pass
48

    
49
class QMPShellBadPort(QMPShellError):
50
    pass
51

    
52
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
53
#       _execute_cmd()). Let's design a better one.
54
class QMPShell(qmp.QEMUMonitorProtocol):
55
    def __init__(self, address):
56
        qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
57
        self._greeting = None
58
        self._completer = None
59

    
60
    def __get_address(self, arg):
61
        """
62
        Figure out if the argument is in the port:host form, if it's not it's
63
        probably a file path.
64
        """
65
        addr = arg.split(':')
66
        if len(addr) == 2:
67
            try:
68
                port = int(addr[1])
69
            except ValueError:
70
                raise QMPShellBadPort
71
            return ( addr[0], port )
72
        # socket path
73
        return arg
74

    
75
    def _fill_completion(self):
76
        for cmd in self.cmd('query-commands')['return']:
77
            self._completer.append(cmd['name'])
78

    
79
    def __completer_setup(self):
80
        self._completer = QMPCompleter()
81
        self._fill_completion()
82
        readline.set_completer(self._completer.complete)
83
        readline.parse_and_bind("tab: complete")
84
        # XXX: default delimiters conflict with some command names (eg. query-),
85
        # clearing everything as it doesn't seem to matter
86
        readline.set_completer_delims('')
87

    
88
    def __build_cmd(self, cmdline):
89
        """
90
        Build a QMP input object from a user provided command-line in the
91
        following format:
92
    
93
            < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
94
        """
95
        cmdargs = cmdline.split()
96
        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
97
        for arg in cmdargs[1:]:
98
            opt = arg.split('=')
99
            try:
100
                value = int(opt[1])
101
            except ValueError:
102
                value = opt[1]
103
            qmpcmd['arguments'][opt[0]] = value
104
        return qmpcmd
105

    
106
    def _execute_cmd(self, cmdline):
107
        try:
108
            qmpcmd = self.__build_cmd(cmdline)
109
        except:
110
            print 'command format: <command-name> ',
111
            print '[arg-name1=arg1] ... [arg-nameN=argN]'
112
            return True
113
        resp = self.cmd_obj(qmpcmd)
114
        if resp is None:
115
            print 'Disconnected'
116
            return False
117
        print resp
118
        return True
119

    
120
    def connect(self):
121
        self._greeting = qmp.QEMUMonitorProtocol.connect(self)
122
        self.__completer_setup()
123

    
124
    def show_banner(self, msg='Welcome to the QMP low-level shell!'):
125
        print msg
126
        version = self._greeting['QMP']['version']['qemu']
127
        print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
128

    
129
    def read_exec_command(self, prompt):
130
        """
131
        Read and execute a command.
132

    
133
        @return True if execution was ok, return False if disconnected.
134
        """
135
        try:
136
            cmdline = raw_input(prompt)
137
        except EOFError:
138
            print
139
            return False
140
        if cmdline == '':
141
            for ev in self.get_events():
142
                print ev
143
            self.clear_events()
144
            return True
145
        else:
146
            return self._execute_cmd(cmdline)
147

    
148
def die(msg):
149
    sys.stderr.write('ERROR: %s\n' % msg)
150
    sys.exit(1)
151

    
152
def fail_cmdline(option=None):
153
    if option:
154
        sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
155
    sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
156
    sys.exit(1)
157

    
158
def main():
159
    try:
160
        if len(sys.argv) == 2:
161
            qemu = QMPShell(sys.argv[1])
162
        else:
163
                fail_cmdline()
164
    except QMPShellBadPort:
165
        die('bad port number in command-line')
166

    
167
    try:
168
        qemu.connect()
169
    except qmp.QMPConnectError:
170
        die('Didn\'t get QMP greeting message')
171
    except qmp.QMPCapabilitiesError:
172
        die('Could not negotiate capabilities')
173
    except qemu.error:
174
        die('Could not connect to %s' % sys.argv[1])
175

    
176
    qemu.show_banner()
177
    while qemu.read_exec_command('(QEMU) '):
178
        pass
179
    qemu.close()
180

    
181
if __name__ == '__main__':
182
    main()