blob: a0cf69786b4bb7e851b5eeb2517b67036ddb3be1 [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 sock_dir: Optional[str] = None,
102 drain_console: bool = False,
Cleber Rosab306e262021-02-11 16:55:05 -0500103 console_log: Optional[str] = None,
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200104 log_dir: Optional[str] = None,
105 qmp_timer: Optional[float] = None):
John Snowabf0bf92019-06-27 17:28:14 -0400106 '''
107 Initialize a QEMUMachine
108
109 @param binary: path to the qemu binary
110 @param args: list of extra arguments
111 @param wrapper: list of arguments used as prefix to qemu binary
112 @param name: prefix for socket and log file names (default: qemu-PID)
John Snow859aeb62021-05-27 17:16:51 -0400113 @param base_temp_dir: default location where temp files are created
John Snowabf0bf92019-06-27 17:28:14 -0400114 @param monitor_address: address for QMP monitor
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500115 @param sock_dir: where to create socket (defaults to base_temp_dir)
Robert Foley0fc8f662020-07-01 14:56:24 +0100116 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -0400117 @param console_log: (optional) path to console log file
Cleber Rosab306e262021-02-11 16:55:05 -0500118 @param log_dir: where to create and keep log files
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200119 @param qmp_timer: (optional) default QMP socket timeout
John Snowabf0bf92019-06-27 17:28:14 -0400120 @note: Qemu process is not started until launch() is used.
121 '''
John Snow82e65172021-06-29 17:43:11 -0400122 # pylint: disable=too-many-arguments
123
John Snowc5e61a62020-10-06 19:58:00 -0400124 # Direct user configuration
125
126 self._binary = binary
John Snowc5e61a62020-10-06 19:58:00 -0400127 self._args = list(args)
John Snowc5e61a62020-10-06 19:58:00 -0400128 self._wrapper = wrapper
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200129 self._qmp_timer = qmp_timer
John Snowc5e61a62020-10-06 19:58:00 -0400130
131 self._name = name or "qemu-%d" % os.getpid()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500132 self._base_temp_dir = base_temp_dir
133 self._sock_dir = sock_dir or self._base_temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500134 self._log_dir = log_dir
John Snowc5e61a62020-10-06 19:58:00 -0400135
John Snowc4e60232020-10-06 19:57:59 -0400136 if monitor_address is not None:
137 self._monitor_address = monitor_address
138 self._remove_monitor_sockfile = False
139 else:
140 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400141 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400142 )
143 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400144
145 self._console_log_path = console_log
146 if self._console_log_path:
147 # In order to log the console, buffering needs to be enabled.
148 self._drain_console = True
149 else:
150 self._drain_console = drain_console
151
152 # Runstate
John Snowf12a2822020-10-06 19:58:08 -0400153 self._qemu_log_path: Optional[str] = None
154 self._qemu_log_file: Optional[BinaryIO] = None
John Snow9223fda2020-10-06 19:58:05 -0400155 self._popen: Optional['subprocess.Popen[bytes]'] = None
John Snowf12a2822020-10-06 19:58:08 -0400156 self._events: List[QMPMessage] = []
157 self._iolog: Optional[str] = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500158 self._qmp_set = True # Enable QMP monitor by default.
John Snowbeb6b572021-05-27 17:16:53 -0400159 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
John Snowaad3f3b2020-10-06 19:58:06 -0400160 self._qemu_full_args: Tuple[str, ...] = ()
John Snowf12a2822020-10-06 19:58:08 -0400161 self._temp_dir: Optional[str] = None
John Snowabf0bf92019-06-27 17:28:14 -0400162 self._launched = False
John Snowf12a2822020-10-06 19:58:08 -0400163 self._machine: Optional[str] = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100164 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400165 self._console_set = False
John Snowf12a2822020-10-06 19:58:08 -0400166 self._console_device_type: Optional[str] = None
John Snow652809d2020-10-06 19:58:01 -0400167 self._console_address = os.path.join(
168 self._sock_dir, f"{self._name}-console.sock"
169 )
John Snowf12a2822020-10-06 19:58:08 -0400170 self._console_socket: Optional[socket.socket] = None
171 self._remove_files: List[str] = []
John Snowde6e08b2020-07-10 01:06:48 -0400172 self._user_killed = False
John Snowb9420e42021-10-26 13:56:05 -0400173 self._quit_issued = False
John Snowabf0bf92019-06-27 17:28:14 -0400174
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300175 def __enter__(self: _T) -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400176 return self
177
John Snow1dda0402020-05-14 01:53:44 -0400178 def __exit__(self,
179 exc_type: Optional[Type[BaseException]],
180 exc_val: Optional[BaseException],
181 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400182 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400183
John Snowf12a2822020-10-06 19:58:08 -0400184 def add_monitor_null(self) -> None:
John Snow306dfcd2019-06-27 17:28:15 -0400185 """
186 This can be used to add an unused monitor instance.
187 """
John Snowabf0bf92019-06-27 17:28:14 -0400188 self._args.append('-monitor')
189 self._args.append('null')
190
Vladimir Sementsov-Ogievskiy15c3b862021-08-24 11:38:47 +0300191 def add_fd(self: _T, fd: int, fdset: int,
192 opaque: str, opts: str = '') -> _T:
John Snowabf0bf92019-06-27 17:28:14 -0400193 """
194 Pass a file descriptor to the VM
195 """
196 options = ['fd=%d' % fd,
197 'set=%d' % fdset,
198 'opaque=%s' % opaque]
199 if opts:
200 options.append(opts)
201
202 # This did not exist before 3.4, but since then it is
203 # mandatory for our purpose
204 if hasattr(os, 'set_inheritable'):
205 os.set_inheritable(fd, True)
206
207 self._args.append('-add-fd')
208 self._args.append(','.join(options))
209 return self
210
John Snowf12a2822020-10-06 19:58:08 -0400211 def send_fd_scm(self, fd: Optional[int] = None,
212 file_path: Optional[str] = None) -> int:
John Snow306dfcd2019-06-27 17:28:15 -0400213 """
John Snow514d00d2021-09-22 20:49:30 -0400214 Send an fd or file_path to the remote via SCM_RIGHTS.
John Snow306dfcd2019-06-27 17:28:15 -0400215
John Snow514d00d2021-09-22 20:49:30 -0400216 Exactly one of fd and file_path must be given. If it is
217 file_path, the file will be opened read-only and the new file
218 descriptor will be sent to the remote.
John Snow306dfcd2019-06-27 17:28:15 -0400219 """
John Snowabf0bf92019-06-27 17:28:14 -0400220 if file_path is not None:
221 assert fd is None
John Snow514d00d2021-09-22 20:49:30 -0400222 with open(file_path, "rb") as passfile:
223 fd = passfile.fileno()
224 self._qmp.send_fd_scm(fd)
John Snowabf0bf92019-06-27 17:28:14 -0400225 else:
226 assert fd is not None
John Snow514d00d2021-09-22 20:49:30 -0400227 self._qmp.send_fd_scm(fd)
John Snowabf0bf92019-06-27 17:28:14 -0400228
John Snow514d00d2021-09-22 20:49:30 -0400229 return 0
John Snowabf0bf92019-06-27 17:28:14 -0400230
231 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400232 def _remove_if_exists(path: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400233 """
234 Remove file object at path if it exists
235 """
236 try:
237 os.remove(path)
238 except OSError as exception:
239 if exception.errno == errno.ENOENT:
240 return
241 raise
242
John Snowf12a2822020-10-06 19:58:08 -0400243 def is_running(self) -> bool:
John Snow306dfcd2019-06-27 17:28:15 -0400244 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400245 return self._popen is not None and self._popen.poll() is None
246
John Snow9223fda2020-10-06 19:58:05 -0400247 @property
248 def _subp(self) -> 'subprocess.Popen[bytes]':
249 if self._popen is None:
250 raise QEMUMachineError('Subprocess pipe not present')
251 return self._popen
252
John Snowf12a2822020-10-06 19:58:08 -0400253 def exitcode(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400254 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400255 if self._popen is None:
256 return None
257 return self._popen.poll()
258
John Snowf12a2822020-10-06 19:58:08 -0400259 def get_pid(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400260 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400261 if not self.is_running():
262 return None
John Snow9223fda2020-10-06 19:58:05 -0400263 return self._subp.pid
John Snowabf0bf92019-06-27 17:28:14 -0400264
John Snowf12a2822020-10-06 19:58:08 -0400265 def _load_io_log(self) -> None:
John Snow5690b432021-09-16 14:22:47 -0400266 # Assume that the output encoding of QEMU's terminal output is
267 # defined by our locale. If indeterminate, allow open() to fall
268 # back to the platform default.
269 _, encoding = locale.getlocale()
John Snowabf0bf92019-06-27 17:28:14 -0400270 if self._qemu_log_path is not None:
John Snow5690b432021-09-16 14:22:47 -0400271 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
John Snowabf0bf92019-06-27 17:28:14 -0400272 self._iolog = iolog.read()
273
John Snow652809d2020-10-06 19:58:01 -0400274 @property
275 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500276 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400277
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500278 if self._qmp_set:
279 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400280 moncdev = "socket,id=mon,host={},port={}".format(
281 *self._monitor_address
282 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500283 else:
John Snowc4e60232020-10-06 19:57:59 -0400284 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500285 args.extend(['-chardev', moncdev, '-mon',
286 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400287
John Snowabf0bf92019-06-27 17:28:14 -0400288 if self._machine is not None:
289 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400290 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100291 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400292 if self._console_set:
Paolo Bonzini991c1802020-11-13 03:10:52 -0500293 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
John Snowabf0bf92019-06-27 17:28:14 -0400294 self._console_address)
295 args.extend(['-chardev', chardev])
296 if self._console_device_type is None:
297 args.extend(['-serial', 'chardev:console'])
298 else:
299 device = '%s,chardev=console' % self._console_device_type
300 args.extend(['-device', device])
301 return args
302
Wainer dos Santos Moschetta555fe0c2021-04-30 10:34:12 -0300303 @property
304 def args(self) -> List[str]:
305 """Returns the list of arguments given to the QEMU binary."""
306 return self._args
307
John Snowf12a2822020-10-06 19:58:08 -0400308 def _pre_launch(self) -> None:
John Snow652809d2020-10-06 19:58:01 -0400309 if self._console_set:
310 self._remove_files.append(self._console_address)
311
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500312 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400313 if self._remove_monitor_sockfile:
314 assert isinstance(self._monitor_address, str)
315 self._remove_files.append(self._monitor_address)
John Snowbeb6b572021-05-27 17:16:53 -0400316 self._qmp_connection = QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400317 self._monitor_address,
318 server=True,
319 nickname=self._name
320 )
John Snowabf0bf92019-06-27 17:28:14 -0400321
John Snow63c33f32021-05-27 17:16:49 -0400322 # NOTE: Make sure any opened resources are *definitely* freed in
323 # _post_shutdown()!
324 # pylint: disable=consider-using-with
Cleber Rosab306e262021-02-11 16:55:05 -0500325 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
John Snow63c33f32021-05-27 17:16:49 -0400326 self._qemu_log_file = open(self._qemu_log_path, 'wb')
327
John Snowf12a2822020-10-06 19:58:08 -0400328 def _post_launch(self) -> None:
John Snowbe1183e2020-10-06 19:58:04 -0400329 if self._qmp_connection:
Emanuele Giuseppe Espositoe2f948a2021-08-09 11:00:59 +0200330 self._qmp.accept(self._qmp_timer)
John Snowabf0bf92019-06-27 17:28:14 -0400331
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200332 def _close_qemu_log_file(self) -> None:
333 if self._qemu_log_file is not None:
334 self._qemu_log_file.close()
335 self._qemu_log_file = None
336
John Snowf12a2822020-10-06 19:58:08 -0400337 def _post_shutdown(self) -> None:
John Snowa3842cb2020-07-10 01:06:42 -0400338 """
339 Called to cleanup the VM instance after the process has exited.
340 May also be called after a failed launch.
341 """
342 # Comprehensive reset for the failed launch case:
343 self._early_cleanup()
344
John Snow49a608b2021-10-26 13:56:06 -0400345 try:
346 self._close_qmp_connection()
347 except Exception as err: # pylint: disable=broad-except
348 LOG.warning(
349 "Exception closing QMP connection: %s",
350 str(err) if str(err) else type(err).__name__
351 )
352 finally:
353 assert self._qmp_connection is None
John Snow671940e2020-07-10 01:06:39 -0400354
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +0200355 self._close_qemu_log_file()
John Snowabf0bf92019-06-27 17:28:14 -0400356
Cleber Rosa3c1e16c2021-02-11 17:01:41 -0500357 self._load_io_log()
358
John Snowabf0bf92019-06-27 17:28:14 -0400359 self._qemu_log_path = None
360
John Snowabf0bf92019-06-27 17:28:14 -0400361 if self._temp_dir is not None:
362 shutil.rmtree(self._temp_dir)
363 self._temp_dir = None
364
Max Reitz32558ce2019-10-17 15:31:34 +0200365 while len(self._remove_files) > 0:
366 self._remove_if_exists(self._remove_files.pop())
367
John Snow14661d92020-07-10 01:06:38 -0400368 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400369 if (exitcode is not None and exitcode < 0
370 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400371 msg = 'qemu received signal %i; command: "%s"'
372 if self._qemu_full_args:
373 command = ' '.join(self._qemu_full_args)
374 else:
375 command = ''
376 LOG.warning(msg, -int(exitcode), command)
377
John Snowb9420e42021-10-26 13:56:05 -0400378 self._quit_issued = False
John Snowde6e08b2020-07-10 01:06:48 -0400379 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400380 self._launched = False
381
John Snowf12a2822020-10-06 19:58:08 -0400382 def launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400383 """
384 Launch the VM and make sure we cleanup and expose the
385 command line/output in case of exception
386 """
387
388 if self._launched:
389 raise QEMUMachineError('VM already launched')
390
391 self._iolog = None
John Snowaad3f3b2020-10-06 19:58:06 -0400392 self._qemu_full_args = ()
John Snowabf0bf92019-06-27 17:28:14 -0400393 try:
394 self._launch()
395 self._launched = True
396 except:
John Snowa3842cb2020-07-10 01:06:42 -0400397 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400398
399 LOG.debug('Error launching VM')
400 if self._qemu_full_args:
401 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
402 if self._iolog:
403 LOG.debug('Output: %r', self._iolog)
404 raise
405
John Snowf12a2822020-10-06 19:58:08 -0400406 def _launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400407 """
408 Launch the VM and establish a QMP connection
409 """
John Snowabf0bf92019-06-27 17:28:14 -0400410 self._pre_launch()
John Snowaad3f3b2020-10-06 19:58:06 -0400411 self._qemu_full_args = tuple(
412 chain(self._wrapper,
413 [self._binary],
414 self._base_args,
415 self._args)
416 )
John Snowabf0bf92019-06-27 17:28:14 -0400417 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
John Snowa0eae172021-05-27 17:16:50 -0400418
419 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
420 # pylint: disable=consider-using-with
John Snowabf0bf92019-06-27 17:28:14 -0400421 self._popen = subprocess.Popen(self._qemu_full_args,
John Snow07b71232021-05-27 17:16:46 -0400422 stdin=subprocess.DEVNULL,
John Snowabf0bf92019-06-27 17:28:14 -0400423 stdout=self._qemu_log_file,
424 stderr=subprocess.STDOUT,
425 shell=False,
426 close_fds=False)
427 self._post_launch()
428
John Snow49a608b2021-10-26 13:56:06 -0400429 def _close_qmp_connection(self) -> None:
430 """
431 Close the underlying QMP connection, if any.
432
433 Dutifully report errors that occurred while closing, but assume
434 that any error encountered indicates an abnormal termination
435 process and not a failure to close.
436 """
437 if self._qmp_connection is None:
438 return
439
440 try:
441 self._qmp.close()
442 except EOFError:
443 # EOF can occur as an Exception here when using the Async
444 # QMP backend. It indicates that the server closed the
445 # stream. If we successfully issued 'quit' at any point,
446 # then this was expected. If the remote went away without
447 # our permission, it's worth reporting that as an abnormal
448 # shutdown case.
449 if not (self._user_killed or self._quit_issued):
450 raise
451 finally:
452 self._qmp_connection = None
453
John Snowe2c97f12020-07-10 01:06:40 -0400454 def _early_cleanup(self) -> None:
455 """
456 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400457
John Snow193bf1c2020-07-10 01:06:47 -0400458 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400459 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400460 """
461 # If we keep the console socket open, we may deadlock waiting
462 # for QEMU to exit, while QEMU is waiting for the socket to
463 # become writeable.
464 if self._console_socket is not None:
465 self._console_socket.close()
466 self._console_socket = None
467
John Snow193bf1c2020-07-10 01:06:47 -0400468 def _hard_shutdown(self) -> None:
469 """
470 Perform early cleanup, kill the VM, and wait for it to terminate.
471
472 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
473 waiting for the QEMU process to terminate.
474 """
475 self._early_cleanup()
John Snow9223fda2020-10-06 19:58:05 -0400476 self._subp.kill()
477 self._subp.wait(timeout=60)
John Snow193bf1c2020-07-10 01:06:47 -0400478
John Snowb9420e42021-10-26 13:56:05 -0400479 def _soft_shutdown(self, timeout: Optional[int]) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400480 """
481 Perform early cleanup, attempt to gracefully shut down the VM, and wait
482 for it to terminate.
483
John Snow8226a4b2020-07-20 12:02:52 -0400484 :param timeout: Timeout in seconds for graceful shutdown.
485 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400486
487 :raise ConnectionReset: On QMP communication errors
488 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
489 the QEMU process to terminate.
490 """
491 self._early_cleanup()
492
John Snowbe1183e2020-10-06 19:58:04 -0400493 if self._qmp_connection:
John Snow49a608b2021-10-26 13:56:06 -0400494 try:
495 if not self._quit_issued:
496 # May raise ExecInterruptedError or StateError if the
497 # connection dies or has *already* died.
498 self.qmp('quit')
499 finally:
500 # Regardless, we want to quiesce the connection.
501 self._close_qmp_connection()
John Snow193bf1c2020-07-10 01:06:47 -0400502
503 # May raise subprocess.TimeoutExpired
John Snow9223fda2020-10-06 19:58:05 -0400504 self._subp.wait(timeout=timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400505
John Snowb9420e42021-10-26 13:56:05 -0400506 def _do_shutdown(self, timeout: Optional[int]) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400507 """
508 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
509
John Snow8226a4b2020-07-20 12:02:52 -0400510 :param timeout: Timeout in seconds for graceful shutdown.
511 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400512
513 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
514 The inner exception will likely be ConnectionReset or
515 subprocess.TimeoutExpired. In rare cases, non-graceful termination
516 may result in its own exceptions, likely subprocess.TimeoutExpired.
517 """
518 try:
John Snowb9420e42021-10-26 13:56:05 -0400519 self._soft_shutdown(timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400520 except Exception as exc:
521 self._hard_shutdown()
522 raise AbnormalShutdown("Could not perform graceful shutdown") \
523 from exc
524
John Snowb9420e42021-10-26 13:56:05 -0400525 def shutdown(self,
John Snowc9b30452020-07-10 01:06:43 -0400526 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400527 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400528 """
John Snow193bf1c2020-07-10 01:06:47 -0400529 Terminate the VM (gracefully if possible) and perform cleanup.
530 Cleanup will always be performed.
531
532 If the VM has not yet been launched, or shutdown(), wait(), or kill()
533 have already been called, this method does nothing.
534
John Snow193bf1c2020-07-10 01:06:47 -0400535 :param hard: When true, do not attempt graceful shutdown, and
536 suppress the SIGKILL warning log message.
537 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400538 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400539 """
John Snowa3842cb2020-07-10 01:06:42 -0400540 if not self._launched:
541 return
542
John Snow193bf1c2020-07-10 01:06:47 -0400543 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300544 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400545 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400546 self._hard_shutdown()
547 else:
John Snowb9420e42021-10-26 13:56:05 -0400548 self._do_shutdown(timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400549 finally:
550 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400551
John Snowf12a2822020-10-06 19:58:08 -0400552 def kill(self) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400553 """
554 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
555 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300556 self.shutdown(hard=True)
557
John Snow8226a4b2020-07-20 12:02:52 -0400558 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400559 """
560 Wait for the VM to power off and perform post-shutdown cleanup.
561
John Snow8226a4b2020-07-20 12:02:52 -0400562 :param timeout: Optional timeout in seconds. Default 30 seconds.
563 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400564 """
John Snowb9420e42021-10-26 13:56:05 -0400565 self._quit_issued = True
566 self.shutdown(timeout=timeout)
John Snow89528052020-07-10 01:06:44 -0400567
John Snowf12a2822020-10-06 19:58:08 -0400568 def set_qmp_monitor(self, enabled: bool = True) -> None:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500569 """
570 Set the QMP monitor.
571
572 @param enabled: if False, qmp monitor options will be removed from
573 the base arguments of the resulting QEMU command
574 line. Default is True.
John Snow5c02c862021-06-29 17:43:23 -0400575
576 .. note:: Call this function before launch().
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500577 """
John Snowbe1183e2020-10-06 19:58:04 -0400578 self._qmp_set = enabled
579
580 @property
John Snowbeb6b572021-05-27 17:16:53 -0400581 def _qmp(self) -> QEMUMonitorProtocol:
John Snowbe1183e2020-10-06 19:58:04 -0400582 if self._qmp_connection is None:
583 raise QEMUMachineError("Attempt to access QMP with no connection")
584 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500585
John Snowaaa81ec2020-10-06 19:58:03 -0400586 @classmethod
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300587 def _qmp_args(cls, conv_keys: bool,
588 args: Dict[str, Any]) -> Dict[str, object]:
589 if conv_keys:
590 return {k.replace('_', '-'): v for k, v in args.items()}
591
592 return args
John Snowabf0bf92019-06-27 17:28:14 -0400593
John Snowaaa81ec2020-10-06 19:58:03 -0400594 def qmp(self, cmd: str,
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300595 args_dict: Optional[Dict[str, object]] = None,
596 conv_keys: Optional[bool] = None,
John Snowaaa81ec2020-10-06 19:58:03 -0400597 **args: Any) -> QMPMessage:
598 """
599 Invoke a QMP command and return the response dict
600 """
Vladimir Sementsov-Ogievskiy3f3c9b42021-08-24 11:38:46 +0300601 if args_dict is not None:
602 assert not args
603 assert conv_keys is None
604 args = args_dict
605 conv_keys = False
606
607 if conv_keys is None:
608 conv_keys = True
609
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300610 qmp_args = self._qmp_args(conv_keys, args)
John Snowb9420e42021-10-26 13:56:05 -0400611 ret = self._qmp.cmd(cmd, args=qmp_args)
612 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
613 self._quit_issued = True
614 return ret
John Snowabf0bf92019-06-27 17:28:14 -0400615
John Snowf12a2822020-10-06 19:58:08 -0400616 def command(self, cmd: str,
617 conv_keys: bool = True,
618 **args: Any) -> QMPReturnValue:
John Snowabf0bf92019-06-27 17:28:14 -0400619 """
620 Invoke a QMP command.
621 On success return the response dict.
622 On failure raise an exception.
623 """
Vladimir Sementsov-Ogievskiyc7daa572021-08-24 11:38:45 +0300624 qmp_args = self._qmp_args(conv_keys, args)
John Snowb9420e42021-10-26 13:56:05 -0400625 ret = self._qmp.command(cmd, **qmp_args)
626 if cmd == 'quit':
627 self._quit_issued = True
628 return ret
John Snowabf0bf92019-06-27 17:28:14 -0400629
John Snowf12a2822020-10-06 19:58:08 -0400630 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400631 """
632 Poll for one queued QMP events and return it
633 """
John Snow306dfcd2019-06-27 17:28:15 -0400634 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400635 return self._events.pop(0)
636 return self._qmp.pull_event(wait=wait)
637
John Snowf12a2822020-10-06 19:58:08 -0400638 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400639 """
640 Poll for queued QMP events and return a list of dicts
641 """
642 events = self._qmp.get_events(wait=wait)
643 events.extend(self._events)
644 del self._events[:]
John Snowabf0bf92019-06-27 17:28:14 -0400645 return events
646
647 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400648 def event_match(event: Any, match: Optional[Any]) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400649 """
650 Check if an event matches optional match criteria.
651
652 The match criteria takes the form of a matching subdict. The event is
653 checked to be a superset of the subdict, recursively, with matching
654 values whenever the subdict values are not None.
655
656 This has a limitation that you cannot explicitly check for None values.
657
658 Examples, with the subdict queries on the left:
659 - None matches any object.
660 - {"foo": None} matches {"foo": {"bar": 1}}
661 - {"foo": None} matches {"foo": 5}
662 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
663 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
664 """
665 if match is None:
666 return True
667
668 try:
669 for key in match:
670 if key in event:
671 if not QEMUMachine.event_match(event[key], match[key]):
672 return False
673 else:
674 return False
675 return True
676 except TypeError:
677 # either match or event wasn't iterable (not a dict)
John Snowf12a2822020-10-06 19:58:08 -0400678 return bool(match == event)
John Snowabf0bf92019-06-27 17:28:14 -0400679
John Snowf12a2822020-10-06 19:58:08 -0400680 def event_wait(self, name: str,
681 timeout: float = 60.0,
682 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400683 """
684 event_wait waits for and returns a named event from QMP with a timeout.
685
686 name: The event to wait for.
687 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
688 match: Optional match criteria. See event_match for details.
689 """
690 return self.events_wait([(name, match)], timeout)
691
John Snowf12a2822020-10-06 19:58:08 -0400692 def events_wait(self,
693 events: Sequence[Tuple[str, Any]],
694 timeout: float = 60.0) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400695 """
John Snow1847a4a2020-10-06 19:58:02 -0400696 events_wait waits for and returns a single named event from QMP.
697 In the case of multiple qualifying events, this function returns the
698 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400699
John Snow1847a4a2020-10-06 19:58:02 -0400700 :param events: A sequence of (name, match_criteria) tuples.
701 The match criteria are optional and may be None.
702 See event_match for details.
703 :param timeout: Optional timeout, in seconds.
704 See QEMUMonitorProtocol.pull_event.
705
706 :raise QMPTimeoutError: If timeout was non-zero and no matching events
707 were found.
708 :return: A QMP event matching the filter criteria.
709 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400710 """
John Snowf12a2822020-10-06 19:58:08 -0400711 def _match(event: QMPMessage) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400712 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400713 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400714 return True
715 return False
716
John Snow1847a4a2020-10-06 19:58:02 -0400717 event: Optional[QMPMessage]
718
John Snowabf0bf92019-06-27 17:28:14 -0400719 # Search cached events
720 for event in self._events:
721 if _match(event):
722 self._events.remove(event)
723 return event
724
725 # Poll for new events
726 while True:
727 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400728 if event is None:
729 # NB: None is only returned when timeout is false-ish.
730 # Timeouts raise QMPTimeoutError instead!
731 break
John Snowabf0bf92019-06-27 17:28:14 -0400732 if _match(event):
733 return event
734 self._events.append(event)
735
736 return None
737
John Snowf12a2822020-10-06 19:58:08 -0400738 def get_log(self) -> Optional[str]:
John Snowabf0bf92019-06-27 17:28:14 -0400739 """
740 After self.shutdown or failed qemu execution, this returns the output
741 of the qemu process.
742 """
743 return self._iolog
744
John Snowf12a2822020-10-06 19:58:08 -0400745 def add_args(self, *args: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400746 """
747 Adds to the list of extra arguments to be given to the QEMU binary
748 """
749 self._args.extend(args)
750
John Snowf12a2822020-10-06 19:58:08 -0400751 def set_machine(self, machine_type: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400752 """
753 Sets the machine type
754
755 If set, the machine type will be added to the base arguments
756 of the resulting QEMU command line.
757 """
758 self._machine = machine_type
759
John Snowf12a2822020-10-06 19:58:08 -0400760 def set_console(self,
761 device_type: Optional[str] = None,
762 console_index: int = 0) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400763 """
764 Sets the device type for a console device
765
766 If set, the console device and a backing character device will
767 be added to the base arguments of the resulting QEMU command
768 line.
769
770 This is a convenience method that will either use the provided
771 device type, or default to a "-serial chardev:console" command
772 line argument.
773
774 The actual setting of command line arguments will be be done at
775 machine launch time, as it depends on the temporary directory
776 to be created.
777
778 @param device_type: the device type, such as "isa-serial". If
779 None is given (the default value) a "-serial
780 chardev:console" command line argument will
781 be used instead, resorting to the machine's
782 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100783 @param console_index: the index of the console device to use.
784 If not zero, the command line will create
785 'index - 1' consoles and connect them to
786 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400787 """
788 self._console_set = True
789 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100790 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400791
792 @property
John Snowf12a2822020-10-06 19:58:08 -0400793 def console_socket(self) -> socket.socket:
John Snowabf0bf92019-06-27 17:28:14 -0400794 """
795 Returns a socket connected to the console
796 """
797 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100798 self._console_socket = console_socket.ConsoleSocket(
799 self._console_address,
800 file=self._console_log_path,
801 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400802 return self._console_socket
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500803
804 @property
805 def temp_dir(self) -> str:
806 """
807 Returns a temporary directory to be used for this machine
808 """
809 if self._temp_dir is None:
810 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
811 dir=self._base_temp_dir)
812 return self._temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500813
814 @property
815 def log_dir(self) -> str:
816 """
817 Returns a directory to be used for writing logs
818 """
819 if self._log_dir is None:
820 return self.temp_dir
821 return self._log_dir