blob: 748a0d807c9da83205973d12a5665791840a4dcb [file] [log] [blame]
John Snow306dfcd2019-06-27 17:28:15 -04001"""
2QEMU machine module:
3
4The machine module primarily provides the QEMUMachine class,
5which provides facilities for managing the lifetime of a QEMU VM.
6"""
7
John Snowabf0bf92019-06-27 17:28:14 -04008# Copyright (C) 2015-2016 Red Hat Inc.
9# Copyright (C) 2012 IBM Corp.
10#
11# Authors:
12# Fam Zheng <famz@redhat.com>
13#
14# This work is licensed under the terms of the GNU GPL, version 2. See
15# the COPYING file in the top-level directory.
16#
17# Based on qmp.py.
18#
19
20import errno
John Snowaad3f3b2020-10-06 19:58:06 -040021from itertools import chain
John Snow5690b432021-09-16 14:22:47 -040022import locale
John Snowabf0bf92019-06-27 17:28:14 -040023import logging
24import os
John Snowabf0bf92019-06-27 17:28:14 -040025import shutil
John Snowde6e08b2020-07-10 01:06:48 -040026import signal
John Snowf12a2822020-10-06 19:58:08 -040027import socket
John Snow932ca4b2020-10-06 19:57:58 -040028import subprocess
John Snowabf0bf92019-06-27 17:28:14 -040029import tempfile
John Snow1dda0402020-05-14 01:53:44 -040030from types import TracebackType
John Snowaaa81ec2020-10-06 19:58:03 -040031from typing import (
32 Any,
John Snowf12a2822020-10-06 19:58:08 -040033 BinaryIO,
John Snowaaa81ec2020-10-06 19:58:03 -040034 Dict,
35 List,
36 Optional,
John Snowaad3f3b2020-10-06 19:58:06 -040037 Sequence,
38 Tuple,
John Snowaaa81ec2020-10-06 19:58:03 -040039 Type,
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +030040 TypeVar,
John Snowaaa81ec2020-10-06 19:58:03 -040041)
John Snowabf0bf92019-06-27 17:28:14 -040042
John Snow37094b62022-03-30 13:28:10 -040043from qemu.qmp import SocketAddrT
44from qemu.qmp.legacy import (
John Snowa4225302022-03-21 16:33:12 -040045 QEMUMonitorProtocol,
John Snowbeb6b572021-05-27 17:16:53 -040046 QMPMessage,
47 QMPReturnValue,
John Snowbeb6b572021-05-27 17:16:53 -040048)
49
50from . import console_socket
John Snow932ca4b2020-10-06 19:57:58 -040051
John Snowabf0bf92019-06-27 17:28:14 -040052
53LOG = logging.getLogger(__name__)
54
John Snow8dfac2e2020-05-28 18:21:29 -040055
John Snowabf0bf92019-06-27 17:28:14 -040056class QEMUMachineError(Exception):
57 """
58 Exception called when an error in QEMUMachine happens.
59 """
60
61
62class QEMUMachineAddDeviceError(QEMUMachineError):
63 """
64 Exception raised when a request to add a device can not be fulfilled
65
66 The failures are caused by limitations, lack of information or conflicting
67 requests on the QEMUMachine methods. This exception does not represent
68 failures reported by the QEMU binary itself.
69 """
70
71
John Snow50465f92022-01-31 23:11:32 -050072class VMLaunchFailure(QEMUMachineError):
73 """
74 Exception raised when a VM launch was attempted, but failed.
75 """
76 def __init__(self, exitcode: Optional[int],
77 command: str, output: Optional[str]):
78 super().__init__(exitcode, command, output)
79 self.exitcode = exitcode
80 self.command = command
81 self.output = output
82
83 def __str__(self) -> str:
84 ret = ''
85 if self.__cause__ is not None:
86 name = type(self.__cause__).__name__
87 reason = str(self.__cause__)
88 if reason:
89 ret += f"{name}: {reason}"
90 else:
91 ret += f"{name}"
92 ret += '\n'
93
94 if self.exitcode is not None:
95 ret += f"\tExit code: {self.exitcode}\n"
96 ret += f"\tCommand: {self.command}\n"
97 ret += f"\tOutput: {self.output}\n"
98 return ret
99
100
John Snow193bf1c2020-07-10 01:06:47 -0400101class AbnormalShutdown(QEMUMachineError):
102 """
103 Exception raised when a graceful shutdown was requested, but not performed.
104 """
105
106
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300107_T = TypeVar('_T', bound='QEMUMachine')
108
109
John Snow9b8ccd62020-05-28 18:21:28 -0400110class QEMUMachine:
John Snowabf0bf92019-06-27 17:28:14 -0400111 """
John Snowf12a2822020-10-06 19:58:08 -0400112 A QEMU VM.
John Snowabf0bf92019-06-27 17:28:14 -0400113
John Snow8dfac2e2020-05-28 18:21:29 -0400114 Use this object as a context manager to ensure
115 the QEMU process terminates::
John Snowabf0bf92019-06-27 17:28:14 -0400116
117 with VM(binary) as vm:
118 ...
119 # vm is guaranteed to be shut down here
120 """
John Snow82e65172021-06-29 17:43:11 -0400121 # pylint: disable=too-many-instance-attributes, too-many-public-methods
John Snowabf0bf92019-06-27 17:28:14 -0400122
John Snowaad3f3b2020-10-06 19:58:06 -0400123 def __init__(self,
124 binary: str,
125 args: Sequence[str] = (),
126 wrapper: Sequence[str] = (),
127 name: Optional[str] = None,
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500128 base_temp_dir: str = "/var/tmp",
John Snowc4e60232020-10-06 19:57:59 -0400129 monitor_address: Optional[SocketAddrT] = None,
John Snowf12a2822020-10-06 19:58:08 -0400130 sock_dir: Optional[str] = None,
131 drain_console: bool = False,
Cleber Rosab306e262021-02-11 16:55:05 -0500132 console_log: Optional[str] = None,
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200133 log_dir: Optional[str] = None,
134 qmp_timer: Optional[float] = None):
John Snowabf0bf92019-06-27 17:28:14 -0400135 '''
136 Initialize a QEMUMachine
137
138 @param binary: path to the qemu binary
139 @param args: list of extra arguments
140 @param wrapper: list of arguments used as prefix to qemu binary
141 @param name: prefix for socket and log file names (default: qemu-PID)
John Snow859aeb62021-05-27 17:16:51 -0400142 @param base_temp_dir: default location where temp files are created
John Snowabf0bf92019-06-27 17:28:14 -0400143 @param monitor_address: address for QMP monitor
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500144 @param sock_dir: where to create socket (defaults to base_temp_dir)
Robert Foley0fc8f662020-07-01 14:56:24 +0100145 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -0400146 @param console_log: (optional) path to console log file
Cleber Rosab306e262021-02-11 16:55:05 -0500147 @param log_dir: where to create and keep log files
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200148 @param qmp_timer: (optional) default QMP socket timeout
John Snowabf0bf92019-06-27 17:28:14 -0400149 @note: Qemu process is not started until launch() is used.
150 '''
John Snow82e65172021-06-29 17:43:11 -0400151 # pylint: disable=too-many-arguments
152
John Snowc5e61a62020-10-06 19:58:00 -0400153 # Direct user configuration
154
155 self._binary = binary
John Snowc5e61a62020-10-06 19:58:00 -0400156 self._args = list(args)
John Snowc5e61a62020-10-06 19:58:00 -0400157 self._wrapper = wrapper
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200158 self._qmp_timer = qmp_timer
John Snowc5e61a62020-10-06 19:58:00 -0400159
John Snow72b17fe2021-11-18 15:46:16 -0500160 self._name = name or f"qemu-{os.getpid()}-{id(self):02x}"
John Snow87bf1fe2021-11-18 15:46:14 -0500161 self._temp_dir: Optional[str] = None
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500162 self._base_temp_dir = base_temp_dir
John Snow87bf1fe2021-11-18 15:46:14 -0500163 self._sock_dir = sock_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500164 self._log_dir = log_dir
John Snowc5e61a62020-10-06 19:58:00 -0400165
John Snowc4e60232020-10-06 19:57:59 -0400166 if monitor_address is not None:
167 self._monitor_address = monitor_address
John Snowc4e60232020-10-06 19:57:59 -0400168 else:
169 self._monitor_address = os.path.join(
John Snow87bf1fe2021-11-18 15:46:14 -0500170 self.sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400171 )
John Snowc5e61a62020-10-06 19:58:00 -0400172
173 self._console_log_path = console_log
174 if self._console_log_path:
175 # In order to log the console, buffering needs to be enabled.
176 self._drain_console = True
177 else:
178 self._drain_console = drain_console
179
180 # Runstate
John Snowf12a2822020-10-06 19:58:08 -0400181 self._qemu_log_path: Optional[str] = None
182 self._qemu_log_file: Optional[BinaryIO] = None
John Snow9223fda2020-10-06 19:58:05 -0400183 self._popen: Optional['subprocess.Popen[bytes]'] = None
John Snowf12a2822020-10-06 19:58:08 -0400184 self._events: List[QMPMessage] = []
185 self._iolog: Optional[str] = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500186 self._qmp_set = True # Enable QMP monitor by default.
John Snowbeb6b572021-05-27 17:16:53 -0400187 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
John Snowaad3f3b2020-10-06 19:58:06 -0400188 self._qemu_full_args: Tuple[str, ...] = ()
John Snowabf0bf92019-06-27 17:28:14 -0400189 self._launched = False
John Snowf12a2822020-10-06 19:58:08 -0400190 self._machine: Optional[str] = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100191 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400192 self._console_set = False
John Snowf12a2822020-10-06 19:58:08 -0400193 self._console_device_type: Optional[str] = None
John Snow652809d2020-10-06 19:58:01 -0400194 self._console_address = os.path.join(
John Snow87bf1fe2021-11-18 15:46:14 -0500195 self.sock_dir, f"{self._name}-console.sock"
John Snow652809d2020-10-06 19:58:01 -0400196 )
John Snowf12a2822020-10-06 19:58:08 -0400197 self._console_socket: Optional[socket.socket] = None
198 self._remove_files: List[str] = []
John Snowde6e08b2020-07-10 01:06:48 -0400199 self._user_killed = False
John Snowb9420e42021-10-26 13:56:05 -0400200 self._quit_issued = False
John Snowabf0bf92019-06-27 17:28:14 -0400201
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300202 def __enter__(self: _T) -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400203 return self
204
John Snow1dda0402020-05-14 01:53:44 -0400205 def __exit__(self,
206 exc_type: Optional[Type[BaseException]],
207 exc_val: Optional[BaseException],
208 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400209 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400210
John Snowf12a2822020-10-06 19:58:08 -0400211 def add_monitor_null(self) -> None:
John Snow306dfcd2019-06-27 17:28:15 -0400212 """
213 This can be used to add an unused monitor instance.
214 """
John Snowabf0bf92019-06-27 17:28:14 -0400215 self._args.append('-monitor')
216 self._args.append('null')
217
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300218 def add_fd(self: _T, fd: int, fdset: int,
219 opaque: str, opts: str = '') -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400220 """
221 Pass a file descriptor to the VM
222 """
223 options = ['fd=%d' % fd,
224 'set=%d' % fdset,
225 'opaque=%s' % opaque]
226 if opts:
227 options.append(opts)
228
229 # This did not exist before 3.4, but since then it is
230 # mandatory for our purpose
231 if hasattr(os, 'set_inheritable'):
232 os.set_inheritable(fd, True)
233
234 self._args.append('-add-fd')
235 self._args.append(','.join(options))
236 return self
237
John Snowf12a2822020-10-06 19:58:08 -0400238 def send_fd_scm(self, fd: Optional[int] = None,
239 file_path: Optional[str] = None) -> int:
John Snow306dfcd2019-06-27 17:28:15 -0400240 """
John Snow514d00d2021-09-22 20:49:30 -0400241 Send an fd or file_path to the remote via SCM_RIGHTS.
John Snow306dfcd2019-06-27 17:28:15 -0400242
John Snow514d00d2021-09-22 20:49:30 -0400243 Exactly one of fd and file_path must be given. If it is
244 file_path, the file will be opened read-only and the new file
245 descriptor will be sent to the remote.
John Snow306dfcd2019-06-27 17:28:15 -0400246 """
John Snowabf0bf92019-06-27 17:28:14 -0400247 if file_path is not None:
248 assert fd is None
John Snow514d00d2021-09-22 20:49:30 -0400249 with open(file_path, "rb") as passfile:
250 fd = passfile.fileno()
251 self._qmp.send_fd_scm(fd)
John Snowabf0bf92019-06-27 17:28:14 -0400252 else:
253 assert fd is not None
John Snow514d00d2021-09-22 20:49:30 -0400254 self._qmp.send_fd_scm(fd)
John Snowabf0bf92019-06-27 17:28:14 -0400255
John Snow514d00d2021-09-22 20:49:30 -0400256 return 0
John Snowabf0bf92019-06-27 17:28:14 -0400257
258 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400259 def _remove_if_exists(path: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400260 """
261 Remove file object at path if it exists
262 """
263 try:
264 os.remove(path)
265 except OSError as exception:
266 if exception.errno == errno.ENOENT:
267 return
268 raise
269
John Snowf12a2822020-10-06 19:58:08 -0400270 def is_running(self) -> bool:
John Snow306dfcd2019-06-27 17:28:15 -0400271 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400272 return self._popen is not None and self._popen.poll() is None
273
John Snow9223fda2020-10-06 19:58:05 -0400274 @property
275 def _subp(self) -> 'subprocess.Popen[bytes]':
276 if self._popen is None:
277 raise QEMUMachineError('Subprocess pipe not present')
278 return self._popen
279
John Snowf12a2822020-10-06 19:58:08 -0400280 def exitcode(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400281 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400282 if self._popen is None:
283 return None
284 return self._popen.poll()
285
John Snowf12a2822020-10-06 19:58:08 -0400286 def get_pid(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400287 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400288 if not self.is_running():
289 return None
John Snow9223fda2020-10-06 19:58:05 -0400290 return self._subp.pid
John Snowabf0bf92019-06-27 17:28:14 -0400291
John Snowf12a2822020-10-06 19:58:08 -0400292 def _load_io_log(self) -> None:
John Snow5690b432021-09-16 14:22:47 -0400293 # Assume that the output encoding of QEMU's terminal output is
294 # defined by our locale. If indeterminate, allow open() to fall
295 # back to the platform default.
296 _, encoding = locale.getlocale()
John Snowabf0bf92019-06-27 17:28:14 -0400297 if self._qemu_log_path is not None:
John Snow5690b432021-09-16 14:22:47 -0400298 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
John Snowabf0bf92019-06-27 17:28:14 -0400299 self._iolog = iolog.read()
300
John Snow652809d2020-10-06 19:58:01 -0400301 @property
302 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500303 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400304
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500305 if self._qmp_set:
306 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400307 moncdev = "socket,id=mon,host={},port={}".format(
308 *self._monitor_address
309 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500310 else:
John Snowc4e60232020-10-06 19:57:59 -0400311 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500312 args.extend(['-chardev', moncdev, '-mon',
313 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400314
John Snowabf0bf92019-06-27 17:28:14 -0400315 if self._machine is not None:
316 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400317 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100318 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400319 if self._console_set:
Paolo Bonzini991c1802020-11-13 03:10:52 -0500320 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
John Snowabf0bf92019-06-27 17:28:14 -0400321 self._console_address)
322 args.extend(['-chardev', chardev])
323 if self._console_device_type is None:
324 args.extend(['-serial', 'chardev:console'])
325 else:
326 device = '%s,chardev=console' % self._console_device_type
327 args.extend(['-device', device])
328 return args
329
Wainer dos Santos Moschetta555fe0c2021-04-30 10:34:12 -0300330 @property
331 def args(self) -> List[str]:
332 """Returns the list of arguments given to the QEMU binary."""
333 return self._args
334
John Snowf12a2822020-10-06 19:58:08 -0400335 def _pre_launch(self) -> None:
John Snow652809d2020-10-06 19:58:01 -0400336 if self._console_set:
337 self._remove_files.append(self._console_address)
338
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500339 if self._qmp_set:
John Snow6eeb3de2021-11-18 15:46:15 -0500340 if isinstance(self._monitor_address, str):
John Snowc4e60232020-10-06 19:57:59 -0400341 self._remove_files.append(self._monitor_address)
John Snowbeb6b572021-05-27 17:16:53 -0400342 self._qmp_connection = QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400343 self._monitor_address,
344 server=True,
345 nickname=self._name
346 )
John Snowabf0bf92019-06-27 17:28:14 -0400347
John Snow63c33f32021-05-27 17:16:49 -0400348 # NOTE: Make sure any opened resources are *definitely* freed in
349 # _post_shutdown()!
350 # pylint: disable=consider-using-with
Cleber Rosab306e262021-02-11 16:55:05 -0500351 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
John Snow63c33f32021-05-27 17:16:49 -0400352 self._qemu_log_file = open(self._qemu_log_path, 'wb')
353
John Snowb1ca9912021-11-18 15:46:17 -0500354 self._iolog = None
355 self._qemu_full_args = tuple(chain(
356 self._wrapper,
357 [self._binary],
358 self._base_args,
359 self._args
360 ))
361
John Snowf12a2822020-10-06 19:58:08 -0400362 def _post_launch(self) -> None:
John Snowbe1183e2020-10-06 19:58:04 -0400363 if self._qmp_connection:
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200364 self._qmp.accept(self._qmp_timer)
John Snowabf0bf92019-06-27 17:28:14 -0400365
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200366 def _close_qemu_log_file(self) -> None:
367 if self._qemu_log_file is not None:
368 self._qemu_log_file.close()
369 self._qemu_log_file = None
370
John Snowf12a2822020-10-06 19:58:08 -0400371 def _post_shutdown(self) -> None:
John Snowa3842cb2020-07-10 01:06:42 -0400372 """
373 Called to cleanup the VM instance after the process has exited.
374 May also be called after a failed launch.
375 """
John Snow9cccb332022-10-27 14:58:35 -0400376 LOG.debug("Cleaning up after VM process")
John Snow49a608b2021-10-26 13:56:06 -0400377 try:
378 self._close_qmp_connection()
379 except Exception as err: # pylint: disable=broad-except
380 LOG.warning(
381 "Exception closing QMP connection: %s",
382 str(err) if str(err) else type(err).__name__
383 )
384 finally:
385 assert self._qmp_connection is None
John Snow671940e2020-07-10 01:06:39 -0400386
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200387 self._close_qemu_log_file()
John Snowabf0bf92019-06-27 17:28:14 -0400388
Cleber Rosa3c1e16c2021-02-11 17:01:41 -0500389 self._load_io_log()
390
John Snowabf0bf92019-06-27 17:28:14 -0400391 self._qemu_log_path = None
392
John Snowabf0bf92019-06-27 17:28:14 -0400393 if self._temp_dir is not None:
394 shutil.rmtree(self._temp_dir)
395 self._temp_dir = None
396
Max Reitz32558ce2019-10-17 15:31:34 +0200397 while len(self._remove_files) > 0:
398 self._remove_if_exists(self._remove_files.pop())
399
John Snow14661d92020-07-10 01:06:38 -0400400 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400401 if (exitcode is not None and exitcode < 0
402 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400403 msg = 'qemu received signal %i; command: "%s"'
404 if self._qemu_full_args:
405 command = ' '.join(self._qemu_full_args)
406 else:
407 command = ''
408 LOG.warning(msg, -int(exitcode), command)
409
John Snowb9420e42021-10-26 13:56:05 -0400410 self._quit_issued = False
John Snowde6e08b2020-07-10 01:06:48 -0400411 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400412 self._launched = False
413
John Snowf12a2822020-10-06 19:58:08 -0400414 def launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400415 """
416 Launch the VM and make sure we cleanup and expose the
417 command line/output in case of exception
418 """
419
420 if self._launched:
421 raise QEMUMachineError('VM already launched')
422
John Snowabf0bf92019-06-27 17:28:14 -0400423 try:
424 self._launch()
John Snow50465f92022-01-31 23:11:32 -0500425 except BaseException as exc:
John Snow1611e6c2021-11-18 15:46:18 -0500426 # We may have launched the process but it may
427 # have exited before we could connect via QMP.
428 # Assume the VM didn't launch or is exiting.
429 # If we don't wait for the process, exitcode() may still be
430 # 'None' by the time control is ceded back to the caller.
431 if self._launched:
432 self.wait()
433 else:
434 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400435
John Snow50465f92022-01-31 23:11:32 -0500436 if isinstance(exc, Exception):
437 raise VMLaunchFailure(
438 exitcode=self.exitcode(),
439 command=' '.join(self._qemu_full_args),
440 output=self._iolog
441 ) from exc
442
443 # Don't wrap 'BaseException'; doing so would downgrade
444 # that exception. However, we still want to clean up.
John Snowabf0bf92019-06-27 17:28:14 -0400445 raise
446
John Snowf12a2822020-10-06 19:58:08 -0400447 def _launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400448 """
449 Launch the VM and establish a QMP connection
450 """
John Snowabf0bf92019-06-27 17:28:14 -0400451 self._pre_launch()
John Snowabf0bf92019-06-27 17:28:14 -0400452 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
John Snowa0eae172021-05-27 17:16:50 -0400453
454 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
455 # pylint: disable=consider-using-with
John Snowabf0bf92019-06-27 17:28:14 -0400456 self._popen = subprocess.Popen(self._qemu_full_args,
John Snow07b71232021-05-27 17:16:46 -0400457 stdin=subprocess.DEVNULL,
John Snowabf0bf92019-06-27 17:28:14 -0400458 stdout=self._qemu_log_file,
459 stderr=subprocess.STDOUT,
460 shell=False,
461 close_fds=False)
John Snow1611e6c2021-11-18 15:46:18 -0500462 self._launched = True
John Snowabf0bf92019-06-27 17:28:14 -0400463 self._post_launch()
464
John Snow49a608b2021-10-26 13:56:06 -0400465 def _close_qmp_connection(self) -> None:
466 """
467 Close the underlying QMP connection, if any.
468
469 Dutifully report errors that occurred while closing, but assume
470 that any error encountered indicates an abnormal termination
471 process and not a failure to close.
472 """
473 if self._qmp_connection is None:
474 return
475
476 try:
477 self._qmp.close()
478 except EOFError:
479 # EOF can occur as an Exception here when using the Async
480 # QMP backend. It indicates that the server closed the
481 # stream. If we successfully issued 'quit' at any point,
482 # then this was expected. If the remote went away without
483 # our permission, it's worth reporting that as an abnormal
484 # shutdown case.
485 if not (self._user_killed or self._quit_issued):
486 raise
487 finally:
488 self._qmp_connection = None
489
John Snowe2c97f12020-07-10 01:06:40 -0400490 def _early_cleanup(self) -> None:
491 """
492 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400493
John Snow1611e6c2021-11-18 15:46:18 -0500494 This method may be called twice upon shutdown, once each by soft
495 and hard shutdown in failover scenarios.
John Snowe2c97f12020-07-10 01:06:40 -0400496 """
497 # If we keep the console socket open, we may deadlock waiting
498 # for QEMU to exit, while QEMU is waiting for the socket to
Peter Maydell9323e792022-06-08 19:38:47 +0100499 # become writable.
John Snowe2c97f12020-07-10 01:06:40 -0400500 if self._console_socket is not None:
John Snow9cccb332022-10-27 14:58:35 -0400501 LOG.debug("Closing console socket")
John Snowe2c97f12020-07-10 01:06:40 -0400502 self._console_socket.close()
503 self._console_socket = None
504
John Snow193bf1c2020-07-10 01:06:47 -0400505 def _hard_shutdown(self) -> None:
506 """
507 Perform early cleanup, kill the VM, and wait for it to terminate.
508
509 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
510 waiting for the QEMU process to terminate.
511 """
John Snow9cccb332022-10-27 14:58:35 -0400512 LOG.debug("Performing hard shutdown")
John Snow193bf1c2020-07-10 01:06:47 -0400513 self._early_cleanup()
John Snow9223fda2020-10-06 19:58:05 -0400514 self._subp.kill()
515 self._subp.wait(timeout=60)
John Snow193bf1c2020-07-10 01:06:47 -0400516
John Snowb9420e42021-10-26 13:56:05 -0400517 def _soft_shutdown(self, timeout: Optional[int]) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400518 """
519 Perform early cleanup, attempt to gracefully shut down the VM, and wait
520 for it to terminate.
521
John Snow8226a4b2020-07-20 12:02:52 -0400522 :param timeout: Timeout in seconds for graceful shutdown.
523 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400524
525 :raise ConnectionReset: On QMP communication errors
526 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
527 the QEMU process to terminate.
528 """
John Snow9cccb332022-10-27 14:58:35 -0400529 LOG.debug("Attempting graceful termination")
530
John Snow193bf1c2020-07-10 01:06:47 -0400531 self._early_cleanup()
532
John Snow9cccb332022-10-27 14:58:35 -0400533 if self._quit_issued:
534 LOG.debug(
535 "Anticipating QEMU termination due to prior 'quit' command, "
536 "or explicit call to wait()"
537 )
538 else:
539 LOG.debug("Politely asking QEMU to terminate")
540
John Snowbe1183e2020-10-06 19:58:04 -0400541 if self._qmp_connection:
John Snow49a608b2021-10-26 13:56:06 -0400542 try:
543 if not self._quit_issued:
544 # May raise ExecInterruptedError or StateError if the
545 # connection dies or has *already* died.
546 self.qmp('quit')
547 finally:
548 # Regardless, we want to quiesce the connection.
549 self._close_qmp_connection()
John Snow3c6e5e82022-10-27 14:58:36 -0400550 elif not self._quit_issued:
551 LOG.debug(
552 "Not anticipating QEMU quit and no QMP connection present, "
553 "issuing SIGTERM"
554 )
555 self._subp.terminate()
John Snow193bf1c2020-07-10 01:06:47 -0400556
557 # May raise subprocess.TimeoutExpired
John Snow9cccb332022-10-27 14:58:35 -0400558 LOG.debug(
559 "Waiting (timeout=%s) for QEMU process (pid=%s) to terminate",
560 timeout, self._subp.pid
561 )
John Snow9223fda2020-10-06 19:58:05 -0400562 self._subp.wait(timeout=timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400563
John Snowb9420e42021-10-26 13:56:05 -0400564 def _do_shutdown(self, timeout: Optional[int]) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400565 """
566 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
567
John Snow8226a4b2020-07-20 12:02:52 -0400568 :param timeout: Timeout in seconds for graceful shutdown.
569 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400570
571 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
572 The inner exception will likely be ConnectionReset or
573 subprocess.TimeoutExpired. In rare cases, non-graceful termination
574 may result in its own exceptions, likely subprocess.TimeoutExpired.
575 """
576 try:
John Snowb9420e42021-10-26 13:56:05 -0400577 self._soft_shutdown(timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400578 except Exception as exc:
John Snow9cccb332022-10-27 14:58:35 -0400579 if isinstance(exc, subprocess.TimeoutExpired):
580 LOG.debug("Timed out waiting for QEMU process to exit")
581 LOG.debug("Graceful shutdown failed", exc_info=True)
582 LOG.debug("Falling back to hard shutdown")
John Snow193bf1c2020-07-10 01:06:47 -0400583 self._hard_shutdown()
584 raise AbnormalShutdown("Could not perform graceful shutdown") \
585 from exc
586
John Snowb9420e42021-10-26 13:56:05 -0400587 def shutdown(self,
John Snowc9b30452020-07-10 01:06:43 -0400588 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400589 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400590 """
John Snow193bf1c2020-07-10 01:06:47 -0400591 Terminate the VM (gracefully if possible) and perform cleanup.
592 Cleanup will always be performed.
593
594 If the VM has not yet been launched, or shutdown(), wait(), or kill()
595 have already been called, this method does nothing.
596
John Snow193bf1c2020-07-10 01:06:47 -0400597 :param hard: When true, do not attempt graceful shutdown, and
598 suppress the SIGKILL warning log message.
599 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400600 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400601 """
John Snowa3842cb2020-07-10 01:06:42 -0400602 if not self._launched:
603 return
604
John Snow9cccb332022-10-27 14:58:35 -0400605 LOG.debug("Shutting down VM appliance; timeout=%s", timeout)
606 if hard:
607 LOG.debug("Caller requests immediate termination of QEMU process.")
608
John Snow193bf1c2020-07-10 01:06:47 -0400609 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300610 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400611 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400612 self._hard_shutdown()
613 else:
John Snowb9420e42021-10-26 13:56:05 -0400614 self._do_shutdown(timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400615 finally:
616 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400617
John Snowf12a2822020-10-06 19:58:08 -0400618 def kill(self) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400619 """
620 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
621 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300622 self.shutdown(hard=True)
623
John Snow8226a4b2020-07-20 12:02:52 -0400624 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400625 """
626 Wait for the VM to power off and perform post-shutdown cleanup.
627
John Snow8226a4b2020-07-20 12:02:52 -0400628 :param timeout: Optional timeout in seconds. Default 30 seconds.
629 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400630 """
John Snowb9420e42021-10-26 13:56:05 -0400631 self._quit_issued = True
632 self.shutdown(timeout=timeout)
John Snow89528052020-07-10 01:06:44 -0400633
John Snowf12a2822020-10-06 19:58:08 -0400634 def set_qmp_monitor(self, enabled: bool = True) -> None:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500635 """
636 Set the QMP monitor.
637
638 @param enabled: if False, qmp monitor options will be removed from
639 the base arguments of the resulting QEMU command
640 line. Default is True.
John Snow5c02c862021-06-29 17:43:23 -0400641
642 .. note:: Call this function before launch().
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500643 """
John Snowbe1183e2020-10-06 19:58:04 -0400644 self._qmp_set = enabled
645
646 @property
John Snowbeb6b572021-05-27 17:16:53 -0400647 def _qmp(self) -> QEMUMonitorProtocol:
John Snowbe1183e2020-10-06 19:58:04 -0400648 if self._qmp_connection is None:
649 raise QEMUMachineError("Attempt to access QMP with no connection")
650 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500651
John Snowaaa81ec2020-10-06 19:58:03 -0400652 @classmethod
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300653 def _qmp_args(cls, conv_keys: bool,
654 args: Dict[str, Any]) -> Dict[str, object]:
655 if conv_keys:
656 return {k.replace('_', '-'): v for k, v in args.items()}
657
658 return args
John Snowabf0bf92019-06-27 17:28:14 -0400659
John Snowaaa81ec2020-10-06 19:58:03 -0400660 def qmp(self, cmd: str,
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300661 args_dict: Optional[Dict[str, object]] = None,
662 conv_keys: Optional[bool] = None,
John Snowaaa81ec2020-10-06 19:58:03 -0400663 **args: Any) -> QMPMessage:
664 """
665 Invoke a QMP command and return the response dict
666 """
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300667 if args_dict is not None:
668 assert not args
669 assert conv_keys is None
670 args = args_dict
671 conv_keys = False
672
673 if conv_keys is None:
674 conv_keys = True
675
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300676 qmp_args = self._qmp_args(conv_keys, args)
John Snowb9420e42021-10-26 13:56:05 -0400677 ret = self._qmp.cmd(cmd, args=qmp_args)
678 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
679 self._quit_issued = True
680 return ret
John Snowabf0bf92019-06-27 17:28:14 -0400681
John Snowf12a2822020-10-06 19:58:08 -0400682 def command(self, cmd: str,
683 conv_keys: bool = True,
684 **args: Any) -> QMPReturnValue:
John Snowabf0bf92019-06-27 17:28:14 -0400685 """
686 Invoke a QMP command.
687 On success return the response dict.
688 On failure raise an exception.
689 """
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300690 qmp_args = self._qmp_args(conv_keys, args)
John Snowb9420e42021-10-26 13:56:05 -0400691 ret = self._qmp.command(cmd, **qmp_args)
692 if cmd == 'quit':
693 self._quit_issued = True
694 return ret
John Snowabf0bf92019-06-27 17:28:14 -0400695
John Snowf12a2822020-10-06 19:58:08 -0400696 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400697 """
698 Poll for one queued QMP events and return it
699 """
John Snow306dfcd2019-06-27 17:28:15 -0400700 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400701 return self._events.pop(0)
702 return self._qmp.pull_event(wait=wait)
703
John Snowf12a2822020-10-06 19:58:08 -0400704 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400705 """
706 Poll for queued QMP events and return a list of dicts
707 """
708 events = self._qmp.get_events(wait=wait)
709 events.extend(self._events)
710 del self._events[:]
John Snowabf0bf92019-06-27 17:28:14 -0400711 return events
712
713 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400714 def event_match(event: Any, match: Optional[Any]) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400715 """
716 Check if an event matches optional match criteria.
717
718 The match criteria takes the form of a matching subdict. The event is
719 checked to be a superset of the subdict, recursively, with matching
720 values whenever the subdict values are not None.
721
722 This has a limitation that you cannot explicitly check for None values.
723
724 Examples, with the subdict queries on the left:
725 - None matches any object.
726 - {"foo": None} matches {"foo": {"bar": 1}}
727 - {"foo": None} matches {"foo": 5}
728 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
729 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
730 """
731 if match is None:
732 return True
733
734 try:
735 for key in match:
736 if key in event:
737 if not QEMUMachine.event_match(event[key], match[key]):
738 return False
739 else:
740 return False
741 return True
742 except TypeError:
743 # either match or event wasn't iterable (not a dict)
John Snowf12a2822020-10-06 19:58:08 -0400744 return bool(match == event)
John Snowabf0bf92019-06-27 17:28:14 -0400745
John Snowf12a2822020-10-06 19:58:08 -0400746 def event_wait(self, name: str,
747 timeout: float = 60.0,
748 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400749 """
750 event_wait waits for and returns a named event from QMP with a timeout.
751
752 name: The event to wait for.
753 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
754 match: Optional match criteria. See event_match for details.
755 """
756 return self.events_wait([(name, match)], timeout)
757
John Snowf12a2822020-10-06 19:58:08 -0400758 def events_wait(self,
759 events: Sequence[Tuple[str, Any]],
760 timeout: float = 60.0) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400761 """
John Snow1847a4a2020-10-06 19:58:02 -0400762 events_wait waits for and returns a single named event from QMP.
763 In the case of multiple qualifying events, this function returns the
764 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400765
John Snow1847a4a2020-10-06 19:58:02 -0400766 :param events: A sequence of (name, match_criteria) tuples.
767 The match criteria are optional and may be None.
768 See event_match for details.
769 :param timeout: Optional timeout, in seconds.
770 See QEMUMonitorProtocol.pull_event.
771
John Snowa4225302022-03-21 16:33:12 -0400772 :raise asyncio.TimeoutError:
773 If timeout was non-zero and no matching events were found.
774
John Snow1847a4a2020-10-06 19:58:02 -0400775 :return: A QMP event matching the filter criteria.
776 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400777 """
John Snowf12a2822020-10-06 19:58:08 -0400778 def _match(event: QMPMessage) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400779 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400780 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400781 return True
782 return False
783
John Snow1847a4a2020-10-06 19:58:02 -0400784 event: Optional[QMPMessage]
785
John Snowabf0bf92019-06-27 17:28:14 -0400786 # Search cached events
787 for event in self._events:
788 if _match(event):
789 self._events.remove(event)
790 return event
791
792 # Poll for new events
793 while True:
794 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400795 if event is None:
796 # NB: None is only returned when timeout is false-ish.
John Snowa4225302022-03-21 16:33:12 -0400797 # Timeouts raise asyncio.TimeoutError instead!
John Snow1847a4a2020-10-06 19:58:02 -0400798 break
John Snowabf0bf92019-06-27 17:28:14 -0400799 if _match(event):
800 return event
801 self._events.append(event)
802
803 return None
804
John Snowf12a2822020-10-06 19:58:08 -0400805 def get_log(self) -> Optional[str]:
John Snowabf0bf92019-06-27 17:28:14 -0400806 """
807 After self.shutdown or failed qemu execution, this returns the output
808 of the qemu process.
809 """
810 return self._iolog
811
John Snowf12a2822020-10-06 19:58:08 -0400812 def add_args(self, *args: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400813 """
814 Adds to the list of extra arguments to be given to the QEMU binary
815 """
816 self._args.extend(args)
817
John Snowf12a2822020-10-06 19:58:08 -0400818 def set_machine(self, machine_type: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400819 """
820 Sets the machine type
821
822 If set, the machine type will be added to the base arguments
823 of the resulting QEMU command line.
824 """
825 self._machine = machine_type
826
John Snowf12a2822020-10-06 19:58:08 -0400827 def set_console(self,
828 device_type: Optional[str] = None,
829 console_index: int = 0) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400830 """
831 Sets the device type for a console device
832
833 If set, the console device and a backing character device will
834 be added to the base arguments of the resulting QEMU command
835 line.
836
837 This is a convenience method that will either use the provided
838 device type, or default to a "-serial chardev:console" command
839 line argument.
840
841 The actual setting of command line arguments will be be done at
842 machine launch time, as it depends on the temporary directory
843 to be created.
844
845 @param device_type: the device type, such as "isa-serial". If
846 None is given (the default value) a "-serial
847 chardev:console" command line argument will
848 be used instead, resorting to the machine's
849 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100850 @param console_index: the index of the console device to use.
851 If not zero, the command line will create
852 'index - 1' consoles and connect them to
853 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400854 """
855 self._console_set = True
856 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100857 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400858
859 @property
John Snowf12a2822020-10-06 19:58:08 -0400860 def console_socket(self) -> socket.socket:
John Snowabf0bf92019-06-27 17:28:14 -0400861 """
862 Returns a socket connected to the console
863 """
864 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100865 self._console_socket = console_socket.ConsoleSocket(
866 self._console_address,
867 file=self._console_log_path,
868 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400869 return self._console_socket
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500870
871 @property
872 def temp_dir(self) -> str:
873 """
874 Returns a temporary directory to be used for this machine
875 """
876 if self._temp_dir is None:
877 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
878 dir=self._base_temp_dir)
879 return self._temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500880
881 @property
John Snow87bf1fe2021-11-18 15:46:14 -0500882 def sock_dir(self) -> str:
883 """
884 Returns the directory used for sockfiles by this machine.
885 """
886 if self._sock_dir:
887 return self._sock_dir
888 return self.temp_dir
889
890 @property
Cleber Rosab306e262021-02-11 16:55:05 -0500891 def log_dir(self) -> str:
892 """
893 Returns a directory to be used for writing logs
894 """
895 if self._log_dir is None:
896 return self.temp_dir
897 return self._log_dir