blob: d47ab3d8965bd415921b137a1e1c9967b492081d [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,
99 console_log: Optional[str] = None):
John Snowabf0bf92019-06-27 17:28:14 -0400100 '''
101 Initialize a QEMUMachine
102
103 @param binary: path to the qemu binary
104 @param args: list of extra arguments
105 @param wrapper: list of arguments used as prefix to qemu binary
106 @param name: prefix for socket and log file names (default: qemu-PID)
John Snow859aeb62021-05-27 17:16:51 -0400107 @param base_temp_dir: default location where temp files are created
John Snowabf0bf92019-06-27 17:28:14 -0400108 @param monitor_address: address for QMP monitor
109 @param socket_scm_helper: helper program, required for send_fd_scm()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500110 @param sock_dir: where to create socket (defaults to base_temp_dir)
Robert Foley0fc8f662020-07-01 14:56:24 +0100111 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -0400112 @param console_log: (optional) path to console log file
John Snowabf0bf92019-06-27 17:28:14 -0400113 @note: Qemu process is not started until launch() is used.
114 '''
John Snow82e65172021-06-29 17:43:11 -0400115 # pylint: disable=too-many-arguments
116
John Snowc5e61a62020-10-06 19:58:00 -0400117 # Direct user configuration
118
119 self._binary = binary
John Snowc5e61a62020-10-06 19:58:00 -0400120 self._args = list(args)
John Snowc5e61a62020-10-06 19:58:00 -0400121 self._wrapper = wrapper
122
123 self._name = name or "qemu-%d" % os.getpid()
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500124 self._base_temp_dir = base_temp_dir
125 self._sock_dir = sock_dir or self._base_temp_dir
John Snowc5e61a62020-10-06 19:58:00 -0400126 self._socket_scm_helper = socket_scm_helper
127
John Snowc4e60232020-10-06 19:57:59 -0400128 if monitor_address is not None:
129 self._monitor_address = monitor_address
130 self._remove_monitor_sockfile = False
131 else:
132 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400133 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400134 )
135 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400136
137 self._console_log_path = console_log
138 if self._console_log_path:
139 # In order to log the console, buffering needs to be enabled.
140 self._drain_console = True
141 else:
142 self._drain_console = drain_console
143
144 # Runstate
John Snowf12a2822020-10-06 19:58:08 -0400145 self._qemu_log_path: Optional[str] = None
146 self._qemu_log_file: Optional[BinaryIO] = None
John Snow9223fda2020-10-06 19:58:05 -0400147 self._popen: Optional['subprocess.Popen[bytes]'] = None
John Snowf12a2822020-10-06 19:58:08 -0400148 self._events: List[QMPMessage] = []
149 self._iolog: Optional[str] = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500150 self._qmp_set = True # Enable QMP monitor by default.
John Snowbeb6b572021-05-27 17:16:53 -0400151 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
John Snowaad3f3b2020-10-06 19:58:06 -0400152 self._qemu_full_args: Tuple[str, ...] = ()
John Snowf12a2822020-10-06 19:58:08 -0400153 self._temp_dir: Optional[str] = None
John Snowabf0bf92019-06-27 17:28:14 -0400154 self._launched = False
John Snowf12a2822020-10-06 19:58:08 -0400155 self._machine: Optional[str] = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100156 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400157 self._console_set = False
John Snowf12a2822020-10-06 19:58:08 -0400158 self._console_device_type: Optional[str] = None
John Snow652809d2020-10-06 19:58:01 -0400159 self._console_address = os.path.join(
160 self._sock_dir, f"{self._name}-console.sock"
161 )
John Snowf12a2822020-10-06 19:58:08 -0400162 self._console_socket: Optional[socket.socket] = None
163 self._remove_files: List[str] = []
John Snowde6e08b2020-07-10 01:06:48 -0400164 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400165
John Snowf12a2822020-10-06 19:58:08 -0400166 def __enter__(self) -> 'QEMUMachine':
John Snowabf0bf92019-06-27 17:28:14 -0400167 return self
168
John Snow1dda0402020-05-14 01:53:44 -0400169 def __exit__(self,
170 exc_type: Optional[Type[BaseException]],
171 exc_val: Optional[BaseException],
172 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400173 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400174
John Snowf12a2822020-10-06 19:58:08 -0400175 def add_monitor_null(self) -> None:
John Snow306dfcd2019-06-27 17:28:15 -0400176 """
177 This can be used to add an unused monitor instance.
178 """
John Snowabf0bf92019-06-27 17:28:14 -0400179 self._args.append('-monitor')
180 self._args.append('null')
181
John Snowf12a2822020-10-06 19:58:08 -0400182 def add_fd(self, fd: int, fdset: int,
183 opaque: str, opts: str = '') -> 'QEMUMachine':
John Snowabf0bf92019-06-27 17:28:14 -0400184 """
185 Pass a file descriptor to the VM
186 """
187 options = ['fd=%d' % fd,
188 'set=%d' % fdset,
189 'opaque=%s' % opaque]
190 if opts:
191 options.append(opts)
192
193 # This did not exist before 3.4, but since then it is
194 # mandatory for our purpose
195 if hasattr(os, 'set_inheritable'):
196 os.set_inheritable(fd, True)
197
198 self._args.append('-add-fd')
199 self._args.append(','.join(options))
200 return self
201
John Snowf12a2822020-10-06 19:58:08 -0400202 def send_fd_scm(self, fd: Optional[int] = None,
203 file_path: Optional[str] = None) -> int:
John Snow306dfcd2019-06-27 17:28:15 -0400204 """
205 Send an fd or file_path to socket_scm_helper.
206
207 Exactly one of fd and file_path must be given.
208 If it is file_path, the helper will open that file and pass its own fd.
209 """
John Snowabf0bf92019-06-27 17:28:14 -0400210 # In iotest.py, the qmp should always use unix socket.
211 assert self._qmp.is_scm_available()
212 if self._socket_scm_helper is None:
213 raise QEMUMachineError("No path to socket_scm_helper set")
214 if not os.path.exists(self._socket_scm_helper):
215 raise QEMUMachineError("%s does not exist" %
216 self._socket_scm_helper)
217
218 # This did not exist before 3.4, but since then it is
219 # mandatory for our purpose
220 if hasattr(os, 'set_inheritable'):
221 os.set_inheritable(self._qmp.get_sock_fd(), True)
222 if fd is not None:
223 os.set_inheritable(fd, True)
224
225 fd_param = ["%s" % self._socket_scm_helper,
226 "%d" % self._qmp.get_sock_fd()]
227
228 if file_path is not None:
229 assert fd is None
230 fd_param.append(file_path)
231 else:
232 assert fd is not None
233 fd_param.append(str(fd))
234
John Snow14b41792021-05-27 17:16:47 -0400235 proc = subprocess.run(
236 fd_param,
237 stdin=subprocess.DEVNULL,
238 stdout=subprocess.PIPE,
239 stderr=subprocess.STDOUT,
240 check=False,
241 close_fds=False,
John Snow8dfac2e2020-05-28 18:21:29 -0400242 )
John Snow14b41792021-05-27 17:16:47 -0400243 if proc.stdout:
244 LOG.debug(proc.stdout)
John Snowabf0bf92019-06-27 17:28:14 -0400245
246 return proc.returncode
247
248 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400249 def _remove_if_exists(path: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400250 """
251 Remove file object at path if it exists
252 """
253 try:
254 os.remove(path)
255 except OSError as exception:
256 if exception.errno == errno.ENOENT:
257 return
258 raise
259
John Snowf12a2822020-10-06 19:58:08 -0400260 def is_running(self) -> bool:
John Snow306dfcd2019-06-27 17:28:15 -0400261 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400262 return self._popen is not None and self._popen.poll() is None
263
John Snow9223fda2020-10-06 19:58:05 -0400264 @property
265 def _subp(self) -> 'subprocess.Popen[bytes]':
266 if self._popen is None:
267 raise QEMUMachineError('Subprocess pipe not present')
268 return self._popen
269
John Snowf12a2822020-10-06 19:58:08 -0400270 def exitcode(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400271 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400272 if self._popen is None:
273 return None
274 return self._popen.poll()
275
John Snowf12a2822020-10-06 19:58:08 -0400276 def get_pid(self) -> Optional[int]:
John Snow306dfcd2019-06-27 17:28:15 -0400277 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400278 if not self.is_running():
279 return None
John Snow9223fda2020-10-06 19:58:05 -0400280 return self._subp.pid
John Snowabf0bf92019-06-27 17:28:14 -0400281
John Snowf12a2822020-10-06 19:58:08 -0400282 def _load_io_log(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400283 if self._qemu_log_path is not None:
284 with open(self._qemu_log_path, "r") as iolog:
285 self._iolog = iolog.read()
286
John Snow652809d2020-10-06 19:58:01 -0400287 @property
288 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500289 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400290
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500291 if self._qmp_set:
292 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400293 moncdev = "socket,id=mon,host={},port={}".format(
294 *self._monitor_address
295 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500296 else:
John Snowc4e60232020-10-06 19:57:59 -0400297 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500298 args.extend(['-chardev', moncdev, '-mon',
299 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400300
John Snowabf0bf92019-06-27 17:28:14 -0400301 if self._machine is not None:
302 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400303 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100304 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400305 if self._console_set:
Paolo Bonzini991c1802020-11-13 03:10:52 -0500306 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
John Snowabf0bf92019-06-27 17:28:14 -0400307 self._console_address)
308 args.extend(['-chardev', chardev])
309 if self._console_device_type is None:
310 args.extend(['-serial', 'chardev:console'])
311 else:
312 device = '%s,chardev=console' % self._console_device_type
313 args.extend(['-device', device])
314 return args
315
John Snowf12a2822020-10-06 19:58:08 -0400316 def _pre_launch(self) -> None:
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500317 self._qemu_log_path = os.path.join(self.temp_dir, self._name + ".log")
John Snowabf0bf92019-06-27 17:28:14 -0400318
John Snow652809d2020-10-06 19:58:01 -0400319 if self._console_set:
320 self._remove_files.append(self._console_address)
321
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500322 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400323 if self._remove_monitor_sockfile:
324 assert isinstance(self._monitor_address, str)
325 self._remove_files.append(self._monitor_address)
John Snowbeb6b572021-05-27 17:16:53 -0400326 self._qmp_connection = QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400327 self._monitor_address,
328 server=True,
329 nickname=self._name
330 )
John Snowabf0bf92019-06-27 17:28:14 -0400331
John Snow63c33f32021-05-27 17:16:49 -0400332 # NOTE: Make sure any opened resources are *definitely* freed in
333 # _post_shutdown()!
334 # pylint: disable=consider-using-with
335 self._qemu_log_file = open(self._qemu_log_path, 'wb')
336
John Snowf12a2822020-10-06 19:58:08 -0400337 def _post_launch(self) -> None:
John Snowbe1183e2020-10-06 19:58:04 -0400338 if self._qmp_connection:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500339 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400340
John Snowf12a2822020-10-06 19:58:08 -0400341 def _post_shutdown(self) -> None:
John Snowa3842cb2020-07-10 01:06:42 -0400342 """
343 Called to cleanup the VM instance after the process has exited.
344 May also be called after a failed launch.
345 """
346 # Comprehensive reset for the failed launch case:
347 self._early_cleanup()
348
John Snowbe1183e2020-10-06 19:58:04 -0400349 if self._qmp_connection:
John Snow671940e2020-07-10 01:06:39 -0400350 self._qmp.close()
John Snowbe1183e2020-10-06 19:58:04 -0400351 self._qmp_connection = None
John Snow671940e2020-07-10 01:06:39 -0400352
John Snowabf0bf92019-06-27 17:28:14 -0400353 if self._qemu_log_file is not None:
354 self._qemu_log_file.close()
355 self._qemu_log_file = None
356
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 Snowde6e08b2020-07-10 01:06:48 -0400378 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400379 self._launched = False
380
John Snowf12a2822020-10-06 19:58:08 -0400381 def launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400382 """
383 Launch the VM and make sure we cleanup and expose the
384 command line/output in case of exception
385 """
386
387 if self._launched:
388 raise QEMUMachineError('VM already launched')
389
390 self._iolog = None
John Snowaad3f3b2020-10-06 19:58:06 -0400391 self._qemu_full_args = ()
John Snowabf0bf92019-06-27 17:28:14 -0400392 try:
393 self._launch()
394 self._launched = True
395 except:
John Snowa3842cb2020-07-10 01:06:42 -0400396 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400397
398 LOG.debug('Error launching VM')
399 if self._qemu_full_args:
400 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
401 if self._iolog:
402 LOG.debug('Output: %r', self._iolog)
403 raise
404
John Snowf12a2822020-10-06 19:58:08 -0400405 def _launch(self) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400406 """
407 Launch the VM and establish a QMP connection
408 """
John Snowabf0bf92019-06-27 17:28:14 -0400409 self._pre_launch()
John Snowaad3f3b2020-10-06 19:58:06 -0400410 self._qemu_full_args = tuple(
411 chain(self._wrapper,
412 [self._binary],
413 self._base_args,
414 self._args)
415 )
John Snowabf0bf92019-06-27 17:28:14 -0400416 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
John Snowa0eae172021-05-27 17:16:50 -0400417
418 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
419 # pylint: disable=consider-using-with
John Snowabf0bf92019-06-27 17:28:14 -0400420 self._popen = subprocess.Popen(self._qemu_full_args,
John Snow07b71232021-05-27 17:16:46 -0400421 stdin=subprocess.DEVNULL,
John Snowabf0bf92019-06-27 17:28:14 -0400422 stdout=self._qemu_log_file,
423 stderr=subprocess.STDOUT,
424 shell=False,
425 close_fds=False)
426 self._post_launch()
427
John Snowe2c97f12020-07-10 01:06:40 -0400428 def _early_cleanup(self) -> None:
429 """
430 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400431
John Snow193bf1c2020-07-10 01:06:47 -0400432 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400433 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400434 """
435 # If we keep the console socket open, we may deadlock waiting
436 # for QEMU to exit, while QEMU is waiting for the socket to
437 # become writeable.
438 if self._console_socket is not None:
439 self._console_socket.close()
440 self._console_socket = None
441
John Snow193bf1c2020-07-10 01:06:47 -0400442 def _hard_shutdown(self) -> None:
443 """
444 Perform early cleanup, kill the VM, and wait for it to terminate.
445
446 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
447 waiting for the QEMU process to terminate.
448 """
449 self._early_cleanup()
John Snow9223fda2020-10-06 19:58:05 -0400450 self._subp.kill()
451 self._subp.wait(timeout=60)
John Snow193bf1c2020-07-10 01:06:47 -0400452
John Snow8226a4b2020-07-20 12:02:52 -0400453 def _soft_shutdown(self, timeout: Optional[int],
454 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400455 """
456 Perform early cleanup, attempt to gracefully shut down the VM, and wait
457 for it to terminate.
458
John Snow8226a4b2020-07-20 12:02:52 -0400459 :param timeout: Timeout in seconds for graceful shutdown.
460 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400461 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400462
463 :raise ConnectionReset: On QMP communication errors
464 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
465 the QEMU process to terminate.
466 """
467 self._early_cleanup()
468
John Snowbe1183e2020-10-06 19:58:04 -0400469 if self._qmp_connection:
John Snow193bf1c2020-07-10 01:06:47 -0400470 if not has_quit:
471 # Might raise ConnectionReset
472 self._qmp.cmd('quit')
473
474 # May raise subprocess.TimeoutExpired
John Snow9223fda2020-10-06 19:58:05 -0400475 self._subp.wait(timeout=timeout)
John Snow193bf1c2020-07-10 01:06:47 -0400476
John Snow8226a4b2020-07-20 12:02:52 -0400477 def _do_shutdown(self, timeout: Optional[int],
478 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400479 """
480 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
481
John Snow8226a4b2020-07-20 12:02:52 -0400482 :param timeout: Timeout in seconds for graceful shutdown.
483 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400484 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400485
486 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
487 The inner exception will likely be ConnectionReset or
488 subprocess.TimeoutExpired. In rare cases, non-graceful termination
489 may result in its own exceptions, likely subprocess.TimeoutExpired.
490 """
491 try:
John Snow8226a4b2020-07-20 12:02:52 -0400492 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400493 except Exception as exc:
494 self._hard_shutdown()
495 raise AbnormalShutdown("Could not perform graceful shutdown") \
496 from exc
497
John Snowc9b30452020-07-10 01:06:43 -0400498 def shutdown(self, has_quit: bool = False,
499 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400500 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400501 """
John Snow193bf1c2020-07-10 01:06:47 -0400502 Terminate the VM (gracefully if possible) and perform cleanup.
503 Cleanup will always be performed.
504
505 If the VM has not yet been launched, or shutdown(), wait(), or kill()
506 have already been called, this method does nothing.
507
508 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
509 :param hard: When true, do not attempt graceful shutdown, and
510 suppress the SIGKILL warning log message.
511 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400512 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400513 """
John Snowa3842cb2020-07-10 01:06:42 -0400514 if not self._launched:
515 return
516
John Snow193bf1c2020-07-10 01:06:47 -0400517 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300518 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400519 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400520 self._hard_shutdown()
521 else:
John Snow8226a4b2020-07-20 12:02:52 -0400522 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400523 finally:
524 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400525
John Snowf12a2822020-10-06 19:58:08 -0400526 def kill(self) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400527 """
528 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
529 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300530 self.shutdown(hard=True)
531
John Snow8226a4b2020-07-20 12:02:52 -0400532 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400533 """
534 Wait for the VM to power off and perform post-shutdown cleanup.
535
John Snow8226a4b2020-07-20 12:02:52 -0400536 :param timeout: Optional timeout in seconds. Default 30 seconds.
537 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400538 """
539 self.shutdown(has_quit=True, timeout=timeout)
540
John Snowf12a2822020-10-06 19:58:08 -0400541 def set_qmp_monitor(self, enabled: bool = True) -> None:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500542 """
543 Set the QMP monitor.
544
545 @param enabled: if False, qmp monitor options will be removed from
546 the base arguments of the resulting QEMU command
547 line. Default is True.
John Snow5c02c862021-06-29 17:43:23 -0400548
549 .. note:: Call this function before launch().
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500550 """
John Snowbe1183e2020-10-06 19:58:04 -0400551 self._qmp_set = enabled
552
553 @property
John Snowbeb6b572021-05-27 17:16:53 -0400554 def _qmp(self) -> QEMUMonitorProtocol:
John Snowbe1183e2020-10-06 19:58:04 -0400555 if self._qmp_connection is None:
556 raise QEMUMachineError("Attempt to access QMP with no connection")
557 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500558
John Snowaaa81ec2020-10-06 19:58:03 -0400559 @classmethod
560 def _qmp_args(cls, _conv_keys: bool = True, **args: Any) -> Dict[str, Any]:
John Snowabf0bf92019-06-27 17:28:14 -0400561 qmp_args = dict()
562 for key, value in args.items():
John Snowaaa81ec2020-10-06 19:58:03 -0400563 if _conv_keys:
John Snowabf0bf92019-06-27 17:28:14 -0400564 qmp_args[key.replace('_', '-')] = value
565 else:
566 qmp_args[key] = value
John Snowaaa81ec2020-10-06 19:58:03 -0400567 return qmp_args
John Snowabf0bf92019-06-27 17:28:14 -0400568
John Snowaaa81ec2020-10-06 19:58:03 -0400569 def qmp(self, cmd: str,
570 conv_keys: bool = True,
571 **args: Any) -> QMPMessage:
572 """
573 Invoke a QMP command and return the response dict
574 """
575 qmp_args = self._qmp_args(conv_keys, **args)
John Snowabf0bf92019-06-27 17:28:14 -0400576 return self._qmp.cmd(cmd, args=qmp_args)
577
John Snowf12a2822020-10-06 19:58:08 -0400578 def command(self, cmd: str,
579 conv_keys: bool = True,
580 **args: Any) -> QMPReturnValue:
John Snowabf0bf92019-06-27 17:28:14 -0400581 """
582 Invoke a QMP command.
583 On success return the response dict.
584 On failure raise an exception.
585 """
John Snowaaa81ec2020-10-06 19:58:03 -0400586 qmp_args = self._qmp_args(conv_keys, **args)
587 return self._qmp.command(cmd, **qmp_args)
John Snowabf0bf92019-06-27 17:28:14 -0400588
John Snowf12a2822020-10-06 19:58:08 -0400589 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400590 """
591 Poll for one queued QMP events and return it
592 """
John Snow306dfcd2019-06-27 17:28:15 -0400593 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400594 return self._events.pop(0)
595 return self._qmp.pull_event(wait=wait)
596
John Snowf12a2822020-10-06 19:58:08 -0400597 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400598 """
599 Poll for queued QMP events and return a list of dicts
600 """
601 events = self._qmp.get_events(wait=wait)
602 events.extend(self._events)
603 del self._events[:]
604 self._qmp.clear_events()
605 return events
606
607 @staticmethod
John Snowf12a2822020-10-06 19:58:08 -0400608 def event_match(event: Any, match: Optional[Any]) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400609 """
610 Check if an event matches optional match criteria.
611
612 The match criteria takes the form of a matching subdict. The event is
613 checked to be a superset of the subdict, recursively, with matching
614 values whenever the subdict values are not None.
615
616 This has a limitation that you cannot explicitly check for None values.
617
618 Examples, with the subdict queries on the left:
619 - None matches any object.
620 - {"foo": None} matches {"foo": {"bar": 1}}
621 - {"foo": None} matches {"foo": 5}
622 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
623 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
624 """
625 if match is None:
626 return True
627
628 try:
629 for key in match:
630 if key in event:
631 if not QEMUMachine.event_match(event[key], match[key]):
632 return False
633 else:
634 return False
635 return True
636 except TypeError:
637 # either match or event wasn't iterable (not a dict)
John Snowf12a2822020-10-06 19:58:08 -0400638 return bool(match == event)
John Snowabf0bf92019-06-27 17:28:14 -0400639
John Snowf12a2822020-10-06 19:58:08 -0400640 def event_wait(self, name: str,
641 timeout: float = 60.0,
642 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400643 """
644 event_wait waits for and returns a named event from QMP with a timeout.
645
646 name: The event to wait for.
647 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
648 match: Optional match criteria. See event_match for details.
649 """
650 return self.events_wait([(name, match)], timeout)
651
John Snowf12a2822020-10-06 19:58:08 -0400652 def events_wait(self,
653 events: Sequence[Tuple[str, Any]],
654 timeout: float = 60.0) -> Optional[QMPMessage]:
John Snowabf0bf92019-06-27 17:28:14 -0400655 """
John Snow1847a4a2020-10-06 19:58:02 -0400656 events_wait waits for and returns a single named event from QMP.
657 In the case of multiple qualifying events, this function returns the
658 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400659
John Snow1847a4a2020-10-06 19:58:02 -0400660 :param events: A sequence of (name, match_criteria) tuples.
661 The match criteria are optional and may be None.
662 See event_match for details.
663 :param timeout: Optional timeout, in seconds.
664 See QEMUMonitorProtocol.pull_event.
665
666 :raise QMPTimeoutError: If timeout was non-zero and no matching events
667 were found.
668 :return: A QMP event matching the filter criteria.
669 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400670 """
John Snowf12a2822020-10-06 19:58:08 -0400671 def _match(event: QMPMessage) -> bool:
John Snowabf0bf92019-06-27 17:28:14 -0400672 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400673 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400674 return True
675 return False
676
John Snow1847a4a2020-10-06 19:58:02 -0400677 event: Optional[QMPMessage]
678
John Snowabf0bf92019-06-27 17:28:14 -0400679 # Search cached events
680 for event in self._events:
681 if _match(event):
682 self._events.remove(event)
683 return event
684
685 # Poll for new events
686 while True:
687 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400688 if event is None:
689 # NB: None is only returned when timeout is false-ish.
690 # Timeouts raise QMPTimeoutError instead!
691 break
John Snowabf0bf92019-06-27 17:28:14 -0400692 if _match(event):
693 return event
694 self._events.append(event)
695
696 return None
697
John Snowf12a2822020-10-06 19:58:08 -0400698 def get_log(self) -> Optional[str]:
John Snowabf0bf92019-06-27 17:28:14 -0400699 """
700 After self.shutdown or failed qemu execution, this returns the output
701 of the qemu process.
702 """
703 return self._iolog
704
John Snowf12a2822020-10-06 19:58:08 -0400705 def add_args(self, *args: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400706 """
707 Adds to the list of extra arguments to be given to the QEMU binary
708 """
709 self._args.extend(args)
710
John Snowf12a2822020-10-06 19:58:08 -0400711 def set_machine(self, machine_type: str) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400712 """
713 Sets the machine type
714
715 If set, the machine type will be added to the base arguments
716 of the resulting QEMU command line.
717 """
718 self._machine = machine_type
719
John Snowf12a2822020-10-06 19:58:08 -0400720 def set_console(self,
721 device_type: Optional[str] = None,
722 console_index: int = 0) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400723 """
724 Sets the device type for a console device
725
726 If set, the console device and a backing character device will
727 be added to the base arguments of the resulting QEMU command
728 line.
729
730 This is a convenience method that will either use the provided
731 device type, or default to a "-serial chardev:console" command
732 line argument.
733
734 The actual setting of command line arguments will be be done at
735 machine launch time, as it depends on the temporary directory
736 to be created.
737
738 @param device_type: the device type, such as "isa-serial". If
739 None is given (the default value) a "-serial
740 chardev:console" command line argument will
741 be used instead, resorting to the machine's
742 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100743 @param console_index: the index of the console device to use.
744 If not zero, the command line will create
745 'index - 1' consoles and connect them to
746 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400747 """
748 self._console_set = True
749 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100750 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400751
752 @property
John Snowf12a2822020-10-06 19:58:08 -0400753 def console_socket(self) -> socket.socket:
John Snowabf0bf92019-06-27 17:28:14 -0400754 """
755 Returns a socket connected to the console
756 """
757 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100758 self._console_socket = console_socket.ConsoleSocket(
759 self._console_address,
760 file=self._console_log_path,
761 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400762 return self._console_socket
Cleber Rosa2ca6e262021-02-11 17:01:42 -0500763
764 @property
765 def temp_dir(self) -> str:
766 """
767 Returns a temporary directory to be used for this machine
768 """
769 if self._temp_dir is None:
770 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
771 dir=self._base_temp_dir)
772 return self._temp_dir