Revision 9bed0d0d QMP/qmp-shell
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 |
|
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