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 socket_scm_helper: Optional[str] = None,
102 sock_dir: Optional[str] = None,
103 drain_console: bool = False,
104 console_log: Optional[str] = None,
105 log_dir: Optional[str] = None,
106 qmp_timer: Optional[float] = None):
108 Initialize a QEMUMachine
110 @param binary: path to the qemu binary
111 @param args: list of extra arguments
112 @param wrapper: list of arguments used as prefix to qemu binary
113 @param name: prefix for socket and log file names (default: qemu-PID)
114 @param base_temp_dir: default location where temp files are created
115 @param monitor_address: address for QMP monitor
116 @param socket_scm_helper: helper program, required for send_fd_scm()
117 @param sock_dir: where to create socket (defaults to base_temp_dir)
118 @param drain_console: (optional) True to drain console socket to buffer
119 @param console_log: (optional) path to console log file
120 @param log_dir: where to create and keep log files
121 @param qmp_timer: (optional) default QMP socket timeout
122 @note: Qemu process is not started until launch() is used.
124 # pylint: disable=too-many-arguments
126 # Direct user configuration
128 self._binary = binary
129 self._args = list(args)
130 self._wrapper = wrapper
131 self._qmp_timer = qmp_timer
133 self._name = name or "qemu-%d" % os.getpid()
134 self._base_temp_dir = base_temp_dir
135 self._sock_dir = sock_dir or self._base_temp_dir
136 self._log_dir = log_dir
137 self._socket_scm_helper = socket_scm_helper
139 if monitor_address is not None:
140 self._monitor_address = monitor_address
141 self._remove_monitor_sockfile = False
143 self._monitor_address = os.path.join(
144 self._sock_dir, f"{self._name}-monitor.sock"
146 self._remove_monitor_sockfile = True
148 self._console_log_path = console_log
149 if self._console_log_path:
150 # In order to log the console, buffering needs to be enabled.
151 self._drain_console = True
153 self._drain_console = drain_console
156 self._qemu_log_path: Optional[str] = None
157 self._qemu_log_file: Optional[BinaryIO] = None
158 self._popen: Optional['subprocess.Popen[bytes]'] = None
159 self._events: List[QMPMessage] = []
160 self._iolog: Optional[str] = None
161 self._qmp_set = True # Enable QMP monitor by default.
162 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
163 self._qemu_full_args: Tuple[str, ...] = ()
164 self._temp_dir: Optional[str] = None
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
177 def __enter__(self: _T) -> _T:
181 exc_type: Optional[Type[BaseException]],
182 exc_val: Optional[BaseException],
183 exc_tb: Optional[TracebackType]) -> None:
186 def add_monitor_null(self) -> None:
188 This can be used to add an unused monitor instance.
190 self._args.append('-monitor')
191 self._args.append('null')
193 def add_fd(self: _T, fd: int, fdset: int,
194 opaque: str, opts: str = '') -> _T:
196 Pass a file descriptor to the VM
198 options = ['fd=%d' % fd,
200 'opaque=%s' % opaque]
204 # This did not exist before 3.4, but since then it is
205 # mandatory for our purpose
206 if hasattr(os, 'set_inheritable'):
207 os.set_inheritable(fd, True)
209 self._args.append('-add-fd')
210 self._args.append(','.join(options))
213 def send_fd_scm(self, fd: Optional[int] = None,
214 file_path: Optional[str] = None) -> int:
216 Send an fd or file_path to socket_scm_helper.
218 Exactly one of fd and file_path must be given.
219 If it is file_path, the helper will open that file and pass its own fd.
221 # In iotest.py, the qmp should always use unix socket.
222 assert self._qmp.is_scm_available()
223 if self._socket_scm_helper is None:
224 raise QEMUMachineError("No path to socket_scm_helper set")
225 if not os.path.exists(self._socket_scm_helper):
226 raise QEMUMachineError("%s does not exist" %
227 self._socket_scm_helper)
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(self._qmp.get_sock_fd(), True)
234 os.set_inheritable(fd, True)
236 fd_param = ["%s" % self._socket_scm_helper,
237 "%d" % self._qmp.get_sock_fd()]
239 if file_path is not None:
241 fd_param.append(file_path)
243 assert fd is not None
244 fd_param.append(str(fd))
246 proc = subprocess.run(
248 stdin=subprocess.DEVNULL,
249 stdout=subprocess.PIPE,
250 stderr=subprocess.STDOUT,
255 LOG.debug(proc.stdout)
257 return proc.returncode
260 def _remove_if_exists(path: str) -> None:
262 Remove file object at path if it exists
266 except OSError as exception:
267 if exception.errno == errno.ENOENT:
271 def is_running(self) -> bool:
272 """Returns true if the VM is running."""
273 return self._popen is not None and self._popen.poll() is None
276 def _subp(self) -> 'subprocess.Popen[bytes]':
277 if self._popen is None:
278 raise QEMUMachineError('Subprocess pipe not present')
281 def exitcode(self) -> Optional[int]:
282 """Returns the exit code if possible, or None."""
283 if self._popen is None:
285 return self._popen.poll()
287 def get_pid(self) -> Optional[int]:
288 """Returns the PID of the running process, or None."""
289 if not self.is_running():
291 return self._subp.pid
293 def _load_io_log(self) -> None:
294 # Assume that the output encoding of QEMU's terminal output is
295 # defined by our locale. If indeterminate, allow open() to fall
296 # back to the platform default.
297 _, encoding = locale.getlocale()
298 if self._qemu_log_path is not None:
299 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
300 self._iolog = iolog.read()
303 def _base_args(self) -> List[str]:
304 args = ['-display', 'none', '-vga', 'none']
307 if isinstance(self._monitor_address, tuple):
308 moncdev = "socket,id=mon,host={},port={}".format(
309 *self._monitor_address
312 moncdev = f"socket,id=mon,path={self._monitor_address}"
313 args.extend(['-chardev', moncdev, '-mon',
314 'chardev=mon,mode=control'])
316 if self._machine is not None:
317 args.extend(['-machine', self._machine])
318 for _ in range(self._console_index):
319 args.extend(['-serial', 'null'])
320 if self._console_set:
321 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
322 self._console_address)
323 args.extend(['-chardev', chardev])
324 if self._console_device_type is None:
325 args.extend(['-serial', 'chardev:console'])
327 device = '%s,chardev=console' % self._console_device_type
328 args.extend(['-device', device])
332 def args(self) -> List[str]:
333 """Returns the list of arguments given to the QEMU binary."""
336 def _pre_launch(self) -> None:
337 if self._console_set:
338 self._remove_files.append(self._console_address)
341 if self._remove_monitor_sockfile:
342 assert isinstance(self._monitor_address, str)
343 self._remove_files.append(self._monitor_address)
344 self._qmp_connection = QEMUMonitorProtocol(
345 self._monitor_address,
350 # NOTE: Make sure any opened resources are *definitely* freed in
352 # pylint: disable=consider-using-with
353 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
354 self._qemu_log_file = open(self._qemu_log_path, 'wb')
356 def _post_launch(self) -> None:
357 if self._qmp_connection:
358 self._qmp.accept(self._qmp_timer)
360 def _close_qemu_log_file(self) -> None:
361 if self._qemu_log_file is not None:
362 self._qemu_log_file.close()
363 self._qemu_log_file = None
365 def _post_shutdown(self) -> None:
367 Called to cleanup the VM instance after the process has exited.
368 May also be called after a failed launch.
370 # Comprehensive reset for the failed launch case:
371 self._early_cleanup()
373 if self._qmp_connection:
375 self._qmp_connection = None
377 self._close_qemu_log_file()
381 self._qemu_log_path = None
383 if self._temp_dir is not None:
384 shutil.rmtree(self._temp_dir)
385 self._temp_dir = None
387 while len(self._remove_files) > 0:
388 self._remove_if_exists(self._remove_files.pop())
390 exitcode = self.exitcode()
391 if (exitcode is not None and exitcode < 0
392 and not (self._user_killed and exitcode == -signal.SIGKILL)):
393 msg = 'qemu received signal %i; command: "%s"'
394 if self._qemu_full_args:
395 command = ' '.join(self._qemu_full_args)
398 LOG.warning(msg, -int(exitcode), command)
400 self._user_killed = False
401 self._launched = False
403 def launch(self) -> None:
405 Launch the VM and make sure we cleanup and expose the
406 command line/output in case of exception
410 raise QEMUMachineError('VM already launched')
413 self._qemu_full_args = ()
416 self._launched = True
418 self._post_shutdown()
420 LOG.debug('Error launching VM')
421 if self._qemu_full_args:
422 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
424 LOG.debug('Output: %r', self._iolog)
427 def _launch(self) -> None:
429 Launch the VM and establish a QMP connection
432 self._qemu_full_args = tuple(
438 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
440 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
441 # pylint: disable=consider-using-with
442 self._popen = subprocess.Popen(self._qemu_full_args,
443 stdin=subprocess.DEVNULL,
444 stdout=self._qemu_log_file,
445 stderr=subprocess.STDOUT,
450 def _early_cleanup(self) -> None:
452 Perform any cleanup that needs to happen before the VM exits.
454 May be invoked by both soft and hard shutdown in failover scenarios.
455 Called additionally by _post_shutdown for comprehensive cleanup.
457 # If we keep the console socket open, we may deadlock waiting
458 # for QEMU to exit, while QEMU is waiting for the socket to
460 if self._console_socket is not None:
461 self._console_socket.close()
462 self._console_socket = None
464 def _hard_shutdown(self) -> None:
466 Perform early cleanup, kill the VM, and wait for it to terminate.
468 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
469 waiting for the QEMU process to terminate.
471 self._early_cleanup()
473 self._subp.wait(timeout=60)
475 def _soft_shutdown(self, timeout: Optional[int],
476 has_quit: bool = False) -> None:
478 Perform early cleanup, attempt to gracefully shut down the VM, and wait
481 :param timeout: Timeout in seconds for graceful shutdown.
482 A value of None is an infinite wait.
483 :param has_quit: When True, don't attempt to issue 'quit' QMP command
485 :raise ConnectionReset: On QMP communication errors
486 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
487 the QEMU process to terminate.
489 self._early_cleanup()
491 if self._qmp_connection:
493 # Might raise ConnectionReset
494 self._qmp.cmd('quit')
496 # May raise subprocess.TimeoutExpired
497 self._subp.wait(timeout=timeout)
499 def _do_shutdown(self, timeout: Optional[int],
500 has_quit: bool = False) -> None:
502 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
504 :param timeout: Timeout in seconds for graceful shutdown.
505 A value of None is an infinite wait.
506 :param has_quit: When True, don't attempt to issue 'quit' QMP command
508 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
509 The inner exception will likely be ConnectionReset or
510 subprocess.TimeoutExpired. In rare cases, non-graceful termination
511 may result in its own exceptions, likely subprocess.TimeoutExpired.
514 self._soft_shutdown(timeout, has_quit)
515 except Exception as exc:
516 self._hard_shutdown()
517 raise AbnormalShutdown("Could not perform graceful shutdown") \
520 def shutdown(self, has_quit: bool = False,
522 timeout: Optional[int] = 30) -> None:
524 Terminate the VM (gracefully if possible) and perform cleanup.
525 Cleanup will always be performed.
527 If the VM has not yet been launched, or shutdown(), wait(), or kill()
528 have already been called, this method does nothing.
530 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
531 :param hard: When true, do not attempt graceful shutdown, and
532 suppress the SIGKILL warning log message.
533 :param timeout: Optional timeout in seconds for graceful shutdown.
534 Default 30 seconds, A `None` value is an infinite wait.
536 if not self._launched:
541 self._user_killed = True
542 self._hard_shutdown()
544 self._do_shutdown(timeout, has_quit)
546 self._post_shutdown()
548 def kill(self) -> None:
550 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
552 self.shutdown(hard=True)
554 def wait(self, timeout: Optional[int] = 30) -> None:
556 Wait for the VM to power off and perform post-shutdown cleanup.
558 :param timeout: Optional timeout in seconds. Default 30 seconds.
559 A value of `None` is an infinite wait.
561 self.shutdown(has_quit=True, timeout=timeout)
563 def set_qmp_monitor(self, enabled: bool = True) -> None:
567 @param enabled: if False, qmp monitor options will be removed from
568 the base arguments of the resulting QEMU command
569 line. Default is True.
571 .. note:: Call this function before launch().
573 self._qmp_set = enabled
576 def _qmp(self) -> QEMUMonitorProtocol:
577 if self._qmp_connection is None:
578 raise QEMUMachineError("Attempt to access QMP with no connection")
579 return self._qmp_connection
582 def _qmp_args(cls, conv_keys: bool,
583 args: Dict[str, Any]) -> Dict[str, object]:
585 return {k.replace('_', '-'): v for k, v in args.items()}
589 def qmp(self, cmd: str,
590 args_dict: Optional[Dict[str, object]] = None,
591 conv_keys: Optional[bool] = None,
592 **args: Any) -> QMPMessage:
594 Invoke a QMP command and return the response dict
596 if args_dict is not None:
598 assert conv_keys is None
602 if conv_keys is None:
605 qmp_args = self._qmp_args(conv_keys, args)
606 return self._qmp.cmd(cmd, args=qmp_args)
608 def command(self, cmd: str,
609 conv_keys: bool = True,
610 **args: Any) -> QMPReturnValue:
612 Invoke a QMP command.
613 On success return the response dict.
614 On failure raise an exception.
616 qmp_args = self._qmp_args(conv_keys, args)
617 return self._qmp.command(cmd, **qmp_args)
619 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
621 Poll for one queued QMP events and return it
624 return self._events.pop(0)
625 return self._qmp.pull_event(wait=wait)
627 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
629 Poll for queued QMP events and return a list of dicts
631 events = self._qmp.get_events(wait=wait)
632 events.extend(self._events)
634 self._qmp.clear_events()
638 def event_match(event: Any, match: Optional[Any]) -> bool:
640 Check if an event matches optional match criteria.
642 The match criteria takes the form of a matching subdict. The event is
643 checked to be a superset of the subdict, recursively, with matching
644 values whenever the subdict values are not None.
646 This has a limitation that you cannot explicitly check for None values.
648 Examples, with the subdict queries on the left:
649 - None matches any object.
650 - {"foo": None} matches {"foo": {"bar": 1}}
651 - {"foo": None} matches {"foo": 5}
652 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
653 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
661 if not QEMUMachine.event_match(event[key], match[key]):
667 # either match or event wasn't iterable (not a dict)
668 return bool(match == event)
670 def event_wait(self, name: str,
671 timeout: float = 60.0,
672 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
674 event_wait waits for and returns a named event from QMP with a timeout.
676 name: The event to wait for.
677 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
678 match: Optional match criteria. See event_match for details.
680 return self.events_wait([(name, match)], timeout)
682 def events_wait(self,
683 events: Sequence[Tuple[str, Any]],
684 timeout: float = 60.0) -> Optional[QMPMessage]:
686 events_wait waits for and returns a single named event from QMP.
687 In the case of multiple qualifying events, this function returns the
690 :param events: A sequence of (name, match_criteria) tuples.
691 The match criteria are optional and may be None.
692 See event_match for details.
693 :param timeout: Optional timeout, in seconds.
694 See QEMUMonitorProtocol.pull_event.
696 :raise QMPTimeoutError: If timeout was non-zero and no matching events
698 :return: A QMP event matching the filter criteria.
699 If timeout was 0 and no event matched, None.
701 def _match(event: QMPMessage) -> bool:
702 for name, match in events:
703 if event['event'] == name and self.event_match(event, match):
707 event: Optional[QMPMessage]
709 # Search cached events
710 for event in self._events:
712 self._events.remove(event)
715 # Poll for new events
717 event = self._qmp.pull_event(wait=timeout)
719 # NB: None is only returned when timeout is false-ish.
720 # Timeouts raise QMPTimeoutError instead!
724 self._events.append(event)
728 def get_log(self) -> Optional[str]:
730 After self.shutdown or failed qemu execution, this returns the output
735 def add_args(self, *args: str) -> None:
737 Adds to the list of extra arguments to be given to the QEMU binary
739 self._args.extend(args)
741 def set_machine(self, machine_type: str) -> None:
743 Sets the machine type
745 If set, the machine type will be added to the base arguments
746 of the resulting QEMU command line.
748 self._machine = machine_type
750 def set_console(self,
751 device_type: Optional[str] = None,
752 console_index: int = 0) -> None:
754 Sets the device type for a console device
756 If set, the console device and a backing character device will
757 be added to the base arguments of the resulting QEMU command
760 This is a convenience method that will either use the provided
761 device type, or default to a "-serial chardev:console" command
764 The actual setting of command line arguments will be be done at
765 machine launch time, as it depends on the temporary directory
768 @param device_type: the device type, such as "isa-serial". If
769 None is given (the default value) a "-serial
770 chardev:console" command line argument will
771 be used instead, resorting to the machine's
773 @param console_index: the index of the console device to use.
774 If not zero, the command line will create
775 'index - 1' consoles and connect them to
776 the 'null' backing character device.
778 self._console_set = True
779 self._console_device_type = device_type
780 self._console_index = console_index
783 def console_socket(self) -> socket.socket:
785 Returns a socket connected to the console
787 if self._console_socket is None:
788 self._console_socket = console_socket.ConsoleSocket(
789 self._console_address,
790 file=self._console_log_path,
791 drain=self._drain_console)
792 return self._console_socket
795 def temp_dir(self) -> str:
797 Returns a temporary directory to be used for this machine
799 if self._temp_dir is None:
800 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
801 dir=self._base_temp_dir)
802 return self._temp_dir
805 def log_dir(self) -> str:
807 Returns a directory to be used for writing logs
809 if self._log_dir is None: