blob: ae945ca3c945f02af390902543a2a3eae234871b [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 Snowd1e04762021-05-27 17:16:59 -040043from qemu.qmp import ( # pylint: disable=import-error
John Snowbeb6b572021-05-27 17:16:53 -040044 QEMUMonitorProtocol,
45 QMPMessage,
46 QMPReturnValue,
47 SocketAddrT,
48)
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 Snow193bf1c2020-07-10 01:06:47 -040072class AbnormalShutdown(QEMUMachineError):
73 """
74 Exception raised when a graceful shutdown was requested, but not performed.
75 """
76
77
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +030078_T = TypeVar('_T', bound='QEMUMachine')
79
80
John Snow9b8ccd62020-05-28 18:21:28 -040081class QEMUMachine:
John Snowabf0bf92019-06-27 17:28:14 -040082 """
John Snowf12a2822020-10-06 19:58:08 -040083 A QEMU VM.
John Snowabf0bf92019-06-27 17:28:14 -040084
John Snow8dfac2e2020-05-28 18:21:29 -040085 Use this object as a context manager to ensure
86 the QEMU process terminates::
John Snowabf0bf92019-06-27 17:28:14 -040087
88 with VM(binary) as vm:
89 ...
90 # vm is guaranteed to be shut down here
91 """
John Snow82e65172021-06-29 17:43:11 -040092 # pylint: disable=too-many-instance-attributes, too-many-public-methods
John Snowabf0bf92019-06-27 17:28:14 -040093
John Snowaad3f3b2020-10-06 19:58:06 -040094 def __init__(self,
95 binary: str,
96 args: Sequence[str] = (),
97 wrapper: Sequence[str] = (),
98 name: Optional[str] = None,
Cleber Rosa2ca6e262021-02-11 17:01:42 -050099 base_temp_dir: str = "/var/tmp",
John Snowc4e60232020-10-06 19:57:59 -0400100 monitor_address: Optional[SocketAddrT] = None,
John Snowf12a2822020-10-06 19:58:08 -0400101 socket_scm_helper: Optional[str] = None,
102 sock_dir: Optional[str] = None,
103 drain_console: bool = False,
Cleber Rosab306e262021-02-11 16:55:05 -0500104 console_log: Optional[str] = None,
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200105 log_dir: Optional[str] = None,
106 qmp_timer: Optional[float] = None):
John Snowabf0bf92019-06-27 17:28:14 -0400107 '''
108 Initialize a QEMUMachine
109
110 @param binary: path to the qemu binary
111 @param args: list of extra arguments
112 @param wrapper: list of arguments used as prefix to qemu binary
113 @param name: prefix for socket and log file names (default: qemu-PID)
John Snow859aeb62021-05-27 17:16:51 -0400114 @param base_temp_dir: default location where temp files are created
John Snowabf0bf92019-06-27 17:28:14 -0400115 @param monitor_address: address for QMP monitor
116 @param socket_scm_helper: helper program, required for send_fd_scm()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500117 @param sock_dir: where to create socket (defaults to base_temp_dir)
Robert Foley0fc8f662020-07-01 14:56:24 +0100118 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -0400119 @param console_log: (optional) path to console log file
Cleber Rosab306e262021-02-11 16:55:05 -0500120 @param log_dir: where to create and keep log files
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200121 @param qmp_timer: (optional) default QMP socket timeout
John Snowabf0bf92019-06-27 17:28:14 -0400122 @note: Qemu process is not started until launch() is used.
123 '''
John Snow82e65172021-06-29 17:43:11 -0400124 # pylint: disable=too-many-arguments
125
John Snowc5e61a62020-10-06 19:58:00 -0400126 # Direct user configuration
127
128 self._binary = binary
John Snowc5e61a62020-10-06 19:58:00 -0400129 self._args = list(args)
John Snowc5e61a62020-10-06 19:58:00 -0400130 self._wrapper = wrapper
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200131 self._qmp_timer = qmp_timer
John Snowc5e61a62020-10-06 19:58:00 -0400132
133 self._name = name or "qemu-%d" % os.getpid()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500134 self._base_temp_dir = base_temp_dir
135 self._sock_dir = sock_dir or self._base_temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500136 self._log_dir = log_dir
John Snowc5e61a62020-10-06 19:58:00 -0400137 self._socket_scm_helper = socket_scm_helper
138
John Snowc4e60232020-10-06 19:57:59 -0400139 if monitor_address is not None:
140 self._monitor_address = monitor_address
141 self._remove_monitor_sockfile = False
142 else:
143 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400144 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400145 )
146 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400147
148 self._console_log_path = console_log
149 if self._console_log_path:
150 # In order to log the console, buffering needs to be enabled.
151 self._drain_console = True
152 else:
153 self._drain_console = drain_console
154
155 # Runstate
John Snowf12a2822020-10-06 19:58:08 -0400156 self._qemu_log_path: Optional[str] = None
157 self._qemu_log_file: Optional[BinaryIO] = None
John Snow9223fda2020-10-06 19:58:05 -0400158 self._popen: Optional['subprocess.Popen[bytes]'] = None
John Snowf12a2822020-10-06 19:58:08 -0400159 self._events: List[QMPMessage] = []
160 self._iolog: Optional[str] = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500161 self._qmp_set = True # Enable QMP monitor by default.
John Snowbeb6b572021-05-27 17:16:53 -0400162 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
John Snowaad3f3b2020-10-06 19:58:06 -0400163 self._qemu_full_args: Tuple[str, ...] = ()
John Snowf12a2822020-10-06 19:58:08 -0400164 self._temp_dir: Optional[str] = None
John Snowabf0bf92019-06-27 17:28:14 -0400165 self._launched = False
John Snowf12a2822020-10-06 19:58:08 -0400166 self._machine: Optional[str] = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100167 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400168 self._console_set = False
John Snowf12a2822020-10-06 19:58:08 -0400169 self._console_device_type: Optional[str] = None
John Snow652809d2020-10-06 19:58:01 -0400170 self._console_address = os.path.join(
171 self._sock_dir, f"{self._name}-console.sock"
172 )
John Snowf12a2822020-10-06 19:58:08 -0400173 self._console_socket: Optional[socket.socket] = None
174 self._remove_files: List[str] = []
John Snowde6e08b2020-07-10 01:06:48 -0400175 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400176
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300177 def __enter__(self: _T) -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400178 return self
179
John Snow1dda0402020-05-14 01:53:44 -0400180 def __exit__(self,
181 exc_type: Optional[Type[BaseException]],
182 exc_val: Optional[BaseException],
183 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400184 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400185
John Snowf12a2822020-10-06 19:58:08 -0400186 def add_monitor_null(self) -> None:
John Snow306dfcd2019-06-27 17:28:15 -0400187 """
188 This can be used to add an unused monitor instance.
189 """
John Snowabf0bf92019-06-27 17:28:14 -0400190 self._args.append('-monitor')
191 self._args.append('null')
192
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300193 def add_fd(self: _T, fd: int, fdset: int,
194 opaque: str, opts: str = '') -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400195 """
196 Pass a file descriptor to the VM
197 """
198 options = ['fd=%d' % fd,
199 'set=%d' % fdset,
200 'opaque=%s' % opaque]
201 if opts:
202 options.append(opts)
203
204 # This did not exist before 3.4, but since then it is
205 # mandatory for our purpose
206 if hasattr(os, 'set_inheritable'):
207 os.set_inheritable(fd, True)
208
209 self._args.append('-add-fd')
210 self._args.append(','.join(options))
211 return self
212
John Snowf12a2822020-10-06 19:58:08 -0400213 def send_fd_scm(self, fd: Optional[int] = None,
214 file_path: Optional[str] = None) -> int:
John Snow306dfcd2019-06-27 17:28:15 -0400215 """
216 Send an fd or file_path to socket_scm_helper.
217
218 Exactly one of fd and file_path must be given.
219 If it is file_path, the helper will open that file and pass its own fd.
220 """
John Snowabf0bf92019-06-27 17:28:14 -0400221 # In iotest.py, the qmp should always use unix socket.
222 assert self._qmp.is_scm_available()
223 if self._socket_scm_helper is None:
224 raise QEMUMachineError("No path to socket_scm_helper set")
225 if not os.path.exists(self._socket_scm_helper):
226 raise QEMUMachineError("%s does not exist" %
227 self._socket_scm_helper)
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(self._qmp.get_sock_fd(), True)
233 if fd is not None:
234 os.set_inheritable(fd, True)
235
236 fd_param = ["%s" % self._socket_scm_helper,
237 "%d" % self._qmp.get_sock_fd()]
238
239 if file_path is not None:
240 assert fd is None
241 fd_param.append(file_path)
242 else:
243 assert fd is not None
244 fd_param.append(str(fd))
245
John Snow14b41792021-05-27 17:16:47 -0400246 proc = subprocess.run(
247 fd_param,
248 stdin=subprocess.DEVNULL,
249 stdout=subprocess.PIPE,
250 stderr=subprocess.STDOUT,
251 check=False,
252 close_fds=False,
John Snow8dfac2e2020-05-28 18:21:29 -0400253 )
John Snow14b41792021-05-27 17:16:47 -0400254 if proc.stdout:
255 LOG.debug(proc.stdout)
John Snowabf0bf92019-06-27 17:28:14 -0400256
257 return proc.returncode
258
259 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400260 def _remove_if_exists(path: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400261 """
262 Remove file object at path if it exists
263 """
264 try:
265 os.remove(path)
266 except OSError as exception:
267 if exception.errno == errno.ENOENT:
268 return
269 raise
270
John Snowf12a2822020-10-06 19:58:08 -0400271 def is_running(self) -> bool:
John Snow306dfcd2019-06-27 17:28:15 -0400272 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400273 return self._popen is not None and self._popen.poll() is None
274
John Snow9223fda2020-10-06 19:58:05 -0400275 @property
276 def _subp(self) -> 'subprocess.Popen[bytes]':
277 if self._popen is None:
278 raise QEMUMachineError('Subprocess pipe not present')
279 return self._popen
280
John Snowf12a2822020-10-06 19:58:08 -0400281 def exitcode(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400282 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400283 if self._popen is None:
284 return None
285 return self._popen.poll()
286
John Snowf12a2822020-10-06 19:58:08 -0400287 def get_pid(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400288 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400289 if not self.is_running():
290 return None
John Snow9223fda2020-10-06 19:58:05 -0400291 return self._subp.pid
John Snowabf0bf92019-06-27 17:28:14 -0400292
John Snowf12a2822020-10-06 19:58:08 -0400293 def _load_io_log(self) -> None:
John Snow5690b432021-09-16 14:22:47 -0400294 # Assume that the output encoding of QEMU's terminal output is
295 # defined by our locale. If indeterminate, allow open() to fall
296 # back to the platform default.
297 _, encoding = locale.getlocale()
John Snowabf0bf92019-06-27 17:28:14 -0400298 if self._qemu_log_path is not None:
John Snow5690b432021-09-16 14:22:47 -0400299 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
John Snowabf0bf92019-06-27 17:28:14 -0400300 self._iolog = iolog.read()
301
John Snow652809d2020-10-06 19:58:01 -0400302 @property
303 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500304 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400305
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500306 if self._qmp_set:
307 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400308 moncdev = "socket,id=mon,host={},port={}".format(
309 *self._monitor_address
310 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500311 else:
John Snowc4e60232020-10-06 19:57:59 -0400312 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500313 args.extend(['-chardev', moncdev, '-mon',
314 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400315
John Snowabf0bf92019-06-27 17:28:14 -0400316 if self._machine is not None:
317 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400318 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100319 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400320 if self._console_set:
Paolo Bonzini991c1802020-11-13 03:10:52 -0500321 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
John Snowabf0bf92019-06-27 17:28:14 -0400322 self._console_address)
323 args.extend(['-chardev', chardev])
324 if self._console_device_type is None:
325 args.extend(['-serial', 'chardev:console'])
326 else:
327 device = '%s,chardev=console' % self._console_device_type
328 args.extend(['-device', device])
329 return args
330
Wainer dos Santos Moschetta555fe0c2021-04-30 10:34:12 -0300331 @property
332 def args(self) -> List[str]:
333 """Returns the list of arguments given to the QEMU binary."""
334 return self._args
335
John Snowf12a2822020-10-06 19:58:08 -0400336 def _pre_launch(self) -> None:
John Snow652809d2020-10-06 19:58:01 -0400337 if self._console_set:
338 self._remove_files.append(self._console_address)
339
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500340 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400341 if self._remove_monitor_sockfile:
342 assert isinstance(self._monitor_address, str)
343 self._remove_files.append(self._monitor_address)
John Snowbeb6b572021-05-27 17:16:53 -0400344 self._qmp_connection = QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400345 self._monitor_address,
346 server=True,
347 nickname=self._name
348 )
John Snowabf0bf92019-06-27 17:28:14 -0400349
John Snow63c33f32021-05-27 17:16:49 -0400350 # NOTE: Make sure any opened resources are *definitely* freed in
351 # _post_shutdown()!
352 # pylint: disable=consider-using-with
Cleber Rosab306e262021-02-11 16:55:05 -0500353 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
John Snow63c33f32021-05-27 17:16:49 -0400354 self._qemu_log_file = open(self._qemu_log_path, 'wb')
355
John Snowf12a2822020-10-06 19:58:08 -0400356 def _post_launch(self) -> None:
John Snowbe1183e2020-10-06 19:58:04 -0400357 if self._qmp_connection:
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200358 self._qmp.accept(self._qmp_timer)
John Snowabf0bf92019-06-27 17:28:14 -0400359
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200360 def _close_qemu_log_file(self) -> None:
361 if self._qemu_log_file is not None:
362 self._qemu_log_file.close()
363 self._qemu_log_file = None
364
John Snowf12a2822020-10-06 19:58:08 -0400365 def _post_shutdown(self) -> None:
John Snowa3842cb2020-07-10 01:06:42 -0400366 """
367 Called to cleanup the VM instance after the process has exited.
368 May also be called after a failed launch.
369 """
370 # Comprehensive reset for the failed launch case:
371 self._early_cleanup()
372
John Snowbe1183e2020-10-06 19:58:04 -0400373 if self._qmp_connection:
John Snow671940e2020-07-10 01:06:39 -0400374 self._qmp.close()
John Snowbe1183e2020-10-06 19:58:04 -0400375 self._qmp_connection = None
John Snow671940e2020-07-10 01:06:39 -0400376
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200377 self._close_qemu_log_file()
John Snowabf0bf92019-06-27 17:28:14 -0400378
Cleber Rosa3c1e16c2021-02-11 17:01:41 -0500379 self._load_io_log()
380
John Snowabf0bf92019-06-27 17:28:14 -0400381 self._qemu_log_path = None
382
John Snowabf0bf92019-06-27 17:28:14 -0400383 if self._temp_dir is not None:
384 shutil.rmtree(self._temp_dir)
385 self._temp_dir = None
386
Max Reitz32558ce2019-10-17 15:31:34 +0200387 while len(self._remove_files) > 0:
388 self._remove_if_exists(self._remove_files.pop())
389
John Snow14661d92020-07-10 01:06:38 -0400390 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400391 if (exitcode is not None and exitcode < 0
392 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400393 msg = 'qemu received signal %i; command: "%s"'
394 if self._qemu_full_args:
395 command = ' '.join(self._qemu_full_args)
396 else:
397 command = ''
398 LOG.warning(msg, -int(exitcode), command)
399
John Snowde6e08b2020-07-10 01:06:48 -0400400 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400401 self._launched = False
402
John Snowf12a2822020-10-06 19:58:08 -0400403 def launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400404 """
405 Launch the VM and make sure we cleanup and expose the
406 command line/output in case of exception
407 """
408
409 if self._launched:
410 raise QEMUMachineError('VM already launched')
411
412 self._iolog = None
John Snowaad3f3b2020-10-06 19:58:06 -0400413 self._qemu_full_args = ()
John Snowabf0bf92019-06-27 17:28:14 -0400414 try:
415 self._launch()
416 self._launched = True
417 except:
John Snowa3842cb2020-07-10 01:06:42 -0400418 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400419
420 LOG.debug('Error launching VM')
421 if self._qemu_full_args:
422 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
423 if self._iolog:
424 LOG.debug('Output: %r', self._iolog)
425 raise
426
John Snowf12a2822020-10-06 19:58:08 -0400427 def _launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400428 """
429 Launch the VM and establish a QMP connection
430 """
John Snowabf0bf92019-06-27 17:28:14 -0400431 self._pre_launch()
John Snowaad3f3b2020-10-06 19:58:06 -0400432 self._qemu_full_args = tuple(
433 chain(self._wrapper,
434 [self._binary],
435 self._base_args,
436 self._args)
437 )
John Snowabf0bf92019-06-27 17:28:14 -0400438 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
John Snowa0eae172021-05-27 17:16:50 -0400439
440 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
441 # pylint: disable=consider-using-with
John Snowabf0bf92019-06-27 17:28:14 -0400442 self._popen = subprocess.Popen(self._qemu_full_args,
John Snow07b71232021-05-27 17:16:46 -0400443 stdin=subprocess.DEVNULL,
John Snowabf0bf92019-06-27 17:28:14 -0400444 stdout=self._qemu_log_file,
445 stderr=subprocess.STDOUT,
446 shell=False,
447 close_fds=False)
448 self._post_launch()
449
John Snowe2c97f12020-07-10 01:06:40 -0400450 def _early_cleanup(self) -> None:
451 """
452 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400453
John Snow193bf1c2020-07-10 01:06:47 -0400454 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400455 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400456 """
457 # If we keep the console socket open, we may deadlock waiting
458 # for QEMU to exit, while QEMU is waiting for the socket to
459 # become writeable.
460 if self._console_socket is not None:
461 self._console_socket.close()
462 self._console_socket = None
463
John Snow193bf1c2020-07-10 01:06:47 -0400464 def _hard_shutdown(self) -> None:
465 """
466 Perform early cleanup, kill the VM, and wait for it to terminate.
467
468 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
469 waiting for the QEMU process to terminate.
470 """
471 self._early_cleanup()
John Snow9223fda2020-10-06 19:58:05 -0400472 self._subp.kill()
473 self._subp.wait(timeout=60)
John Snow193bf1c2020-07-10 01:06:47 -0400474
John Snow8226a4b2020-07-20 12:02:52 -0400475 def _soft_shutdown(self, timeout: Optional[int],
476 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400477 """
478 Perform early cleanup, attempt to gracefully shut down the VM, and wait
479 for it to terminate.
480
John Snow8226a4b2020-07-20 12:02:52 -0400481 :param timeout: Timeout in seconds for graceful shutdown.
482 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400483 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400484
485 :raise ConnectionReset: On QMP communication errors
486 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
487 the QEMU process to terminate.
488 """
489 self._early_cleanup()
490
John Snowbe1183e2020-10-06 19:58:04 -0400491 if self._qmp_connection:
John Snow193bf1c2020-07-10 01:06:47 -0400492 if not has_quit:
493 # Might raise ConnectionReset
494 self._qmp.cmd('quit')
495
496 # May raise subprocess.TimeoutExpired
John Snow9223fda2020-10-06 19:58:05 -0400497 self._subp.wait(timeout=timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400498
John Snow8226a4b2020-07-20 12:02:52 -0400499 def _do_shutdown(self, timeout: Optional[int],
500 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400501 """
502 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
503
John Snow8226a4b2020-07-20 12:02:52 -0400504 :param timeout: Timeout in seconds for graceful shutdown.
505 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400506 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400507
508 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
509 The inner exception will likely be ConnectionReset or
510 subprocess.TimeoutExpired. In rare cases, non-graceful termination
511 may result in its own exceptions, likely subprocess.TimeoutExpired.
512 """
513 try:
John Snow8226a4b2020-07-20 12:02:52 -0400514 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400515 except Exception as exc:
516 self._hard_shutdown()
517 raise AbnormalShutdown("Could not perform graceful shutdown") \
518 from exc
519
John Snowc9b30452020-07-10 01:06:43 -0400520 def shutdown(self, has_quit: bool = False,
521 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400522 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400523 """
John Snow193bf1c2020-07-10 01:06:47 -0400524 Terminate the VM (gracefully if possible) and perform cleanup.
525 Cleanup will always be performed.
526
527 If the VM has not yet been launched, or shutdown(), wait(), or kill()
528 have already been called, this method does nothing.
529
530 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
531 :param hard: When true, do not attempt graceful shutdown, and
532 suppress the SIGKILL warning log message.
533 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400534 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400535 """
John Snowa3842cb2020-07-10 01:06:42 -0400536 if not self._launched:
537 return
538
John Snow193bf1c2020-07-10 01:06:47 -0400539 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300540 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400541 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400542 self._hard_shutdown()
543 else:
John Snow8226a4b2020-07-20 12:02:52 -0400544 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400545 finally:
546 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400547
John Snowf12a2822020-10-06 19:58:08 -0400548 def kill(self) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400549 """
550 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
551 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300552 self.shutdown(hard=True)
553
John Snow8226a4b2020-07-20 12:02:52 -0400554 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400555 """
556 Wait for the VM to power off and perform post-shutdown cleanup.
557
John Snow8226a4b2020-07-20 12:02:52 -0400558 :param timeout: Optional timeout in seconds. Default 30 seconds.
559 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400560 """
561 self.shutdown(has_quit=True, timeout=timeout)
562
John Snowf12a2822020-10-06 19:58:08 -0400563 def set_qmp_monitor(self, enabled: bool = True) -> None:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500564 """
565 Set the QMP monitor.
566
567 @param enabled: if False, qmp monitor options will be removed from
568 the base arguments of the resulting QEMU command
569 line. Default is True.
John Snow5c02c862021-06-29 17:43:23 -0400570
571 .. note:: Call this function before launch().
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500572 """
John Snowbe1183e2020-10-06 19:58:04 -0400573 self._qmp_set = enabled
574
575 @property
John Snowbeb6b572021-05-27 17:16:53 -0400576 def _qmp(self) -> QEMUMonitorProtocol:
John Snowbe1183e2020-10-06 19:58:04 -0400577 if self._qmp_connection is None:
578 raise QEMUMachineError("Attempt to access QMP with no connection")
579 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500580
John Snowaaa81ec2020-10-06 19:58:03 -0400581 @classmethod
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300582 def _qmp_args(cls, conv_keys: bool,
583 args: Dict[str, Any]) -> Dict[str, object]:
584 if conv_keys:
585 return {k.replace('_', '-'): v for k, v in args.items()}
586
587 return args
John Snowabf0bf92019-06-27 17:28:14 -0400588
John Snowaaa81ec2020-10-06 19:58:03 -0400589 def qmp(self, cmd: str,
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300590 args_dict: Optional[Dict[str, object]] = None,
591 conv_keys: Optional[bool] = None,
John Snowaaa81ec2020-10-06 19:58:03 -0400592 **args: Any) -> QMPMessage:
593 """
594 Invoke a QMP command and return the response dict
595 """
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300596 if args_dict is not None:
597 assert not args
598 assert conv_keys is None
599 args = args_dict
600 conv_keys = False
601
602 if conv_keys is None:
603 conv_keys = True
604
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300605 qmp_args = self._qmp_args(conv_keys, args)
John Snowabf0bf92019-06-27 17:28:14 -0400606 return self._qmp.cmd(cmd, args=qmp_args)
607
John Snowf12a2822020-10-06 19:58:08 -0400608 def command(self, cmd: str,
609 conv_keys: bool = True,
610 **args: Any) -> QMPReturnValue:
John Snowabf0bf92019-06-27 17:28:14 -0400611 """
612 Invoke a QMP command.
613 On success return the response dict.
614 On failure raise an exception.
615 """
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300616 qmp_args = self._qmp_args(conv_keys, args)
John Snowaaa81ec2020-10-06 19:58:03 -0400617 return self._qmp.command(cmd, **qmp_args)
John Snowabf0bf92019-06-27 17:28:14 -0400618
John Snowf12a2822020-10-06 19:58:08 -0400619 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400620 """
621 Poll for one queued QMP events and return it
622 """
John Snow306dfcd2019-06-27 17:28:15 -0400623 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400624 return self._events.pop(0)
625 return self._qmp.pull_event(wait=wait)
626
John Snowf12a2822020-10-06 19:58:08 -0400627 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400628 """
629 Poll for queued QMP events and return a list of dicts
630 """
631 events = self._qmp.get_events(wait=wait)
632 events.extend(self._events)
633 del self._events[:]
John Snowabf0bf92019-06-27 17:28:14 -0400634 return events
635
636 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400637 def event_match(event: Any, match: Optional[Any]) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400638 """
639 Check if an event matches optional match criteria.
640
641 The match criteria takes the form of a matching subdict. The event is
642 checked to be a superset of the subdict, recursively, with matching
643 values whenever the subdict values are not None.
644
645 This has a limitation that you cannot explicitly check for None values.
646
647 Examples, with the subdict queries on the left:
648 - None matches any object.
649 - {"foo": None} matches {"foo": {"bar": 1}}
650 - {"foo": None} matches {"foo": 5}
651 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
652 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
653 """
654 if match is None:
655 return True
656
657 try:
658 for key in match:
659 if key in event:
660 if not QEMUMachine.event_match(event[key], match[key]):
661 return False
662 else:
663 return False
664 return True
665 except TypeError:
666 # either match or event wasn't iterable (not a dict)
John Snowf12a2822020-10-06 19:58:08 -0400667 return bool(match == event)
John Snowabf0bf92019-06-27 17:28:14 -0400668
John Snowf12a2822020-10-06 19:58:08 -0400669 def event_wait(self, name: str,
670 timeout: float = 60.0,
671 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400672 """
673 event_wait waits for and returns a named event from QMP with a timeout.
674
675 name: The event to wait for.
676 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
677 match: Optional match criteria. See event_match for details.
678 """
679 return self.events_wait([(name, match)], timeout)
680
John Snowf12a2822020-10-06 19:58:08 -0400681 def events_wait(self,
682 events: Sequence[Tuple[str, Any]],
683 timeout: float = 60.0) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400684 """
John Snow1847a4a2020-10-06 19:58:02 -0400685 events_wait waits for and returns a single named event from QMP.
686 In the case of multiple qualifying events, this function returns the
687 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400688
John Snow1847a4a2020-10-06 19:58:02 -0400689 :param events: A sequence of (name, match_criteria) tuples.
690 The match criteria are optional and may be None.
691 See event_match for details.
692 :param timeout: Optional timeout, in seconds.
693 See QEMUMonitorProtocol.pull_event.
694
695 :raise QMPTimeoutError: If timeout was non-zero and no matching events
696 were found.
697 :return: A QMP event matching the filter criteria.
698 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400699 """
John Snowf12a2822020-10-06 19:58:08 -0400700 def _match(event: QMPMessage) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400701 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400702 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400703 return True
704 return False
705
John Snow1847a4a2020-10-06 19:58:02 -0400706 event: Optional[QMPMessage]
707
John Snowabf0bf92019-06-27 17:28:14 -0400708 # Search cached events
709 for event in self._events:
710 if _match(event):
711 self._events.remove(event)
712 return event
713
714 # Poll for new events
715 while True:
716 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400717 if event is None:
718 # NB: None is only returned when timeout is false-ish.
719 # Timeouts raise QMPTimeoutError instead!
720 break
John Snowabf0bf92019-06-27 17:28:14 -0400721 if _match(event):
722 return event
723 self._events.append(event)
724
725 return None
726
John Snowf12a2822020-10-06 19:58:08 -0400727 def get_log(self) -> Optional[str]:
John Snowabf0bf92019-06-27 17:28:14 -0400728 """
729 After self.shutdown or failed qemu execution, this returns the output
730 of the qemu process.
731 """
732 return self._iolog
733
John Snowf12a2822020-10-06 19:58:08 -0400734 def add_args(self, *args: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400735 """
736 Adds to the list of extra arguments to be given to the QEMU binary
737 """
738 self._args.extend(args)
739
John Snowf12a2822020-10-06 19:58:08 -0400740 def set_machine(self, machine_type: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400741 """
742 Sets the machine type
743
744 If set, the machine type will be added to the base arguments
745 of the resulting QEMU command line.
746 """
747 self._machine = machine_type
748
John Snowf12a2822020-10-06 19:58:08 -0400749 def set_console(self,
750 device_type: Optional[str] = None,
751 console_index: int = 0) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400752 """
753 Sets the device type for a console device
754
755 If set, the console device and a backing character device will
756 be added to the base arguments of the resulting QEMU command
757 line.
758
759 This is a convenience method that will either use the provided
760 device type, or default to a "-serial chardev:console" command
761 line argument.
762
763 The actual setting of command line arguments will be be done at
764 machine launch time, as it depends on the temporary directory
765 to be created.
766
767 @param device_type: the device type, such as "isa-serial". If
768 None is given (the default value) a "-serial
769 chardev:console" command line argument will
770 be used instead, resorting to the machine's
771 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100772 @param console_index: the index of the console device to use.
773 If not zero, the command line will create
774 'index - 1' consoles and connect them to
775 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400776 """
777 self._console_set = True
778 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100779 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400780
781 @property
John Snowf12a2822020-10-06 19:58:08 -0400782 def console_socket(self) -> socket.socket:
John Snowabf0bf92019-06-27 17:28:14 -0400783 """
784 Returns a socket connected to the console
785 """
786 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100787 self._console_socket = console_socket.ConsoleSocket(
788 self._console_address,
789 file=self._console_log_path,
790 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400791 return self._console_socket
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500792
793 @property
794 def temp_dir(self) -> str:
795 """
796 Returns a temporary directory to be used for this machine
797 """
798 if self._temp_dir is None:
799 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
800 dir=self._base_temp_dir)
801 return self._temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500802
803 @property
804 def log_dir(self) -> str:
805 """
806 Returns a directory to be used for writing logs
807 """
808 if self._log_dir is None:
809 return self.temp_dir
810 return self._log_dir