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