blob: 812ca7d3497ebca45187e0d07554ac5af90bd375 [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 Snow652809d2020-10-06 19:58:01 -040028from typing import List, 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
John Snow652809d2020-10-06 19:58:01 -0400140 self._console_address = os.path.join(
141 self._sock_dir, f"{self._name}-console.sock"
142 )
John Snowabf0bf92019-06-27 17:28:14 -0400143 self._console_socket = None
Max Reitz32558ce2019-10-17 15:31:34 +0200144 self._remove_files = []
John Snowde6e08b2020-07-10 01:06:48 -0400145 self._user_killed = False
John Snowabf0bf92019-06-27 17:28:14 -0400146
John Snowabf0bf92019-06-27 17:28:14 -0400147 def __enter__(self):
148 return self
149
John Snow1dda0402020-05-14 01:53:44 -0400150 def __exit__(self,
151 exc_type: Optional[Type[BaseException]],
152 exc_val: Optional[BaseException],
153 exc_tb: Optional[TracebackType]) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400154 self.shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400155
John Snowabf0bf92019-06-27 17:28:14 -0400156 def add_monitor_null(self):
John Snow306dfcd2019-06-27 17:28:15 -0400157 """
158 This can be used to add an unused monitor instance.
159 """
John Snowabf0bf92019-06-27 17:28:14 -0400160 self._args.append('-monitor')
161 self._args.append('null')
162
163 def add_fd(self, fd, fdset, opaque, opts=''):
164 """
165 Pass a file descriptor to the VM
166 """
167 options = ['fd=%d' % fd,
168 'set=%d' % fdset,
169 'opaque=%s' % opaque]
170 if opts:
171 options.append(opts)
172
173 # This did not exist before 3.4, but since then it is
174 # mandatory for our purpose
175 if hasattr(os, 'set_inheritable'):
176 os.set_inheritable(fd, True)
177
178 self._args.append('-add-fd')
179 self._args.append(','.join(options))
180 return self
181
John Snowabf0bf92019-06-27 17:28:14 -0400182 def send_fd_scm(self, fd=None, file_path=None):
John Snow306dfcd2019-06-27 17:28:15 -0400183 """
184 Send an fd or file_path to socket_scm_helper.
185
186 Exactly one of fd and file_path must be given.
187 If it is file_path, the helper will open that file and pass its own fd.
188 """
John Snowabf0bf92019-06-27 17:28:14 -0400189 # In iotest.py, the qmp should always use unix socket.
190 assert self._qmp.is_scm_available()
191 if self._socket_scm_helper is None:
192 raise QEMUMachineError("No path to socket_scm_helper set")
193 if not os.path.exists(self._socket_scm_helper):
194 raise QEMUMachineError("%s does not exist" %
195 self._socket_scm_helper)
196
197 # This did not exist before 3.4, but since then it is
198 # mandatory for our purpose
199 if hasattr(os, 'set_inheritable'):
200 os.set_inheritable(self._qmp.get_sock_fd(), True)
201 if fd is not None:
202 os.set_inheritable(fd, True)
203
204 fd_param = ["%s" % self._socket_scm_helper,
205 "%d" % self._qmp.get_sock_fd()]
206
207 if file_path is not None:
208 assert fd is None
209 fd_param.append(file_path)
210 else:
211 assert fd is not None
212 fd_param.append(str(fd))
213
214 devnull = open(os.path.devnull, 'rb')
John Snow8dfac2e2020-05-28 18:21:29 -0400215 proc = subprocess.Popen(
216 fd_param, stdin=devnull, stdout=subprocess.PIPE,
217 stderr=subprocess.STDOUT, close_fds=False
218 )
John Snowabf0bf92019-06-27 17:28:14 -0400219 output = proc.communicate()[0]
220 if output:
221 LOG.debug(output)
222
223 return proc.returncode
224
225 @staticmethod
226 def _remove_if_exists(path):
227 """
228 Remove file object at path if it exists
229 """
230 try:
231 os.remove(path)
232 except OSError as exception:
233 if exception.errno == errno.ENOENT:
234 return
235 raise
236
237 def is_running(self):
John Snow306dfcd2019-06-27 17:28:15 -0400238 """Returns true if the VM is running."""
John Snowabf0bf92019-06-27 17:28:14 -0400239 return self._popen is not None and self._popen.poll() is None
240
241 def exitcode(self):
John Snow306dfcd2019-06-27 17:28:15 -0400242 """Returns the exit code if possible, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400243 if self._popen is None:
244 return None
245 return self._popen.poll()
246
247 def get_pid(self):
John Snow306dfcd2019-06-27 17:28:15 -0400248 """Returns the PID of the running process, or None."""
John Snowabf0bf92019-06-27 17:28:14 -0400249 if not self.is_running():
250 return None
251 return self._popen.pid
252
253 def _load_io_log(self):
254 if self._qemu_log_path is not None:
255 with open(self._qemu_log_path, "r") as iolog:
256 self._iolog = iolog.read()
257
John Snow652809d2020-10-06 19:58:01 -0400258 @property
259 def _base_args(self) -> List[str]:
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500260 args = ['-display', 'none', '-vga', 'none']
John Snowc4e60232020-10-06 19:57:59 -0400261
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500262 if self._qmp_set:
263 if isinstance(self._monitor_address, tuple):
John Snowc4e60232020-10-06 19:57:59 -0400264 moncdev = "socket,id=mon,host={},port={}".format(
265 *self._monitor_address
266 )
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500267 else:
John Snowc4e60232020-10-06 19:57:59 -0400268 moncdev = f"socket,id=mon,path={self._monitor_address}"
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500269 args.extend(['-chardev', moncdev, '-mon',
270 'chardev=mon,mode=control'])
John Snowc4e60232020-10-06 19:57:59 -0400271
John Snowabf0bf92019-06-27 17:28:14 -0400272 if self._machine is not None:
273 args.extend(['-machine', self._machine])
John Snow9b8ccd62020-05-28 18:21:28 -0400274 for _ in range(self._console_index):
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100275 args.extend(['-serial', 'null'])
John Snowabf0bf92019-06-27 17:28:14 -0400276 if self._console_set:
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
John Snow652809d2020-10-06 19:58:01 -0400292 if self._console_set:
293 self._remove_files.append(self._console_address)
294
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500295 if self._qmp_set:
John Snowc4e60232020-10-06 19:57:59 -0400296 if self._remove_monitor_sockfile:
297 assert isinstance(self._monitor_address, str)
298 self._remove_files.append(self._monitor_address)
299 self._qmp = qmp.QEMUMonitorProtocol(
300 self._monitor_address,
301 server=True,
302 nickname=self._name
303 )
John Snowabf0bf92019-06-27 17:28:14 -0400304
305 def _post_launch(self):
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500306 if self._qmp:
307 self._qmp.accept()
John Snowabf0bf92019-06-27 17:28:14 -0400308
309 def _post_shutdown(self):
John Snowa3842cb2020-07-10 01:06:42 -0400310 """
311 Called to cleanup the VM instance after the process has exited.
312 May also be called after a failed launch.
313 """
314 # Comprehensive reset for the failed launch case:
315 self._early_cleanup()
316
John Snow671940e2020-07-10 01:06:39 -0400317 if self._qmp:
318 self._qmp.close()
319 self._qmp = None
320
John Snow14661d92020-07-10 01:06:38 -0400321 self._load_io_log()
322
John Snowabf0bf92019-06-27 17:28:14 -0400323 if self._qemu_log_file is not None:
324 self._qemu_log_file.close()
325 self._qemu_log_file = None
326
327 self._qemu_log_path = None
328
John Snowabf0bf92019-06-27 17:28:14 -0400329 if self._temp_dir is not None:
330 shutil.rmtree(self._temp_dir)
331 self._temp_dir = None
332
Max Reitz32558ce2019-10-17 15:31:34 +0200333 while len(self._remove_files) > 0:
334 self._remove_if_exists(self._remove_files.pop())
335
John Snow14661d92020-07-10 01:06:38 -0400336 exitcode = self.exitcode()
John Snowde6e08b2020-07-10 01:06:48 -0400337 if (exitcode is not None and exitcode < 0
338 and not (self._user_killed and exitcode == -signal.SIGKILL)):
John Snow14661d92020-07-10 01:06:38 -0400339 msg = 'qemu received signal %i; command: "%s"'
340 if self._qemu_full_args:
341 command = ' '.join(self._qemu_full_args)
342 else:
343 command = ''
344 LOG.warning(msg, -int(exitcode), command)
345
John Snowde6e08b2020-07-10 01:06:48 -0400346 self._user_killed = False
John Snow14661d92020-07-10 01:06:38 -0400347 self._launched = False
348
John Snowabf0bf92019-06-27 17:28:14 -0400349 def launch(self):
350 """
351 Launch the VM and make sure we cleanup and expose the
352 command line/output in case of exception
353 """
354
355 if self._launched:
356 raise QEMUMachineError('VM already launched')
357
358 self._iolog = None
359 self._qemu_full_args = None
360 try:
361 self._launch()
362 self._launched = True
363 except:
John Snowa3842cb2020-07-10 01:06:42 -0400364 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400365
366 LOG.debug('Error launching VM')
367 if self._qemu_full_args:
368 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
369 if self._iolog:
370 LOG.debug('Output: %r', self._iolog)
371 raise
372
373 def _launch(self):
374 """
375 Launch the VM and establish a QMP connection
376 """
377 devnull = open(os.path.devnull, 'rb')
378 self._pre_launch()
379 self._qemu_full_args = (self._wrapper + [self._binary] +
John Snow652809d2020-10-06 19:58:01 -0400380 self._base_args + self._args)
John Snowabf0bf92019-06-27 17:28:14 -0400381 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
382 self._popen = subprocess.Popen(self._qemu_full_args,
383 stdin=devnull,
384 stdout=self._qemu_log_file,
385 stderr=subprocess.STDOUT,
386 shell=False,
387 close_fds=False)
388 self._post_launch()
389
John Snowe2c97f12020-07-10 01:06:40 -0400390 def _early_cleanup(self) -> None:
391 """
392 Perform any cleanup that needs to happen before the VM exits.
John Snowa3842cb2020-07-10 01:06:42 -0400393
John Snow193bf1c2020-07-10 01:06:47 -0400394 May be invoked by both soft and hard shutdown in failover scenarios.
John Snowa3842cb2020-07-10 01:06:42 -0400395 Called additionally by _post_shutdown for comprehensive cleanup.
John Snowe2c97f12020-07-10 01:06:40 -0400396 """
397 # If we keep the console socket open, we may deadlock waiting
398 # for QEMU to exit, while QEMU is waiting for the socket to
399 # become writeable.
400 if self._console_socket is not None:
401 self._console_socket.close()
402 self._console_socket = None
403
John Snow193bf1c2020-07-10 01:06:47 -0400404 def _hard_shutdown(self) -> None:
405 """
406 Perform early cleanup, kill the VM, and wait for it to terminate.
407
408 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
409 waiting for the QEMU process to terminate.
410 """
411 self._early_cleanup()
412 self._popen.kill()
413 self._popen.wait(timeout=60)
414
John Snow8226a4b2020-07-20 12:02:52 -0400415 def _soft_shutdown(self, timeout: Optional[int],
416 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400417 """
418 Perform early cleanup, attempt to gracefully shut down the VM, and wait
419 for it to terminate.
420
John Snow8226a4b2020-07-20 12:02:52 -0400421 :param timeout: Timeout in seconds for graceful shutdown.
422 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400423 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400424
425 :raise ConnectionReset: On QMP communication errors
426 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
427 the QEMU process to terminate.
428 """
429 self._early_cleanup()
430
431 if self._qmp is not None:
432 if not has_quit:
433 # Might raise ConnectionReset
434 self._qmp.cmd('quit')
435
436 # May raise subprocess.TimeoutExpired
437 self._popen.wait(timeout=timeout)
438
John Snow8226a4b2020-07-20 12:02:52 -0400439 def _do_shutdown(self, timeout: Optional[int],
440 has_quit: bool = False) -> None:
John Snow193bf1c2020-07-10 01:06:47 -0400441 """
442 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
443
John Snow8226a4b2020-07-20 12:02:52 -0400444 :param timeout: Timeout in seconds for graceful shutdown.
445 A value of None is an infinite wait.
John Snow193bf1c2020-07-10 01:06:47 -0400446 :param has_quit: When True, don't attempt to issue 'quit' QMP command
John Snow193bf1c2020-07-10 01:06:47 -0400447
448 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
449 The inner exception will likely be ConnectionReset or
450 subprocess.TimeoutExpired. In rare cases, non-graceful termination
451 may result in its own exceptions, likely subprocess.TimeoutExpired.
452 """
453 try:
John Snow8226a4b2020-07-20 12:02:52 -0400454 self._soft_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400455 except Exception as exc:
456 self._hard_shutdown()
457 raise AbnormalShutdown("Could not perform graceful shutdown") \
458 from exc
459
John Snowc9b30452020-07-10 01:06:43 -0400460 def shutdown(self, has_quit: bool = False,
461 hard: bool = False,
John Snow8226a4b2020-07-20 12:02:52 -0400462 timeout: Optional[int] = 30) -> None:
John Snowabf0bf92019-06-27 17:28:14 -0400463 """
John Snow193bf1c2020-07-10 01:06:47 -0400464 Terminate the VM (gracefully if possible) and perform cleanup.
465 Cleanup will always be performed.
466
467 If the VM has not yet been launched, or shutdown(), wait(), or kill()
468 have already been called, this method does nothing.
469
470 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
471 :param hard: When true, do not attempt graceful shutdown, and
472 suppress the SIGKILL warning log message.
473 :param timeout: Optional timeout in seconds for graceful shutdown.
John Snow8226a4b2020-07-20 12:02:52 -0400474 Default 30 seconds, A `None` value is an infinite wait.
John Snowabf0bf92019-06-27 17:28:14 -0400475 """
John Snowa3842cb2020-07-10 01:06:42 -0400476 if not self._launched:
477 return
478
John Snow193bf1c2020-07-10 01:06:47 -0400479 try:
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300480 if hard:
John Snowde6e08b2020-07-10 01:06:48 -0400481 self._user_killed = True
John Snow193bf1c2020-07-10 01:06:47 -0400482 self._hard_shutdown()
483 else:
John Snow8226a4b2020-07-20 12:02:52 -0400484 self._do_shutdown(timeout, has_quit)
John Snow193bf1c2020-07-10 01:06:47 -0400485 finally:
486 self._post_shutdown()
John Snowabf0bf92019-06-27 17:28:14 -0400487
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300488 def kill(self):
John Snow193bf1c2020-07-10 01:06:47 -0400489 """
490 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
491 """
Vladimir Sementsov-Ogievskiye0e925a2020-02-17 18:02:42 +0300492 self.shutdown(hard=True)
493
John Snow8226a4b2020-07-20 12:02:52 -0400494 def wait(self, timeout: Optional[int] = 30) -> None:
John Snow89528052020-07-10 01:06:44 -0400495 """
496 Wait for the VM to power off and perform post-shutdown cleanup.
497
John Snow8226a4b2020-07-20 12:02:52 -0400498 :param timeout: Optional timeout in seconds. Default 30 seconds.
499 A value of `None` is an infinite wait.
John Snow89528052020-07-10 01:06:44 -0400500 """
501 self.shutdown(has_quit=True, timeout=timeout)
502
Wainer dos Santos Moschetta74b56bb2019-12-11 13:55:35 -0500503 def set_qmp_monitor(self, enabled=True):
504 """
505 Set the QMP monitor.
506
507 @param enabled: if False, qmp monitor options will be removed from
508 the base arguments of the resulting QEMU command
509 line. Default is True.
510 @note: call this function before launch().
511 """
512 if enabled:
513 self._qmp_set = True
514 else:
515 self._qmp_set = False
516 self._qmp = None
517
John Snowabf0bf92019-06-27 17:28:14 -0400518 def qmp(self, cmd, conv_keys=True, **args):
519 """
520 Invoke a QMP command and return the response dict
521 """
522 qmp_args = dict()
523 for key, value in args.items():
524 if conv_keys:
525 qmp_args[key.replace('_', '-')] = value
526 else:
527 qmp_args[key] = value
528
529 return self._qmp.cmd(cmd, args=qmp_args)
530
531 def command(self, cmd, conv_keys=True, **args):
532 """
533 Invoke a QMP command.
534 On success return the response dict.
535 On failure raise an exception.
536 """
537 reply = self.qmp(cmd, conv_keys, **args)
538 if reply is None:
539 raise qmp.QMPError("Monitor is closed")
540 if "error" in reply:
John Snowe3a23b42020-07-10 01:22:07 -0400541 raise qmp.QMPResponseError(reply)
John Snowabf0bf92019-06-27 17:28:14 -0400542 return reply["return"]
543
544 def get_qmp_event(self, wait=False):
545 """
546 Poll for one queued QMP events and return it
547 """
John Snow306dfcd2019-06-27 17:28:15 -0400548 if self._events:
John Snowabf0bf92019-06-27 17:28:14 -0400549 return self._events.pop(0)
550 return self._qmp.pull_event(wait=wait)
551
552 def get_qmp_events(self, wait=False):
553 """
554 Poll for queued QMP events and return a list of dicts
555 """
556 events = self._qmp.get_events(wait=wait)
557 events.extend(self._events)
558 del self._events[:]
559 self._qmp.clear_events()
560 return events
561
562 @staticmethod
563 def event_match(event, match=None):
564 """
565 Check if an event matches optional match criteria.
566
567 The match criteria takes the form of a matching subdict. The event is
568 checked to be a superset of the subdict, recursively, with matching
569 values whenever the subdict values are not None.
570
571 This has a limitation that you cannot explicitly check for None values.
572
573 Examples, with the subdict queries on the left:
574 - None matches any object.
575 - {"foo": None} matches {"foo": {"bar": 1}}
576 - {"foo": None} matches {"foo": 5}
577 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
578 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
579 """
580 if match is None:
581 return True
582
583 try:
584 for key in match:
585 if key in event:
586 if not QEMUMachine.event_match(event[key], match[key]):
587 return False
588 else:
589 return False
590 return True
591 except TypeError:
592 # either match or event wasn't iterable (not a dict)
593 return match == event
594
595 def event_wait(self, name, timeout=60.0, match=None):
596 """
597 event_wait waits for and returns a named event from QMP with a timeout.
598
599 name: The event to wait for.
600 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
601 match: Optional match criteria. See event_match for details.
602 """
603 return self.events_wait([(name, match)], timeout)
604
605 def events_wait(self, events, timeout=60.0):
606 """
John Snow8dfac2e2020-05-28 18:21:29 -0400607 events_wait waits for and returns a named event
608 from QMP with a timeout.
John Snowabf0bf92019-06-27 17:28:14 -0400609
610 events: a sequence of (name, match_criteria) tuples.
611 The match criteria are optional and may be None.
612 See event_match for details.
613 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
614 """
615 def _match(event):
616 for name, match in events:
John Snow306dfcd2019-06-27 17:28:15 -0400617 if event['event'] == name and self.event_match(event, match):
John Snowabf0bf92019-06-27 17:28:14 -0400618 return True
619 return False
620
621 # Search cached events
622 for event in self._events:
623 if _match(event):
624 self._events.remove(event)
625 return event
626
627 # Poll for new events
628 while True:
629 event = self._qmp.pull_event(wait=timeout)
630 if _match(event):
631 return event
632 self._events.append(event)
633
634 return None
635
636 def get_log(self):
637 """
638 After self.shutdown or failed qemu execution, this returns the output
639 of the qemu process.
640 """
641 return self._iolog
642
643 def add_args(self, *args):
644 """
645 Adds to the list of extra arguments to be given to the QEMU binary
646 """
647 self._args.extend(args)
648
649 def set_machine(self, machine_type):
650 """
651 Sets the machine type
652
653 If set, the machine type will be added to the base arguments
654 of the resulting QEMU command line.
655 """
656 self._machine = machine_type
657
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100658 def set_console(self, device_type=None, console_index=0):
John Snowabf0bf92019-06-27 17:28:14 -0400659 """
660 Sets the device type for a console device
661
662 If set, the console device and a backing character device will
663 be added to the base arguments of the resulting QEMU command
664 line.
665
666 This is a convenience method that will either use the provided
667 device type, or default to a "-serial chardev:console" command
668 line argument.
669
670 The actual setting of command line arguments will be be done at
671 machine launch time, as it depends on the temporary directory
672 to be created.
673
674 @param device_type: the device type, such as "isa-serial". If
675 None is given (the default value) a "-serial
676 chardev:console" command line argument will
677 be used instead, resorting to the machine's
678 default device type.
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100679 @param console_index: the index of the console device to use.
680 If not zero, the command line will create
681 'index - 1' consoles and connect them to
682 the 'null' backing character device.
John Snowabf0bf92019-06-27 17:28:14 -0400683 """
684 self._console_set = True
685 self._console_device_type = device_type
Philippe Mathieu-Daudé746f2442020-01-21 00:51:56 +0100686 self._console_index = console_index
John Snowabf0bf92019-06-27 17:28:14 -0400687
688 @property
689 def console_socket(self):
690 """
691 Returns a socket connected to the console
692 """
693 if self._console_socket is None:
Robert Foley80ded8e2020-07-24 07:45:08 +0100694 self._console_socket = console_socket.ConsoleSocket(
695 self._console_address,
696 file=self._console_log_path,
697 drain=self._drain_console)
John Snowabf0bf92019-06-27 17:28:14 -0400698 return self._console_socket