blob: 94846dd71b4a96a2307c53cda5b997d2c65066bd [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 Snowabf0bf92019-06-27 17:28:14 -040022import logging
23import os
John Snowabf0bf92019-06-27 17:28:14 -040024import shutil
John Snowde6e08b2020-07-10 01:06:48 -040025import signal
John Snowf12a2822020-10-06 19:58:08 -040026import socket
John Snow932ca4b2020-10-06 19:57:58 -040027import subprocess
John Snowabf0bf92019-06-27 17:28:14 -040028import tempfile
John Snow1dda0402020-05-14 01:53:44 -040029from types import TracebackType
John Snowaaa81ec2020-10-06 19:58:03 -040030from typing import (
31 Any,
John Snowf12a2822020-10-06 19:58:08 -040032 BinaryIO,
John Snowaaa81ec2020-10-06 19:58:03 -040033 Dict,
34 List,
35 Optional,
John Snowaad3f3b2020-10-06 19:58:06 -040036 Sequence,
37 Tuple,
John Snowaaa81ec2020-10-06 19:58:03 -040038 Type,
39)
John Snowabf0bf92019-06-27 17:28:14 -040040
John Snowd1e04762021-05-27 17:16:59 -040041from qemu.qmp import ( # pylint: disable=import-error
John Snowbeb6b572021-05-27 17:16:53 -040042 QEMUMonitorProtocol,
43 QMPMessage,
44 QMPReturnValue,
45 SocketAddrT,
46)
47
48from . import console_socket
John Snow932ca4b2020-10-06 19:57:58 -040049
John Snowabf0bf92019-06-27 17:28:14 -040050
51LOG = logging.getLogger(__name__)
52
John Snow8dfac2e2020-05-28 18:21:29 -040053
John Snowabf0bf92019-06-27 17:28:14 -040054class QEMUMachineError(Exception):
55 """
56 Exception called when an error in QEMUMachine happens.
57 """
58
59
60class QEMUMachineAddDeviceError(QEMUMachineError):
61 """
62 Exception raised when a request to add a device can not be fulfilled
63
64 The failures are caused by limitations, lack of information or conflicting
65 requests on the QEMUMachine methods. This exception does not represent
66 failures reported by the QEMU binary itself.
67 """
68
69
John Snow193bf1c2020-07-10 01:06:47 -040070class AbnormalShutdown(QEMUMachineError):
71 """
72 Exception raised when a graceful shutdown was requested, but not performed.
73 """
74
75
John Snow9b8ccd62020-05-28 18:21:28 -040076class QEMUMachine:
John Snowabf0bf92019-06-27 17:28:14 -040077 """
John Snowf12a2822020-10-06 19:58:08 -040078 A QEMU VM.
John Snowabf0bf92019-06-27 17:28:14 -040079
John Snow8dfac2e2020-05-28 18:21:29 -040080 Use this object as a context manager to ensure
81 the QEMU process terminates::
John Snowabf0bf92019-06-27 17:28:14 -040082
83 with VM(binary) as vm:
84 ...
85 # vm is guaranteed to be shut down here
86 """
John Snow82e65172021-06-29 17:43:11 -040087 # pylint: disable=too-many-instance-attributes, too-many-public-methods
John Snowabf0bf92019-06-27 17:28:14 -040088
John Snowaad3f3b2020-10-06 19:58:06 -040089 def __init__(self,
90 binary: str,
91 args: Sequence[str] = (),
92 wrapper: Sequence[str] = (),
93 name: Optional[str] = None,
Cleber Rosa2ca6e262021-02-11 17:01:42 -050094 base_temp_dir: str = "/var/tmp",
John Snowc4e60232020-10-06 19:57:59 -040095 monitor_address: Optional[SocketAddrT] = None,
John Snowf12a2822020-10-06 19:58:08 -040096 socket_scm_helper: Optional[str] = None,
97 sock_dir: Optional[str] = None,
98 drain_console: bool = False,
Cleber Rosab306e262021-02-11 16:55:05 -050099 console_log: Optional[str] = None,
100 log_dir: Optional[str] = None):
John Snowabf0bf92019-06-27 17:28:14 -0400101 '''
102 Initialize a QEMUMachine
103
104 @param binary: path to the qemu binary
105 @param args: list of extra arguments
106 @param wrapper: list of arguments used as prefix to qemu binary
107 @param name: prefix for socket and log file names (default: qemu-PID)
John Snow859aeb62021-05-27 17:16:51 -0400108 @param base_temp_dir: default location where temp files are created
John Snowabf0bf92019-06-27 17:28:14 -0400109 @param monitor_address: address for QMP monitor
110 @param socket_scm_helper: helper program, required for send_fd_scm()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500111 @param sock_dir: where to create socket (defaults to base_temp_dir)
Robert Foley0fc8f662020-07-01 14:56:24 +0100112 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -0400113 @param console_log: (optional) path to console log file
Cleber Rosab306e262021-02-11 16:55:05 -0500114 @param log_dir: where to create and keep log files
John Snowabf0bf92019-06-27 17:28:14 -0400115 @note: Qemu process is not started until launch() is used.
116 '''
John Snow82e65172021-06-29 17:43:11 -0400117 # pylint: disable=too-many-arguments
118
John Snowc5e61a62020-10-06 19:58:00 -0400119 # Direct user configuration
120
121 self._binary = binary
John Snowc5e61a62020-10-06 19:58:00 -0400122 self._args = list(args)
John Snowc5e61a62020-10-06 19:58:00 -0400123 self._wrapper = wrapper
124
125 self._name = name or "qemu-%d" % os.getpid()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500126 self._base_temp_dir = base_temp_dir
127 self._sock_dir = sock_dir or self._base_temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500128 self._log_dir = log_dir
John Snowc5e61a62020-10-06 19:58:00 -0400129 self._socket_scm_helper = socket_scm_helper
130
John Snowc4e60232020-10-06 19:57:59 -0400131 if monitor_address is not None:
132 self._monitor_address = monitor_address
133 self._remove_monitor_sockfile = False
134 else:
135 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400136 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400137 )
138 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400139
140 self._console_log_path = console_log
141 if self._console_log_path:
142 # In order to log the console, buffering needs to be enabled.
143 self._drain_console = True
144 else:
145 self._drain_console = drain_console
146
147 # Runstate
John Snowf12a2822020-10-06 19:58:08 -0400148 self._qemu_log_path: Optional[str] = None
149 self._qemu_log_file: Optional[BinaryIO] = None
John Snow9223fda2020-10-06 19:58:05 -0400150 self._popen: Optional['subprocess.Popen[bytes]'] = None
John Snowf12a2822020-10-06 19:58:08 -0400151 self._events: List[QMPMessage] = []
152 self._iolog: Optional[str] = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500153 self._qmp_set = True # Enable QMP monitor by default.
John Snowbeb6b572021-05-27 17:16:53 -0400154 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
John Snowaad3f3b2020-10-06 19:58:06 -0400155 self._qemu_full_args: Tuple[str, ...] = ()
John Snowf12a2822020-10-06 19:58:08 -0400156 self._temp_dir: Optional[str] = None
John Snowabf0bf92019-06-27 17:28:14 -0400157 self._launched = False
John Snowf12a2822020-10-06 19:58:08 -0400158 self._machine: Optional[str] = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100159 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400160 self._console_set = False
John Snowf12a2822020-10-06 19:58:08 -0400161 self._console_device_type: Optional[str] = None
John Snow652809d2020-10-06 19:58:01 -0400162 self._console_address = os.path.join(
163 self._sock_dir, f"{self._name}-console.sock"
164 )
John Snowf12a2822020-10-06 19:58:08 -0400165 self._console_socket: Optional[socket.socket] = None
166 self._remove_files: List[str] = []
John Snowde6e08b2020-07-10 01:06:48 -0400167 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400168
John Snowf12a2822020-10-06 19:58:08 -0400169 def __enter__(self) -> 'QEMUMachine':
John Snowabf0bf92019-06-27 17:28:14 -0400170 return self
171
John Snow1dda0402020-05-14 01:53:44 -0400172 def __exit__(self,
173 exc_type: Optional[Type[BaseException]],
174 exc_val: Optional[BaseException],
175 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400176 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400177
John Snowf12a2822020-10-06 19:58:08 -0400178 def add_monitor_null(self) -> None:
John Snow306dfcd2019-06-27 17:28:15 -0400179 """
180 This can be used to add an unused monitor instance.
181 """
John Snowabf0bf92019-06-27 17:28:14 -0400182 self._args.append('-monitor')
183 self._args.append('null')
184
John Snowf12a2822020-10-06 19:58:08 -0400185 def add_fd(self, fd: int, fdset: int,
186 opaque: str, opts: str = '') -> 'QEMUMachine':
John Snowabf0bf92019-06-27 17:28:14 -0400187 """
188 Pass a file descriptor to the VM
189 """
190 options = ['fd=%d' % fd,
191 'set=%d' % fdset,
192 'opaque=%s' % opaque]
193 if opts:
194 options.append(opts)
195
196 # This did not exist before 3.4, but since then it is
197 # mandatory for our purpose
198 if hasattr(os, 'set_inheritable'):
199 os.set_inheritable(fd, True)
200
201 self._args.append('-add-fd')
202 self._args.append(','.join(options))
203 return self
204
John Snowf12a2822020-10-06 19:58:08 -0400205 def send_fd_scm(self, fd: Optional[int] = None,
206 file_path: Optional[str] = None) -> int:
John Snow306dfcd2019-06-27 17:28:15 -0400207 """
208 Send an fd or file_path to socket_scm_helper.
209
210 Exactly one of fd and file_path must be given.
211 If it is file_path, the helper will open that file and pass its own fd.
212 """
John Snowabf0bf92019-06-27 17:28:14 -0400213 # In iotest.py, the qmp should always use unix socket.
214 assert self._qmp.is_scm_available()
215 if self._socket_scm_helper is None:
216 raise QEMUMachineError("No path to socket_scm_helper set")
217 if not os.path.exists(self._socket_scm_helper):
218 raise QEMUMachineError("%s does not exist" %
219 self._socket_scm_helper)
220
221 # This did not exist before 3.4, but since then it is
222 # mandatory for our purpose
223 if hasattr(os, 'set_inheritable'):
224 os.set_inheritable(self._qmp.get_sock_fd(), True)
225 if fd is not None:
226 os.set_inheritable(fd, True)
227
228 fd_param = ["%s" % self._socket_scm_helper,
229 "%d" % self._qmp.get_sock_fd()]
230
231 if file_path is not None:
232 assert fd is None
233 fd_param.append(file_path)
234 else:
235 assert fd is not None
236 fd_param.append(str(fd))
237
John Snow14b41792021-05-27 17:16:47 -0400238 proc = subprocess.run(
239 fd_param,
240 stdin=subprocess.DEVNULL,
241 stdout=subprocess.PIPE,
242 stderr=subprocess.STDOUT,
243 check=False,
244 close_fds=False,
John Snow8dfac2e2020-05-28 18:21:29 -0400245 )
John Snow14b41792021-05-27 17:16:47 -0400246 if proc.stdout:
247 LOG.debug(proc.stdout)
John Snowabf0bf92019-06-27 17:28:14 -0400248
249 return proc.returncode
250
251 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400252 def _remove_if_exists(path: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400253 """
254 Remove file object at path if it exists
255 """
256 try:
257 os.remove(path)
258 except OSError as exception:
259 if exception.errno == errno.ENOENT:
260 return
261 raise
262
John Snowf12a2822020-10-06 19:58:08 -0400263 def is_running(self) -> bool:
John Snow306dfcd2019-06-27 17:28:15 -0400264 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400265 return self._popen is not None and self._popen.poll() is None
266
John Snow9223fda2020-10-06 19:58:05 -0400267 @property
268 def _subp(self) -> 'subprocess.Popen[bytes]':
269 if self._popen is None:
270 raise QEMUMachineError('Subprocess pipe not present')
271 return self._popen
272
John Snowf12a2822020-10-06 19:58:08 -0400273 def exitcode(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400274 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400275 if self._popen is None:
276 return None
277 return self._popen.poll()
278
John Snowf12a2822020-10-06 19:58:08 -0400279 def get_pid(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400280 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400281 if not self.is_running():
282 return None
John Snow9223fda2020-10-06 19:58:05 -0400283 return self._subp.pid
John Snowabf0bf92019-06-27 17:28:14 -0400284
John Snowf12a2822020-10-06 19:58:08 -0400285 def _load_io_log(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400286 if self._qemu_log_path is not None:
287 with open(self._qemu_log_path, "r") as iolog:
288 self._iolog = iolog.read()
289
John Snow652809d2020-10-06 19:58:01 -0400290 @property
291 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500292 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400293
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500294 if self._qmp_set:
295 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400296 moncdev = "socket,id=mon,host={},port={}".format(
297 *self._monitor_address
298 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500299 else:
John Snowc4e60232020-10-06 19:57:59 -0400300 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500301 args.extend(['-chardev', moncdev, '-mon',
302 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400303
John Snowabf0bf92019-06-27 17:28:14 -0400304 if self._machine is not None:
305 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400306 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100307 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400308 if self._console_set:
Paolo Bonzini991c1802020-11-13 03:10:52 -0500309 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
John Snowabf0bf92019-06-27 17:28:14 -0400310 self._console_address)
311 args.extend(['-chardev', chardev])
312 if self._console_device_type is None:
313 args.extend(['-serial', 'chardev:console'])
314 else:
315 device = '%s,chardev=console' % self._console_device_type
316 args.extend(['-device', device])
317 return args
318
John Snowf12a2822020-10-06 19:58:08 -0400319 def _pre_launch(self) -> None:
John Snow652809d2020-10-06 19:58:01 -0400320 if self._console_set:
321 self._remove_files.append(self._console_address)
322
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500323 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400324 if self._remove_monitor_sockfile:
325 assert isinstance(self._monitor_address, str)
326 self._remove_files.append(self._monitor_address)
John Snowbeb6b572021-05-27 17:16:53 -0400327 self._qmp_connection = QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400328 self._monitor_address,
329 server=True,
330 nickname=self._name
331 )
John Snowabf0bf92019-06-27 17:28:14 -0400332
John Snow63c33f32021-05-27 17:16:49 -0400333 # NOTE: Make sure any opened resources are *definitely* freed in
334 # _post_shutdown()!
335 # pylint: disable=consider-using-with
Cleber Rosab306e262021-02-11 16:55:05 -0500336 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
John Snow63c33f32021-05-27 17:16:49 -0400337 self._qemu_log_file = open(self._qemu_log_path, 'wb')
338
John Snowf12a2822020-10-06 19:58:08 -0400339 def _post_launch(self) -> None:
John Snowbe1183e2020-10-06 19:58:04 -0400340 if self._qmp_connection:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500341 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400342
John Snowf12a2822020-10-06 19:58:08 -0400343 def _post_shutdown(self) -> None:
John Snowa3842cb2020-07-10 01:06:42 -0400344 """
345 Called to cleanup the VM instance after the process has exited.
346 May also be called after a failed launch.
347 """
348 # Comprehensive reset for the failed launch case:
349 self._early_cleanup()
350
John Snowbe1183e2020-10-06 19:58:04 -0400351 if self._qmp_connection:
John Snow671940e2020-07-10 01:06:39 -0400352 self._qmp.close()
John Snowbe1183e2020-10-06 19:58:04 -0400353 self._qmp_connection = None
John Snow671940e2020-07-10 01:06:39 -0400354
John Snowabf0bf92019-06-27 17:28:14 -0400355 if self._qemu_log_file is not None:
356 self._qemu_log_file.close()
357 self._qemu_log_file = None
358
Cleber Rosa3c1e16c2021-02-11 17:01:41 -0500359 self._load_io_log()
360
John Snowabf0bf92019-06-27 17:28:14 -0400361 self._qemu_log_path = None
362
John Snowabf0bf92019-06-27 17:28:14 -0400363 if self._temp_dir is not None:
364 shutil.rmtree(self._temp_dir)
365 self._temp_dir = None
366
Max Reitz32558ce2019-10-17 15:31:34 +0200367 while len(self._remove_files) > 0:
368 self._remove_if_exists(self._remove_files.pop())
369
John Snow14661d92020-07-10 01:06:38 -0400370 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400371 if (exitcode is not None and exitcode < 0
372 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400373 msg = 'qemu received signal %i; command: "%s"'
374 if self._qemu_full_args:
375 command = ' '.join(self._qemu_full_args)
376 else:
377 command = ''
378 LOG.warning(msg, -int(exitcode), command)
379
John Snowde6e08b2020-07-10 01:06:48 -0400380 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400381 self._launched = False
382
John Snowf12a2822020-10-06 19:58:08 -0400383 def launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400384 """
385 Launch the VM and make sure we cleanup and expose the
386 command line/output in case of exception
387 """
388
389 if self._launched:
390 raise QEMUMachineError('VM already launched')
391
392 self._iolog = None
John Snowaad3f3b2020-10-06 19:58:06 -0400393 self._qemu_full_args = ()
John Snowabf0bf92019-06-27 17:28:14 -0400394 try:
395 self._launch()
396 self._launched = True
397 except:
John Snowa3842cb2020-07-10 01:06:42 -0400398 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400399
400 LOG.debug('Error launching VM')
401 if self._qemu_full_args:
402 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
403 if self._iolog:
404 LOG.debug('Output: %r', self._iolog)
405 raise
406
John Snowf12a2822020-10-06 19:58:08 -0400407 def _launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400408 """
409 Launch the VM and establish a QMP connection
410 """
John Snowabf0bf92019-06-27 17:28:14 -0400411 self._pre_launch()
John Snowaad3f3b2020-10-06 19:58:06 -0400412 self._qemu_full_args = tuple(
413 chain(self._wrapper,
414 [self._binary],
415 self._base_args,
416 self._args)
417 )
John Snowabf0bf92019-06-27 17:28:14 -0400418 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
John Snowa0eae172021-05-27 17:16:50 -0400419
420 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
421 # pylint: disable=consider-using-with
John Snowabf0bf92019-06-27 17:28:14 -0400422 self._popen = subprocess.Popen(self._qemu_full_args,
John Snow07b71232021-05-27 17:16:46 -0400423 stdin=subprocess.DEVNULL,
John Snowabf0bf92019-06-27 17:28:14 -0400424 stdout=self._qemu_log_file,
425 stderr=subprocess.STDOUT,
426 shell=False,
427 close_fds=False)
428 self._post_launch()
429
John Snowe2c97f12020-07-10 01:06:40 -0400430 def _early_cleanup(self) -> None:
431 """
432 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400433
John Snow193bf1c2020-07-10 01:06:47 -0400434 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400435 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400436 """
437 # If we keep the console socket open, we may deadlock waiting
438 # for QEMU to exit, while QEMU is waiting for the socket to
439 # become writeable.
440 if self._console_socket is not None:
441 self._console_socket.close()
442 self._console_socket = None
443
John Snow193bf1c2020-07-10 01:06:47 -0400444 def _hard_shutdown(self) -> None:
445 """
446 Perform early cleanup, kill the VM, and wait for it to terminate.
447
448 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
449 waiting for the QEMU process to terminate.
450 """
451 self._early_cleanup()
John Snow9223fda2020-10-06 19:58:05 -0400452 self._subp.kill()
453 self._subp.wait(timeout=60)
John Snow193bf1c2020-07-10 01:06:47 -0400454
John Snow8226a4b2020-07-20 12:02:52 -0400455 def _soft_shutdown(self, timeout: Optional[int],
456 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400457 """
458 Perform early cleanup, attempt to gracefully shut down the VM, and wait
459 for it to terminate.
460
John Snow8226a4b2020-07-20 12:02:52 -0400461 :param timeout: Timeout in seconds for graceful shutdown.
462 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400463 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400464
465 :raise ConnectionReset: On QMP communication errors
466 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
467 the QEMU process to terminate.
468 """
469 self._early_cleanup()
470
John Snowbe1183e2020-10-06 19:58:04 -0400471 if self._qmp_connection:
John Snow193bf1c2020-07-10 01:06:47 -0400472 if not has_quit:
473 # Might raise ConnectionReset
474 self._qmp.cmd('quit')
475
476 # May raise subprocess.TimeoutExpired
John Snow9223fda2020-10-06 19:58:05 -0400477 self._subp.wait(timeout=timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400478
John Snow8226a4b2020-07-20 12:02:52 -0400479 def _do_shutdown(self, timeout: Optional[int],
480 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400481 """
482 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
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 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400487
488 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
489 The inner exception will likely be ConnectionReset or
490 subprocess.TimeoutExpired. In rare cases, non-graceful termination
491 may result in its own exceptions, likely subprocess.TimeoutExpired.
492 """
493 try:
John Snow8226a4b2020-07-20 12:02:52 -0400494 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400495 except Exception as exc:
496 self._hard_shutdown()
497 raise AbnormalShutdown("Could not perform graceful shutdown") \
498 from exc
499
John Snowc9b30452020-07-10 01:06:43 -0400500 def shutdown(self, has_quit: bool = False,
501 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400502 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400503 """
John Snow193bf1c2020-07-10 01:06:47 -0400504 Terminate the VM (gracefully if possible) and perform cleanup.
505 Cleanup will always be performed.
506
507 If the VM has not yet been launched, or shutdown(), wait(), or kill()
508 have already been called, this method does nothing.
509
510 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
511 :param hard: When true, do not attempt graceful shutdown, and
512 suppress the SIGKILL warning log message.
513 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400514 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400515 """
John Snowa3842cb2020-07-10 01:06:42 -0400516 if not self._launched:
517 return
518
John Snow193bf1c2020-07-10 01:06:47 -0400519 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300520 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400521 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400522 self._hard_shutdown()
523 else:
John Snow8226a4b2020-07-20 12:02:52 -0400524 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400525 finally:
526 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400527
John Snowf12a2822020-10-06 19:58:08 -0400528 def kill(self) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400529 """
530 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
531 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300532 self.shutdown(hard=True)
533
John Snow8226a4b2020-07-20 12:02:52 -0400534 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400535 """
536 Wait for the VM to power off and perform post-shutdown cleanup.
537
John Snow8226a4b2020-07-20 12:02:52 -0400538 :param timeout: Optional timeout in seconds. Default 30 seconds.
539 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400540 """
541 self.shutdown(has_quit=True, timeout=timeout)
542
John Snowf12a2822020-10-06 19:58:08 -0400543 def set_qmp_monitor(self, enabled: bool = True) -> None:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500544 """
545 Set the QMP monitor.
546
547 @param enabled: if False, qmp monitor options will be removed from
548 the base arguments of the resulting QEMU command
549 line. Default is True.
John Snow5c02c862021-06-29 17:43:23 -0400550
551 .. note:: Call this function before launch().
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500552 """
John Snowbe1183e2020-10-06 19:58:04 -0400553 self._qmp_set = enabled
554
555 @property
John Snowbeb6b572021-05-27 17:16:53 -0400556 def _qmp(self) -> QEMUMonitorProtocol:
John Snowbe1183e2020-10-06 19:58:04 -0400557 if self._qmp_connection is None:
558 raise QEMUMachineError("Attempt to access QMP with no connection")
559 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500560
John Snowaaa81ec2020-10-06 19:58:03 -0400561 @classmethod
562 def _qmp_args(cls, _conv_keys: bool = True, **args: Any) -> Dict[str, Any]:
John Snowabf0bf92019-06-27 17:28:14 -0400563 qmp_args = dict()
564 for key, value in args.items():
John Snowaaa81ec2020-10-06 19:58:03 -0400565 if _conv_keys:
John Snowabf0bf92019-06-27 17:28:14 -0400566 qmp_args[key.replace('_', '-')] = value
567 else:
568 qmp_args[key] = value
John Snowaaa81ec2020-10-06 19:58:03 -0400569 return qmp_args
John Snowabf0bf92019-06-27 17:28:14 -0400570
John Snowaaa81ec2020-10-06 19:58:03 -0400571 def qmp(self, cmd: str,
572 conv_keys: bool = True,
573 **args: Any) -> QMPMessage:
574 """
575 Invoke a QMP command and return the response dict
576 """
577 qmp_args = self._qmp_args(conv_keys, **args)
John Snowabf0bf92019-06-27 17:28:14 -0400578 return self._qmp.cmd(cmd, args=qmp_args)
579
John Snowf12a2822020-10-06 19:58:08 -0400580 def command(self, cmd: str,
581 conv_keys: bool = True,
582 **args: Any) -> QMPReturnValue:
John Snowabf0bf92019-06-27 17:28:14 -0400583 """
584 Invoke a QMP command.
585 On success return the response dict.
586 On failure raise an exception.
587 """
John Snowaaa81ec2020-10-06 19:58:03 -0400588 qmp_args = self._qmp_args(conv_keys, **args)
589 return self._qmp.command(cmd, **qmp_args)
John Snowabf0bf92019-06-27 17:28:14 -0400590
John Snowf12a2822020-10-06 19:58:08 -0400591 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400592 """
593 Poll for one queued QMP events and return it
594 """
John Snow306dfcd2019-06-27 17:28:15 -0400595 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400596 return self._events.pop(0)
597 return self._qmp.pull_event(wait=wait)
598
John Snowf12a2822020-10-06 19:58:08 -0400599 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400600 """
601 Poll for queued QMP events and return a list of dicts
602 """
603 events = self._qmp.get_events(wait=wait)
604 events.extend(self._events)
605 del self._events[:]
606 self._qmp.clear_events()
607 return events
608
609 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400610 def event_match(event: Any, match: Optional[Any]) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400611 """
612 Check if an event matches optional match criteria.
613
614 The match criteria takes the form of a matching subdict. The event is
615 checked to be a superset of the subdict, recursively, with matching
616 values whenever the subdict values are not None.
617
618 This has a limitation that you cannot explicitly check for None values.
619
620 Examples, with the subdict queries on the left:
621 - None matches any object.
622 - {"foo": None} matches {"foo": {"bar": 1}}
623 - {"foo": None} matches {"foo": 5}
624 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
625 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
626 """
627 if match is None:
628 return True
629
630 try:
631 for key in match:
632 if key in event:
633 if not QEMUMachine.event_match(event[key], match[key]):
634 return False
635 else:
636 return False
637 return True
638 except TypeError:
639 # either match or event wasn't iterable (not a dict)
John Snowf12a2822020-10-06 19:58:08 -0400640 return bool(match == event)
John Snowabf0bf92019-06-27 17:28:14 -0400641
John Snowf12a2822020-10-06 19:58:08 -0400642 def event_wait(self, name: str,
643 timeout: float = 60.0,
644 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400645 """
646 event_wait waits for and returns a named event from QMP with a timeout.
647
648 name: The event to wait for.
649 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
650 match: Optional match criteria. See event_match for details.
651 """
652 return self.events_wait([(name, match)], timeout)
653
John Snowf12a2822020-10-06 19:58:08 -0400654 def events_wait(self,
655 events: Sequence[Tuple[str, Any]],
656 timeout: float = 60.0) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400657 """
John Snow1847a4a2020-10-06 19:58:02 -0400658 events_wait waits for and returns a single named event from QMP.
659 In the case of multiple qualifying events, this function returns the
660 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400661
John Snow1847a4a2020-10-06 19:58:02 -0400662 :param events: A sequence of (name, match_criteria) tuples.
663 The match criteria are optional and may be None.
664 See event_match for details.
665 :param timeout: Optional timeout, in seconds.
666 See QEMUMonitorProtocol.pull_event.
667
668 :raise QMPTimeoutError: If timeout was non-zero and no matching events
669 were found.
670 :return: A QMP event matching the filter criteria.
671 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400672 """
John Snowf12a2822020-10-06 19:58:08 -0400673 def _match(event: QMPMessage) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400674 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400675 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400676 return True
677 return False
678
John Snow1847a4a2020-10-06 19:58:02 -0400679 event: Optional[QMPMessage]
680
John Snowabf0bf92019-06-27 17:28:14 -0400681 # Search cached events
682 for event in self._events:
683 if _match(event):
684 self._events.remove(event)
685 return event
686
687 # Poll for new events
688 while True:
689 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400690 if event is None:
691 # NB: None is only returned when timeout is false-ish.
692 # Timeouts raise QMPTimeoutError instead!
693 break
John Snowabf0bf92019-06-27 17:28:14 -0400694 if _match(event):
695 return event
696 self._events.append(event)
697
698 return None
699
John Snowf12a2822020-10-06 19:58:08 -0400700 def get_log(self) -> Optional[str]:
John Snowabf0bf92019-06-27 17:28:14 -0400701 """
702 After self.shutdown or failed qemu execution, this returns the output
703 of the qemu process.
704 """
705 return self._iolog
706
John Snowf12a2822020-10-06 19:58:08 -0400707 def add_args(self, *args: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400708 """
709 Adds to the list of extra arguments to be given to the QEMU binary
710 """
711 self._args.extend(args)
712
John Snowf12a2822020-10-06 19:58:08 -0400713 def set_machine(self, machine_type: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400714 """
715 Sets the machine type
716
717 If set, the machine type will be added to the base arguments
718 of the resulting QEMU command line.
719 """
720 self._machine = machine_type
721
John Snowf12a2822020-10-06 19:58:08 -0400722 def set_console(self,
723 device_type: Optional[str] = None,
724 console_index: int = 0) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400725 """
726 Sets the device type for a console device
727
728 If set, the console device and a backing character device will
729 be added to the base arguments of the resulting QEMU command
730 line.
731
732 This is a convenience method that will either use the provided
733 device type, or default to a "-serial chardev:console" command
734 line argument.
735
736 The actual setting of command line arguments will be be done at
737 machine launch time, as it depends on the temporary directory
738 to be created.
739
740 @param device_type: the device type, such as "isa-serial". If
741 None is given (the default value) a "-serial
742 chardev:console" command line argument will
743 be used instead, resorting to the machine's
744 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100745 @param console_index: the index of the console device to use.
746 If not zero, the command line will create
747 'index - 1' consoles and connect them to
748 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400749 """
750 self._console_set = True
751 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100752 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400753
754 @property
John Snowf12a2822020-10-06 19:58:08 -0400755 def console_socket(self) -> socket.socket:
John Snowabf0bf92019-06-27 17:28:14 -0400756 """
757 Returns a socket connected to the console
758 """
759 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100760 self._console_socket = console_socket.ConsoleSocket(
761 self._console_address,
762 file=self._console_log_path,
763 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400764 return self._console_socket
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500765
766 @property
767 def temp_dir(self) -> str:
768 """
769 Returns a temporary directory to be used for this machine
770 """
771 if self._temp_dir is None:
772 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
773 dir=self._base_temp_dir)
774 return self._temp_dir
Cleber Rosab306e262021-02-11 16:55:05 -0500775
776 @property
777 def log_dir(self) -> str:
778 """
779 Returns a directory to be used for writing logs
780 """
781 if self._log_dir is None:
782 return self.temp_dir
783 return self._log_dir