blob: 3017ec072dfe2e691e4dee4ec68fa9beddf3618c [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 Snow932ca4b2020-10-06 19:57:58 -040028from typing import Optional, Type
John Snowabf0bf92019-06-27 17:28:14 -040029
John Snow932ca4b2020-10-06 19:57:58 -040030from . import console_socket, qmp
John Snowc4e60232020-10-06 19:57:59 -040031from .qmp import SocketAddrT
John Snow932ca4b2020-10-06 19:57:58 -040032
John Snowabf0bf92019-06-27 17:28:14 -040033
34LOG = logging.getLogger(__name__)
35
John Snow8dfac2e2020-05-28 18:21:29 -040036
John Snowabf0bf92019-06-27 17:28:14 -040037class QEMUMachineError(Exception):
38 """
39 Exception called when an error in QEMUMachine happens.
40 """
41
42
43class QEMUMachineAddDeviceError(QEMUMachineError):
44 """
45 Exception raised when a request to add a device can not be fulfilled
46
47 The failures are caused by limitations, lack of information or conflicting
48 requests on the QEMUMachine methods. This exception does not represent
49 failures reported by the QEMU binary itself.
50 """
51
52
John Snow193bf1c2020-07-10 01:06:47 -040053class AbnormalShutdown(QEMUMachineError):
54 """
55 Exception raised when a graceful shutdown was requested, but not performed.
56 """
57
58
John Snow9b8ccd62020-05-28 18:21:28 -040059class QEMUMachine:
John Snowabf0bf92019-06-27 17:28:14 -040060 """
61 A QEMU VM
62
John Snow8dfac2e2020-05-28 18:21:29 -040063 Use this object as a context manager to ensure
64 the QEMU process terminates::
John Snowabf0bf92019-06-27 17:28:14 -040065
66 with VM(binary) as vm:
67 ...
68 # vm is guaranteed to be shut down here
69 """
70
71 def __init__(self, binary, args=None, wrapper=None, name=None,
John Snowc4e60232020-10-06 19:57:59 -040072 test_dir="/var/tmp",
73 monitor_address: Optional[SocketAddrT] = None,
Robert Foley0fc8f662020-07-01 14:56:24 +010074 socket_scm_helper=None, sock_dir=None,
75 drain_console=False, console_log=None):
John Snowabf0bf92019-06-27 17:28:14 -040076 '''
77 Initialize a QEMUMachine
78
79 @param binary: path to the qemu binary
80 @param args: list of extra arguments
81 @param wrapper: list of arguments used as prefix to qemu binary
82 @param name: prefix for socket and log file names (default: qemu-PID)
83 @param test_dir: where to create socket and log file
84 @param monitor_address: address for QMP monitor
85 @param socket_scm_helper: helper program, required for send_fd_scm()
Robert Foley0fc8f662020-07-01 14:56:24 +010086 @param sock_dir: where to create socket (overrides test_dir for sock)
87 @param console_log: (optional) path to console log file
88 @param drain_console: (optional) True to drain console socket to buffer
John Snowabf0bf92019-06-27 17:28:14 -040089 @note: Qemu process is not started until launch() is used.
90 '''
91 if args is None:
92 args = []
93 if wrapper is None:
94 wrapper = []
95 if name is None:
96 name = "qemu-%d" % os.getpid()
Max Reitz32558ce2019-10-17 15:31:34 +020097 if sock_dir is None:
98 sock_dir = test_dir
John Snowabf0bf92019-06-27 17:28:14 -040099 self._name = name
John Snowc4e60232020-10-06 19:57:59 -0400100 if monitor_address is not None:
101 self._monitor_address = monitor_address
102 self._remove_monitor_sockfile = False
103 else:
104 self._monitor_address = os.path.join(
105 sock_dir, f"{name}-monitor.sock"
106 )
107 self._remove_monitor_sockfile = True
John Snowabf0bf92019-06-27 17:28:14 -0400108 self._qemu_log_path = None
109 self._qemu_log_file = None
110 self._popen = None
111 self._binary = binary
112 self._args = list(args) # Force copy args in case we modify them
113 self._wrapper = wrapper
114 self._events = []
115 self._iolog = None
116 self._socket_scm_helper = socket_scm_helper
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500117 self._qmp_set = True # Enable QMP monitor by default.
John Snowabf0bf92019-06-27 17:28:14 -0400118 self._qmp = None
119 self._qemu_full_args = None
120 self._test_dir = test_dir
121 self._temp_dir = None
Max Reitz32558ce2019-10-17 15:31:34 +0200122 self._sock_dir = sock_dir
John Snowabf0bf92019-06-27 17:28:14 -0400123 self._launched = False
124 self._machine = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100125 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400126 self._console_set = False
127 self._console_device_type = None
128 self._console_address = None
129 self._console_socket = None
Max Reitz32558ce2019-10-17 15:31:34 +0200130 self._remove_files = []
John Snowde6e08b2020-07-10 01:06:48 -0400131 self._user_killed = False
Robert Foley0fc8f662020-07-01 14:56:24 +0100132 self._console_log_path = console_log
133 if self._console_log_path:
134 # In order to log the console, buffering needs to be enabled.
135 self._drain_console = True
136 else:
137 self._drain_console = drain_console
John Snowabf0bf92019-06-27 17:28:14 -0400138
John Snowabf0bf92019-06-27 17:28:14 -0400139 def __enter__(self):
140 return self
141
John Snow1dda0402020-05-14 01:53:44 -0400142 def __exit__(self,
143 exc_type: Optional[Type[BaseException]],
144 exc_val: Optional[BaseException],
145 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400146 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400147
John Snowabf0bf92019-06-27 17:28:14 -0400148 def add_monitor_null(self):
John Snow306dfcd2019-06-27 17:28:15 -0400149 """
150 This can be used to add an unused monitor instance.
151 """
John Snowabf0bf92019-06-27 17:28:14 -0400152 self._args.append('-monitor')
153 self._args.append('null')
154
155 def add_fd(self, fd, fdset, opaque, opts=''):
156 """
157 Pass a file descriptor to the VM
158 """
159 options = ['fd=%d' % fd,
160 'set=%d' % fdset,
161 'opaque=%s' % opaque]
162 if opts:
163 options.append(opts)
164
165 # This did not exist before 3.4, but since then it is
166 # mandatory for our purpose
167 if hasattr(os, 'set_inheritable'):
168 os.set_inheritable(fd, True)
169
170 self._args.append('-add-fd')
171 self._args.append(','.join(options))
172 return self
173
John Snowabf0bf92019-06-27 17:28:14 -0400174 def send_fd_scm(self, fd=None, file_path=None):
John Snow306dfcd2019-06-27 17:28:15 -0400175 """
176 Send an fd or file_path to socket_scm_helper.
177
178 Exactly one of fd and file_path must be given.
179 If it is file_path, the helper will open that file and pass its own fd.
180 """
John Snowabf0bf92019-06-27 17:28:14 -0400181 # In iotest.py, the qmp should always use unix socket.
182 assert self._qmp.is_scm_available()
183 if self._socket_scm_helper is None:
184 raise QEMUMachineError("No path to socket_scm_helper set")
185 if not os.path.exists(self._socket_scm_helper):
186 raise QEMUMachineError("%s does not exist" %
187 self._socket_scm_helper)
188
189 # This did not exist before 3.4, but since then it is
190 # mandatory for our purpose
191 if hasattr(os, 'set_inheritable'):
192 os.set_inheritable(self._qmp.get_sock_fd(), True)
193 if fd is not None:
194 os.set_inheritable(fd, True)
195
196 fd_param = ["%s" % self._socket_scm_helper,
197 "%d" % self._qmp.get_sock_fd()]
198
199 if file_path is not None:
200 assert fd is None
201 fd_param.append(file_path)
202 else:
203 assert fd is not None
204 fd_param.append(str(fd))
205
206 devnull = open(os.path.devnull, 'rb')
John Snow8dfac2e2020-05-28 18:21:29 -0400207 proc = subprocess.Popen(
208 fd_param, stdin=devnull, stdout=subprocess.PIPE,
209 stderr=subprocess.STDOUT, close_fds=False
210 )
John Snowabf0bf92019-06-27 17:28:14 -0400211 output = proc.communicate()[0]
212 if output:
213 LOG.debug(output)
214
215 return proc.returncode
216
217 @staticmethod
218 def _remove_if_exists(path):
219 """
220 Remove file object at path if it exists
221 """
222 try:
223 os.remove(path)
224 except OSError as exception:
225 if exception.errno == errno.ENOENT:
226 return
227 raise
228
229 def is_running(self):
John Snow306dfcd2019-06-27 17:28:15 -0400230 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400231 return self._popen is not None and self._popen.poll() is None
232
233 def exitcode(self):
John Snow306dfcd2019-06-27 17:28:15 -0400234 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400235 if self._popen is None:
236 return None
237 return self._popen.poll()
238
239 def get_pid(self):
John Snow306dfcd2019-06-27 17:28:15 -0400240 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400241 if not self.is_running():
242 return None
243 return self._popen.pid
244
245 def _load_io_log(self):
246 if self._qemu_log_path is not None:
247 with open(self._qemu_log_path, "r") as iolog:
248 self._iolog = iolog.read()
249
250 def _base_args(self):
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500251 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400252
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500253 if self._qmp_set:
254 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400255 moncdev = "socket,id=mon,host={},port={}".format(
256 *self._monitor_address
257 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500258 else:
John Snowc4e60232020-10-06 19:57:59 -0400259 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500260 args.extend(['-chardev', moncdev, '-mon',
261 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400262
John Snowabf0bf92019-06-27 17:28:14 -0400263 if self._machine is not None:
264 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400265 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100266 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400267 if self._console_set:
Max Reitz32558ce2019-10-17 15:31:34 +0200268 self._console_address = os.path.join(self._sock_dir,
John Snowabf0bf92019-06-27 17:28:14 -0400269 self._name + "-console.sock")
Max Reitz32558ce2019-10-17 15:31:34 +0200270 self._remove_files.append(self._console_address)
John Snowabf0bf92019-06-27 17:28:14 -0400271 chardev = ('socket,id=console,path=%s,server,nowait' %
272 self._console_address)
273 args.extend(['-chardev', chardev])
274 if self._console_device_type is None:
275 args.extend(['-serial', 'chardev:console'])
276 else:
277 device = '%s,chardev=console' % self._console_device_type
278 args.extend(['-device', device])
279 return args
280
281 def _pre_launch(self):
282 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
John Snowabf0bf92019-06-27 17:28:14 -0400283 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
284 self._qemu_log_file = open(self._qemu_log_path, 'wb')
285
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500286 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400287 if self._remove_monitor_sockfile:
288 assert isinstance(self._monitor_address, str)
289 self._remove_files.append(self._monitor_address)
290 self._qmp = qmp.QEMUMonitorProtocol(
291 self._monitor_address,
292 server=True,
293 nickname=self._name
294 )
John Snowabf0bf92019-06-27 17:28:14 -0400295
296 def _post_launch(self):
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500297 if self._qmp:
298 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400299
300 def _post_shutdown(self):
John Snowa3842cb2020-07-10 01:06:42 -0400301 """
302 Called to cleanup the VM instance after the process has exited.
303 May also be called after a failed launch.
304 """
305 # Comprehensive reset for the failed launch case:
306 self._early_cleanup()
307
John Snow671940e2020-07-10 01:06:39 -0400308 if self._qmp:
309 self._qmp.close()
310 self._qmp = None
311
John Snow14661d92020-07-10 01:06:38 -0400312 self._load_io_log()
313
John Snowabf0bf92019-06-27 17:28:14 -0400314 if self._qemu_log_file is not None:
315 self._qemu_log_file.close()
316 self._qemu_log_file = None
317
318 self._qemu_log_path = None
319
John Snowabf0bf92019-06-27 17:28:14 -0400320 if self._temp_dir is not None:
321 shutil.rmtree(self._temp_dir)
322 self._temp_dir = None
323
Max Reitz32558ce2019-10-17 15:31:34 +0200324 while len(self._remove_files) > 0:
325 self._remove_if_exists(self._remove_files.pop())
326
John Snow14661d92020-07-10 01:06:38 -0400327 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400328 if (exitcode is not None and exitcode < 0
329 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400330 msg = 'qemu received signal %i; command: "%s"'
331 if self._qemu_full_args:
332 command = ' '.join(self._qemu_full_args)
333 else:
334 command = ''
335 LOG.warning(msg, -int(exitcode), command)
336
John Snowde6e08b2020-07-10 01:06:48 -0400337 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400338 self._launched = False
339
John Snowabf0bf92019-06-27 17:28:14 -0400340 def launch(self):
341 """
342 Launch the VM and make sure we cleanup and expose the
343 command line/output in case of exception
344 """
345
346 if self._launched:
347 raise QEMUMachineError('VM already launched')
348
349 self._iolog = None
350 self._qemu_full_args = None
351 try:
352 self._launch()
353 self._launched = True
354 except:
John Snowa3842cb2020-07-10 01:06:42 -0400355 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400356
357 LOG.debug('Error launching VM')
358 if self._qemu_full_args:
359 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
360 if self._iolog:
361 LOG.debug('Output: %r', self._iolog)
362 raise
363
364 def _launch(self):
365 """
366 Launch the VM and establish a QMP connection
367 """
368 devnull = open(os.path.devnull, 'rb')
369 self._pre_launch()
370 self._qemu_full_args = (self._wrapper + [self._binary] +
371 self._base_args() + self._args)
372 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
373 self._popen = subprocess.Popen(self._qemu_full_args,
374 stdin=devnull,
375 stdout=self._qemu_log_file,
376 stderr=subprocess.STDOUT,
377 shell=False,
378 close_fds=False)
379 self._post_launch()
380
John Snowe2c97f12020-07-10 01:06:40 -0400381 def _early_cleanup(self) -> None:
382 """
383 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400384
John Snow193bf1c2020-07-10 01:06:47 -0400385 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400386 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400387 """
388 # If we keep the console socket open, we may deadlock waiting
389 # for QEMU to exit, while QEMU is waiting for the socket to
390 # become writeable.
391 if self._console_socket is not None:
392 self._console_socket.close()
393 self._console_socket = None
394
John Snow193bf1c2020-07-10 01:06:47 -0400395 def _hard_shutdown(self) -> None:
396 """
397 Perform early cleanup, kill the VM, and wait for it to terminate.
398
399 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
400 waiting for the QEMU process to terminate.
401 """
402 self._early_cleanup()
403 self._popen.kill()
404 self._popen.wait(timeout=60)
405
John Snow8226a4b2020-07-20 12:02:52 -0400406 def _soft_shutdown(self, timeout: Optional[int],
407 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400408 """
409 Perform early cleanup, attempt to gracefully shut down the VM, and wait
410 for it to terminate.
411
John Snow8226a4b2020-07-20 12:02:52 -0400412 :param timeout: Timeout in seconds for graceful shutdown.
413 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400414 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400415
416 :raise ConnectionReset: On QMP communication errors
417 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
418 the QEMU process to terminate.
419 """
420 self._early_cleanup()
421
422 if self._qmp is not None:
423 if not has_quit:
424 # Might raise ConnectionReset
425 self._qmp.cmd('quit')
426
427 # May raise subprocess.TimeoutExpired
428 self._popen.wait(timeout=timeout)
429
John Snow8226a4b2020-07-20 12:02:52 -0400430 def _do_shutdown(self, timeout: Optional[int],
431 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400432 """
433 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
434
John Snow8226a4b2020-07-20 12:02:52 -0400435 :param timeout: Timeout in seconds for graceful shutdown.
436 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400437 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400438
439 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
440 The inner exception will likely be ConnectionReset or
441 subprocess.TimeoutExpired. In rare cases, non-graceful termination
442 may result in its own exceptions, likely subprocess.TimeoutExpired.
443 """
444 try:
John Snow8226a4b2020-07-20 12:02:52 -0400445 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400446 except Exception as exc:
447 self._hard_shutdown()
448 raise AbnormalShutdown("Could not perform graceful shutdown") \
449 from exc
450
John Snowc9b30452020-07-10 01:06:43 -0400451 def shutdown(self, has_quit: bool = False,
452 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400453 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400454 """
John Snow193bf1c2020-07-10 01:06:47 -0400455 Terminate the VM (gracefully if possible) and perform cleanup.
456 Cleanup will always be performed.
457
458 If the VM has not yet been launched, or shutdown(), wait(), or kill()
459 have already been called, this method does nothing.
460
461 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
462 :param hard: When true, do not attempt graceful shutdown, and
463 suppress the SIGKILL warning log message.
464 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400465 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400466 """
John Snowa3842cb2020-07-10 01:06:42 -0400467 if not self._launched:
468 return
469
John Snow193bf1c2020-07-10 01:06:47 -0400470 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300471 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400472 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400473 self._hard_shutdown()
474 else:
John Snow8226a4b2020-07-20 12:02:52 -0400475 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400476 finally:
477 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400478
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300479 def kill(self):
John Snow193bf1c2020-07-10 01:06:47 -0400480 """
481 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
482 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300483 self.shutdown(hard=True)
484
John Snow8226a4b2020-07-20 12:02:52 -0400485 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400486 """
487 Wait for the VM to power off and perform post-shutdown cleanup.
488
John Snow8226a4b2020-07-20 12:02:52 -0400489 :param timeout: Optional timeout in seconds. Default 30 seconds.
490 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400491 """
492 self.shutdown(has_quit=True, timeout=timeout)
493
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500494 def set_qmp_monitor(self, enabled=True):
495 """
496 Set the QMP monitor.
497
498 @param enabled: if False, qmp monitor options will be removed from
499 the base arguments of the resulting QEMU command
500 line. Default is True.
501 @note: call this function before launch().
502 """
503 if enabled:
504 self._qmp_set = True
505 else:
506 self._qmp_set = False
507 self._qmp = None
508
John Snowabf0bf92019-06-27 17:28:14 -0400509 def qmp(self, cmd, conv_keys=True, **args):
510 """
511 Invoke a QMP command and return the response dict
512 """
513 qmp_args = dict()
514 for key, value in args.items():
515 if conv_keys:
516 qmp_args[key.replace('_', '-')] = value
517 else:
518 qmp_args[key] = value
519
520 return self._qmp.cmd(cmd, args=qmp_args)
521
522 def command(self, cmd, conv_keys=True, **args):
523 """
524 Invoke a QMP command.
525 On success return the response dict.
526 On failure raise an exception.
527 """
528 reply = self.qmp(cmd, conv_keys, **args)
529 if reply is None:
530 raise qmp.QMPError("Monitor is closed")
531 if "error" in reply:
John Snowe3a23b42020-07-10 01:22:07 -0400532 raise qmp.QMPResponseError(reply)
John Snowabf0bf92019-06-27 17:28:14 -0400533 return reply["return"]
534
535 def get_qmp_event(self, wait=False):
536 """
537 Poll for one queued QMP events and return it
538 """
John Snow306dfcd2019-06-27 17:28:15 -0400539 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400540 return self._events.pop(0)
541 return self._qmp.pull_event(wait=wait)
542
543 def get_qmp_events(self, wait=False):
544 """
545 Poll for queued QMP events and return a list of dicts
546 """
547 events = self._qmp.get_events(wait=wait)
548 events.extend(self._events)
549 del self._events[:]
550 self._qmp.clear_events()
551 return events
552
553 @staticmethod
554 def event_match(event, match=None):
555 """
556 Check if an event matches optional match criteria.
557
558 The match criteria takes the form of a matching subdict. The event is
559 checked to be a superset of the subdict, recursively, with matching
560 values whenever the subdict values are not None.
561
562 This has a limitation that you cannot explicitly check for None values.
563
564 Examples, with the subdict queries on the left:
565 - None matches any object.
566 - {"foo": None} matches {"foo": {"bar": 1}}
567 - {"foo": None} matches {"foo": 5}
568 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
569 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
570 """
571 if match is None:
572 return True
573
574 try:
575 for key in match:
576 if key in event:
577 if not QEMUMachine.event_match(event[key], match[key]):
578 return False
579 else:
580 return False
581 return True
582 except TypeError:
583 # either match or event wasn't iterable (not a dict)
584 return match == event
585
586 def event_wait(self, name, timeout=60.0, match=None):
587 """
588 event_wait waits for and returns a named event from QMP with a timeout.
589
590 name: The event to wait for.
591 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
592 match: Optional match criteria. See event_match for details.
593 """
594 return self.events_wait([(name, match)], timeout)
595
596 def events_wait(self, events, timeout=60.0):
597 """
John Snow8dfac2e2020-05-28 18:21:29 -0400598 events_wait waits for and returns a named event
599 from QMP with a timeout.
John Snowabf0bf92019-06-27 17:28:14 -0400600
601 events: a sequence of (name, match_criteria) tuples.
602 The match criteria are optional and may be None.
603 See event_match for details.
604 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
605 """
606 def _match(event):
607 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400608 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400609 return True
610 return False
611
612 # Search cached events
613 for event in self._events:
614 if _match(event):
615 self._events.remove(event)
616 return event
617
618 # Poll for new events
619 while True:
620 event = self._qmp.pull_event(wait=timeout)
621 if _match(event):
622 return event
623 self._events.append(event)
624
625 return None
626
627 def get_log(self):
628 """
629 After self.shutdown or failed qemu execution, this returns the output
630 of the qemu process.
631 """
632 return self._iolog
633
634 def add_args(self, *args):
635 """
636 Adds to the list of extra arguments to be given to the QEMU binary
637 """
638 self._args.extend(args)
639
640 def set_machine(self, machine_type):
641 """
642 Sets the machine type
643
644 If set, the machine type will be added to the base arguments
645 of the resulting QEMU command line.
646 """
647 self._machine = machine_type
648
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100649 def set_console(self, device_type=None, console_index=0):
John Snowabf0bf92019-06-27 17:28:14 -0400650 """
651 Sets the device type for a console device
652
653 If set, the console device and a backing character device will
654 be added to the base arguments of the resulting QEMU command
655 line.
656
657 This is a convenience method that will either use the provided
658 device type, or default to a "-serial chardev:console" command
659 line argument.
660
661 The actual setting of command line arguments will be be done at
662 machine launch time, as it depends on the temporary directory
663 to be created.
664
665 @param device_type: the device type, such as "isa-serial". If
666 None is given (the default value) a "-serial
667 chardev:console" command line argument will
668 be used instead, resorting to the machine's
669 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100670 @param console_index: the index of the console device to use.
671 If not zero, the command line will create
672 'index - 1' consoles and connect them to
673 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400674 """
675 self._console_set = True
676 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100677 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400678
679 @property
680 def console_socket(self):
681 """
682 Returns a socket connected to the console
683 """
684 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100685 self._console_socket = console_socket.ConsoleSocket(
686 self._console_address,
687 file=self._console_log_path,
688 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400689 return self._console_socket