Revision 9bed0d0d

b/QMP/qmp-shell
1 1
#!/usr/bin/python
2 2
#
3
# Simple QEMU shell on top of QMP
3
# Low-level QEMU shell on top of QMP.
4 4
#
5
# Copyright (C) 2009 Red Hat Inc.
5
# Copyright (C) 2009, 2010 Red Hat Inc.
6 6
#
7 7
# Authors:
8 8
#  Luiz Capitulino <lcapitulino@redhat.com>
......
14 14
#
15 15
# Start QEMU with:
16 16
#
17
# $ qemu [...] -monitor control,unix:./qmp,server
17
# # qemu [...] -qmp unix:./qmp-sock,server
18 18
#
19 19
# Run the shell:
20 20
#
21
# $ qmp-shell ./qmp
21
# $ qmp-shell ./qmp-sock
22 22
#
23 23
# Commands have the following format:
24 24
#
25
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
25
#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
26 26
#
27 27
# For example:
28 28
#
29
# (QEMU) info item=network
29
# (QEMU) device_add driver=e1000 id=net1
30
# {u'return': {}}
31
# (QEMU)
30 32

  
31 33
import qmp
32 34
import readline
33
from sys import argv,exit
35
import sys
34 36

  
35
def shell_help():
36
    print 'bye  exit from the shell'
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
37 45

  
38
def main():
39
    if len(argv) != 2:
40
        print 'qemu-shell <unix-socket>'
41
        exit(1)
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()
42 123

  
43
    qemu = qmp.QEMUMonitorProtocol(argv[1])
44
    qemu.connect()
45
    qemu.send("qmp_capabilities")
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'])
46 128

  
47
    print 'Connected!'
129
    def read_exec_command(self, prompt):
130
        """
131
        Read and execute a command.
48 132

  
49
    while True:
133
        @return True if execution was ok, return False if disconnected.
134
        """
50 135
        try:
51
            cmd = raw_input('(QEMU) ')
136
            cmdline = raw_input(prompt)
52 137
        except EOFError:
53 138
            print
54
            break
55
        if cmd == '':
56
            continue
57
        elif cmd == 'bye':
58
            break
59
        elif cmd == 'help':
60
            shell_help()
139
            return False
140
        if cmdline == '':
141
            for ev in self.get_events():
142
                print ev
143
            self.clear_events()
144
            return True
61 145
        else:
62
            try:
63
                resp = qemu.send(cmd)
64
                if resp == None:
65
                    print 'Disconnected'
66
                    break
67
                print resp
68
            except IndexError:
69
                print '-> command format: <command-name> ',
70
                print '[arg-name1=arg1] ... [arg-nameN=argN]'
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()
71 180

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

Also available in: Unified diff