root / QMP / qmp-shell @ 1ab516ed
History | View | Annotate | Download (8.2 kB)
1 | cedebdac | Luiz Capitulino | #!/usr/bin/python |
---|---|---|---|
2 | cedebdac | Luiz Capitulino | # |
3 | 9bed0d0d | Luiz Capitulino | # Low-level QEMU shell on top of QMP. |
4 | cedebdac | Luiz Capitulino | # |
5 | 9bed0d0d | Luiz Capitulino | # Copyright (C) 2009, 2010 Red Hat Inc. |
6 | cedebdac | Luiz Capitulino | # |
7 | cedebdac | Luiz Capitulino | # Authors: |
8 | cedebdac | Luiz Capitulino | # Luiz Capitulino <lcapitulino@redhat.com> |
9 | cedebdac | Luiz Capitulino | # |
10 | cedebdac | Luiz Capitulino | # This work is licensed under the terms of the GNU GPL, version 2. See |
11 | cedebdac | Luiz Capitulino | # the COPYING file in the top-level directory. |
12 | cedebdac | Luiz Capitulino | # |
13 | cedebdac | Luiz Capitulino | # Usage: |
14 | cedebdac | Luiz Capitulino | # |
15 | cedebdac | Luiz Capitulino | # Start QEMU with: |
16 | cedebdac | Luiz Capitulino | # |
17 | 9bed0d0d | Luiz Capitulino | # # qemu [...] -qmp unix:./qmp-sock,server |
18 | cedebdac | Luiz Capitulino | # |
19 | cedebdac | Luiz Capitulino | # Run the shell: |
20 | cedebdac | Luiz Capitulino | # |
21 | 9bed0d0d | Luiz Capitulino | # $ qmp-shell ./qmp-sock |
22 | cedebdac | Luiz Capitulino | # |
23 | cedebdac | Luiz Capitulino | # Commands have the following format: |
24 | cedebdac | Luiz Capitulino | # |
25 | 9bed0d0d | Luiz Capitulino | # < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] |
26 | cedebdac | Luiz Capitulino | # |
27 | cedebdac | Luiz Capitulino | # For example: |
28 | cedebdac | Luiz Capitulino | # |
29 | 9bed0d0d | Luiz Capitulino | # (QEMU) device_add driver=e1000 id=net1 |
30 | 9bed0d0d | Luiz Capitulino | # {u'return': {}} |
31 | 9bed0d0d | Luiz Capitulino | # (QEMU) |
32 | cedebdac | Luiz Capitulino | |
33 | cedebdac | Luiz Capitulino | import qmp |
34 | cedebdac | Luiz Capitulino | import readline |
35 | 9bed0d0d | Luiz Capitulino | import sys |
36 | fa779b65 | Daniel P. Berrange | import pprint |
37 | cedebdac | Luiz Capitulino | |
38 | 9bed0d0d | Luiz Capitulino | class QMPCompleter(list): |
39 | 9bed0d0d | Luiz Capitulino | def complete(self, text, state): |
40 | 9bed0d0d | Luiz Capitulino | for cmd in self: |
41 | 9bed0d0d | Luiz Capitulino | if cmd.startswith(text): |
42 | 9bed0d0d | Luiz Capitulino | if not state: |
43 | 9bed0d0d | Luiz Capitulino | return cmd |
44 | 9bed0d0d | Luiz Capitulino | else: |
45 | 9bed0d0d | Luiz Capitulino | state -= 1 |
46 | cedebdac | Luiz Capitulino | |
47 | 9bed0d0d | Luiz Capitulino | class QMPShellError(Exception): |
48 | 9bed0d0d | Luiz Capitulino | pass |
49 | 9bed0d0d | Luiz Capitulino | |
50 | 9bed0d0d | Luiz Capitulino | class QMPShellBadPort(QMPShellError): |
51 | 9bed0d0d | Luiz Capitulino | pass |
52 | 9bed0d0d | Luiz Capitulino | |
53 | 9bed0d0d | Luiz Capitulino | # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and |
54 | 9bed0d0d | Luiz Capitulino | # _execute_cmd()). Let's design a better one. |
55 | 9bed0d0d | Luiz Capitulino | class QMPShell(qmp.QEMUMonitorProtocol): |
56 | fa779b65 | Daniel P. Berrange | def __init__(self, address, pp=None): |
57 | 9bed0d0d | Luiz Capitulino | qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) |
58 | 9bed0d0d | Luiz Capitulino | self._greeting = None |
59 | 9bed0d0d | Luiz Capitulino | self._completer = None |
60 | fa779b65 | Daniel P. Berrange | self._pp = pp |
61 | 9bed0d0d | Luiz Capitulino | |
62 | 9bed0d0d | Luiz Capitulino | def __get_address(self, arg): |
63 | 9bed0d0d | Luiz Capitulino | """ |
64 | 9bed0d0d | Luiz Capitulino | Figure out if the argument is in the port:host form, if it's not it's |
65 | 9bed0d0d | Luiz Capitulino | probably a file path. |
66 | 9bed0d0d | Luiz Capitulino | """ |
67 | 9bed0d0d | Luiz Capitulino | addr = arg.split(':') |
68 | 9bed0d0d | Luiz Capitulino | if len(addr) == 2: |
69 | 9bed0d0d | Luiz Capitulino | try: |
70 | 9bed0d0d | Luiz Capitulino | port = int(addr[1]) |
71 | 9bed0d0d | Luiz Capitulino | except ValueError: |
72 | 9bed0d0d | Luiz Capitulino | raise QMPShellBadPort |
73 | 9bed0d0d | Luiz Capitulino | return ( addr[0], port ) |
74 | 9bed0d0d | Luiz Capitulino | # socket path |
75 | 9bed0d0d | Luiz Capitulino | return arg |
76 | 9bed0d0d | Luiz Capitulino | |
77 | 9bed0d0d | Luiz Capitulino | def _fill_completion(self): |
78 | 9bed0d0d | Luiz Capitulino | for cmd in self.cmd('query-commands')['return']: |
79 | 9bed0d0d | Luiz Capitulino | self._completer.append(cmd['name']) |
80 | 9bed0d0d | Luiz Capitulino | |
81 | 9bed0d0d | Luiz Capitulino | def __completer_setup(self): |
82 | 9bed0d0d | Luiz Capitulino | self._completer = QMPCompleter() |
83 | 9bed0d0d | Luiz Capitulino | self._fill_completion() |
84 | 9bed0d0d | Luiz Capitulino | readline.set_completer(self._completer.complete) |
85 | 9bed0d0d | Luiz Capitulino | readline.parse_and_bind("tab: complete") |
86 | 9bed0d0d | Luiz Capitulino | # XXX: default delimiters conflict with some command names (eg. query-), |
87 | 9bed0d0d | Luiz Capitulino | # clearing everything as it doesn't seem to matter |
88 | 9bed0d0d | Luiz Capitulino | readline.set_completer_delims('') |
89 | 9bed0d0d | Luiz Capitulino | |
90 | 9bed0d0d | Luiz Capitulino | def __build_cmd(self, cmdline): |
91 | 9bed0d0d | Luiz Capitulino | """ |
92 | 9bed0d0d | Luiz Capitulino | Build a QMP input object from a user provided command-line in the |
93 | 9bed0d0d | Luiz Capitulino | following format: |
94 | 9bed0d0d | Luiz Capitulino | |
95 | 9bed0d0d | Luiz Capitulino | < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] |
96 | 9bed0d0d | Luiz Capitulino | """ |
97 | 9bed0d0d | Luiz Capitulino | cmdargs = cmdline.split() |
98 | 9bed0d0d | Luiz Capitulino | qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } |
99 | 9bed0d0d | Luiz Capitulino | for arg in cmdargs[1:]: |
100 | 9bed0d0d | Luiz Capitulino | opt = arg.split('=') |
101 | 9bed0d0d | Luiz Capitulino | try: |
102 | 9bed0d0d | Luiz Capitulino | value = int(opt[1]) |
103 | 9bed0d0d | Luiz Capitulino | except ValueError: |
104 | 9bed0d0d | Luiz Capitulino | value = opt[1] |
105 | 9bed0d0d | Luiz Capitulino | qmpcmd['arguments'][opt[0]] = value |
106 | 9bed0d0d | Luiz Capitulino | return qmpcmd |
107 | 9bed0d0d | Luiz Capitulino | |
108 | 9bed0d0d | Luiz Capitulino | def _execute_cmd(self, cmdline): |
109 | 9bed0d0d | Luiz Capitulino | try: |
110 | 9bed0d0d | Luiz Capitulino | qmpcmd = self.__build_cmd(cmdline) |
111 | 9bed0d0d | Luiz Capitulino | except: |
112 | 9bed0d0d | Luiz Capitulino | print 'command format: <command-name> ', |
113 | 9bed0d0d | Luiz Capitulino | print '[arg-name1=arg1] ... [arg-nameN=argN]' |
114 | 9bed0d0d | Luiz Capitulino | return True |
115 | 9bed0d0d | Luiz Capitulino | resp = self.cmd_obj(qmpcmd) |
116 | 9bed0d0d | Luiz Capitulino | if resp is None: |
117 | 9bed0d0d | Luiz Capitulino | print 'Disconnected' |
118 | 9bed0d0d | Luiz Capitulino | return False |
119 | fa779b65 | Daniel P. Berrange | |
120 | fa779b65 | Daniel P. Berrange | if self._pp is not None: |
121 | fa779b65 | Daniel P. Berrange | self._pp.pprint(resp) |
122 | fa779b65 | Daniel P. Berrange | else: |
123 | fa779b65 | Daniel P. Berrange | print resp |
124 | 9bed0d0d | Luiz Capitulino | return True |
125 | 9bed0d0d | Luiz Capitulino | |
126 | 9bed0d0d | Luiz Capitulino | def connect(self): |
127 | 9bed0d0d | Luiz Capitulino | self._greeting = qmp.QEMUMonitorProtocol.connect(self) |
128 | 9bed0d0d | Luiz Capitulino | self.__completer_setup() |
129 | cedebdac | Luiz Capitulino | |
130 | 9bed0d0d | Luiz Capitulino | def show_banner(self, msg='Welcome to the QMP low-level shell!'): |
131 | 9bed0d0d | Luiz Capitulino | print msg |
132 | 9bed0d0d | Luiz Capitulino | version = self._greeting['QMP']['version']['qemu'] |
133 | 9bed0d0d | Luiz Capitulino | print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) |
134 | cedebdac | Luiz Capitulino | |
135 | 9bed0d0d | Luiz Capitulino | def read_exec_command(self, prompt): |
136 | 9bed0d0d | Luiz Capitulino | """ |
137 | 9bed0d0d | Luiz Capitulino | Read and execute a command. |
138 | cedebdac | Luiz Capitulino | |
139 | 9bed0d0d | Luiz Capitulino | @return True if execution was ok, return False if disconnected. |
140 | 9bed0d0d | Luiz Capitulino | """ |
141 | cedebdac | Luiz Capitulino | try: |
142 | 9bed0d0d | Luiz Capitulino | cmdline = raw_input(prompt) |
143 | cedebdac | Luiz Capitulino | except EOFError: |
144 | cedebdac | Luiz Capitulino | |
145 | 9bed0d0d | Luiz Capitulino | return False |
146 | 9bed0d0d | Luiz Capitulino | if cmdline == '': |
147 | 9bed0d0d | Luiz Capitulino | for ev in self.get_events(): |
148 | 9bed0d0d | Luiz Capitulino | print ev |
149 | 9bed0d0d | Luiz Capitulino | self.clear_events() |
150 | 9bed0d0d | Luiz Capitulino | return True |
151 | cedebdac | Luiz Capitulino | else: |
152 | 9bed0d0d | Luiz Capitulino | return self._execute_cmd(cmdline) |
153 | 9bed0d0d | Luiz Capitulino | |
154 | 11217a75 | Luiz Capitulino | class HMPShell(QMPShell): |
155 | 11217a75 | Luiz Capitulino | def __init__(self, address): |
156 | 11217a75 | Luiz Capitulino | QMPShell.__init__(self, address) |
157 | 11217a75 | Luiz Capitulino | self.__cpu_index = 0 |
158 | 11217a75 | Luiz Capitulino | |
159 | 11217a75 | Luiz Capitulino | def __cmd_completion(self): |
160 | 11217a75 | Luiz Capitulino | for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'): |
161 | 11217a75 | Luiz Capitulino | if cmd and cmd[0] != '[' and cmd[0] != '\t': |
162 | 11217a75 | Luiz Capitulino | name = cmd.split()[0] # drop help text |
163 | 11217a75 | Luiz Capitulino | if name == 'info': |
164 | 11217a75 | Luiz Capitulino | continue |
165 | 11217a75 | Luiz Capitulino | if name.find('|') != -1: |
166 | 11217a75 | Luiz Capitulino | # Command in the form 'foobar|f' or 'f|foobar', take the |
167 | 11217a75 | Luiz Capitulino | # full name |
168 | 11217a75 | Luiz Capitulino | opt = name.split('|') |
169 | 11217a75 | Luiz Capitulino | if len(opt[0]) == 1: |
170 | 11217a75 | Luiz Capitulino | name = opt[1] |
171 | 11217a75 | Luiz Capitulino | else: |
172 | 11217a75 | Luiz Capitulino | name = opt[0] |
173 | 11217a75 | Luiz Capitulino | self._completer.append(name) |
174 | 11217a75 | Luiz Capitulino | self._completer.append('help ' + name) # help completion |
175 | 11217a75 | Luiz Capitulino | |
176 | 11217a75 | Luiz Capitulino | def __info_completion(self): |
177 | 11217a75 | Luiz Capitulino | for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'): |
178 | 11217a75 | Luiz Capitulino | if cmd: |
179 | 11217a75 | Luiz Capitulino | self._completer.append('info ' + cmd.split()[1]) |
180 | 11217a75 | Luiz Capitulino | |
181 | 11217a75 | Luiz Capitulino | def __other_completion(self): |
182 | 11217a75 | Luiz Capitulino | # special cases |
183 | 11217a75 | Luiz Capitulino | self._completer.append('help info') |
184 | 11217a75 | Luiz Capitulino | |
185 | 11217a75 | Luiz Capitulino | def _fill_completion(self): |
186 | 11217a75 | Luiz Capitulino | self.__cmd_completion() |
187 | 11217a75 | Luiz Capitulino | self.__info_completion() |
188 | 11217a75 | Luiz Capitulino | self.__other_completion() |
189 | 11217a75 | Luiz Capitulino | |
190 | 11217a75 | Luiz Capitulino | def __cmd_passthrough(self, cmdline, cpu_index = 0): |
191 | 11217a75 | Luiz Capitulino | return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments': |
192 | 11217a75 | Luiz Capitulino | { 'command-line': cmdline, |
193 | 11217a75 | Luiz Capitulino | 'cpu-index': cpu_index } }) |
194 | 11217a75 | Luiz Capitulino | |
195 | 11217a75 | Luiz Capitulino | def _execute_cmd(self, cmdline): |
196 | 11217a75 | Luiz Capitulino | if cmdline.split()[0] == "cpu": |
197 | 11217a75 | Luiz Capitulino | # trap the cpu command, it requires special setting |
198 | 11217a75 | Luiz Capitulino | try: |
199 | 11217a75 | Luiz Capitulino | idx = int(cmdline.split()[1]) |
200 | 11217a75 | Luiz Capitulino | if not 'return' in self.__cmd_passthrough('info version', idx): |
201 | 11217a75 | Luiz Capitulino | print 'bad CPU index' |
202 | 11217a75 | Luiz Capitulino | return True |
203 | 11217a75 | Luiz Capitulino | self.__cpu_index = idx |
204 | 11217a75 | Luiz Capitulino | except ValueError: |
205 | 11217a75 | Luiz Capitulino | print 'cpu command takes an integer argument' |
206 | 11217a75 | Luiz Capitulino | return True |
207 | 11217a75 | Luiz Capitulino | resp = self.__cmd_passthrough(cmdline, self.__cpu_index) |
208 | 11217a75 | Luiz Capitulino | if resp is None: |
209 | 11217a75 | Luiz Capitulino | print 'Disconnected' |
210 | 11217a75 | Luiz Capitulino | return False |
211 | 11217a75 | Luiz Capitulino | assert 'return' in resp or 'error' in resp |
212 | 11217a75 | Luiz Capitulino | if 'return' in resp: |
213 | 11217a75 | Luiz Capitulino | # Success |
214 | 11217a75 | Luiz Capitulino | if len(resp['return']) > 0: |
215 | 11217a75 | Luiz Capitulino | print resp['return'], |
216 | 11217a75 | Luiz Capitulino | else: |
217 | 11217a75 | Luiz Capitulino | # Error |
218 | 11217a75 | Luiz Capitulino | print '%s: %s' % (resp['error']['class'], resp['error']['desc']) |
219 | 11217a75 | Luiz Capitulino | return True |
220 | 11217a75 | Luiz Capitulino | |
221 | 11217a75 | Luiz Capitulino | def show_banner(self): |
222 | 11217a75 | Luiz Capitulino | QMPShell.show_banner(self, msg='Welcome to the HMP shell!') |
223 | 11217a75 | Luiz Capitulino | |
224 | 9bed0d0d | Luiz Capitulino | def die(msg): |
225 | 9bed0d0d | Luiz Capitulino | sys.stderr.write('ERROR: %s\n' % msg) |
226 | 9bed0d0d | Luiz Capitulino | sys.exit(1) |
227 | 9bed0d0d | Luiz Capitulino | |
228 | 9bed0d0d | Luiz Capitulino | def fail_cmdline(option=None): |
229 | 9bed0d0d | Luiz Capitulino | if option: |
230 | 9bed0d0d | Luiz Capitulino | sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) |
231 | fa779b65 | Daniel P. Berrange | sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n') |
232 | 9bed0d0d | Luiz Capitulino | sys.exit(1) |
233 | 9bed0d0d | Luiz Capitulino | |
234 | 9bed0d0d | Luiz Capitulino | def main(): |
235 | 11217a75 | Luiz Capitulino | addr = '' |
236 | fa779b65 | Daniel P. Berrange | qemu = None |
237 | fa779b65 | Daniel P. Berrange | hmp = False |
238 | fa779b65 | Daniel P. Berrange | pp = None |
239 | fa779b65 | Daniel P. Berrange | |
240 | 9bed0d0d | Luiz Capitulino | try: |
241 | fa779b65 | Daniel P. Berrange | for arg in sys.argv[1:]: |
242 | fa779b65 | Daniel P. Berrange | if arg == "-H": |
243 | fa779b65 | Daniel P. Berrange | if qemu is not None: |
244 | fa779b65 | Daniel P. Berrange | fail_cmdline(arg) |
245 | fa779b65 | Daniel P. Berrange | hmp = True |
246 | fa779b65 | Daniel P. Berrange | elif arg == "-p": |
247 | fa779b65 | Daniel P. Berrange | if pp is not None: |
248 | fa779b65 | Daniel P. Berrange | fail_cmdline(arg) |
249 | fa779b65 | Daniel P. Berrange | pp = pprint.PrettyPrinter(indent=4) |
250 | fa779b65 | Daniel P. Berrange | else: |
251 | fa779b65 | Daniel P. Berrange | if qemu is not None: |
252 | fa779b65 | Daniel P. Berrange | fail_cmdline(arg) |
253 | fa779b65 | Daniel P. Berrange | if hmp: |
254 | fa779b65 | Daniel P. Berrange | qemu = HMPShell(arg) |
255 | fa779b65 | Daniel P. Berrange | else: |
256 | fa779b65 | Daniel P. Berrange | qemu = QMPShell(arg, pp) |
257 | fa779b65 | Daniel P. Berrange | addr = arg |
258 | fa779b65 | Daniel P. Berrange | |
259 | fa779b65 | Daniel P. Berrange | if qemu is None: |
260 | fa779b65 | Daniel P. Berrange | fail_cmdline() |
261 | 9bed0d0d | Luiz Capitulino | except QMPShellBadPort: |
262 | 9bed0d0d | Luiz Capitulino | die('bad port number in command-line') |
263 | 9bed0d0d | Luiz Capitulino | |
264 | 9bed0d0d | Luiz Capitulino | try: |
265 | 9bed0d0d | Luiz Capitulino | qemu.connect() |
266 | 9bed0d0d | Luiz Capitulino | except qmp.QMPConnectError: |
267 | 9bed0d0d | Luiz Capitulino | die('Didn\'t get QMP greeting message') |
268 | 9bed0d0d | Luiz Capitulino | except qmp.QMPCapabilitiesError: |
269 | 9bed0d0d | Luiz Capitulino | die('Could not negotiate capabilities') |
270 | 9bed0d0d | Luiz Capitulino | except qemu.error: |
271 | 11217a75 | Luiz Capitulino | die('Could not connect to %s' % addr) |
272 | 9bed0d0d | Luiz Capitulino | |
273 | 9bed0d0d | Luiz Capitulino | qemu.show_banner() |
274 | 9bed0d0d | Luiz Capitulino | while qemu.read_exec_command('(QEMU) '): |
275 | 9bed0d0d | Luiz Capitulino | pass |
276 | 9bed0d0d | Luiz Capitulino | qemu.close() |
277 | cedebdac | Luiz Capitulino | |
278 | cedebdac | Luiz Capitulino | if __name__ == '__main__': |
279 | cedebdac | Luiz Capitulino | main() |