]> Git Repo - qemu.git/blame - scripts/qemu.py
Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20181031' into staging
[qemu.git] / scripts / qemu.py
CommitLineData
66613974
DB
1# QEMU library
2#
3# Copyright (C) 2015-2016 Red Hat Inc.
4# Copyright (C) 2012 IBM Corp.
5#
6# Authors:
7# Fam Zheng <[email protected]>
8#
9# This work is licensed under the terms of the GNU GPL, version 2. See
10# the COPYING file in the top-level directory.
11#
12# Based on qmp.py.
13#
14
15import errno
4738b0a8 16import logging
66613974 17import os
66613974
DB
18import subprocess
19import qmp.qmp
22dea9db 20import re
af99fa9f 21import shutil
22dea9db 22import socket
af99fa9f 23import tempfile
66613974
DB
24
25
4738b0a8
AP
26LOG = logging.getLogger(__name__)
27
28
b59b82ed 29def kvm_available(target_arch=None):
67a52f34
PMD
30 if target_arch and target_arch != os.uname()[4]:
31 return False
b59b82ed
PMD
32 return os.access("/dev/kvm", os.R_OK | os.W_OK)
33
34
22dea9db
CR
35#: Maps machine types to the preferred console device types
36CONSOLE_DEV_TYPES = {
37 r'^clipper$': 'isa-serial',
38 r'^malta': 'isa-serial',
39 r'^(pc.*|q35.*|isapc)$': 'isa-serial',
40 r'^(40p|powernv|prep)$': 'isa-serial',
41 r'^pseries.*': 'spapr-vty',
42 r'^s390-ccw-virtio.*': 'sclpconsole',
43 }
44
45
4738b0a8
AP
46class QEMUMachineError(Exception):
47 """
48 Exception called when an error in QEMUMachine happens.
49 """
50
51
22dea9db
CR
52class QEMUMachineAddDeviceError(QEMUMachineError):
53 """
54 Exception raised when a request to add a device can not be fulfilled
55
56 The failures are caused by limitations, lack of information or conflicting
57 requests on the QEMUMachine methods. This exception does not represent
58 failures reported by the QEMU binary itself.
59 """
60
a004e249 61class MonitorResponseError(qmp.qmp.QMPError):
e301e65c 62 """
a004e249 63 Represents erroneous QMP monitor reply
e301e65c 64 """
a004e249
LD
65 def __init__(self, reply):
66 try:
67 desc = reply["error"]["desc"]
68 except KeyError:
69 desc = reply
70 super(MonitorResponseError, self).__init__(desc)
71 self.reply = reply
72
73
66613974 74class QEMUMachine(object):
e301e65c
CR
75 """
76 A QEMU VM
d792bc38
SH
77
78 Use this object as a context manager to ensure the QEMU process terminates::
79
80 with VM(binary) as vm:
81 ...
82 # vm is guaranteed to be shut down here
e301e65c 83 """
66613974 84
2782fc51 85 def __init__(self, binary, args=None, wrapper=None, name=None,
2d853c70 86 test_dir="/var/tmp", monitor_address=None,
1a6d3757 87 socket_scm_helper=None):
2d853c70
LD
88 '''
89 Initialize a QEMUMachine
90
91 @param binary: path to the qemu binary
92 @param args: list of extra arguments
93 @param wrapper: list of arguments used as prefix to qemu binary
94 @param name: prefix for socket and log file names (default: qemu-PID)
95 @param test_dir: where to create socket and log file
96 @param monitor_address: address for QMP monitor
a5a98620 97 @param socket_scm_helper: helper program, required for send_fd_scm()
2d853c70
LD
98 @note: Qemu process is not started until launch() is used.
99 '''
2782fc51
LD
100 if args is None:
101 args = []
102 if wrapper is None:
103 wrapper = []
66613974
DB
104 if name is None:
105 name = "qemu-%d" % os.getpid()
af99fa9f 106 self._name = name
66613974 107 self._monitor_address = monitor_address
af99fa9f
AP
108 self._vm_monitor = None
109 self._qemu_log_path = None
110 self._qemu_log_file = None
66613974
DB
111 self._popen = None
112 self._binary = binary
2d853c70 113 self._args = list(args) # Force copy args in case we modify them
66613974
DB
114 self._wrapper = wrapper
115 self._events = []
116 self._iolog = None
4c44b4a4 117 self._socket_scm_helper = socket_scm_helper
2d853c70 118 self._qmp = None
dab91d9a 119 self._qemu_full_args = None
af99fa9f
AP
120 self._test_dir = test_dir
121 self._temp_dir = None
156dc7b1 122 self._launched = False
22dea9db
CR
123 self._machine = None
124 self._console_device_type = None
125 self._console_address = None
126 self._console_socket = None
66613974 127
5810314e 128 # just in case logging wasn't configured by the main script:
1a6d3757 129 logging.basicConfig()
5810314e 130
d792bc38
SH
131 def __enter__(self):
132 return self
133
134 def __exit__(self, exc_type, exc_val, exc_tb):
135 self.shutdown()
136 return False
137
66613974
DB
138 # This can be used to add an unused monitor instance.
139 def add_monitor_telnet(self, ip, port):
140 args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
141 self._args.append('-monitor')
142 self._args.append(args)
143
144 def add_fd(self, fd, fdset, opaque, opts=''):
e301e65c
CR
145 """
146 Pass a file descriptor to the VM
147 """
66613974
DB
148 options = ['fd=%d' % fd,
149 'set=%d' % fdset,
150 'opaque=%s' % opaque]
151 if opts:
152 options.append(opts)
153
bf43b29d
HR
154 # This did not exist before 3.4, but since then it is
155 # mandatory for our purpose
156 if hasattr(os, 'set_inheritable'):
157 os.set_inheritable(fd, True)
158
66613974
DB
159 self._args.append('-add-fd')
160 self._args.append(','.join(options))
161 return self
162
bf43b29d
HR
163 # Exactly one of fd and file_path must be given.
164 # (If it is file_path, the helper will open that file and pass its
165 # own fd)
166 def send_fd_scm(self, fd=None, file_path=None):
66613974
DB
167 # In iotest.py, the qmp should always use unix socket.
168 assert self._qmp.is_scm_available()
4c44b4a4 169 if self._socket_scm_helper is None:
4738b0a8 170 raise QEMUMachineError("No path to socket_scm_helper set")
2d853c70 171 if not os.path.exists(self._socket_scm_helper):
4738b0a8
AP
172 raise QEMUMachineError("%s does not exist" %
173 self._socket_scm_helper)
bf43b29d
HR
174
175 # This did not exist before 3.4, but since then it is
176 # mandatory for our purpose
177 if hasattr(os, 'set_inheritable'):
178 os.set_inheritable(self._qmp.get_sock_fd(), True)
179 if fd is not None:
180 os.set_inheritable(fd, True)
181
4c44b4a4 182 fd_param = ["%s" % self._socket_scm_helper,
bf43b29d
HR
183 "%d" % self._qmp.get_sock_fd()]
184
185 if file_path is not None:
186 assert fd is None
187 fd_param.append(file_path)
188 else:
189 assert fd is not None
190 fd_param.append(str(fd))
191
63e0ba55 192 devnull = open(os.path.devnull, 'rb')
4738b0a8 193 proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
bf43b29d 194 stderr=subprocess.STDOUT, close_fds=False)
4738b0a8
AP
195 output = proc.communicate()[0]
196 if output:
197 LOG.debug(output)
198
199 return proc.returncode
66613974
DB
200
201 @staticmethod
202 def _remove_if_exists(path):
e301e65c
CR
203 """
204 Remove file object at path if it exists
205 """
66613974
DB
206 try:
207 os.remove(path)
208 except OSError as exception:
209 if exception.errno == errno.ENOENT:
210 return
211 raise
212
37bbcd57 213 def is_running(self):
17589cae 214 return self._popen is not None and self._popen.poll() is None
37bbcd57 215
b2b8d986
EH
216 def exitcode(self):
217 if self._popen is None:
218 return None
17589cae 219 return self._popen.poll()
b2b8d986 220
66613974 221 def get_pid(self):
37bbcd57 222 if not self.is_running():
66613974
DB
223 return None
224 return self._popen.pid
225
226 def _load_io_log(self):
04a963b4
AP
227 if self._qemu_log_path is not None:
228 with open(self._qemu_log_path, "r") as iolog:
229 self._iolog = iolog.read()
66613974
DB
230
231 def _base_args(self):
232 if isinstance(self._monitor_address, tuple):
233 moncdev = "socket,id=mon,host=%s,port=%s" % (
234 self._monitor_address[0],
235 self._monitor_address[1])
236 else:
af99fa9f 237 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
22dea9db 238 args = ['-chardev', moncdev,
66613974
DB
239 '-mon', 'chardev=mon,mode=control',
240 '-display', 'none', '-vga', 'none']
22dea9db
CR
241 if self._machine is not None:
242 args.extend(['-machine', self._machine])
243 if self._console_device_type is not None:
244 self._console_address = os.path.join(self._temp_dir,
245 self._name + "-console.sock")
246 chardev = ('socket,id=console,path=%s,server,nowait' %
247 self._console_address)
248 device = '%s,chardev=console' % self._console_device_type
249 args.extend(['-chardev', chardev, '-device', device])
250 return args
66613974
DB
251
252 def _pre_launch(self):
af99fa9f
AP
253 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
254 if self._monitor_address is not None:
255 self._vm_monitor = self._monitor_address
256 else:
257 self._vm_monitor = os.path.join(self._temp_dir,
258 self._name + "-monitor.sock")
259 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
260 self._qemu_log_file = open(self._qemu_log_path, 'wb')
261
262 self._qmp = qmp.qmp.QEMUMonitorProtocol(self._vm_monitor,
09177654 263 server=True)
66613974
DB
264
265 def _post_launch(self):
266 self._qmp.accept()
267
268 def _post_shutdown(self):
af99fa9f
AP
269 if self._qemu_log_file is not None:
270 self._qemu_log_file.close()
271 self._qemu_log_file = None
272
273 self._qemu_log_path = None
274
22dea9db
CR
275 if self._console_socket is not None:
276 self._console_socket.close()
277 self._console_socket = None
278
af99fa9f
AP
279 if self._temp_dir is not None:
280 shutil.rmtree(self._temp_dir)
281 self._temp_dir = None
66613974
DB
282
283 def launch(self):
d301bccf
AP
284 """
285 Launch the VM and make sure we cleanup and expose the
286 command line/output in case of exception
287 """
156dc7b1
AP
288
289 if self._launched:
290 raise QEMUMachineError('VM already launched')
291
b92a0011 292 self._iolog = None
dab91d9a 293 self._qemu_full_args = None
66613974 294 try:
d301bccf 295 self._launch()
156dc7b1 296 self._launched = True
66613974 297 except:
c58b535f 298 self.shutdown()
b92a0011
AP
299
300 LOG.debug('Error launching VM')
301 if self._qemu_full_args:
302 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
303 if self._iolog:
304 LOG.debug('Output: %r', self._iolog)
66613974
DB
305 raise
306
d301bccf 307 def _launch(self):
e301e65c
CR
308 """
309 Launch the VM and establish a QMP connection
310 """
d301bccf
AP
311 devnull = open(os.path.devnull, 'rb')
312 self._pre_launch()
313 self._qemu_full_args = (self._wrapper + [self._binary] +
314 self._base_args() + self._args)
315 self._popen = subprocess.Popen(self._qemu_full_args,
316 stdin=devnull,
317 stdout=self._qemu_log_file,
318 stderr=subprocess.STDOUT,
bf43b29d
HR
319 shell=False,
320 close_fds=False)
d301bccf
AP
321 self._post_launch()
322
22491a2f 323 def wait(self):
e301e65c
CR
324 """
325 Wait for the VM to power off
326 """
22491a2f
FZ
327 self._popen.wait()
328 self._qmp.close()
329 self._load_io_log()
330 self._post_shutdown()
331
66613974 332 def shutdown(self):
e301e65c
CR
333 """
334 Terminate the VM and clean up
335 """
37bbcd57 336 if self.is_running():
66613974
DB
337 try:
338 self._qmp.cmd('quit')
339 self._qmp.close()
340 except:
341 self._popen.kill()
dab91d9a 342 self._popen.wait()
66613974 343
04a963b4
AP
344 self._load_io_log()
345 self._post_shutdown()
66613974 346
dab91d9a
AP
347 exitcode = self.exitcode()
348 if exitcode is not None and exitcode < 0:
349 msg = 'qemu received signal %i: %s'
350 if self._qemu_full_args:
351 command = ' '.join(self._qemu_full_args)
352 else:
353 command = ''
354 LOG.warn(msg, exitcode, command)
355
156dc7b1
AP
356 self._launched = False
357
66613974 358 def qmp(self, cmd, conv_keys=True, **args):
e301e65c
CR
359 """
360 Invoke a QMP command and return the response dict
361 """
66613974 362 qmp_args = dict()
fb2e1cc6 363 for key, value in args.items():
66613974 364 if conv_keys:
41f714b1 365 qmp_args[key.replace('_', '-')] = value
66613974 366 else:
7f33ca78 367 qmp_args[key] = value
66613974
DB
368
369 return self._qmp.cmd(cmd, args=qmp_args)
370
371 def command(self, cmd, conv_keys=True, **args):
e301e65c 372 """
2d853c70
LD
373 Invoke a QMP command.
374 On success return the response dict.
375 On failure raise an exception.
e301e65c 376 """
66613974
DB
377 reply = self.qmp(cmd, conv_keys, **args)
378 if reply is None:
a004e249 379 raise qmp.qmp.QMPError("Monitor is closed")
66613974 380 if "error" in reply:
a004e249 381 raise MonitorResponseError(reply)
66613974
DB
382 return reply["return"]
383
384 def get_qmp_event(self, wait=False):
e301e65c
CR
385 """
386 Poll for one queued QMP events and return it
387 """
66613974
DB
388 if len(self._events) > 0:
389 return self._events.pop(0)
390 return self._qmp.pull_event(wait=wait)
391
392 def get_qmp_events(self, wait=False):
e301e65c
CR
393 """
394 Poll for queued QMP events and return a list of dicts
395 """
66613974
DB
396 events = self._qmp.get_events(wait=wait)
397 events.extend(self._events)
398 del self._events[:]
399 self._qmp.clear_events()
400 return events
401
402 def event_wait(self, name, timeout=60.0, match=None):
e301e65c 403 """
2d853c70
LD
404 Wait for specified timeout on named event in QMP; optionally filter
405 results by match.
406
407 The 'match' is checked to be a recursive subset of the 'event'; skips
408 branch processing on match's value None
409 {"foo": {"bar": 1}} matches {"foo": None}
410 {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
e301e65c 411 """
4c44b4a4
DB
412 def event_match(event, match=None):
413 if match is None:
414 return True
415
416 for key in match:
417 if key in event:
418 if isinstance(event[key], dict):
419 if not event_match(event[key], match[key]):
420 return False
421 elif event[key] != match[key]:
422 return False
423 else:
424 return False
425
426 return True
427
66613974
DB
428 # Search cached events
429 for event in self._events:
430 if (event['event'] == name) and event_match(event, match):
431 self._events.remove(event)
432 return event
433
434 # Poll for new events
435 while True:
436 event = self._qmp.pull_event(wait=timeout)
437 if (event['event'] == name) and event_match(event, match):
438 return event
439 self._events.append(event)
440
441 return None
442
443 def get_log(self):
e301e65c 444 """
2d853c70
LD
445 After self.shutdown or failed qemu execution, this returns the output
446 of the qemu process.
e301e65c 447 """
66613974 448 return self._iolog
572a8243
CR
449
450 def add_args(self, *args):
e301e65c 451 """
572a8243 452 Adds to the list of extra arguments to be given to the QEMU binary
e301e65c 453 """
572a8243 454 self._args.extend(args)
22dea9db
CR
455
456 def set_machine(self, machine_type):
e301e65c 457 """
22dea9db
CR
458 Sets the machine type
459
460 If set, the machine type will be added to the base arguments
461 of the resulting QEMU command line.
e301e65c 462 """
22dea9db
CR
463 self._machine = machine_type
464
465 def set_console(self, device_type=None):
e301e65c 466 """
22dea9db
CR
467 Sets the device type for a console device
468
469 If set, the console device and a backing character device will
470 be added to the base arguments of the resulting QEMU command
471 line.
472
473 This is a convenience method that will either use the provided
474 device type, of if not given, it will used the device type set
475 on CONSOLE_DEV_TYPES.
476
477 The actual setting of command line arguments will be be done at
478 machine launch time, as it depends on the temporary directory
479 to be created.
480
481 @param device_type: the device type, such as "isa-serial"
482 @raises: QEMUMachineAddDeviceError if the device type is not given
483 and can not be determined.
e301e65c 484 """
22dea9db
CR
485 if device_type is None:
486 if self._machine is None:
487 raise QEMUMachineAddDeviceError("Can not add a console device:"
488 " QEMU instance without a "
489 "defined machine type")
490 for regex, device in CONSOLE_DEV_TYPES.items():
491 if re.match(regex, self._machine):
492 device_type = device
493 break
494 if device_type is None:
495 raise QEMUMachineAddDeviceError("Can not add a console device:"
496 " no matching console device "
497 "type definition")
498 self._console_device_type = device_type
499
500 @property
501 def console_socket(self):
502 """
503 Returns a socket connected to the console
504 """
505 if self._console_socket is None:
506 self._console_socket = socket.socket(socket.AF_UNIX,
507 socket.SOCK_STREAM)
508 self._console_socket.connect(self._console_address)
509 return self._console_socket
This page took 0.187303 seconds and 4 git commands to generate.