4 The machine module primarily provides the QEMUMachine class,
5 which provides facilities for managing the lifetime of a QEMU VM.
8 # Copyright (C) 2015-2016 Red Hat Inc.
9 # Copyright (C) 2012 IBM Corp.
14 # This work is licensed under the terms of the GNU GPL, version 2. See
15 # the COPYING file in the top-level directory.
21 from itertools import chain
30 from types import TracebackType
43 from qemu.qmp import ( # pylint: disable=import-error
49 from . import console_socket
52 if os.environ.get('QEMU_PYTHON_LEGACY_QMP'):
53 from qemu.qmp import QEMUMonitorProtocol
55 from qemu.aqmp.legacy import QEMUMonitorProtocol
58 LOG = logging.getLogger(__name__)
61 class QEMUMachineError(Exception):
63 Exception called when an error in QEMUMachine happens.
67 class QEMUMachineAddDeviceError(QEMUMachineError):
69 Exception raised when a request to add a device can not be fulfilled
71 The failures are caused by limitations, lack of information or conflicting
72 requests on the QEMUMachine methods. This exception does not represent
73 failures reported by the QEMU binary itself.
77 class AbnormalShutdown(QEMUMachineError):
79 Exception raised when a graceful shutdown was requested, but not performed.
83 _T = TypeVar('_T', bound='QEMUMachine')
90 Use this object as a context manager to ensure
91 the QEMU process terminates::
93 with VM(binary) as vm:
95 # vm is guaranteed to be shut down here
97 # pylint: disable=too-many-instance-attributes, too-many-public-methods
101 args: Sequence[str] = (),
102 wrapper: Sequence[str] = (),
103 name: Optional[str] = None,
104 base_temp_dir: str = "/var/tmp",
105 monitor_address: Optional[SocketAddrT] = None,
106 sock_dir: Optional[str] = None,
107 drain_console: bool = False,
108 console_log: Optional[str] = None,
109 log_dir: Optional[str] = None,
110 qmp_timer: Optional[float] = None):
112 Initialize a QEMUMachine
114 @param binary: path to the qemu binary
115 @param args: list of extra arguments
116 @param wrapper: list of arguments used as prefix to qemu binary
117 @param name: prefix for socket and log file names (default: qemu-PID)
118 @param base_temp_dir: default location where temp files are created
119 @param monitor_address: address for QMP monitor
120 @param sock_dir: where to create socket (defaults to base_temp_dir)
121 @param drain_console: (optional) True to drain console socket to buffer
122 @param console_log: (optional) path to console log file
123 @param log_dir: where to create and keep log files
124 @param qmp_timer: (optional) default QMP socket timeout
125 @note: Qemu process is not started until launch() is used.
127 # pylint: disable=too-many-arguments
129 # Direct user configuration
131 self._binary = binary
132 self._args = list(args)
133 self._wrapper = wrapper
134 self._qmp_timer = qmp_timer
136 self._name = name or f"qemu-{os.getpid()}-{id(self):02x}"
137 self._temp_dir: Optional[str] = None
138 self._base_temp_dir = base_temp_dir
139 self._sock_dir = sock_dir
140 self._log_dir = log_dir
142 if monitor_address is not None:
143 self._monitor_address = monitor_address
145 self._monitor_address = os.path.join(
146 self.sock_dir, f"{self._name}-monitor.sock"
149 self._console_log_path = console_log
150 if self._console_log_path:
151 # In order to log the console, buffering needs to be enabled.
152 self._drain_console = True
154 self._drain_console = drain_console
157 self._qemu_log_path: Optional[str] = None
158 self._qemu_log_file: Optional[BinaryIO] = None
159 self._popen: Optional['subprocess.Popen[bytes]'] = None
160 self._events: List[QMPMessage] = []
161 self._iolog: Optional[str] = None
162 self._qmp_set = True # Enable QMP monitor by default.
163 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
164 self._qemu_full_args: Tuple[str, ...] = ()
165 self._launched = False
166 self._machine: Optional[str] = None
167 self._console_index = 0
168 self._console_set = False
169 self._console_device_type: Optional[str] = None
170 self._console_address = os.path.join(
171 self.sock_dir, f"{self._name}-console.sock"
173 self._console_socket: Optional[socket.socket] = None
174 self._remove_files: List[str] = []
175 self._user_killed = False
176 self._quit_issued = False
178 def __enter__(self: _T) -> _T:
182 exc_type: Optional[Type[BaseException]],
183 exc_val: Optional[BaseException],
184 exc_tb: Optional[TracebackType]) -> None:
187 def add_monitor_null(self) -> None:
189 This can be used to add an unused monitor instance.
191 self._args.append('-monitor')
192 self._args.append('null')
194 def add_fd(self: _T, fd: int, fdset: int,
195 opaque: str, opts: str = '') -> _T:
197 Pass a file descriptor to the VM
199 options = ['fd=%d' % fd,
201 'opaque=%s' % opaque]
205 # This did not exist before 3.4, but since then it is
206 # mandatory for our purpose
207 if hasattr(os, 'set_inheritable'):
208 os.set_inheritable(fd, True)
210 self._args.append('-add-fd')
211 self._args.append(','.join(options))
214 def send_fd_scm(self, fd: Optional[int] = None,
215 file_path: Optional[str] = None) -> int:
217 Send an fd or file_path to the remote via SCM_RIGHTS.
219 Exactly one of fd and file_path must be given. If it is
220 file_path, the file will be opened read-only and the new file
221 descriptor will be sent to the remote.
223 if file_path is not None:
225 with open(file_path, "rb") as passfile:
226 fd = passfile.fileno()
227 self._qmp.send_fd_scm(fd)
229 assert fd is not None
230 self._qmp.send_fd_scm(fd)
235 def _remove_if_exists(path: str) -> None:
237 Remove file object at path if it exists
241 except OSError as exception:
242 if exception.errno == errno.ENOENT:
246 def is_running(self) -> bool:
247 """Returns true if the VM is running."""
248 return self._popen is not None and self._popen.poll() is None
251 def _subp(self) -> 'subprocess.Popen[bytes]':
252 if self._popen is None:
253 raise QEMUMachineError('Subprocess pipe not present')
256 def exitcode(self) -> Optional[int]:
257 """Returns the exit code if possible, or None."""
258 if self._popen is None:
260 return self._popen.poll()
262 def get_pid(self) -> Optional[int]:
263 """Returns the PID of the running process, or None."""
264 if not self.is_running():
266 return self._subp.pid
268 def _load_io_log(self) -> None:
269 # Assume that the output encoding of QEMU's terminal output is
270 # defined by our locale. If indeterminate, allow open() to fall
271 # back to the platform default.
272 _, encoding = locale.getlocale()
273 if self._qemu_log_path is not None:
274 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
275 self._iolog = iolog.read()
278 def _base_args(self) -> List[str]:
279 args = ['-display', 'none', '-vga', 'none']
282 if isinstance(self._monitor_address, tuple):
283 moncdev = "socket,id=mon,host={},port={}".format(
284 *self._monitor_address
287 moncdev = f"socket,id=mon,path={self._monitor_address}"
288 args.extend(['-chardev', moncdev, '-mon',
289 'chardev=mon,mode=control'])
291 if self._machine is not None:
292 args.extend(['-machine', self._machine])
293 for _ in range(self._console_index):
294 args.extend(['-serial', 'null'])
295 if self._console_set:
296 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
297 self._console_address)
298 args.extend(['-chardev', chardev])
299 if self._console_device_type is None:
300 args.extend(['-serial', 'chardev:console'])
302 device = '%s,chardev=console' % self._console_device_type
303 args.extend(['-device', device])
307 def args(self) -> List[str]:
308 """Returns the list of arguments given to the QEMU binary."""
311 def _pre_launch(self) -> None:
312 if self._console_set:
313 self._remove_files.append(self._console_address)
316 if isinstance(self._monitor_address, str):
317 self._remove_files.append(self._monitor_address)
318 self._qmp_connection = QEMUMonitorProtocol(
319 self._monitor_address,
324 # NOTE: Make sure any opened resources are *definitely* freed in
326 # pylint: disable=consider-using-with
327 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
328 self._qemu_log_file = open(self._qemu_log_path, 'wb')
331 self._qemu_full_args = tuple(chain(
338 def _post_launch(self) -> None:
339 if self._qmp_connection:
340 self._qmp.accept(self._qmp_timer)
342 def _close_qemu_log_file(self) -> None:
343 if self._qemu_log_file is not None:
344 self._qemu_log_file.close()
345 self._qemu_log_file = None
347 def _post_shutdown(self) -> None:
349 Called to cleanup the VM instance after the process has exited.
350 May also be called after a failed launch.
352 # Comprehensive reset for the failed launch case:
353 self._early_cleanup()
356 self._close_qmp_connection()
357 except Exception as err: # pylint: disable=broad-except
359 "Exception closing QMP connection: %s",
360 str(err) if str(err) else type(err).__name__
363 assert self._qmp_connection is None
365 self._close_qemu_log_file()
369 self._qemu_log_path = None
371 if self._temp_dir is not None:
372 shutil.rmtree(self._temp_dir)
373 self._temp_dir = None
375 while len(self._remove_files) > 0:
376 self._remove_if_exists(self._remove_files.pop())
378 exitcode = self.exitcode()
379 if (exitcode is not None and exitcode < 0
380 and not (self._user_killed and exitcode == -signal.SIGKILL)):
381 msg = 'qemu received signal %i; command: "%s"'
382 if self._qemu_full_args:
383 command = ' '.join(self._qemu_full_args)
386 LOG.warning(msg, -int(exitcode), command)
388 self._quit_issued = False
389 self._user_killed = False
390 self._launched = False
392 def launch(self) -> None:
394 Launch the VM and make sure we cleanup and expose the
395 command line/output in case of exception
399 raise QEMUMachineError('VM already launched')
403 self._launched = True
405 self._post_shutdown()
407 LOG.debug('Error launching VM')
408 if self._qemu_full_args:
409 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
411 LOG.debug('Output: %r', self._iolog)
414 def _launch(self) -> None:
416 Launch the VM and establish a QMP connection
419 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
421 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
422 # pylint: disable=consider-using-with
423 self._popen = subprocess.Popen(self._qemu_full_args,
424 stdin=subprocess.DEVNULL,
425 stdout=self._qemu_log_file,
426 stderr=subprocess.STDOUT,
431 def _close_qmp_connection(self) -> None:
433 Close the underlying QMP connection, if any.
435 Dutifully report errors that occurred while closing, but assume
436 that any error encountered indicates an abnormal termination
437 process and not a failure to close.
439 if self._qmp_connection is None:
445 # EOF can occur as an Exception here when using the Async
446 # QMP backend. It indicates that the server closed the
447 # stream. If we successfully issued 'quit' at any point,
448 # then this was expected. If the remote went away without
449 # our permission, it's worth reporting that as an abnormal
451 if not (self._user_killed or self._quit_issued):
454 self._qmp_connection = None
456 def _early_cleanup(self) -> None:
458 Perform any cleanup that needs to happen before the VM exits.
460 May be invoked by both soft and hard shutdown in failover scenarios.
461 Called additionally by _post_shutdown for comprehensive cleanup.
463 # If we keep the console socket open, we may deadlock waiting
464 # for QEMU to exit, while QEMU is waiting for the socket to
466 if self._console_socket is not None:
467 self._console_socket.close()
468 self._console_socket = None
470 def _hard_shutdown(self) -> None:
472 Perform early cleanup, kill the VM, and wait for it to terminate.
474 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
475 waiting for the QEMU process to terminate.
477 self._early_cleanup()
479 self._subp.wait(timeout=60)
481 def _soft_shutdown(self, timeout: Optional[int]) -> None:
483 Perform early cleanup, attempt to gracefully shut down the VM, and wait
486 :param timeout: Timeout in seconds for graceful shutdown.
487 A value of None is an infinite wait.
489 :raise ConnectionReset: On QMP communication errors
490 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
491 the QEMU process to terminate.
493 self._early_cleanup()
495 if self._qmp_connection:
497 if not self._quit_issued:
498 # May raise ExecInterruptedError or StateError if the
499 # connection dies or has *already* died.
502 # Regardless, we want to quiesce the connection.
503 self._close_qmp_connection()
505 # May raise subprocess.TimeoutExpired
506 self._subp.wait(timeout=timeout)
508 def _do_shutdown(self, timeout: Optional[int]) -> None:
510 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
512 :param timeout: Timeout in seconds for graceful shutdown.
513 A value of None is an infinite wait.
515 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
516 The inner exception will likely be ConnectionReset or
517 subprocess.TimeoutExpired. In rare cases, non-graceful termination
518 may result in its own exceptions, likely subprocess.TimeoutExpired.
521 self._soft_shutdown(timeout)
522 except Exception as exc:
523 self._hard_shutdown()
524 raise AbnormalShutdown("Could not perform graceful shutdown") \
529 timeout: Optional[int] = 30) -> None:
531 Terminate the VM (gracefully if possible) and perform cleanup.
532 Cleanup will always be performed.
534 If the VM has not yet been launched, or shutdown(), wait(), or kill()
535 have already been called, this method does nothing.
537 :param hard: When true, do not attempt graceful shutdown, and
538 suppress the SIGKILL warning log message.
539 :param timeout: Optional timeout in seconds for graceful shutdown.
540 Default 30 seconds, A `None` value is an infinite wait.
542 if not self._launched:
547 self._user_killed = True
548 self._hard_shutdown()
550 self._do_shutdown(timeout)
552 self._post_shutdown()
554 def kill(self) -> None:
556 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
558 self.shutdown(hard=True)
560 def wait(self, timeout: Optional[int] = 30) -> None:
562 Wait for the VM to power off and perform post-shutdown cleanup.
564 :param timeout: Optional timeout in seconds. Default 30 seconds.
565 A value of `None` is an infinite wait.
567 self._quit_issued = True
568 self.shutdown(timeout=timeout)
570 def set_qmp_monitor(self, enabled: bool = True) -> None:
574 @param enabled: if False, qmp monitor options will be removed from
575 the base arguments of the resulting QEMU command
576 line. Default is True.
578 .. note:: Call this function before launch().
580 self._qmp_set = enabled
583 def _qmp(self) -> QEMUMonitorProtocol:
584 if self._qmp_connection is None:
585 raise QEMUMachineError("Attempt to access QMP with no connection")
586 return self._qmp_connection
589 def _qmp_args(cls, conv_keys: bool,
590 args: Dict[str, Any]) -> Dict[str, object]:
592 return {k.replace('_', '-'): v for k, v in args.items()}
596 def qmp(self, cmd: str,
597 args_dict: Optional[Dict[str, object]] = None,
598 conv_keys: Optional[bool] = None,
599 **args: Any) -> QMPMessage:
601 Invoke a QMP command and return the response dict
603 if args_dict is not None:
605 assert conv_keys is None
609 if conv_keys is None:
612 qmp_args = self._qmp_args(conv_keys, args)
613 ret = self._qmp.cmd(cmd, args=qmp_args)
614 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
615 self._quit_issued = True
618 def command(self, cmd: str,
619 conv_keys: bool = True,
620 **args: Any) -> QMPReturnValue:
622 Invoke a QMP command.
623 On success return the response dict.
624 On failure raise an exception.
626 qmp_args = self._qmp_args(conv_keys, args)
627 ret = self._qmp.command(cmd, **qmp_args)
629 self._quit_issued = True
632 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
634 Poll for one queued QMP events and return it
637 return self._events.pop(0)
638 return self._qmp.pull_event(wait=wait)
640 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
642 Poll for queued QMP events and return a list of dicts
644 events = self._qmp.get_events(wait=wait)
645 events.extend(self._events)
650 def event_match(event: Any, match: Optional[Any]) -> bool:
652 Check if an event matches optional match criteria.
654 The match criteria takes the form of a matching subdict. The event is
655 checked to be a superset of the subdict, recursively, with matching
656 values whenever the subdict values are not None.
658 This has a limitation that you cannot explicitly check for None values.
660 Examples, with the subdict queries on the left:
661 - None matches any object.
662 - {"foo": None} matches {"foo": {"bar": 1}}
663 - {"foo": None} matches {"foo": 5}
664 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
665 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
673 if not QEMUMachine.event_match(event[key], match[key]):
679 # either match or event wasn't iterable (not a dict)
680 return bool(match == event)
682 def event_wait(self, name: str,
683 timeout: float = 60.0,
684 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
686 event_wait waits for and returns a named event from QMP with a timeout.
688 name: The event to wait for.
689 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
690 match: Optional match criteria. See event_match for details.
692 return self.events_wait([(name, match)], timeout)
694 def events_wait(self,
695 events: Sequence[Tuple[str, Any]],
696 timeout: float = 60.0) -> Optional[QMPMessage]:
698 events_wait waits for and returns a single named event from QMP.
699 In the case of multiple qualifying events, this function returns the
702 :param events: A sequence of (name, match_criteria) tuples.
703 The match criteria are optional and may be None.
704 See event_match for details.
705 :param timeout: Optional timeout, in seconds.
706 See QEMUMonitorProtocol.pull_event.
708 :raise QMPTimeoutError: If timeout was non-zero and no matching events
710 :return: A QMP event matching the filter criteria.
711 If timeout was 0 and no event matched, None.
713 def _match(event: QMPMessage) -> bool:
714 for name, match in events:
715 if event['event'] == name and self.event_match(event, match):
719 event: Optional[QMPMessage]
721 # Search cached events
722 for event in self._events:
724 self._events.remove(event)
727 # Poll for new events
729 event = self._qmp.pull_event(wait=timeout)
731 # NB: None is only returned when timeout is false-ish.
732 # Timeouts raise QMPTimeoutError instead!
736 self._events.append(event)
740 def get_log(self) -> Optional[str]:
742 After self.shutdown or failed qemu execution, this returns the output
747 def add_args(self, *args: str) -> None:
749 Adds to the list of extra arguments to be given to the QEMU binary
751 self._args.extend(args)
753 def set_machine(self, machine_type: str) -> None:
755 Sets the machine type
757 If set, the machine type will be added to the base arguments
758 of the resulting QEMU command line.
760 self._machine = machine_type
762 def set_console(self,
763 device_type: Optional[str] = None,
764 console_index: int = 0) -> None:
766 Sets the device type for a console device
768 If set, the console device and a backing character device will
769 be added to the base arguments of the resulting QEMU command
772 This is a convenience method that will either use the provided
773 device type, or default to a "-serial chardev:console" command
776 The actual setting of command line arguments will be be done at
777 machine launch time, as it depends on the temporary directory
780 @param device_type: the device type, such as "isa-serial". If
781 None is given (the default value) a "-serial
782 chardev:console" command line argument will
783 be used instead, resorting to the machine's
785 @param console_index: the index of the console device to use.
786 If not zero, the command line will create
787 'index - 1' consoles and connect them to
788 the 'null' backing character device.
790 self._console_set = True
791 self._console_device_type = device_type
792 self._console_index = console_index
795 def console_socket(self) -> socket.socket:
797 Returns a socket connected to the console
799 if self._console_socket is None:
800 self._console_socket = console_socket.ConsoleSocket(
801 self._console_address,
802 file=self._console_log_path,
803 drain=self._drain_console)
804 return self._console_socket
807 def temp_dir(self) -> str:
809 Returns a temporary directory to be used for this machine
811 if self._temp_dir is None:
812 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
813 dir=self._base_temp_dir)
814 return self._temp_dir
817 def sock_dir(self) -> str:
819 Returns the directory used for sockfiles by this machine.
822 return self._sock_dir
826 def log_dir(self) -> str:
828 Returns a directory to be used for writing logs
830 if self._log_dir is None: