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 VMLaunchFailure(QEMUMachineError):
79 Exception raised when a VM launch was attempted, but failed.
81 def __init__(self, exitcode: Optional[int],
82 command: str, output: Optional[str]):
83 super().__init__(exitcode, command, output)
84 self.exitcode = exitcode
85 self.command = command
88 def __str__(self) -> str:
90 if self.__cause__ is not None:
91 name = type(self.__cause__).__name__
92 reason = str(self.__cause__)
94 ret += f"{name}: {reason}"
99 if self.exitcode is not None:
100 ret += f"\tExit code: {self.exitcode}\n"
101 ret += f"\tCommand: {self.command}\n"
102 ret += f"\tOutput: {self.output}\n"
106 class AbnormalShutdown(QEMUMachineError):
108 Exception raised when a graceful shutdown was requested, but not performed.
112 _T = TypeVar('_T', bound='QEMUMachine')
119 Use this object as a context manager to ensure
120 the QEMU process terminates::
122 with VM(binary) as vm:
124 # vm is guaranteed to be shut down here
126 # pylint: disable=too-many-instance-attributes, too-many-public-methods
130 args: Sequence[str] = (),
131 wrapper: Sequence[str] = (),
132 name: Optional[str] = None,
133 base_temp_dir: str = "/var/tmp",
134 monitor_address: Optional[SocketAddrT] = None,
135 sock_dir: Optional[str] = None,
136 drain_console: bool = False,
137 console_log: Optional[str] = None,
138 log_dir: Optional[str] = None,
139 qmp_timer: Optional[float] = None):
141 Initialize a QEMUMachine
143 @param binary: path to the qemu binary
144 @param args: list of extra arguments
145 @param wrapper: list of arguments used as prefix to qemu binary
146 @param name: prefix for socket and log file names (default: qemu-PID)
147 @param base_temp_dir: default location where temp files are created
148 @param monitor_address: address for QMP monitor
149 @param sock_dir: where to create socket (defaults to base_temp_dir)
150 @param drain_console: (optional) True to drain console socket to buffer
151 @param console_log: (optional) path to console log file
152 @param log_dir: where to create and keep log files
153 @param qmp_timer: (optional) default QMP socket timeout
154 @note: Qemu process is not started until launch() is used.
156 # pylint: disable=too-many-arguments
158 # Direct user configuration
160 self._binary = binary
161 self._args = list(args)
162 self._wrapper = wrapper
163 self._qmp_timer = qmp_timer
165 self._name = name or f"qemu-{os.getpid()}-{id(self):02x}"
166 self._temp_dir: Optional[str] = None
167 self._base_temp_dir = base_temp_dir
168 self._sock_dir = sock_dir
169 self._log_dir = log_dir
171 if monitor_address is not None:
172 self._monitor_address = monitor_address
174 self._monitor_address = os.path.join(
175 self.sock_dir, f"{self._name}-monitor.sock"
178 self._console_log_path = console_log
179 if self._console_log_path:
180 # In order to log the console, buffering needs to be enabled.
181 self._drain_console = True
183 self._drain_console = drain_console
186 self._qemu_log_path: Optional[str] = None
187 self._qemu_log_file: Optional[BinaryIO] = None
188 self._popen: Optional['subprocess.Popen[bytes]'] = None
189 self._events: List[QMPMessage] = []
190 self._iolog: Optional[str] = None
191 self._qmp_set = True # Enable QMP monitor by default.
192 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
193 self._qemu_full_args: Tuple[str, ...] = ()
194 self._launched = False
195 self._machine: Optional[str] = None
196 self._console_index = 0
197 self._console_set = False
198 self._console_device_type: Optional[str] = None
199 self._console_address = os.path.join(
200 self.sock_dir, f"{self._name}-console.sock"
202 self._console_socket: Optional[socket.socket] = None
203 self._remove_files: List[str] = []
204 self._user_killed = False
205 self._quit_issued = False
207 def __enter__(self: _T) -> _T:
211 exc_type: Optional[Type[BaseException]],
212 exc_val: Optional[BaseException],
213 exc_tb: Optional[TracebackType]) -> None:
216 def add_monitor_null(self) -> None:
218 This can be used to add an unused monitor instance.
220 self._args.append('-monitor')
221 self._args.append('null')
223 def add_fd(self: _T, fd: int, fdset: int,
224 opaque: str, opts: str = '') -> _T:
226 Pass a file descriptor to the VM
228 options = ['fd=%d' % fd,
230 'opaque=%s' % opaque]
234 # This did not exist before 3.4, but since then it is
235 # mandatory for our purpose
236 if hasattr(os, 'set_inheritable'):
237 os.set_inheritable(fd, True)
239 self._args.append('-add-fd')
240 self._args.append(','.join(options))
243 def send_fd_scm(self, fd: Optional[int] = None,
244 file_path: Optional[str] = None) -> int:
246 Send an fd or file_path to the remote via SCM_RIGHTS.
248 Exactly one of fd and file_path must be given. If it is
249 file_path, the file will be opened read-only and the new file
250 descriptor will be sent to the remote.
252 if file_path is not None:
254 with open(file_path, "rb") as passfile:
255 fd = passfile.fileno()
256 self._qmp.send_fd_scm(fd)
258 assert fd is not None
259 self._qmp.send_fd_scm(fd)
264 def _remove_if_exists(path: str) -> None:
266 Remove file object at path if it exists
270 except OSError as exception:
271 if exception.errno == errno.ENOENT:
275 def is_running(self) -> bool:
276 """Returns true if the VM is running."""
277 return self._popen is not None and self._popen.poll() is None
280 def _subp(self) -> 'subprocess.Popen[bytes]':
281 if self._popen is None:
282 raise QEMUMachineError('Subprocess pipe not present')
285 def exitcode(self) -> Optional[int]:
286 """Returns the exit code if possible, or None."""
287 if self._popen is None:
289 return self._popen.poll()
291 def get_pid(self) -> Optional[int]:
292 """Returns the PID of the running process, or None."""
293 if not self.is_running():
295 return self._subp.pid
297 def _load_io_log(self) -> None:
298 # Assume that the output encoding of QEMU's terminal output is
299 # defined by our locale. If indeterminate, allow open() to fall
300 # back to the platform default.
301 _, encoding = locale.getlocale()
302 if self._qemu_log_path is not None:
303 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
304 self._iolog = iolog.read()
307 def _base_args(self) -> List[str]:
308 args = ['-display', 'none', '-vga', 'none']
311 if isinstance(self._monitor_address, tuple):
312 moncdev = "socket,id=mon,host={},port={}".format(
313 *self._monitor_address
316 moncdev = f"socket,id=mon,path={self._monitor_address}"
317 args.extend(['-chardev', moncdev, '-mon',
318 'chardev=mon,mode=control'])
320 if self._machine is not None:
321 args.extend(['-machine', self._machine])
322 for _ in range(self._console_index):
323 args.extend(['-serial', 'null'])
324 if self._console_set:
325 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
326 self._console_address)
327 args.extend(['-chardev', chardev])
328 if self._console_device_type is None:
329 args.extend(['-serial', 'chardev:console'])
331 device = '%s,chardev=console' % self._console_device_type
332 args.extend(['-device', device])
336 def args(self) -> List[str]:
337 """Returns the list of arguments given to the QEMU binary."""
340 def _pre_launch(self) -> None:
341 if self._console_set:
342 self._remove_files.append(self._console_address)
345 if isinstance(self._monitor_address, str):
346 self._remove_files.append(self._monitor_address)
347 self._qmp_connection = QEMUMonitorProtocol(
348 self._monitor_address,
353 # NOTE: Make sure any opened resources are *definitely* freed in
355 # pylint: disable=consider-using-with
356 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
357 self._qemu_log_file = open(self._qemu_log_path, 'wb')
360 self._qemu_full_args = tuple(chain(
367 def _post_launch(self) -> None:
368 if self._qmp_connection:
369 self._qmp.accept(self._qmp_timer)
371 def _close_qemu_log_file(self) -> None:
372 if self._qemu_log_file is not None:
373 self._qemu_log_file.close()
374 self._qemu_log_file = None
376 def _post_shutdown(self) -> None:
378 Called to cleanup the VM instance after the process has exited.
379 May also be called after a failed launch.
382 self._close_qmp_connection()
383 except Exception as err: # pylint: disable=broad-except
385 "Exception closing QMP connection: %s",
386 str(err) if str(err) else type(err).__name__
389 assert self._qmp_connection is None
391 self._close_qemu_log_file()
395 self._qemu_log_path = None
397 if self._temp_dir is not None:
398 shutil.rmtree(self._temp_dir)
399 self._temp_dir = None
401 while len(self._remove_files) > 0:
402 self._remove_if_exists(self._remove_files.pop())
404 exitcode = self.exitcode()
405 if (exitcode is not None and exitcode < 0
406 and not (self._user_killed and exitcode == -signal.SIGKILL)):
407 msg = 'qemu received signal %i; command: "%s"'
408 if self._qemu_full_args:
409 command = ' '.join(self._qemu_full_args)
412 LOG.warning(msg, -int(exitcode), command)
414 self._quit_issued = False
415 self._user_killed = False
416 self._launched = False
418 def launch(self) -> None:
420 Launch the VM and make sure we cleanup and expose the
421 command line/output in case of exception
425 raise QEMUMachineError('VM already launched')
429 except BaseException as exc:
430 # We may have launched the process but it may
431 # have exited before we could connect via QMP.
432 # Assume the VM didn't launch or is exiting.
433 # If we don't wait for the process, exitcode() may still be
434 # 'None' by the time control is ceded back to the caller.
438 self._post_shutdown()
440 if isinstance(exc, Exception):
441 raise VMLaunchFailure(
442 exitcode=self.exitcode(),
443 command=' '.join(self._qemu_full_args),
447 # Don't wrap 'BaseException'; doing so would downgrade
448 # that exception. However, we still want to clean up.
451 def _launch(self) -> None:
453 Launch the VM and establish a QMP connection
456 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
458 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
459 # pylint: disable=consider-using-with
460 self._popen = subprocess.Popen(self._qemu_full_args,
461 stdin=subprocess.DEVNULL,
462 stdout=self._qemu_log_file,
463 stderr=subprocess.STDOUT,
466 self._launched = True
469 def _close_qmp_connection(self) -> None:
471 Close the underlying QMP connection, if any.
473 Dutifully report errors that occurred while closing, but assume
474 that any error encountered indicates an abnormal termination
475 process and not a failure to close.
477 if self._qmp_connection is None:
483 # EOF can occur as an Exception here when using the Async
484 # QMP backend. It indicates that the server closed the
485 # stream. If we successfully issued 'quit' at any point,
486 # then this was expected. If the remote went away without
487 # our permission, it's worth reporting that as an abnormal
489 if not (self._user_killed or self._quit_issued):
492 self._qmp_connection = None
494 def _early_cleanup(self) -> None:
496 Perform any cleanup that needs to happen before the VM exits.
498 This method may be called twice upon shutdown, once each by soft
499 and hard shutdown in failover scenarios.
501 # If we keep the console socket open, we may deadlock waiting
502 # for QEMU to exit, while QEMU is waiting for the socket to
504 if self._console_socket is not None:
505 self._console_socket.close()
506 self._console_socket = None
508 def _hard_shutdown(self) -> None:
510 Perform early cleanup, kill the VM, and wait for it to terminate.
512 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
513 waiting for the QEMU process to terminate.
515 self._early_cleanup()
517 self._subp.wait(timeout=60)
519 def _soft_shutdown(self, timeout: Optional[int]) -> None:
521 Perform early cleanup, attempt to gracefully shut down the VM, and wait
524 :param timeout: Timeout in seconds for graceful shutdown.
525 A value of None is an infinite wait.
527 :raise ConnectionReset: On QMP communication errors
528 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
529 the QEMU process to terminate.
531 self._early_cleanup()
533 if self._qmp_connection:
535 if not self._quit_issued:
536 # May raise ExecInterruptedError or StateError if the
537 # connection dies or has *already* died.
540 # Regardless, we want to quiesce the connection.
541 self._close_qmp_connection()
543 # May raise subprocess.TimeoutExpired
544 self._subp.wait(timeout=timeout)
546 def _do_shutdown(self, timeout: Optional[int]) -> None:
548 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
550 :param timeout: Timeout in seconds for graceful shutdown.
551 A value of None is an infinite wait.
553 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
554 The inner exception will likely be ConnectionReset or
555 subprocess.TimeoutExpired. In rare cases, non-graceful termination
556 may result in its own exceptions, likely subprocess.TimeoutExpired.
559 self._soft_shutdown(timeout)
560 except Exception as exc:
561 self._hard_shutdown()
562 raise AbnormalShutdown("Could not perform graceful shutdown") \
567 timeout: Optional[int] = 30) -> None:
569 Terminate the VM (gracefully if possible) and perform cleanup.
570 Cleanup will always be performed.
572 If the VM has not yet been launched, or shutdown(), wait(), or kill()
573 have already been called, this method does nothing.
575 :param hard: When true, do not attempt graceful shutdown, and
576 suppress the SIGKILL warning log message.
577 :param timeout: Optional timeout in seconds for graceful shutdown.
578 Default 30 seconds, A `None` value is an infinite wait.
580 if not self._launched:
585 self._user_killed = True
586 self._hard_shutdown()
588 self._do_shutdown(timeout)
590 self._post_shutdown()
592 def kill(self) -> None:
594 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
596 self.shutdown(hard=True)
598 def wait(self, timeout: Optional[int] = 30) -> None:
600 Wait for the VM to power off and perform post-shutdown cleanup.
602 :param timeout: Optional timeout in seconds. Default 30 seconds.
603 A value of `None` is an infinite wait.
605 self._quit_issued = True
606 self.shutdown(timeout=timeout)
608 def set_qmp_monitor(self, enabled: bool = True) -> None:
612 @param enabled: if False, qmp monitor options will be removed from
613 the base arguments of the resulting QEMU command
614 line. Default is True.
616 .. note:: Call this function before launch().
618 self._qmp_set = enabled
621 def _qmp(self) -> QEMUMonitorProtocol:
622 if self._qmp_connection is None:
623 raise QEMUMachineError("Attempt to access QMP with no connection")
624 return self._qmp_connection
627 def _qmp_args(cls, conv_keys: bool,
628 args: Dict[str, Any]) -> Dict[str, object]:
630 return {k.replace('_', '-'): v for k, v in args.items()}
634 def qmp(self, cmd: str,
635 args_dict: Optional[Dict[str, object]] = None,
636 conv_keys: Optional[bool] = None,
637 **args: Any) -> QMPMessage:
639 Invoke a QMP command and return the response dict
641 if args_dict is not None:
643 assert conv_keys is None
647 if conv_keys is None:
650 qmp_args = self._qmp_args(conv_keys, args)
651 ret = self._qmp.cmd(cmd, args=qmp_args)
652 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
653 self._quit_issued = True
656 def command(self, cmd: str,
657 conv_keys: bool = True,
658 **args: Any) -> QMPReturnValue:
660 Invoke a QMP command.
661 On success return the response dict.
662 On failure raise an exception.
664 qmp_args = self._qmp_args(conv_keys, args)
665 ret = self._qmp.command(cmd, **qmp_args)
667 self._quit_issued = True
670 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
672 Poll for one queued QMP events and return it
675 return self._events.pop(0)
676 return self._qmp.pull_event(wait=wait)
678 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
680 Poll for queued QMP events and return a list of dicts
682 events = self._qmp.get_events(wait=wait)
683 events.extend(self._events)
688 def event_match(event: Any, match: Optional[Any]) -> bool:
690 Check if an event matches optional match criteria.
692 The match criteria takes the form of a matching subdict. The event is
693 checked to be a superset of the subdict, recursively, with matching
694 values whenever the subdict values are not None.
696 This has a limitation that you cannot explicitly check for None values.
698 Examples, with the subdict queries on the left:
699 - None matches any object.
700 - {"foo": None} matches {"foo": {"bar": 1}}
701 - {"foo": None} matches {"foo": 5}
702 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
703 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
711 if not QEMUMachine.event_match(event[key], match[key]):
717 # either match or event wasn't iterable (not a dict)
718 return bool(match == event)
720 def event_wait(self, name: str,
721 timeout: float = 60.0,
722 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
724 event_wait waits for and returns a named event from QMP with a timeout.
726 name: The event to wait for.
727 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
728 match: Optional match criteria. See event_match for details.
730 return self.events_wait([(name, match)], timeout)
732 def events_wait(self,
733 events: Sequence[Tuple[str, Any]],
734 timeout: float = 60.0) -> Optional[QMPMessage]:
736 events_wait waits for and returns a single named event from QMP.
737 In the case of multiple qualifying events, this function returns the
740 :param events: A sequence of (name, match_criteria) tuples.
741 The match criteria are optional and may be None.
742 See event_match for details.
743 :param timeout: Optional timeout, in seconds.
744 See QEMUMonitorProtocol.pull_event.
746 :raise QMPTimeoutError: If timeout was non-zero and no matching events
748 :return: A QMP event matching the filter criteria.
749 If timeout was 0 and no event matched, None.
751 def _match(event: QMPMessage) -> bool:
752 for name, match in events:
753 if event['event'] == name and self.event_match(event, match):
757 event: Optional[QMPMessage]
759 # Search cached events
760 for event in self._events:
762 self._events.remove(event)
765 # Poll for new events
767 event = self._qmp.pull_event(wait=timeout)
769 # NB: None is only returned when timeout is false-ish.
770 # Timeouts raise QMPTimeoutError instead!
774 self._events.append(event)
778 def get_log(self) -> Optional[str]:
780 After self.shutdown or failed qemu execution, this returns the output
785 def add_args(self, *args: str) -> None:
787 Adds to the list of extra arguments to be given to the QEMU binary
789 self._args.extend(args)
791 def set_machine(self, machine_type: str) -> None:
793 Sets the machine type
795 If set, the machine type will be added to the base arguments
796 of the resulting QEMU command line.
798 self._machine = machine_type
800 def set_console(self,
801 device_type: Optional[str] = None,
802 console_index: int = 0) -> None:
804 Sets the device type for a console device
806 If set, the console device and a backing character device will
807 be added to the base arguments of the resulting QEMU command
810 This is a convenience method that will either use the provided
811 device type, or default to a "-serial chardev:console" command
814 The actual setting of command line arguments will be be done at
815 machine launch time, as it depends on the temporary directory
818 @param device_type: the device type, such as "isa-serial". If
819 None is given (the default value) a "-serial
820 chardev:console" command line argument will
821 be used instead, resorting to the machine's
823 @param console_index: the index of the console device to use.
824 If not zero, the command line will create
825 'index - 1' consoles and connect them to
826 the 'null' backing character device.
828 self._console_set = True
829 self._console_device_type = device_type
830 self._console_index = console_index
833 def console_socket(self) -> socket.socket:
835 Returns a socket connected to the console
837 if self._console_socket is None:
838 self._console_socket = console_socket.ConsoleSocket(
839 self._console_address,
840 file=self._console_log_path,
841 drain=self._drain_console)
842 return self._console_socket
845 def temp_dir(self) -> str:
847 Returns a temporary directory to be used for this machine
849 if self._temp_dir is None:
850 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
851 dir=self._base_temp_dir)
852 return self._temp_dir
855 def sock_dir(self) -> str:
857 Returns the directory used for sockfiles by this machine.
860 return self._sock_dir
864 def log_dir(self) -> str:
866 Returns a directory to be used for writing logs
868 if self._log_dir is None: