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