1""" 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 8# 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 21from itertools import chain 22import locale 23import logging 24import os 25import shutil 26import signal 27import socket 28import subprocess 29import tempfile 30from types import TracebackType 31from typing import ( 32 Any, 33 BinaryIO, 34 Dict, 35 List, 36 Optional, 37 Sequence, 38 Tuple, 39 Type, 40 TypeVar, 41) 42 43from qemu.qmp import SocketAddrT 44from qemu.qmp.legacy import ( 45 QEMUMonitorProtocol, 46 QMPMessage, 47 QMPReturnValue, 48) 49 50from . import console_socket 51 52 53LOG = logging.getLogger(__name__) 54 55 56class QEMUMachineError(Exception): 57 """ 58 Exception called when an error in QEMUMachine happens. 59 """ 60 61 62class QEMUMachineAddDeviceError(QEMUMachineError): 63 """ 64 Exception raised when a request to add a device can not be fulfilled 65 66 The failures are caused by limitations, lack of information or conflicting 67 requests on the QEMUMachine methods. This exception does not represent 68 failures reported by the QEMU binary itself. 69 """ 70 71 72class VMLaunchFailure(QEMUMachineError): 73 """ 74 Exception raised when a VM launch was attempted, but failed. 75 """ 76 def __init__(self, exitcode: Optional[int], 77 command: str, output: Optional[str]): 78 super().__init__(exitcode, command, output) 79 self.exitcode = exitcode 80 self.command = command 81 self.output = output 82 83 def __str__(self) -> str: 84 ret = '' 85 if self.__cause__ is not None: 86 name = type(self.__cause__).__name__ 87 reason = str(self.__cause__) 88 if reason: 89 ret += f"{name}: {reason}" 90 else: 91 ret += f"{name}" 92 ret += '\n' 93 94 if self.exitcode is not None: 95 ret += f"\tExit code: {self.exitcode}\n" 96 ret += f"\tCommand: {self.command}\n" 97 ret += f"\tOutput: {self.output}\n" 98 return ret 99 100 101class AbnormalShutdown(QEMUMachineError): 102 """ 103 Exception raised when a graceful shutdown was requested, but not performed. 104 """ 105 106 107_T = TypeVar('_T', bound='QEMUMachine') 108 109 110class QEMUMachine: 111 """ 112 A QEMU VM. 113 114 Use this object as a context manager to ensure 115 the QEMU process terminates:: 116 117 with VM(binary) as vm: 118 ... 119 # vm is guaranteed to be shut down here 120 """ 121 # pylint: disable=too-many-instance-attributes, too-many-public-methods 122 123 def __init__(self, 124 binary: str, 125 args: Sequence[str] = (), 126 wrapper: Sequence[str] = (), 127 name: Optional[str] = None, 128 base_temp_dir: str = "/var/tmp", 129 monitor_address: Optional[SocketAddrT] = None, 130 sock_dir: Optional[str] = None, 131 drain_console: bool = False, 132 console_log: Optional[str] = None, 133 log_dir: Optional[str] = None, 134 qmp_timer: Optional[float] = 30): 135 ''' 136 Initialize a QEMUMachine 137 138 @param binary: path to the qemu binary 139 @param args: list of extra arguments 140 @param wrapper: list of arguments used as prefix to qemu binary 141 @param name: prefix for socket and log file names (default: qemu-PID) 142 @param base_temp_dir: default location where temp files are created 143 @param monitor_address: address for QMP monitor 144 @param sock_dir: where to create socket (defaults to base_temp_dir) 145 @param drain_console: (optional) True to drain console socket to buffer 146 @param console_log: (optional) path to console log file 147 @param log_dir: where to create and keep log files 148 @param qmp_timer: (optional) default QMP socket timeout 149 @note: Qemu process is not started until launch() is used. 150 ''' 151 # pylint: disable=too-many-arguments 152 153 # Direct user configuration 154 155 self._binary = binary 156 self._args = list(args) 157 self._wrapper = wrapper 158 self._qmp_timer = qmp_timer 159 160 self._name = name or f"{id(self):x}" 161 self._sock_pair: Optional[Tuple[socket.socket, socket.socket]] = None 162 self._temp_dir: Optional[str] = None 163 self._base_temp_dir = base_temp_dir 164 self._sock_dir = sock_dir 165 self._log_dir = log_dir 166 167 self._monitor_address = monitor_address 168 169 self._console_log_path = console_log 170 if self._console_log_path: 171 # In order to log the console, buffering needs to be enabled. 172 self._drain_console = True 173 else: 174 self._drain_console = drain_console 175 176 # Runstate 177 self._qemu_log_path: Optional[str] = None 178 self._qemu_log_file: Optional[BinaryIO] = None 179 self._popen: Optional['subprocess.Popen[bytes]'] = None 180 self._events: List[QMPMessage] = [] 181 self._iolog: Optional[str] = None 182 self._qmp_set = True # Enable QMP monitor by default. 183 self._qmp_connection: Optional[QEMUMonitorProtocol] = None 184 self._qemu_full_args: Tuple[str, ...] = () 185 self._launched = False 186 self._machine: Optional[str] = None 187 self._console_index = 0 188 self._console_set = False 189 self._console_device_type: Optional[str] = None 190 self._console_address = os.path.join( 191 self.sock_dir, f"{self._name}.con" 192 ) 193 self._console_socket: Optional[socket.socket] = None 194 self._remove_files: List[str] = [] 195 self._user_killed = False 196 self._quit_issued = False 197 198 def __enter__(self: _T) -> _T: 199 return self 200 201 def __exit__(self, 202 exc_type: Optional[Type[BaseException]], 203 exc_val: Optional[BaseException], 204 exc_tb: Optional[TracebackType]) -> None: 205 self.shutdown() 206 207 def add_monitor_null(self) -> None: 208 """ 209 This can be used to add an unused monitor instance. 210 """ 211 self._args.append('-monitor') 212 self._args.append('null') 213 214 def add_fd(self: _T, fd: int, fdset: int, 215 opaque: str, opts: str = '') -> _T: 216 """ 217 Pass a file descriptor to the VM 218 """ 219 options = ['fd=%d' % fd, 220 'set=%d' % fdset, 221 'opaque=%s' % opaque] 222 if opts: 223 options.append(opts) 224 225 # This did not exist before 3.4, but since then it is 226 # mandatory for our purpose 227 if hasattr(os, 'set_inheritable'): 228 os.set_inheritable(fd, True) 229 230 self._args.append('-add-fd') 231 self._args.append(','.join(options)) 232 return self 233 234 def send_fd_scm(self, fd: Optional[int] = None, 235 file_path: Optional[str] = None) -> int: 236 """ 237 Send an fd or file_path to the remote via SCM_RIGHTS. 238 239 Exactly one of fd and file_path must be given. If it is 240 file_path, the file will be opened read-only and the new file 241 descriptor will be sent to the remote. 242 """ 243 if file_path is not None: 244 assert fd is None 245 with open(file_path, "rb") as passfile: 246 fd = passfile.fileno() 247 self._qmp.send_fd_scm(fd) 248 else: 249 assert fd is not None 250 self._qmp.send_fd_scm(fd) 251 252 return 0 253 254 @staticmethod 255 def _remove_if_exists(path: str) -> None: 256 """ 257 Remove file object at path if it exists 258 """ 259 try: 260 os.remove(path) 261 except OSError as exception: 262 if exception.errno == errno.ENOENT: 263 return 264 raise 265 266 def is_running(self) -> bool: 267 """Returns true if the VM is running.""" 268 return self._popen is not None and self._popen.poll() is None 269 270 @property 271 def _subp(self) -> 'subprocess.Popen[bytes]': 272 if self._popen is None: 273 raise QEMUMachineError('Subprocess pipe not present') 274 return self._popen 275 276 def exitcode(self) -> Optional[int]: 277 """Returns the exit code if possible, or None.""" 278 if self._popen is None: 279 return None 280 return self._popen.poll() 281 282 def get_pid(self) -> Optional[int]: 283 """Returns the PID of the running process, or None.""" 284 if not self.is_running(): 285 return None 286 return self._subp.pid 287 288 def _load_io_log(self) -> None: 289 # Assume that the output encoding of QEMU's terminal output is 290 # defined by our locale. If indeterminate, allow open() to fall 291 # back to the platform default. 292 _, encoding = locale.getlocale() 293 if self._qemu_log_path is not None: 294 with open(self._qemu_log_path, "r", encoding=encoding) as iolog: 295 self._iolog = iolog.read() 296 297 @property 298 def _base_args(self) -> List[str]: 299 args = ['-display', 'none', '-vga', 'none'] 300 301 if self._qmp_set: 302 if self._sock_pair: 303 fd = self._sock_pair[0].fileno() 304 os.set_inheritable(fd, True) 305 moncdev = f"socket,id=mon,fd={fd}" 306 elif isinstance(self._monitor_address, tuple): 307 moncdev = "socket,id=mon,host={},port={}".format( 308 *self._monitor_address 309 ) 310 else: 311 moncdev = f"socket,id=mon,path={self._monitor_address}" 312 args.extend(['-chardev', moncdev, '-mon', 313 'chardev=mon,mode=control']) 314 315 if self._machine is not None: 316 args.extend(['-machine', self._machine]) 317 for _ in range(self._console_index): 318 args.extend(['-serial', 'null']) 319 if self._console_set: 320 chardev = ('socket,id=console,path=%s,server=on,wait=off' % 321 self._console_address) 322 args.extend(['-chardev', chardev]) 323 if self._console_device_type is None: 324 args.extend(['-serial', 'chardev:console']) 325 else: 326 device = '%s,chardev=console' % self._console_device_type 327 args.extend(['-device', device]) 328 return args 329 330 @property 331 def args(self) -> List[str]: 332 """Returns the list of arguments given to the QEMU binary.""" 333 return self._args 334 335 def _pre_launch(self) -> None: 336 if self._console_set: 337 self._remove_files.append(self._console_address) 338 339 if self._qmp_set: 340 if self._monitor_address is None: 341 self._sock_pair = socket.socketpair() 342 sock = self._sock_pair[1] 343 if isinstance(self._monitor_address, str): 344 self._remove_files.append(self._monitor_address) 345 346 sock_or_addr = self._monitor_address or sock 347 assert sock_or_addr is not None 348 349 self._qmp_connection = QEMUMonitorProtocol( 350 sock_or_addr, 351 server=bool(self._monitor_address), 352 nickname=self._name 353 ) 354 355 # NOTE: Make sure any opened resources are *definitely* freed in 356 # _post_shutdown()! 357 # pylint: disable=consider-using-with 358 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log") 359 self._qemu_log_file = open(self._qemu_log_path, 'wb') 360 361 self._iolog = None 362 self._qemu_full_args = tuple(chain( 363 self._wrapper, 364 [self._binary], 365 self._base_args, 366 self._args 367 )) 368 369 def _post_launch(self) -> None: 370 if self._sock_pair: 371 self._sock_pair[0].close() 372 if self._qmp_connection: 373 if self._sock_pair: 374 self._qmp.connect() 375 else: 376 self._qmp.accept(self._qmp_timer) 377 378 def _close_qemu_log_file(self) -> None: 379 if self._qemu_log_file is not None: 380 self._qemu_log_file.close() 381 self._qemu_log_file = None 382 383 def _post_shutdown(self) -> None: 384 """ 385 Called to cleanup the VM instance after the process has exited. 386 May also be called after a failed launch. 387 """ 388 LOG.debug("Cleaning up after VM process") 389 try: 390 self._close_qmp_connection() 391 except Exception as err: # pylint: disable=broad-except 392 LOG.warning( 393 "Exception closing QMP connection: %s", 394 str(err) if str(err) else type(err).__name__ 395 ) 396 finally: 397 assert self._qmp_connection is None 398 399 self._close_qemu_log_file() 400 401 self._load_io_log() 402 403 self._qemu_log_path = None 404 405 if self._temp_dir is not None: 406 shutil.rmtree(self._temp_dir) 407 self._temp_dir = None 408 409 while len(self._remove_files) > 0: 410 self._remove_if_exists(self._remove_files.pop()) 411 412 exitcode = self.exitcode() 413 if (exitcode is not None and exitcode < 0 414 and not (self._user_killed and exitcode == -signal.SIGKILL)): 415 msg = 'qemu received signal %i; command: "%s"' 416 if self._qemu_full_args: 417 command = ' '.join(self._qemu_full_args) 418 else: 419 command = '' 420 LOG.warning(msg, -int(exitcode), command) 421 422 self._quit_issued = False 423 self._user_killed = False 424 self._launched = False 425 426 def launch(self) -> None: 427 """ 428 Launch the VM and make sure we cleanup and expose the 429 command line/output in case of exception 430 """ 431 432 if self._launched: 433 raise QEMUMachineError('VM already launched') 434 435 try: 436 self._launch() 437 except BaseException as exc: 438 # We may have launched the process but it may 439 # have exited before we could connect via QMP. 440 # Assume the VM didn't launch or is exiting. 441 # If we don't wait for the process, exitcode() may still be 442 # 'None' by the time control is ceded back to the caller. 443 if self._launched: 444 self.wait() 445 else: 446 self._post_shutdown() 447 448 if isinstance(exc, Exception): 449 raise VMLaunchFailure( 450 exitcode=self.exitcode(), 451 command=' '.join(self._qemu_full_args), 452 output=self._iolog 453 ) from exc 454 455 # Don't wrap 'BaseException'; doing so would downgrade 456 # that exception. However, we still want to clean up. 457 raise 458 459 def _launch(self) -> None: 460 """ 461 Launch the VM and establish a QMP connection 462 """ 463 self._pre_launch() 464 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) 465 466 # Cleaning up of this subprocess is guaranteed by _do_shutdown. 467 # pylint: disable=consider-using-with 468 self._popen = subprocess.Popen(self._qemu_full_args, 469 stdin=subprocess.DEVNULL, 470 stdout=self._qemu_log_file, 471 stderr=subprocess.STDOUT, 472 shell=False, 473 close_fds=False) 474 self._launched = True 475 self._post_launch() 476 477 def _close_qmp_connection(self) -> None: 478 """ 479 Close the underlying QMP connection, if any. 480 481 Dutifully report errors that occurred while closing, but assume 482 that any error encountered indicates an abnormal termination 483 process and not a failure to close. 484 """ 485 if self._qmp_connection is None: 486 return 487 488 try: 489 self._qmp.close() 490 except EOFError: 491 # EOF can occur as an Exception here when using the Async 492 # QMP backend. It indicates that the server closed the 493 # stream. If we successfully issued 'quit' at any point, 494 # then this was expected. If the remote went away without 495 # our permission, it's worth reporting that as an abnormal 496 # shutdown case. 497 if not (self._user_killed or self._quit_issued): 498 raise 499 finally: 500 self._qmp_connection = None 501 502 def _early_cleanup(self) -> None: 503 """ 504 Perform any cleanup that needs to happen before the VM exits. 505 506 This method may be called twice upon shutdown, once each by soft 507 and hard shutdown in failover scenarios. 508 """ 509 # If we keep the console socket open, we may deadlock waiting 510 # for QEMU to exit, while QEMU is waiting for the socket to 511 # become writable. 512 if self._console_socket is not None: 513 LOG.debug("Closing console socket") 514 self._console_socket.close() 515 self._console_socket = None 516 517 def _hard_shutdown(self) -> None: 518 """ 519 Perform early cleanup, kill the VM, and wait for it to terminate. 520 521 :raise subprocess.Timeout: When timeout is exceeds 60 seconds 522 waiting for the QEMU process to terminate. 523 """ 524 LOG.debug("Performing hard shutdown") 525 self._early_cleanup() 526 self._subp.kill() 527 self._subp.wait(timeout=60) 528 529 def _soft_shutdown(self, timeout: Optional[int]) -> None: 530 """ 531 Perform early cleanup, attempt to gracefully shut down the VM, and wait 532 for it to terminate. 533 534 :param timeout: Timeout in seconds for graceful shutdown. 535 A value of None is an infinite wait. 536 537 :raise ConnectionReset: On QMP communication errors 538 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for 539 the QEMU process to terminate. 540 """ 541 LOG.debug("Attempting graceful termination") 542 543 self._early_cleanup() 544 545 if self._quit_issued: 546 LOG.debug( 547 "Anticipating QEMU termination due to prior 'quit' command, " 548 "or explicit call to wait()" 549 ) 550 else: 551 LOG.debug("Politely asking QEMU to terminate") 552 553 if self._qmp_connection: 554 try: 555 if not self._quit_issued: 556 # May raise ExecInterruptedError or StateError if the 557 # connection dies or has *already* died. 558 self.qmp('quit') 559 finally: 560 # Regardless, we want to quiesce the connection. 561 self._close_qmp_connection() 562 elif not self._quit_issued: 563 LOG.debug( 564 "Not anticipating QEMU quit and no QMP connection present, " 565 "issuing SIGTERM" 566 ) 567 self._subp.terminate() 568 569 # May raise subprocess.TimeoutExpired 570 LOG.debug( 571 "Waiting (timeout=%s) for QEMU process (pid=%s) to terminate", 572 timeout, self._subp.pid 573 ) 574 self._subp.wait(timeout=timeout) 575 576 def _do_shutdown(self, timeout: Optional[int]) -> None: 577 """ 578 Attempt to shutdown the VM gracefully; fallback to a hard shutdown. 579 580 :param timeout: Timeout in seconds for graceful shutdown. 581 A value of None is an infinite wait. 582 583 :raise AbnormalShutdown: When the VM could not be shut down gracefully. 584 The inner exception will likely be ConnectionReset or 585 subprocess.TimeoutExpired. In rare cases, non-graceful termination 586 may result in its own exceptions, likely subprocess.TimeoutExpired. 587 """ 588 try: 589 self._soft_shutdown(timeout) 590 except Exception as exc: 591 if isinstance(exc, subprocess.TimeoutExpired): 592 LOG.debug("Timed out waiting for QEMU process to exit") 593 LOG.debug("Graceful shutdown failed", exc_info=True) 594 LOG.debug("Falling back to hard shutdown") 595 self._hard_shutdown() 596 raise AbnormalShutdown("Could not perform graceful shutdown") \ 597 from exc 598 599 def shutdown(self, 600 hard: bool = False, 601 timeout: Optional[int] = 30) -> None: 602 """ 603 Terminate the VM (gracefully if possible) and perform cleanup. 604 Cleanup will always be performed. 605 606 If the VM has not yet been launched, or shutdown(), wait(), or kill() 607 have already been called, this method does nothing. 608 609 :param hard: When true, do not attempt graceful shutdown, and 610 suppress the SIGKILL warning log message. 611 :param timeout: Optional timeout in seconds for graceful shutdown. 612 Default 30 seconds, A `None` value is an infinite wait. 613 """ 614 if not self._launched: 615 return 616 617 LOG.debug("Shutting down VM appliance; timeout=%s", timeout) 618 if hard: 619 LOG.debug("Caller requests immediate termination of QEMU process.") 620 621 try: 622 if hard: 623 self._user_killed = True 624 self._hard_shutdown() 625 else: 626 self._do_shutdown(timeout) 627 finally: 628 self._post_shutdown() 629 630 def kill(self) -> None: 631 """ 632 Terminate the VM forcefully, wait for it to exit, and perform cleanup. 633 """ 634 self.shutdown(hard=True) 635 636 def wait(self, timeout: Optional[int] = 30) -> None: 637 """ 638 Wait for the VM to power off and perform post-shutdown cleanup. 639 640 :param timeout: Optional timeout in seconds. Default 30 seconds. 641 A value of `None` is an infinite wait. 642 """ 643 self._quit_issued = True 644 self.shutdown(timeout=timeout) 645 646 def set_qmp_monitor(self, enabled: bool = True) -> None: 647 """ 648 Set the QMP monitor. 649 650 @param enabled: if False, qmp monitor options will be removed from 651 the base arguments of the resulting QEMU command 652 line. Default is True. 653 654 .. note:: Call this function before launch(). 655 """ 656 self._qmp_set = enabled 657 658 @property 659 def _qmp(self) -> QEMUMonitorProtocol: 660 if self._qmp_connection is None: 661 raise QEMUMachineError("Attempt to access QMP with no connection") 662 return self._qmp_connection 663 664 @classmethod 665 def _qmp_args(cls, conv_keys: bool, 666 args: Dict[str, Any]) -> Dict[str, object]: 667 if conv_keys: 668 return {k.replace('_', '-'): v for k, v in args.items()} 669 670 return args 671 672 def qmp(self, cmd: str, 673 args_dict: Optional[Dict[str, object]] = None, 674 conv_keys: Optional[bool] = None, 675 **args: Any) -> QMPMessage: 676 """ 677 Invoke a QMP command and return the response dict 678 """ 679 if args_dict is not None: 680 assert not args 681 assert conv_keys is None 682 args = args_dict 683 conv_keys = False 684 685 if conv_keys is None: 686 conv_keys = True 687 688 qmp_args = self._qmp_args(conv_keys, args) 689 ret = self._qmp.cmd(cmd, args=qmp_args) 690 if cmd == 'quit' and 'error' not in ret and 'return' in ret: 691 self._quit_issued = True 692 return ret 693 694 def command(self, cmd: str, 695 conv_keys: bool = True, 696 **args: Any) -> QMPReturnValue: 697 """ 698 Invoke a QMP command. 699 On success return the response dict. 700 On failure raise an exception. 701 """ 702 qmp_args = self._qmp_args(conv_keys, args) 703 ret = self._qmp.command(cmd, **qmp_args) 704 if cmd == 'quit': 705 self._quit_issued = True 706 return ret 707 708 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]: 709 """ 710 Poll for one queued QMP events and return it 711 """ 712 if self._events: 713 return self._events.pop(0) 714 return self._qmp.pull_event(wait=wait) 715 716 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]: 717 """ 718 Poll for queued QMP events and return a list of dicts 719 """ 720 events = self._qmp.get_events(wait=wait) 721 events.extend(self._events) 722 del self._events[:] 723 return events 724 725 @staticmethod 726 def event_match(event: Any, match: Optional[Any]) -> bool: 727 """ 728 Check if an event matches optional match criteria. 729 730 The match criteria takes the form of a matching subdict. The event is 731 checked to be a superset of the subdict, recursively, with matching 732 values whenever the subdict values are not None. 733 734 This has a limitation that you cannot explicitly check for None values. 735 736 Examples, with the subdict queries on the left: 737 - None matches any object. 738 - {"foo": None} matches {"foo": {"bar": 1}} 739 - {"foo": None} matches {"foo": 5} 740 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}} 741 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}} 742 """ 743 if match is None: 744 return True 745 746 try: 747 for key in match: 748 if key in event: 749 if not QEMUMachine.event_match(event[key], match[key]): 750 return False 751 else: 752 return False 753 return True 754 except TypeError: 755 # either match or event wasn't iterable (not a dict) 756 return bool(match == event) 757 758 def event_wait(self, name: str, 759 timeout: float = 60.0, 760 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]: 761 """ 762 event_wait waits for and returns a named event from QMP with a timeout. 763 764 name: The event to wait for. 765 timeout: QEMUMonitorProtocol.pull_event timeout parameter. 766 match: Optional match criteria. See event_match for details. 767 """ 768 return self.events_wait([(name, match)], timeout) 769 770 def events_wait(self, 771 events: Sequence[Tuple[str, Any]], 772 timeout: float = 60.0) -> Optional[QMPMessage]: 773 """ 774 events_wait waits for and returns a single named event from QMP. 775 In the case of multiple qualifying events, this function returns the 776 first one. 777 778 :param events: A sequence of (name, match_criteria) tuples. 779 The match criteria are optional and may be None. 780 See event_match for details. 781 :param timeout: Optional timeout, in seconds. 782 See QEMUMonitorProtocol.pull_event. 783 784 :raise asyncio.TimeoutError: 785 If timeout was non-zero and no matching events were found. 786 787 :return: A QMP event matching the filter criteria. 788 If timeout was 0 and no event matched, None. 789 """ 790 def _match(event: QMPMessage) -> bool: 791 for name, match in events: 792 if event['event'] == name and self.event_match(event, match): 793 return True 794 return False 795 796 event: Optional[QMPMessage] 797 798 # Search cached events 799 for event in self._events: 800 if _match(event): 801 self._events.remove(event) 802 return event 803 804 # Poll for new events 805 while True: 806 event = self._qmp.pull_event(wait=timeout) 807 if event is None: 808 # NB: None is only returned when timeout is false-ish. 809 # Timeouts raise asyncio.TimeoutError instead! 810 break 811 if _match(event): 812 return event 813 self._events.append(event) 814 815 return None 816 817 def get_log(self) -> Optional[str]: 818 """ 819 After self.shutdown or failed qemu execution, this returns the output 820 of the qemu process. 821 """ 822 return self._iolog 823 824 def add_args(self, *args: str) -> None: 825 """ 826 Adds to the list of extra arguments to be given to the QEMU binary 827 """ 828 self._args.extend(args) 829 830 def set_machine(self, machine_type: str) -> None: 831 """ 832 Sets the machine type 833 834 If set, the machine type will be added to the base arguments 835 of the resulting QEMU command line. 836 """ 837 self._machine = machine_type 838 839 def set_console(self, 840 device_type: Optional[str] = None, 841 console_index: int = 0) -> None: 842 """ 843 Sets the device type for a console device 844 845 If set, the console device and a backing character device will 846 be added to the base arguments of the resulting QEMU command 847 line. 848 849 This is a convenience method that will either use the provided 850 device type, or default to a "-serial chardev:console" command 851 line argument. 852 853 The actual setting of command line arguments will be be done at 854 machine launch time, as it depends on the temporary directory 855 to be created. 856 857 @param device_type: the device type, such as "isa-serial". If 858 None is given (the default value) a "-serial 859 chardev:console" command line argument will 860 be used instead, resorting to the machine's 861 default device type. 862 @param console_index: the index of the console device to use. 863 If not zero, the command line will create 864 'index - 1' consoles and connect them to 865 the 'null' backing character device. 866 """ 867 self._console_set = True 868 self._console_device_type = device_type 869 self._console_index = console_index 870 871 @property 872 def console_socket(self) -> socket.socket: 873 """ 874 Returns a socket connected to the console 875 """ 876 if self._console_socket is None: 877 self._console_socket = console_socket.ConsoleSocket( 878 self._console_address, 879 file=self._console_log_path, 880 drain=self._drain_console) 881 return self._console_socket 882 883 @property 884 def temp_dir(self) -> str: 885 """ 886 Returns a temporary directory to be used for this machine 887 """ 888 if self._temp_dir is None: 889 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-", 890 dir=self._base_temp_dir) 891 return self._temp_dir 892 893 @property 894 def sock_dir(self) -> str: 895 """ 896 Returns the directory used for sockfiles by this machine. 897 """ 898 if self._sock_dir: 899 return self._sock_dir 900 return self.temp_dir 901 902 @property 903 def log_dir(self) -> str: 904 """ 905 Returns a directory to be used for writing logs 906 """ 907 if self._log_dir is None: 908 return self.temp_dir 909 return self._log_dir 910