blob: 71fe58be0507bb70a0c7e6a2f433cefb252383f5 [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)
Robert Foley0fc8f662020-07-01 14:56:24 +010087 @param drain_console: (optional) True to drain console socket to buffer
John Snowc5e61a62020-10-06 19:58:00 -040088 @param console_log: (optional) path to console log file
John Snowabf0bf92019-06-27 17:28:14 -040089 @note: Qemu process is not started until launch() is used.
90 '''
John Snowc5e61a62020-10-06 19:58:00 -040091 # Direct user configuration
92
93 self._binary = binary
94
John Snowabf0bf92019-06-27 17:28:14 -040095 if args is None:
96 args = []
John Snowc5e61a62020-10-06 19:58:00 -040097 # Copy mutable input: we will be modifying our copy
98 self._args = list(args)
99
John Snowabf0bf92019-06-27 17:28:14 -0400100 if wrapper is None:
101 wrapper = []
John Snowc5e61a62020-10-06 19:58:00 -0400102 self._wrapper = wrapper
103
104 self._name = name or "qemu-%d" % os.getpid()
105 self._test_dir = test_dir
106 self._sock_dir = sock_dir or self._test_dir
107 self._socket_scm_helper = socket_scm_helper
108
John Snowc4e60232020-10-06 19:57:59 -0400109 if monitor_address is not None:
110 self._monitor_address = monitor_address
111 self._remove_monitor_sockfile = False
112 else:
113 self._monitor_address = os.path.join(
John Snowc5e61a62020-10-06 19:58:00 -0400114 self._sock_dir, f"{self._name}-monitor.sock"
John Snowc4e60232020-10-06 19:57:59 -0400115 )
116 self._remove_monitor_sockfile = True
John Snowc5e61a62020-10-06 19:58:00 -0400117
118 self._console_log_path = console_log
119 if self._console_log_path:
120 # In order to log the console, buffering needs to be enabled.
121 self._drain_console = True
122 else:
123 self._drain_console = drain_console
124
125 # Runstate
John Snowabf0bf92019-06-27 17:28:14 -0400126 self._qemu_log_path = None
127 self._qemu_log_file = None
128 self._popen = None
John Snowabf0bf92019-06-27 17:28:14 -0400129 self._events = []
130 self._iolog = None
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500131 self._qmp_set = True # Enable QMP monitor by default.
John Snowabf0bf92019-06-27 17:28:14 -0400132 self._qmp = None
133 self._qemu_full_args = None
John Snowabf0bf92019-06-27 17:28:14 -0400134 self._temp_dir = None
135 self._launched = False
136 self._machine = None
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100137 self._console_index = 0
John Snowabf0bf92019-06-27 17:28:14 -0400138 self._console_set = False
139 self._console_device_type = None
140 self._console_address = None
141 self._console_socket = None
Max Reitz32558ce2019-10-17 15:31:34 +0200142 self._remove_files = []
John Snowde6e08b2020-07-10 01:06:48 -0400143 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400144
John Snowabf0bf92019-06-27 17:28:14 -0400145 def __enter__(self):
146 return self
147
John Snow1dda0402020-05-14 01:53:44 -0400148 def __exit__(self,
149 exc_type: Optional[Type[BaseException]],
150 exc_val: Optional[BaseException],
151 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400152 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400153
John Snowabf0bf92019-06-27 17:28:14 -0400154 def add_monitor_null(self):
John Snow306dfcd2019-06-27 17:28:15 -0400155 """
156 This can be used to add an unused monitor instance.
157 """
John Snowabf0bf92019-06-27 17:28:14 -0400158 self._args.append('-monitor')
159 self._args.append('null')
160
161 def add_fd(self, fd, fdset, opaque, opts=''):
162 """
163 Pass a file descriptor to the VM
164 """
165 options = ['fd=%d' % fd,
166 'set=%d' % fdset,
167 'opaque=%s' % opaque]
168 if opts:
169 options.append(opts)
170
171 # This did not exist before 3.4, but since then it is
172 # mandatory for our purpose
173 if hasattr(os, 'set_inheritable'):
174 os.set_inheritable(fd, True)
175
176 self._args.append('-add-fd')
177 self._args.append(','.join(options))
178 return self
179
John Snowabf0bf92019-06-27 17:28:14 -0400180 def send_fd_scm(self, fd=None, file_path=None):
John Snow306dfcd2019-06-27 17:28:15 -0400181 """
182 Send an fd or file_path to socket_scm_helper.
183
184 Exactly one of fd and file_path must be given.
185 If it is file_path, the helper will open that file and pass its own fd.
186 """
John Snowabf0bf92019-06-27 17:28:14 -0400187 # In iotest.py, the qmp should always use unix socket.
188 assert self._qmp.is_scm_available()
189 if self._socket_scm_helper is None:
190 raise QEMUMachineError("No path to socket_scm_helper set")
191 if not os.path.exists(self._socket_scm_helper):
192 raise QEMUMachineError("%s does not exist" %
193 self._socket_scm_helper)
194
195 # This did not exist before 3.4, but since then it is
196 # mandatory for our purpose
197 if hasattr(os, 'set_inheritable'):
198 os.set_inheritable(self._qmp.get_sock_fd(), True)
199 if fd is not None:
200 os.set_inheritable(fd, True)
201
202 fd_param = ["%s" % self._socket_scm_helper,
203 "%d" % self._qmp.get_sock_fd()]
204
205 if file_path is not None:
206 assert fd is None
207 fd_param.append(file_path)
208 else:
209 assert fd is not None
210 fd_param.append(str(fd))
211
212 devnull = open(os.path.devnull, 'rb')
John Snow8dfac2e2020-05-28 18:21:29 -0400213 proc = subprocess.Popen(
214 fd_param, stdin=devnull, stdout=subprocess.PIPE,
215 stderr=subprocess.STDOUT, close_fds=False
216 )
John Snowabf0bf92019-06-27 17:28:14 -0400217 output = proc.communicate()[0]
218 if output:
219 LOG.debug(output)
220
221 return proc.returncode
222
223 @staticmethod
224 def _remove_if_exists(path):
225 """
226 Remove file object at path if it exists
227 """
228 try:
229 os.remove(path)
230 except OSError as exception:
231 if exception.errno == errno.ENOENT:
232 return
233 raise
234
235 def is_running(self):
John Snow306dfcd2019-06-27 17:28:15 -0400236 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400237 return self._popen is not None and self._popen.poll() is None
238
239 def exitcode(self):
John Snow306dfcd2019-06-27 17:28:15 -0400240 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400241 if self._popen is None:
242 return None
243 return self._popen.poll()
244
245 def get_pid(self):
John Snow306dfcd2019-06-27 17:28:15 -0400246 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400247 if not self.is_running():
248 return None
249 return self._popen.pid
250
251 def _load_io_log(self):
252 if self._qemu_log_path is not None:
253 with open(self._qemu_log_path, "r") as iolog:
254 self._iolog = iolog.read()
255
256 def _base_args(self):
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500257 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400258
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500259 if self._qmp_set:
260 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400261 moncdev = "socket,id=mon,host={},port={}".format(
262 *self._monitor_address
263 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500264 else:
John Snowc4e60232020-10-06 19:57:59 -0400265 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500266 args.extend(['-chardev', moncdev, '-mon',
267 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400268
John Snowabf0bf92019-06-27 17:28:14 -0400269 if self._machine is not None:
270 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400271 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100272 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400273 if self._console_set:
Max Reitz32558ce2019-10-17 15:31:34 +0200274 self._console_address = os.path.join(self._sock_dir,
John Snowabf0bf92019-06-27 17:28:14 -0400275 self._name + "-console.sock")
Max Reitz32558ce2019-10-17 15:31:34 +0200276 self._remove_files.append(self._console_address)
John Snowabf0bf92019-06-27 17:28:14 -0400277 chardev = ('socket,id=console,path=%s,server,nowait' %
278 self._console_address)
279 args.extend(['-chardev', chardev])
280 if self._console_device_type is None:
281 args.extend(['-serial', 'chardev:console'])
282 else:
283 device = '%s,chardev=console' % self._console_device_type
284 args.extend(['-device', device])
285 return args
286
287 def _pre_launch(self):
288 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
John Snowabf0bf92019-06-27 17:28:14 -0400289 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
290 self._qemu_log_file = open(self._qemu_log_path, 'wb')
291
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500292 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400293 if self._remove_monitor_sockfile:
294 assert isinstance(self._monitor_address, str)
295 self._remove_files.append(self._monitor_address)
296 self._qmp = qmp.QEMUMonitorProtocol(
297 self._monitor_address,
298 server=True,
299 nickname=self._name
300 )
John Snowabf0bf92019-06-27 17:28:14 -0400301
302 def _post_launch(self):
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500303 if self._qmp:
304 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400305
306 def _post_shutdown(self):
John Snowa3842cb2020-07-10 01:06:42 -0400307 """
308 Called to cleanup the VM instance after the process has exited.
309 May also be called after a failed launch.
310 """
311 # Comprehensive reset for the failed launch case:
312 self._early_cleanup()
313
John Snow671940e2020-07-10 01:06:39 -0400314 if self._qmp:
315 self._qmp.close()
316 self._qmp = None
317
John Snow14661d92020-07-10 01:06:38 -0400318 self._load_io_log()
319
John Snowabf0bf92019-06-27 17:28:14 -0400320 if self._qemu_log_file is not None:
321 self._qemu_log_file.close()
322 self._qemu_log_file = None
323
324 self._qemu_log_path = None
325
John Snowabf0bf92019-06-27 17:28:14 -0400326 if self._temp_dir is not None:
327 shutil.rmtree(self._temp_dir)
328 self._temp_dir = None
329
Max Reitz32558ce2019-10-17 15:31:34 +0200330 while len(self._remove_files) > 0:
331 self._remove_if_exists(self._remove_files.pop())
332
John Snow14661d92020-07-10 01:06:38 -0400333 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400334 if (exitcode is not None and exitcode < 0
335 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400336 msg = 'qemu received signal %i; command: "%s"'
337 if self._qemu_full_args:
338 command = ' '.join(self._qemu_full_args)
339 else:
340 command = ''
341 LOG.warning(msg, -int(exitcode), command)
342
John Snowde6e08b2020-07-10 01:06:48 -0400343 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400344 self._launched = False
345
John Snowabf0bf92019-06-27 17:28:14 -0400346 def launch(self):
347 """
348 Launch the VM and make sure we cleanup and expose the
349 command line/output in case of exception
350 """
351
352 if self._launched:
353 raise QEMUMachineError('VM already launched')
354
355 self._iolog = None
356 self._qemu_full_args = None
357 try:
358 self._launch()
359 self._launched = True
360 except:
John Snowa3842cb2020-07-10 01:06:42 -0400361 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400362
363 LOG.debug('Error launching VM')
364 if self._qemu_full_args:
365 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
366 if self._iolog:
367 LOG.debug('Output: %r', self._iolog)
368 raise
369
370 def _launch(self):
371 """
372 Launch the VM and establish a QMP connection
373 """
374 devnull = open(os.path.devnull, 'rb')
375 self._pre_launch()
376 self._qemu_full_args = (self._wrapper + [self._binary] +
377 self._base_args() + self._args)
378 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
379 self._popen = subprocess.Popen(self._qemu_full_args,
380 stdin=devnull,
381 stdout=self._qemu_log_file,
382 stderr=subprocess.STDOUT,
383 shell=False,
384 close_fds=False)
385 self._post_launch()
386
John Snowe2c97f12020-07-10 01:06:40 -0400387 def _early_cleanup(self) -> None:
388 """
389 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400390
John Snow193bf1c2020-07-10 01:06:47 -0400391 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400392 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400393 """
394 # If we keep the console socket open, we may deadlock waiting
395 # for QEMU to exit, while QEMU is waiting for the socket to
396 # become writeable.
397 if self._console_socket is not None:
398 self._console_socket.close()
399 self._console_socket = None
400
John Snow193bf1c2020-07-10 01:06:47 -0400401 def _hard_shutdown(self) -> None:
402 """
403 Perform early cleanup, kill the VM, and wait for it to terminate.
404
405 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
406 waiting for the QEMU process to terminate.
407 """
408 self._early_cleanup()
409 self._popen.kill()
410 self._popen.wait(timeout=60)
411
John Snow8226a4b2020-07-20 12:02:52 -0400412 def _soft_shutdown(self, timeout: Optional[int],
413 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400414 """
415 Perform early cleanup, attempt to gracefully shut down the VM, and wait
416 for it to terminate.
417
John Snow8226a4b2020-07-20 12:02:52 -0400418 :param timeout: Timeout in seconds for graceful shutdown.
419 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400420 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400421
422 :raise ConnectionReset: On QMP communication errors
423 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
424 the QEMU process to terminate.
425 """
426 self._early_cleanup()
427
428 if self._qmp is not None:
429 if not has_quit:
430 # Might raise ConnectionReset
431 self._qmp.cmd('quit')
432
433 # May raise subprocess.TimeoutExpired
434 self._popen.wait(timeout=timeout)
435
John Snow8226a4b2020-07-20 12:02:52 -0400436 def _do_shutdown(self, timeout: Optional[int],
437 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400438 """
439 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
440
John Snow8226a4b2020-07-20 12:02:52 -0400441 :param timeout: Timeout in seconds for graceful shutdown.
442 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400443 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400444
445 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
446 The inner exception will likely be ConnectionReset or
447 subprocess.TimeoutExpired. In rare cases, non-graceful termination
448 may result in its own exceptions, likely subprocess.TimeoutExpired.
449 """
450 try:
John Snow8226a4b2020-07-20 12:02:52 -0400451 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400452 except Exception as exc:
453 self._hard_shutdown()
454 raise AbnormalShutdown("Could not perform graceful shutdown") \
455 from exc
456
John Snowc9b30452020-07-10 01:06:43 -0400457 def shutdown(self, has_quit: bool = False,
458 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400459 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400460 """
John Snow193bf1c2020-07-10 01:06:47 -0400461 Terminate the VM (gracefully if possible) and perform cleanup.
462 Cleanup will always be performed.
463
464 If the VM has not yet been launched, or shutdown(), wait(), or kill()
465 have already been called, this method does nothing.
466
467 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
468 :param hard: When true, do not attempt graceful shutdown, and
469 suppress the SIGKILL warning log message.
470 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400471 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400472 """
John Snowa3842cb2020-07-10 01:06:42 -0400473 if not self._launched:
474 return
475
John Snow193bf1c2020-07-10 01:06:47 -0400476 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300477 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400478 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400479 self._hard_shutdown()
480 else:
John Snow8226a4b2020-07-20 12:02:52 -0400481 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400482 finally:
483 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400484
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300485 def kill(self):
John Snow193bf1c2020-07-10 01:06:47 -0400486 """
487 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
488 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300489 self.shutdown(hard=True)
490
John Snow8226a4b2020-07-20 12:02:52 -0400491 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400492 """
493 Wait for the VM to power off and perform post-shutdown cleanup.
494
John Snow8226a4b2020-07-20 12:02:52 -0400495 :param timeout: Optional timeout in seconds. Default 30 seconds.
496 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400497 """
498 self.shutdown(has_quit=True, timeout=timeout)
499
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500500 def set_qmp_monitor(self, enabled=True):
501 """
502 Set the QMP monitor.
503
504 @param enabled: if False, qmp monitor options will be removed from
505 the base arguments of the resulting QEMU command
506 line. Default is True.
507 @note: call this function before launch().
508 """
509 if enabled:
510 self._qmp_set = True
511 else:
512 self._qmp_set = False
513 self._qmp = None
514
John Snowabf0bf92019-06-27 17:28:14 -0400515 def qmp(self, cmd, conv_keys=True, **args):
516 """
517 Invoke a QMP command and return the response dict
518 """
519 qmp_args = dict()
520 for key, value in args.items():
521 if conv_keys:
522 qmp_args[key.replace('_', '-')] = value
523 else:
524 qmp_args[key] = value
525
526 return self._qmp.cmd(cmd, args=qmp_args)
527
528 def command(self, cmd, conv_keys=True, **args):
529 """
530 Invoke a QMP command.
531 On success return the response dict.
532 On failure raise an exception.
533 """
534 reply = self.qmp(cmd, conv_keys, **args)
535 if reply is None:
536 raise qmp.QMPError("Monitor is closed")
537 if "error" in reply:
John Snowe3a23b42020-07-10 01:22:07 -0400538 raise qmp.QMPResponseError(reply)
John Snowabf0bf92019-06-27 17:28:14 -0400539 return reply["return"]
540
541 def get_qmp_event(self, wait=False):
542 """
543 Poll for one queued QMP events and return it
544 """
John Snow306dfcd2019-06-27 17:28:15 -0400545 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400546 return self._events.pop(0)
547 return self._qmp.pull_event(wait=wait)
548
549 def get_qmp_events(self, wait=False):
550 """
551 Poll for queued QMP events and return a list of dicts
552 """
553 events = self._qmp.get_events(wait=wait)
554 events.extend(self._events)
555 del self._events[:]
556 self._qmp.clear_events()
557 return events
558
559 @staticmethod
560 def event_match(event, match=None):
561 """
562 Check if an event matches optional match criteria.
563
564 The match criteria takes the form of a matching subdict. The event is
565 checked to be a superset of the subdict, recursively, with matching
566 values whenever the subdict values are not None.
567
568 This has a limitation that you cannot explicitly check for None values.
569
570 Examples, with the subdict queries on the left:
571 - None matches any object.
572 - {"foo": None} matches {"foo": {"bar": 1}}
573 - {"foo": None} matches {"foo": 5}
574 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
575 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
576 """
577 if match is None:
578 return True
579
580 try:
581 for key in match:
582 if key in event:
583 if not QEMUMachine.event_match(event[key], match[key]):
584 return False
585 else:
586 return False
587 return True
588 except TypeError:
589 # either match or event wasn't iterable (not a dict)
590 return match == event
591
592 def event_wait(self, name, timeout=60.0, match=None):
593 """
594 event_wait waits for and returns a named event from QMP with a timeout.
595
596 name: The event to wait for.
597 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
598 match: Optional match criteria. See event_match for details.
599 """
600 return self.events_wait([(name, match)], timeout)
601
602 def events_wait(self, events, timeout=60.0):
603 """
John Snow8dfac2e2020-05-28 18:21:29 -0400604 events_wait waits for and returns a named event
605 from QMP with a timeout.
John Snowabf0bf92019-06-27 17:28:14 -0400606
607 events: a sequence of (name, match_criteria) tuples.
608 The match criteria are optional and may be None.
609 See event_match for details.
610 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
611 """
612 def _match(event):
613 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400614 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400615 return True
616 return False
617
618 # Search cached events
619 for event in self._events:
620 if _match(event):
621 self._events.remove(event)
622 return event
623
624 # Poll for new events
625 while True:
626 event = self._qmp.pull_event(wait=timeout)
627 if _match(event):
628 return event
629 self._events.append(event)
630
631 return None
632
633 def get_log(self):
634 """
635 After self.shutdown or failed qemu execution, this returns the output
636 of the qemu process.
637 """
638 return self._iolog
639
640 def add_args(self, *args):
641 """
642 Adds to the list of extra arguments to be given to the QEMU binary
643 """
644 self._args.extend(args)
645
646 def set_machine(self, machine_type):
647 """
648 Sets the machine type
649
650 If set, the machine type will be added to the base arguments
651 of the resulting QEMU command line.
652 """
653 self._machine = machine_type
654
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100655 def set_console(self, device_type=None, console_index=0):
John Snowabf0bf92019-06-27 17:28:14 -0400656 """
657 Sets the device type for a console device
658
659 If set, the console device and a backing character device will
660 be added to the base arguments of the resulting QEMU command
661 line.
662
663 This is a convenience method that will either use the provided
664 device type, or default to a "-serial chardev:console" command
665 line argument.
666
667 The actual setting of command line arguments will be be done at
668 machine launch time, as it depends on the temporary directory
669 to be created.
670
671 @param device_type: the device type, such as "isa-serial". If
672 None is given (the default value) a "-serial
673 chardev:console" command line argument will
674 be used instead, resorting to the machine's
675 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100676 @param console_index: the index of the console device to use.
677 If not zero, the command line will create
678 'index - 1' consoles and connect them to
679 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400680 """
681 self._console_set = True
682 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100683 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400684
685 @property
686 def console_socket(self):
687 """
688 Returns a socket connected to the console
689 """
690 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100691 self._console_socket = console_socket.ConsoleSocket(
692 self._console_address,
693 file=self._console_log_path,
694 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400695 return self._console_socket