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
50 from . import console_socket
53 LOG = logging.getLogger(__name__)
56 class QEMUMachineError(Exception):
58 Exception called when an error in QEMUMachine happens.
62 class QEMUMachineAddDeviceError(QEMUMachineError):
64 Exception raised when a request to add a device can not be fulfilled
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.
72 class AbnormalShutdown(QEMUMachineError):
74 Exception raised when a graceful shutdown was requested, but not performed.
78 _T = TypeVar('_T', bound='QEMUMachine')
85 Use this object as a context manager to ensure
86 the QEMU process terminates::
88 with VM(binary) as vm:
90 # vm is guaranteed to be shut down here
92 # pylint: disable=too-many-instance-attributes, too-many-public-methods
96 args: Sequence[str] = (),
97 wrapper: Sequence[str] = (),
98 name: Optional[str] = None,
99 base_temp_dir: str = "/var/tmp",
100 monitor_address: Optional[SocketAddrT] = None,
101 sock_dir: Optional[str] = None,
102 drain_console: bool = False,
103 console_log: Optional[str] = None,
104 log_dir: Optional[str] = None,
105 qmp_timer: Optional[float] = None):
107 Initialize a QEMUMachine
109 @param binary: path to the qemu binary
110 @param args: list of extra arguments
111 @param wrapper: list of arguments used as prefix to qemu binary
112 @param name: prefix for socket and log file names (default: qemu-PID)
113 @param base_temp_dir: default location where temp files are created
114 @param monitor_address: address for QMP monitor
115 @param sock_dir: where to create socket (defaults to base_temp_dir)
116 @param drain_console: (optional) True to drain console socket to buffer
117 @param console_log: (optional) path to console log file
118 @param log_dir: where to create and keep log files
119 @param qmp_timer: (optional) default QMP socket timeout
120 @note: Qemu process is not started until launch() is used.
122 # pylint: disable=too-many-arguments
124 # Direct user configuration
126 self._binary = binary
127 self._args = list(args)
128 self._wrapper = wrapper
129 self._qmp_timer = qmp_timer
131 self._name = name or "qemu-%d" % os.getpid()
132 self._base_temp_dir = base_temp_dir
133 self._sock_dir = sock_dir or self._base_temp_dir
134 self._log_dir = log_dir
136 if monitor_address is not None:
137 self._monitor_address = monitor_address
138 self._remove_monitor_sockfile = False
140 self._monitor_address = os.path.join(
141 self._sock_dir, f"{self._name}-monitor.sock"
143 self._remove_monitor_sockfile = True
145 self._console_log_path = console_log
146 if self._console_log_path:
147 # In order to log the console, buffering needs to be enabled.
148 self._drain_console = True
150 self._drain_console = drain_console
153 self._qemu_log_path: Optional[str] = None
154 self._qemu_log_file: Optional[BinaryIO] = None
155 self._popen: Optional['subprocess.Popen[bytes]'] = None
156 self._events: List[QMPMessage] = []
157 self._iolog: Optional[str] = None
158 self._qmp_set = True # Enable QMP monitor by default.
159 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
160 self._qemu_full_args: Tuple[str, ...] = ()
161 self._temp_dir: Optional[str] = None
162 self._launched = False
163 self._machine: Optional[str] = None
164 self._console_index = 0
165 self._console_set = False
166 self._console_device_type: Optional[str] = None
167 self._console_address = os.path.join(
168 self._sock_dir, f"{self._name}-console.sock"
170 self._console_socket: Optional[socket.socket] = None
171 self._remove_files: List[str] = []
172 self._user_killed = False
173 self._quit_issued = False
175 def __enter__(self: _T) -> _T:
179 exc_type: Optional[Type[BaseException]],
180 exc_val: Optional[BaseException],
181 exc_tb: Optional[TracebackType]) -> None:
184 def add_monitor_null(self) -> None:
186 This can be used to add an unused monitor instance.
188 self._args.append('-monitor')
189 self._args.append('null')
191 def add_fd(self: _T, fd: int, fdset: int,
192 opaque: str, opts: str = '') -> _T:
194 Pass a file descriptor to the VM
196 options = ['fd=%d' % fd,
198 'opaque=%s' % opaque]
202 # This did not exist before 3.4, but since then it is
203 # mandatory for our purpose
204 if hasattr(os, 'set_inheritable'):
205 os.set_inheritable(fd, True)
207 self._args.append('-add-fd')
208 self._args.append(','.join(options))
211 def send_fd_scm(self, fd: Optional[int] = None,
212 file_path: Optional[str] = None) -> int:
214 Send an fd or file_path to the remote via SCM_RIGHTS.
216 Exactly one of fd and file_path must be given. If it is
217 file_path, the file will be opened read-only and the new file
218 descriptor will be sent to the remote.
220 if file_path is not None:
222 with open(file_path, "rb") as passfile:
223 fd = passfile.fileno()
224 self._qmp.send_fd_scm(fd)
226 assert fd is not None
227 self._qmp.send_fd_scm(fd)
232 def _remove_if_exists(path: str) -> None:
234 Remove file object at path if it exists
238 except OSError as exception:
239 if exception.errno == errno.ENOENT:
243 def is_running(self) -> bool:
244 """Returns true if the VM is running."""
245 return self._popen is not None and self._popen.poll() is None
248 def _subp(self) -> 'subprocess.Popen[bytes]':
249 if self._popen is None:
250 raise QEMUMachineError('Subprocess pipe not present')
253 def exitcode(self) -> Optional[int]:
254 """Returns the exit code if possible, or None."""
255 if self._popen is None:
257 return self._popen.poll()
259 def get_pid(self) -> Optional[int]:
260 """Returns the PID of the running process, or None."""
261 if not self.is_running():
263 return self._subp.pid
265 def _load_io_log(self) -> None:
266 # Assume that the output encoding of QEMU's terminal output is
267 # defined by our locale. If indeterminate, allow open() to fall
268 # back to the platform default.
269 _, encoding = locale.getlocale()
270 if self._qemu_log_path is not None:
271 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
272 self._iolog = iolog.read()
275 def _base_args(self) -> List[str]:
276 args = ['-display', 'none', '-vga', 'none']
279 if isinstance(self._monitor_address, tuple):
280 moncdev = "socket,id=mon,host={},port={}".format(
281 *self._monitor_address
284 moncdev = f"socket,id=mon,path={self._monitor_address}"
285 args.extend(['-chardev', moncdev, '-mon',
286 'chardev=mon,mode=control'])
288 if self._machine is not None:
289 args.extend(['-machine', self._machine])
290 for _ in range(self._console_index):
291 args.extend(['-serial', 'null'])
292 if self._console_set:
293 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
294 self._console_address)
295 args.extend(['-chardev', chardev])
296 if self._console_device_type is None:
297 args.extend(['-serial', 'chardev:console'])
299 device = '%s,chardev=console' % self._console_device_type
300 args.extend(['-device', device])
304 def args(self) -> List[str]:
305 """Returns the list of arguments given to the QEMU binary."""
308 def _pre_launch(self) -> None:
309 if self._console_set:
310 self._remove_files.append(self._console_address)
313 if self._remove_monitor_sockfile:
314 assert isinstance(self._monitor_address, str)
315 self._remove_files.append(self._monitor_address)
316 self._qmp_connection = QEMUMonitorProtocol(
317 self._monitor_address,
322 # NOTE: Make sure any opened resources are *definitely* freed in
324 # pylint: disable=consider-using-with
325 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
326 self._qemu_log_file = open(self._qemu_log_path, 'wb')
328 def _post_launch(self) -> None:
329 if self._qmp_connection:
330 self._qmp.accept(self._qmp_timer)
332 def _close_qemu_log_file(self) -> None:
333 if self._qemu_log_file is not None:
334 self._qemu_log_file.close()
335 self._qemu_log_file = None
337 def _post_shutdown(self) -> None:
339 Called to cleanup the VM instance after the process has exited.
340 May also be called after a failed launch.
342 # Comprehensive reset for the failed launch case:
343 self._early_cleanup()
346 self._close_qmp_connection()
347 except Exception as err: # pylint: disable=broad-except
349 "Exception closing QMP connection: %s",
350 str(err) if str(err) else type(err).__name__
353 assert self._qmp_connection is None
355 self._close_qemu_log_file()
359 self._qemu_log_path = None
361 if self._temp_dir is not None:
362 shutil.rmtree(self._temp_dir)
363 self._temp_dir = None
365 while len(self._remove_files) > 0:
366 self._remove_if_exists(self._remove_files.pop())
368 exitcode = self.exitcode()
369 if (exitcode is not None and exitcode < 0
370 and not (self._user_killed and exitcode == -signal.SIGKILL)):
371 msg = 'qemu received signal %i; command: "%s"'
372 if self._qemu_full_args:
373 command = ' '.join(self._qemu_full_args)
376 LOG.warning(msg, -int(exitcode), command)
378 self._quit_issued = False
379 self._user_killed = False
380 self._launched = False
382 def launch(self) -> None:
384 Launch the VM and make sure we cleanup and expose the
385 command line/output in case of exception
389 raise QEMUMachineError('VM already launched')
392 self._qemu_full_args = ()
395 self._launched = True
397 self._post_shutdown()
399 LOG.debug('Error launching VM')
400 if self._qemu_full_args:
401 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
403 LOG.debug('Output: %r', self._iolog)
406 def _launch(self) -> None:
408 Launch the VM and establish a QMP connection
411 self._qemu_full_args = tuple(
417 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
419 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
420 # pylint: disable=consider-using-with
421 self._popen = subprocess.Popen(self._qemu_full_args,
422 stdin=subprocess.DEVNULL,
423 stdout=self._qemu_log_file,
424 stderr=subprocess.STDOUT,
429 def _close_qmp_connection(self) -> None:
431 Close the underlying QMP connection, if any.
433 Dutifully report errors that occurred while closing, but assume
434 that any error encountered indicates an abnormal termination
435 process and not a failure to close.
437 if self._qmp_connection is None:
443 # EOF can occur as an Exception here when using the Async
444 # QMP backend. It indicates that the server closed the
445 # stream. If we successfully issued 'quit' at any point,
446 # then this was expected. If the remote went away without
447 # our permission, it's worth reporting that as an abnormal
449 if not (self._user_killed or self._quit_issued):
452 self._qmp_connection = None
454 def _early_cleanup(self) -> None:
456 Perform any cleanup that needs to happen before the VM exits.
458 May be invoked by both soft and hard shutdown in failover scenarios.
459 Called additionally by _post_shutdown for comprehensive cleanup.
461 # If we keep the console socket open, we may deadlock waiting
462 # for QEMU to exit, while QEMU is waiting for the socket to
464 if self._console_socket is not None:
465 self._console_socket.close()
466 self._console_socket = None
468 def _hard_shutdown(self) -> None:
470 Perform early cleanup, kill the VM, and wait for it to terminate.
472 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
473 waiting for the QEMU process to terminate.
475 self._early_cleanup()
477 self._subp.wait(timeout=60)
479 def _soft_shutdown(self, timeout: Optional[int]) -> None:
481 Perform early cleanup, attempt to gracefully shut down the VM, and wait
484 :param timeout: Timeout in seconds for graceful shutdown.
485 A value of None is an infinite wait.
487 :raise ConnectionReset: On QMP communication errors
488 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
489 the QEMU process to terminate.
491 self._early_cleanup()
493 if self._qmp_connection:
495 if not self._quit_issued:
496 # May raise ExecInterruptedError or StateError if the
497 # connection dies or has *already* died.
500 # Regardless, we want to quiesce the connection.
501 self._close_qmp_connection()
503 # May raise subprocess.TimeoutExpired
504 self._subp.wait(timeout=timeout)
506 def _do_shutdown(self, timeout: Optional[int]) -> None:
508 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
510 :param timeout: Timeout in seconds for graceful shutdown.
511 A value of None is an infinite wait.
513 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
514 The inner exception will likely be ConnectionReset or
515 subprocess.TimeoutExpired. In rare cases, non-graceful termination
516 may result in its own exceptions, likely subprocess.TimeoutExpired.
519 self._soft_shutdown(timeout)
520 except Exception as exc:
521 self._hard_shutdown()
522 raise AbnormalShutdown("Could not perform graceful shutdown") \
527 timeout: Optional[int] = 30) -> None:
529 Terminate the VM (gracefully if possible) and perform cleanup.
530 Cleanup will always be performed.
532 If the VM has not yet been launched, or shutdown(), wait(), or kill()
533 have already been called, this method does nothing.
535 :param hard: When true, do not attempt graceful shutdown, and
536 suppress the SIGKILL warning log message.
537 :param timeout: Optional timeout in seconds for graceful shutdown.
538 Default 30 seconds, A `None` value is an infinite wait.
540 if not self._launched:
545 self._user_killed = True
546 self._hard_shutdown()
548 self._do_shutdown(timeout)
550 self._post_shutdown()
552 def kill(self) -> None:
554 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
556 self.shutdown(hard=True)
558 def wait(self, timeout: Optional[int] = 30) -> None:
560 Wait for the VM to power off and perform post-shutdown cleanup.
562 :param timeout: Optional timeout in seconds. Default 30 seconds.
563 A value of `None` is an infinite wait.
565 self._quit_issued = True
566 self.shutdown(timeout=timeout)
568 def set_qmp_monitor(self, enabled: bool = True) -> None:
572 @param enabled: if False, qmp monitor options will be removed from
573 the base arguments of the resulting QEMU command
574 line. Default is True.
576 .. note:: Call this function before launch().
578 self._qmp_set = enabled
581 def _qmp(self) -> QEMUMonitorProtocol:
582 if self._qmp_connection is None:
583 raise QEMUMachineError("Attempt to access QMP with no connection")
584 return self._qmp_connection
587 def _qmp_args(cls, conv_keys: bool,
588 args: Dict[str, Any]) -> Dict[str, object]:
590 return {k.replace('_', '-'): v for k, v in args.items()}
594 def qmp(self, cmd: str,
595 args_dict: Optional[Dict[str, object]] = None,
596 conv_keys: Optional[bool] = None,
597 **args: Any) -> QMPMessage:
599 Invoke a QMP command and return the response dict
601 if args_dict is not None:
603 assert conv_keys is None
607 if conv_keys is None:
610 qmp_args = self._qmp_args(conv_keys, args)
611 ret = self._qmp.cmd(cmd, args=qmp_args)
612 if cmd == 'quit' and 'error' not in ret and 'return' in ret:
613 self._quit_issued = True
616 def command(self, cmd: str,
617 conv_keys: bool = True,
618 **args: Any) -> QMPReturnValue:
620 Invoke a QMP command.
621 On success return the response dict.
622 On failure raise an exception.
624 qmp_args = self._qmp_args(conv_keys, args)
625 ret = self._qmp.command(cmd, **qmp_args)
627 self._quit_issued = True
630 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
632 Poll for one queued QMP events and return it
635 return self._events.pop(0)
636 return self._qmp.pull_event(wait=wait)
638 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
640 Poll for queued QMP events and return a list of dicts
642 events = self._qmp.get_events(wait=wait)
643 events.extend(self._events)
648 def event_match(event: Any, match: Optional[Any]) -> bool:
650 Check if an event matches optional match criteria.
652 The match criteria takes the form of a matching subdict. The event is
653 checked to be a superset of the subdict, recursively, with matching
654 values whenever the subdict values are not None.
656 This has a limitation that you cannot explicitly check for None values.
658 Examples, with the subdict queries on the left:
659 - None matches any object.
660 - {"foo": None} matches {"foo": {"bar": 1}}
661 - {"foo": None} matches {"foo": 5}
662 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
663 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
671 if not QEMUMachine.event_match(event[key], match[key]):
677 # either match or event wasn't iterable (not a dict)
678 return bool(match == event)
680 def event_wait(self, name: str,
681 timeout: float = 60.0,
682 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
684 event_wait waits for and returns a named event from QMP with a timeout.
686 name: The event to wait for.
687 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
688 match: Optional match criteria. See event_match for details.
690 return self.events_wait([(name, match)], timeout)
692 def events_wait(self,
693 events: Sequence[Tuple[str, Any]],
694 timeout: float = 60.0) -> Optional[QMPMessage]:
696 events_wait waits for and returns a single named event from QMP.
697 In the case of multiple qualifying events, this function returns the
700 :param events: A sequence of (name, match_criteria) tuples.
701 The match criteria are optional and may be None.
702 See event_match for details.
703 :param timeout: Optional timeout, in seconds.
704 See QEMUMonitorProtocol.pull_event.
706 :raise QMPTimeoutError: If timeout was non-zero and no matching events
708 :return: A QMP event matching the filter criteria.
709 If timeout was 0 and no event matched, None.
711 def _match(event: QMPMessage) -> bool:
712 for name, match in events:
713 if event['event'] == name and self.event_match(event, match):
717 event: Optional[QMPMessage]
719 # Search cached events
720 for event in self._events:
722 self._events.remove(event)
725 # Poll for new events
727 event = self._qmp.pull_event(wait=timeout)
729 # NB: None is only returned when timeout is false-ish.
730 # Timeouts raise QMPTimeoutError instead!
734 self._events.append(event)
738 def get_log(self) -> Optional[str]:
740 After self.shutdown or failed qemu execution, this returns the output
745 def add_args(self, *args: str) -> None:
747 Adds to the list of extra arguments to be given to the QEMU binary
749 self._args.extend(args)
751 def set_machine(self, machine_type: str) -> None:
753 Sets the machine type
755 If set, the machine type will be added to the base arguments
756 of the resulting QEMU command line.
758 self._machine = machine_type
760 def set_console(self,
761 device_type: Optional[str] = None,
762 console_index: int = 0) -> None:
764 Sets the device type for a console device
766 If set, the console device and a backing character device will
767 be added to the base arguments of the resulting QEMU command
770 This is a convenience method that will either use the provided
771 device type, or default to a "-serial chardev:console" command
774 The actual setting of command line arguments will be be done at
775 machine launch time, as it depends on the temporary directory
778 @param device_type: the device type, such as "isa-serial". If
779 None is given (the default value) a "-serial
780 chardev:console" command line argument will
781 be used instead, resorting to the machine's
783 @param console_index: the index of the console device to use.
784 If not zero, the command line will create
785 'index - 1' consoles and connect them to
786 the 'null' backing character device.
788 self._console_set = True
789 self._console_device_type = device_type
790 self._console_index = console_index
793 def console_socket(self) -> socket.socket:
795 Returns a socket connected to the console
797 if self._console_socket is None:
798 self._console_socket = console_socket.ConsoleSocket(
799 self._console_address,
800 file=self._console_log_path,
801 drain=self._drain_console)
802 return self._console_socket
805 def temp_dir(self) -> str:
807 Returns a temporary directory to be used for this machine
809 if self._temp_dir is None:
810 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
811 dir=self._base_temp_dir)
812 return self._temp_dir
815 def log_dir(self) -> str:
817 Returns a directory to be used for writing logs
819 if self._log_dir is None: