blob: 3e9cf09fd2d22a692636b0a7fc048ba84e6b340f [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
21import logging
22import os
John Snowabf0bf92019-06-27 17:28:14 -040023import shutil
John Snowde6e08b2020-07-10 01:06:48 -040024import signal
John Snow932ca4b2020-10-06 19:57:58 -040025import subprocess
John Snowabf0bf92019-06-27 17:28:14 -040026import tempfile
John Snow1dda0402020-05-14 01:53:44 -040027from types import TracebackType
John Snowaaa81ec2020-10-06 19:58:03 -040028from typing import (
29 Any,
30 Dict,
31 List,
32 Optional,
33 Type,
34)
John Snowabf0bf92019-06-27 17:28:14 -040035
John Snow932ca4b2020-10-06 19:57:58 -040036from . import console_socket, qmp
John Snow1847a4a2020-10-06 19:58:02 -040037from .qmp import QMPMessage, SocketAddrT
John Snow932ca4b2020-10-06 19:57:58 -040038
John Snowabf0bf92019-06-27 17:28:14 -040039
40LOG = logging.getLogger(__name__)
41
John Snow8dfac2e2020-05-28 18:21:29 -040042
John Snowabf0bf92019-06-27 17:28:14 -040043class QEMUMachineError(Exception):
44 """
45 Exception called when an error in QEMUMachine happens.
46 """
47
48
49class QEMUMachineAddDeviceError(QEMUMachineError):
50 """
51 Exception raised when a request to add a device can not be fulfilled
52
53 The failures are caused by limitations, lack of information or conflicting
54 requests on the QEMUMachine methods. This exception does not represent
55 failures reported by the QEMU binary itself.
56 """
57
58
John Snow193bf1c2020-07-10 01:06:47 -040059class AbnormalShutdown(QEMUMachineError):
60 """
61 Exception raised when a graceful shutdown was requested, but not performed.
62 """
63
64
John Snow9b8ccd62020-05-28 18:21:28 -040065class QEMUMachine:
John Snowabf0bf92019-06-27 17:28:14 -040066 """
67 A QEMU VM
68
John Snow8dfac2e2020-05-28 18:21:29 -040069 Use this object as a context manager to ensure
70 the QEMU process terminates::
John Snowabf0bf92019-06-27 17:28:14 -040071
72 with VM(binary) as vm:
73 ...
74 # vm is guaranteed to be shut down here
75 """
76
77 def __init__(self, binary, args=None, wrapper=None, name=None,
John Snowc4e60232020-10-06 19:57:59 -040078 test_dir="/var/tmp",
79 monitor_address: Optional[SocketAddrT] = None,
Robert Foley0fc8f662020-07-01 14:56:24 +010080 socket_scm_helper=None, sock_dir=None,
81 drain_console=False, console_log=None):
John Snowabf0bf92019-06-27 17:28:14 -040082 '''
83 Initialize a QEMUMachine
84
85 @param binary: path to the qemu binary
86 @param args: list of extra arguments
87 @param wrapper: list of arguments used as prefix to qemu binary
88 @param name: prefix for socket and log file names (default: qemu-PID)
89 @param test_dir: where to create socket and log file
90 @param monitor_address: address for QMP monitor
91 @param socket_scm_helper: helper program, required for send_fd_scm()
Robert Foley0fc8f662020-07-01 14:56:24 +010092 @param sock_dir: where to create socket (overrides test_dir for sock)
Robert Foley0fc8f662020-07-01 14:56:24 +010093 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -040094 @param console_log: (optional) path to console log file
John Snowabf0bf92019-06-27 17:28:14 -040095 @note: Qemu process is not started until launch() is used.
96 '''
John Snowc5e61a62020-10-06 19:58:00 -040097 # Direct user configuration
98
99 self._binary = binary
100
John Snowabf0bf92019-06-27 17:28:14 -0400101 if args is None:
102 args = []
John Snowc5e61a62020-10-06 19:58:00 -0400103 # Copy mutable input: we will be modifying our copy
104 self._args = list(args)
105
John Snowabf0bf92019-06-27 17:28:14 -0400106 if wrapper is None:
107 wrapper = []
John Snowc5e61a62020-10-06 19:58:00 -0400108 self._wrapper = wrapper
109
110 self._name = name or "qemu-%d" % os.getpid()
111 self._test_dir = test_dir
112 self._sock_dir = sock_dir or self._test_dir
113 self._socket_scm_helper = socket_scm_helper
114
John Snowc4e60232020-10-06 19:57:59 -0400115 if monitor_address is not None:
116 self._monitor_address = monitor_address
117 self._remove_monitor_sockfile = False
118 else:
119 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400120 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400121 )
122 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400123
124 self._console_log_path = console_log
125 if self._console_log_path:
126 # In order to log the console, buffering needs to be enabled.
127 self._drain_console = True
128 else:
129 self._drain_console = drain_console
130
131 # Runstate
John Snowabf0bf92019-06-27 17:28:14 -0400132 self._qemu_log_path = None
133 self._qemu_log_file = None
134 self._popen = None
John Snowabf0bf92019-06-27 17:28:14 -0400135 self._events = []
136 self._iolog = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500137 self._qmp_set = True # Enable QMP monitor by default.
John Snowbe1183e2020-10-06 19:58:04 -0400138 self._qmp_connection: Optional[qmp.QEMUMonitorProtocol] = None
John Snowabf0bf92019-06-27 17:28:14 -0400139 self._qemu_full_args = None
John Snowabf0bf92019-06-27 17:28:14 -0400140 self._temp_dir = None
141 self._launched = False
142 self._machine = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100143 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400144 self._console_set = False
145 self._console_device_type = None
John Snow652809d2020-10-06 19:58:01 -0400146 self._console_address = os.path.join(
147 self._sock_dir, f"{self._name}-console.sock"
148 )
John Snowabf0bf92019-06-27 17:28:14 -0400149 self._console_socket = None
Max Reitz32558ce2019-10-17 15:31:34 +0200150 self._remove_files = []
John Snowde6e08b2020-07-10 01:06:48 -0400151 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400152
John Snowabf0bf92019-06-27 17:28:14 -0400153 def __enter__(self):
154 return self
155
John Snow1dda0402020-05-14 01:53:44 -0400156 def __exit__(self,
157 exc_type: Optional[Type[BaseException]],
158 exc_val: Optional[BaseException],
159 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400160 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400161
John Snowabf0bf92019-06-27 17:28:14 -0400162 def add_monitor_null(self):
John Snow306dfcd2019-06-27 17:28:15 -0400163 """
164 This can be used to add an unused monitor instance.
165 """
John Snowabf0bf92019-06-27 17:28:14 -0400166 self._args.append('-monitor')
167 self._args.append('null')
168
169 def add_fd(self, fd, fdset, opaque, opts=''):
170 """
171 Pass a file descriptor to the VM
172 """
173 options = ['fd=%d' % fd,
174 'set=%d' % fdset,
175 'opaque=%s' % opaque]
176 if opts:
177 options.append(opts)
178
179 # This did not exist before 3.4, but since then it is
180 # mandatory for our purpose
181 if hasattr(os, 'set_inheritable'):
182 os.set_inheritable(fd, True)
183
184 self._args.append('-add-fd')
185 self._args.append(','.join(options))
186 return self
187
John Snowabf0bf92019-06-27 17:28:14 -0400188 def send_fd_scm(self, fd=None, file_path=None):
John Snow306dfcd2019-06-27 17:28:15 -0400189 """
190 Send an fd or file_path to socket_scm_helper.
191
192 Exactly one of fd and file_path must be given.
193 If it is file_path, the helper will open that file and pass its own fd.
194 """
John Snowabf0bf92019-06-27 17:28:14 -0400195 # In iotest.py, the qmp should always use unix socket.
196 assert self._qmp.is_scm_available()
197 if self._socket_scm_helper is None:
198 raise QEMUMachineError("No path to socket_scm_helper set")
199 if not os.path.exists(self._socket_scm_helper):
200 raise QEMUMachineError("%s does not exist" %
201 self._socket_scm_helper)
202
203 # This did not exist before 3.4, but since then it is
204 # mandatory for our purpose
205 if hasattr(os, 'set_inheritable'):
206 os.set_inheritable(self._qmp.get_sock_fd(), True)
207 if fd is not None:
208 os.set_inheritable(fd, True)
209
210 fd_param = ["%s" % self._socket_scm_helper,
211 "%d" % self._qmp.get_sock_fd()]
212
213 if file_path is not None:
214 assert fd is None
215 fd_param.append(file_path)
216 else:
217 assert fd is not None
218 fd_param.append(str(fd))
219
220 devnull = open(os.path.devnull, 'rb')
John Snow8dfac2e2020-05-28 18:21:29 -0400221 proc = subprocess.Popen(
222 fd_param, stdin=devnull, stdout=subprocess.PIPE,
223 stderr=subprocess.STDOUT, close_fds=False
224 )
John Snowabf0bf92019-06-27 17:28:14 -0400225 output = proc.communicate()[0]
226 if output:
227 LOG.debug(output)
228
229 return proc.returncode
230
231 @staticmethod
232 def _remove_if_exists(path):
233 """
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
243 def is_running(self):
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
247 def exitcode(self):
John Snow306dfcd2019-06-27 17:28:15 -0400248 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400249 if self._popen is None:
250 return None
251 return self._popen.poll()
252
253 def get_pid(self):
John Snow306dfcd2019-06-27 17:28:15 -0400254 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400255 if not self.is_running():
256 return None
257 return self._popen.pid
258
259 def _load_io_log(self):
260 if self._qemu_log_path is not None:
261 with open(self._qemu_log_path, "r") as iolog:
262 self._iolog = iolog.read()
263
John Snow652809d2020-10-06 19:58:01 -0400264 @property
265 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500266 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400267
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500268 if self._qmp_set:
269 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400270 moncdev = "socket,id=mon,host={},port={}".format(
271 *self._monitor_address
272 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500273 else:
John Snowc4e60232020-10-06 19:57:59 -0400274 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500275 args.extend(['-chardev', moncdev, '-mon',
276 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400277
John Snowabf0bf92019-06-27 17:28:14 -0400278 if self._machine is not None:
279 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400280 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100281 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400282 if self._console_set:
John Snowabf0bf92019-06-27 17:28:14 -0400283 chardev = ('socket,id=console,path=%s,server,nowait' %
284 self._console_address)
285 args.extend(['-chardev', chardev])
286 if self._console_device_type is None:
287 args.extend(['-serial', 'chardev:console'])
288 else:
289 device = '%s,chardev=console' % self._console_device_type
290 args.extend(['-device', device])
291 return args
292
293 def _pre_launch(self):
294 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
John Snowabf0bf92019-06-27 17:28:14 -0400295 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
296 self._qemu_log_file = open(self._qemu_log_path, 'wb')
297
John Snow652809d2020-10-06 19:58:01 -0400298 if self._console_set:
299 self._remove_files.append(self._console_address)
300
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500301 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400302 if self._remove_monitor_sockfile:
303 assert isinstance(self._monitor_address, str)
304 self._remove_files.append(self._monitor_address)
John Snowbe1183e2020-10-06 19:58:04 -0400305 self._qmp_connection = qmp.QEMUMonitorProtocol(
John Snowc4e60232020-10-06 19:57:59 -0400306 self._monitor_address,
307 server=True,
308 nickname=self._name
309 )
John Snowabf0bf92019-06-27 17:28:14 -0400310
311 def _post_launch(self):
John Snowbe1183e2020-10-06 19:58:04 -0400312 if self._qmp_connection:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500313 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400314
315 def _post_shutdown(self):
John Snowa3842cb2020-07-10 01:06:42 -0400316 """
317 Called to cleanup the VM instance after the process has exited.
318 May also be called after a failed launch.
319 """
320 # Comprehensive reset for the failed launch case:
321 self._early_cleanup()
322
John Snowbe1183e2020-10-06 19:58:04 -0400323 if self._qmp_connection:
John Snow671940e2020-07-10 01:06:39 -0400324 self._qmp.close()
John Snowbe1183e2020-10-06 19:58:04 -0400325 self._qmp_connection = None
John Snow671940e2020-07-10 01:06:39 -0400326
John Snow14661d92020-07-10 01:06:38 -0400327 self._load_io_log()
328
John Snowabf0bf92019-06-27 17:28:14 -0400329 if self._qemu_log_file is not None:
330 self._qemu_log_file.close()
331 self._qemu_log_file = None
332
333 self._qemu_log_path = None
334
John Snowabf0bf92019-06-27 17:28:14 -0400335 if self._temp_dir is not None:
336 shutil.rmtree(self._temp_dir)
337 self._temp_dir = None
338
Max Reitz32558ce2019-10-17 15:31:34 +0200339 while len(self._remove_files) > 0:
340 self._remove_if_exists(self._remove_files.pop())
341
John Snow14661d92020-07-10 01:06:38 -0400342 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400343 if (exitcode is not None and exitcode < 0
344 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400345 msg = 'qemu received signal %i; command: "%s"'
346 if self._qemu_full_args:
347 command = ' '.join(self._qemu_full_args)
348 else:
349 command = ''
350 LOG.warning(msg, -int(exitcode), command)
351
John Snowde6e08b2020-07-10 01:06:48 -0400352 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400353 self._launched = False
354
John Snowabf0bf92019-06-27 17:28:14 -0400355 def launch(self):
356 """
357 Launch the VM and make sure we cleanup and expose the
358 command line/output in case of exception
359 """
360
361 if self._launched:
362 raise QEMUMachineError('VM already launched')
363
364 self._iolog = None
365 self._qemu_full_args = None
366 try:
367 self._launch()
368 self._launched = True
369 except:
John Snowa3842cb2020-07-10 01:06:42 -0400370 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400371
372 LOG.debug('Error launching VM')
373 if self._qemu_full_args:
374 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
375 if self._iolog:
376 LOG.debug('Output: %r', self._iolog)
377 raise
378
379 def _launch(self):
380 """
381 Launch the VM and establish a QMP connection
382 """
383 devnull = open(os.path.devnull, 'rb')
384 self._pre_launch()
385 self._qemu_full_args = (self._wrapper + [self._binary] +
John Snow652809d2020-10-06 19:58:01 -0400386 self._base_args + self._args)
John Snowabf0bf92019-06-27 17:28:14 -0400387 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
388 self._popen = subprocess.Popen(self._qemu_full_args,
389 stdin=devnull,
390 stdout=self._qemu_log_file,
391 stderr=subprocess.STDOUT,
392 shell=False,
393 close_fds=False)
394 self._post_launch()
395
John Snowe2c97f12020-07-10 01:06:40 -0400396 def _early_cleanup(self) -> None:
397 """
398 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400399
John Snow193bf1c2020-07-10 01:06:47 -0400400 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400401 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400402 """
403 # If we keep the console socket open, we may deadlock waiting
404 # for QEMU to exit, while QEMU is waiting for the socket to
405 # become writeable.
406 if self._console_socket is not None:
407 self._console_socket.close()
408 self._console_socket = None
409
John Snow193bf1c2020-07-10 01:06:47 -0400410 def _hard_shutdown(self) -> None:
411 """
412 Perform early cleanup, kill the VM, and wait for it to terminate.
413
414 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
415 waiting for the QEMU process to terminate.
416 """
417 self._early_cleanup()
418 self._popen.kill()
419 self._popen.wait(timeout=60)
420
John Snow8226a4b2020-07-20 12:02:52 -0400421 def _soft_shutdown(self, timeout: Optional[int],
422 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400423 """
424 Perform early cleanup, attempt to gracefully shut down the VM, and wait
425 for it to terminate.
426
John Snow8226a4b2020-07-20 12:02:52 -0400427 :param timeout: Timeout in seconds for graceful shutdown.
428 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400429 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400430
431 :raise ConnectionReset: On QMP communication errors
432 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
433 the QEMU process to terminate.
434 """
435 self._early_cleanup()
436
John Snowbe1183e2020-10-06 19:58:04 -0400437 if self._qmp_connection:
John Snow193bf1c2020-07-10 01:06:47 -0400438 if not has_quit:
439 # Might raise ConnectionReset
440 self._qmp.cmd('quit')
441
442 # May raise subprocess.TimeoutExpired
443 self._popen.wait(timeout=timeout)
444
John Snow8226a4b2020-07-20 12:02:52 -0400445 def _do_shutdown(self, timeout: Optional[int],
446 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400447 """
448 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
449
John Snow8226a4b2020-07-20 12:02:52 -0400450 :param timeout: Timeout in seconds for graceful shutdown.
451 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400452 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400453
454 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
455 The inner exception will likely be ConnectionReset or
456 subprocess.TimeoutExpired. In rare cases, non-graceful termination
457 may result in its own exceptions, likely subprocess.TimeoutExpired.
458 """
459 try:
John Snow8226a4b2020-07-20 12:02:52 -0400460 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400461 except Exception as exc:
462 self._hard_shutdown()
463 raise AbnormalShutdown("Could not perform graceful shutdown") \
464 from exc
465
John Snowc9b30452020-07-10 01:06:43 -0400466 def shutdown(self, has_quit: bool = False,
467 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400468 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400469 """
John Snow193bf1c2020-07-10 01:06:47 -0400470 Terminate the VM (gracefully if possible) and perform cleanup.
471 Cleanup will always be performed.
472
473 If the VM has not yet been launched, or shutdown(), wait(), or kill()
474 have already been called, this method does nothing.
475
476 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
477 :param hard: When true, do not attempt graceful shutdown, and
478 suppress the SIGKILL warning log message.
479 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400480 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400481 """
John Snowa3842cb2020-07-10 01:06:42 -0400482 if not self._launched:
483 return
484
John Snow193bf1c2020-07-10 01:06:47 -0400485 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300486 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400487 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400488 self._hard_shutdown()
489 else:
John Snow8226a4b2020-07-20 12:02:52 -0400490 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400491 finally:
492 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400493
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300494 def kill(self):
John Snow193bf1c2020-07-10 01:06:47 -0400495 """
496 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
497 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300498 self.shutdown(hard=True)
499
John Snow8226a4b2020-07-20 12:02:52 -0400500 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400501 """
502 Wait for the VM to power off and perform post-shutdown cleanup.
503
John Snow8226a4b2020-07-20 12:02:52 -0400504 :param timeout: Optional timeout in seconds. Default 30 seconds.
505 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400506 """
507 self.shutdown(has_quit=True, timeout=timeout)
508
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500509 def set_qmp_monitor(self, enabled=True):
510 """
511 Set the QMP monitor.
512
513 @param enabled: if False, qmp monitor options will be removed from
514 the base arguments of the resulting QEMU command
515 line. Default is True.
516 @note: call this function before launch().
517 """
John Snowbe1183e2020-10-06 19:58:04 -0400518 self._qmp_set = enabled
519
520 @property
521 def _qmp(self) -> qmp.QEMUMonitorProtocol:
522 if self._qmp_connection is None:
523 raise QEMUMachineError("Attempt to access QMP with no connection")
524 return self._qmp_connection
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500525
John Snowaaa81ec2020-10-06 19:58:03 -0400526 @classmethod
527 def _qmp_args(cls, _conv_keys: bool = True, **args: Any) -> Dict[str, Any]:
John Snowabf0bf92019-06-27 17:28:14 -0400528 qmp_args = dict()
529 for key, value in args.items():
John Snowaaa81ec2020-10-06 19:58:03 -0400530 if _conv_keys:
John Snowabf0bf92019-06-27 17:28:14 -0400531 qmp_args[key.replace('_', '-')] = value
532 else:
533 qmp_args[key] = value
John Snowaaa81ec2020-10-06 19:58:03 -0400534 return qmp_args
John Snowabf0bf92019-06-27 17:28:14 -0400535
John Snowaaa81ec2020-10-06 19:58:03 -0400536 def qmp(self, cmd: str,
537 conv_keys: bool = True,
538 **args: Any) -> QMPMessage:
539 """
540 Invoke a QMP command and return the response dict
541 """
542 qmp_args = self._qmp_args(conv_keys, **args)
John Snowabf0bf92019-06-27 17:28:14 -0400543 return self._qmp.cmd(cmd, args=qmp_args)
544
545 def command(self, cmd, conv_keys=True, **args):
546 """
547 Invoke a QMP command.
548 On success return the response dict.
549 On failure raise an exception.
550 """
John Snowaaa81ec2020-10-06 19:58:03 -0400551 qmp_args = self._qmp_args(conv_keys, **args)
552 return self._qmp.command(cmd, **qmp_args)
John Snowabf0bf92019-06-27 17:28:14 -0400553
554 def get_qmp_event(self, wait=False):
555 """
556 Poll for one queued QMP events and return it
557 """
John Snow306dfcd2019-06-27 17:28:15 -0400558 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400559 return self._events.pop(0)
560 return self._qmp.pull_event(wait=wait)
561
562 def get_qmp_events(self, wait=False):
563 """
564 Poll for queued QMP events and return a list of dicts
565 """
566 events = self._qmp.get_events(wait=wait)
567 events.extend(self._events)
568 del self._events[:]
569 self._qmp.clear_events()
570 return events
571
572 @staticmethod
573 def event_match(event, match=None):
574 """
575 Check if an event matches optional match criteria.
576
577 The match criteria takes the form of a matching subdict. The event is
578 checked to be a superset of the subdict, recursively, with matching
579 values whenever the subdict values are not None.
580
581 This has a limitation that you cannot explicitly check for None values.
582
583 Examples, with the subdict queries on the left:
584 - None matches any object.
585 - {"foo": None} matches {"foo": {"bar": 1}}
586 - {"foo": None} matches {"foo": 5}
587 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
588 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
589 """
590 if match is None:
591 return True
592
593 try:
594 for key in match:
595 if key in event:
596 if not QEMUMachine.event_match(event[key], match[key]):
597 return False
598 else:
599 return False
600 return True
601 except TypeError:
602 # either match or event wasn't iterable (not a dict)
603 return match == event
604
605 def event_wait(self, name, timeout=60.0, match=None):
606 """
607 event_wait waits for and returns a named event from QMP with a timeout.
608
609 name: The event to wait for.
610 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
611 match: Optional match criteria. See event_match for details.
612 """
613 return self.events_wait([(name, match)], timeout)
614
615 def events_wait(self, events, timeout=60.0):
616 """
John Snow1847a4a2020-10-06 19:58:02 -0400617 events_wait waits for and returns a single named event from QMP.
618 In the case of multiple qualifying events, this function returns the
619 first one.
John Snowabf0bf92019-06-27 17:28:14 -0400620
John Snow1847a4a2020-10-06 19:58:02 -0400621 :param events: A sequence of (name, match_criteria) tuples.
622 The match criteria are optional and may be None.
623 See event_match for details.
624 :param timeout: Optional timeout, in seconds.
625 See QEMUMonitorProtocol.pull_event.
626
627 :raise QMPTimeoutError: If timeout was non-zero and no matching events
628 were found.
629 :return: A QMP event matching the filter criteria.
630 If timeout was 0 and no event matched, None.
John Snowabf0bf92019-06-27 17:28:14 -0400631 """
632 def _match(event):
633 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400634 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400635 return True
636 return False
637
John Snow1847a4a2020-10-06 19:58:02 -0400638 event: Optional[QMPMessage]
639
John Snowabf0bf92019-06-27 17:28:14 -0400640 # Search cached events
641 for event in self._events:
642 if _match(event):
643 self._events.remove(event)
644 return event
645
646 # Poll for new events
647 while True:
648 event = self._qmp.pull_event(wait=timeout)
John Snow1847a4a2020-10-06 19:58:02 -0400649 if event is None:
650 # NB: None is only returned when timeout is false-ish.
651 # Timeouts raise QMPTimeoutError instead!
652 break
John Snowabf0bf92019-06-27 17:28:14 -0400653 if _match(event):
654 return event
655 self._events.append(event)
656
657 return None
658
659 def get_log(self):
660 """
661 After self.shutdown or failed qemu execution, this returns the output
662 of the qemu process.
663 """
664 return self._iolog
665
666 def add_args(self, *args):
667 """
668 Adds to the list of extra arguments to be given to the QEMU binary
669 """
670 self._args.extend(args)
671
672 def set_machine(self, machine_type):
673 """
674 Sets the machine type
675
676 If set, the machine type will be added to the base arguments
677 of the resulting QEMU command line.
678 """
679 self._machine = machine_type
680
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100681 def set_console(self, device_type=None, console_index=0):
John Snowabf0bf92019-06-27 17:28:14 -0400682 """
683 Sets the device type for a console device
684
685 If set, the console device and a backing character device will
686 be added to the base arguments of the resulting QEMU command
687 line.
688
689 This is a convenience method that will either use the provided
690 device type, or default to a "-serial chardev:console" command
691 line argument.
692
693 The actual setting of command line arguments will be be done at
694 machine launch time, as it depends on the temporary directory
695 to be created.
696
697 @param device_type: the device type, such as "isa-serial". If
698 None is given (the default value) a "-serial
699 chardev:console" command line argument will
700 be used instead, resorting to the machine's
701 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100702 @param console_index: the index of the console device to use.
703 If not zero, the command line will create
704 'index - 1' consoles and connect them to
705 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400706 """
707 self._console_set = True
708 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100709 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400710
711 @property
712 def console_socket(self):
713 """
714 Returns a socket connected to the console
715 """
716 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100717 self._console_socket = console_socket.ConsoleSocket(
718 self._console_address,
719 file=self._console_log_path,
720 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400721 return self._console_socket