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
29 from types import TracebackType
41 from qemu.qmp import ( # pylint: disable=import-error
48 from . import console_socket
51 LOG = logging.getLogger(__name__)
54 class QEMUMachineError(Exception):
56 Exception called when an error in QEMUMachine happens.
60 class QEMUMachineAddDeviceError(QEMUMachineError):
62 Exception raised when a request to add a device can not be fulfilled
64 The failures are caused by limitations, lack of information or conflicting
65 requests on the QEMUMachine methods. This exception does not represent
66 failures reported by the QEMU binary itself.
70 class AbnormalShutdown(QEMUMachineError):
72 Exception raised when a graceful shutdown was requested, but not performed.
80 Use this object as a context manager to ensure
81 the QEMU process terminates::
83 with VM(binary) as vm:
85 # vm is guaranteed to be shut down here
87 # pylint: disable=too-many-instance-attributes, too-many-public-methods
91 args: Sequence[str] = (),
92 wrapper: Sequence[str] = (),
93 name: Optional[str] = None,
94 base_temp_dir: str = "/var/tmp",
95 monitor_address: Optional[SocketAddrT] = None,
96 socket_scm_helper: Optional[str] = None,
97 sock_dir: Optional[str] = None,
98 drain_console: bool = False,
99 console_log: Optional[str] = None,
100 log_dir: Optional[str] = None,
101 qmp_timer: Optional[float] = None):
103 Initialize a QEMUMachine
105 @param binary: path to the qemu binary
106 @param args: list of extra arguments
107 @param wrapper: list of arguments used as prefix to qemu binary
108 @param name: prefix for socket and log file names (default: qemu-PID)
109 @param base_temp_dir: default location where temp files are created
110 @param monitor_address: address for QMP monitor
111 @param socket_scm_helper: helper program, required for send_fd_scm()
112 @param sock_dir: where to create socket (defaults to base_temp_dir)
113 @param drain_console: (optional) True to drain console socket to buffer
114 @param console_log: (optional) path to console log file
115 @param log_dir: where to create and keep log files
116 @param qmp_timer: (optional) default QMP socket timeout
117 @note: Qemu process is not started until launch() is used.
119 # pylint: disable=too-many-arguments
121 # Direct user configuration
123 self._binary = binary
124 self._args = list(args)
125 self._wrapper = wrapper
126 self._qmp_timer = qmp_timer
128 self._name = name or "qemu-%d" % os.getpid()
129 self._base_temp_dir = base_temp_dir
130 self._sock_dir = sock_dir or self._base_temp_dir
131 self._log_dir = log_dir
132 self._socket_scm_helper = socket_scm_helper
134 if monitor_address is not None:
135 self._monitor_address = monitor_address
136 self._remove_monitor_sockfile = False
138 self._monitor_address = os.path.join(
139 self._sock_dir, f"{self._name}-monitor.sock"
141 self._remove_monitor_sockfile = True
143 self._console_log_path = console_log
144 if self._console_log_path:
145 # In order to log the console, buffering needs to be enabled.
146 self._drain_console = True
148 self._drain_console = drain_console
151 self._qemu_log_path: Optional[str] = None
152 self._qemu_log_file: Optional[BinaryIO] = None
153 self._popen: Optional['subprocess.Popen[bytes]'] = None
154 self._events: List[QMPMessage] = []
155 self._iolog: Optional[str] = None
156 self._qmp_set = True # Enable QMP monitor by default.
157 self._qmp_connection: Optional[QEMUMonitorProtocol] = None
158 self._qemu_full_args: Tuple[str, ...] = ()
159 self._temp_dir: Optional[str] = None
160 self._launched = False
161 self._machine: Optional[str] = None
162 self._console_index = 0
163 self._console_set = False
164 self._console_device_type: Optional[str] = None
165 self._console_address = os.path.join(
166 self._sock_dir, f"{self._name}-console.sock"
168 self._console_socket: Optional[socket.socket] = None
169 self._remove_files: List[str] = []
170 self._user_killed = False
172 def __enter__(self) -> 'QEMUMachine':
176 exc_type: Optional[Type[BaseException]],
177 exc_val: Optional[BaseException],
178 exc_tb: Optional[TracebackType]) -> None:
181 def add_monitor_null(self) -> None:
183 This can be used to add an unused monitor instance.
185 self._args.append('-monitor')
186 self._args.append('null')
188 def add_fd(self, fd: int, fdset: int,
189 opaque: str, opts: str = '') -> 'QEMUMachine':
191 Pass a file descriptor to the VM
193 options = ['fd=%d' % fd,
195 'opaque=%s' % opaque]
199 # This did not exist before 3.4, but since then it is
200 # mandatory for our purpose
201 if hasattr(os, 'set_inheritable'):
202 os.set_inheritable(fd, True)
204 self._args.append('-add-fd')
205 self._args.append(','.join(options))
208 def send_fd_scm(self, fd: Optional[int] = None,
209 file_path: Optional[str] = None) -> int:
211 Send an fd or file_path to socket_scm_helper.
213 Exactly one of fd and file_path must be given.
214 If it is file_path, the helper will open that file and pass its own fd.
216 # In iotest.py, the qmp should always use unix socket.
217 assert self._qmp.is_scm_available()
218 if self._socket_scm_helper is None:
219 raise QEMUMachineError("No path to socket_scm_helper set")
220 if not os.path.exists(self._socket_scm_helper):
221 raise QEMUMachineError("%s does not exist" %
222 self._socket_scm_helper)
224 # This did not exist before 3.4, but since then it is
225 # mandatory for our purpose
226 if hasattr(os, 'set_inheritable'):
227 os.set_inheritable(self._qmp.get_sock_fd(), True)
229 os.set_inheritable(fd, True)
231 fd_param = ["%s" % self._socket_scm_helper,
232 "%d" % self._qmp.get_sock_fd()]
234 if file_path is not None:
236 fd_param.append(file_path)
238 assert fd is not None
239 fd_param.append(str(fd))
241 proc = subprocess.run(
243 stdin=subprocess.DEVNULL,
244 stdout=subprocess.PIPE,
245 stderr=subprocess.STDOUT,
250 LOG.debug(proc.stdout)
252 return proc.returncode
255 def _remove_if_exists(path: str) -> None:
257 Remove file object at path if it exists
261 except OSError as exception:
262 if exception.errno == errno.ENOENT:
266 def is_running(self) -> bool:
267 """Returns true if the VM is running."""
268 return self._popen is not None and self._popen.poll() is None
271 def _subp(self) -> 'subprocess.Popen[bytes]':
272 if self._popen is None:
273 raise QEMUMachineError('Subprocess pipe not present')
276 def exitcode(self) -> Optional[int]:
277 """Returns the exit code if possible, or None."""
278 if self._popen is None:
280 return self._popen.poll()
282 def get_pid(self) -> Optional[int]:
283 """Returns the PID of the running process, or None."""
284 if not self.is_running():
286 return self._subp.pid
288 def _load_io_log(self) -> None:
289 if self._qemu_log_path is not None:
290 with open(self._qemu_log_path, "r") as iolog:
291 self._iolog = iolog.read()
294 def _base_args(self) -> List[str]:
295 args = ['-display', 'none', '-vga', 'none']
298 if isinstance(self._monitor_address, tuple):
299 moncdev = "socket,id=mon,host={},port={}".format(
300 *self._monitor_address
303 moncdev = f"socket,id=mon,path={self._monitor_address}"
304 args.extend(['-chardev', moncdev, '-mon',
305 'chardev=mon,mode=control'])
307 if self._machine is not None:
308 args.extend(['-machine', self._machine])
309 for _ in range(self._console_index):
310 args.extend(['-serial', 'null'])
311 if self._console_set:
312 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
313 self._console_address)
314 args.extend(['-chardev', chardev])
315 if self._console_device_type is None:
316 args.extend(['-serial', 'chardev:console'])
318 device = '%s,chardev=console' % self._console_device_type
319 args.extend(['-device', device])
323 def args(self) -> List[str]:
324 """Returns the list of arguments given to the QEMU binary."""
327 def _pre_launch(self) -> None:
328 if self._console_set:
329 self._remove_files.append(self._console_address)
332 if self._remove_monitor_sockfile:
333 assert isinstance(self._monitor_address, str)
334 self._remove_files.append(self._monitor_address)
335 self._qmp_connection = QEMUMonitorProtocol(
336 self._monitor_address,
341 # NOTE: Make sure any opened resources are *definitely* freed in
343 # pylint: disable=consider-using-with
344 self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
345 self._qemu_log_file = open(self._qemu_log_path, 'wb')
347 def _post_launch(self) -> None:
348 if self._qmp_connection:
349 self._qmp.accept(self._qmp_timer)
351 def _close_qemu_log_file(self) -> None:
352 if self._qemu_log_file is not None:
353 self._qemu_log_file.close()
354 self._qemu_log_file = None
356 def _post_shutdown(self) -> None:
358 Called to cleanup the VM instance after the process has exited.
359 May also be called after a failed launch.
361 # Comprehensive reset for the failed launch case:
362 self._early_cleanup()
364 if self._qmp_connection:
366 self._qmp_connection = None
368 self._close_qemu_log_file()
372 self._qemu_log_path = None
374 if self._temp_dir is not None:
375 shutil.rmtree(self._temp_dir)
376 self._temp_dir = None
378 while len(self._remove_files) > 0:
379 self._remove_if_exists(self._remove_files.pop())
381 exitcode = self.exitcode()
382 if (exitcode is not None and exitcode < 0
383 and not (self._user_killed and exitcode == -signal.SIGKILL)):
384 msg = 'qemu received signal %i; command: "%s"'
385 if self._qemu_full_args:
386 command = ' '.join(self._qemu_full_args)
389 LOG.warning(msg, -int(exitcode), command)
391 self._user_killed = False
392 self._launched = False
394 def launch(self) -> None:
396 Launch the VM and make sure we cleanup and expose the
397 command line/output in case of exception
401 raise QEMUMachineError('VM already launched')
404 self._qemu_full_args = ()
407 self._launched = True
409 self._post_shutdown()
411 LOG.debug('Error launching VM')
412 if self._qemu_full_args:
413 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
415 LOG.debug('Output: %r', self._iolog)
418 def _launch(self) -> None:
420 Launch the VM and establish a QMP connection
423 self._qemu_full_args = tuple(
429 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
431 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
432 # pylint: disable=consider-using-with
433 self._popen = subprocess.Popen(self._qemu_full_args,
434 stdin=subprocess.DEVNULL,
435 stdout=self._qemu_log_file,
436 stderr=subprocess.STDOUT,
441 def _early_cleanup(self) -> None:
443 Perform any cleanup that needs to happen before the VM exits.
445 May be invoked by both soft and hard shutdown in failover scenarios.
446 Called additionally by _post_shutdown for comprehensive cleanup.
448 # If we keep the console socket open, we may deadlock waiting
449 # for QEMU to exit, while QEMU is waiting for the socket to
451 if self._console_socket is not None:
452 self._console_socket.close()
453 self._console_socket = None
455 def _hard_shutdown(self) -> None:
457 Perform early cleanup, kill the VM, and wait for it to terminate.
459 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
460 waiting for the QEMU process to terminate.
462 self._early_cleanup()
464 self._subp.wait(timeout=60)
466 def _soft_shutdown(self, timeout: Optional[int],
467 has_quit: bool = False) -> None:
469 Perform early cleanup, attempt to gracefully shut down the VM, and wait
472 :param timeout: Timeout in seconds for graceful shutdown.
473 A value of None is an infinite wait.
474 :param has_quit: When True, don't attempt to issue 'quit' QMP command
476 :raise ConnectionReset: On QMP communication errors
477 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
478 the QEMU process to terminate.
480 self._early_cleanup()
482 if self._qmp_connection:
484 # Might raise ConnectionReset
485 self._qmp.cmd('quit')
487 # May raise subprocess.TimeoutExpired
488 self._subp.wait(timeout=timeout)
490 def _do_shutdown(self, timeout: Optional[int],
491 has_quit: bool = False) -> None:
493 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
495 :param timeout: Timeout in seconds for graceful shutdown.
496 A value of None is an infinite wait.
497 :param has_quit: When True, don't attempt to issue 'quit' QMP command
499 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
500 The inner exception will likely be ConnectionReset or
501 subprocess.TimeoutExpired. In rare cases, non-graceful termination
502 may result in its own exceptions, likely subprocess.TimeoutExpired.
505 self._soft_shutdown(timeout, has_quit)
506 except Exception as exc:
507 self._hard_shutdown()
508 raise AbnormalShutdown("Could not perform graceful shutdown") \
511 def shutdown(self, has_quit: bool = False,
513 timeout: Optional[int] = 30) -> None:
515 Terminate the VM (gracefully if possible) and perform cleanup.
516 Cleanup will always be performed.
518 If the VM has not yet been launched, or shutdown(), wait(), or kill()
519 have already been called, this method does nothing.
521 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
522 :param hard: When true, do not attempt graceful shutdown, and
523 suppress the SIGKILL warning log message.
524 :param timeout: Optional timeout in seconds for graceful shutdown.
525 Default 30 seconds, A `None` value is an infinite wait.
527 if not self._launched:
532 self._user_killed = True
533 self._hard_shutdown()
535 self._do_shutdown(timeout, has_quit)
537 self._post_shutdown()
539 def kill(self) -> None:
541 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
543 self.shutdown(hard=True)
545 def wait(self, timeout: Optional[int] = 30) -> None:
547 Wait for the VM to power off and perform post-shutdown cleanup.
549 :param timeout: Optional timeout in seconds. Default 30 seconds.
550 A value of `None` is an infinite wait.
552 self.shutdown(has_quit=True, timeout=timeout)
554 def set_qmp_monitor(self, enabled: bool = True) -> None:
558 @param enabled: if False, qmp monitor options will be removed from
559 the base arguments of the resulting QEMU command
560 line. Default is True.
562 .. note:: Call this function before launch().
564 self._qmp_set = enabled
567 def _qmp(self) -> QEMUMonitorProtocol:
568 if self._qmp_connection is None:
569 raise QEMUMachineError("Attempt to access QMP with no connection")
570 return self._qmp_connection
573 def _qmp_args(cls, conv_keys: bool,
574 args: Dict[str, Any]) -> Dict[str, object]:
576 return {k.replace('_', '-'): v for k, v in args.items()}
580 def qmp(self, cmd: str,
581 conv_keys: bool = True,
582 **args: Any) -> QMPMessage:
584 Invoke a QMP command and return the response dict
586 qmp_args = self._qmp_args(conv_keys, args)
587 return self._qmp.cmd(cmd, args=qmp_args)
589 def command(self, cmd: str,
590 conv_keys: bool = True,
591 **args: Any) -> QMPReturnValue:
593 Invoke a QMP command.
594 On success return the response dict.
595 On failure raise an exception.
597 qmp_args = self._qmp_args(conv_keys, args)
598 return self._qmp.command(cmd, **qmp_args)
600 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
602 Poll for one queued QMP events and return it
605 return self._events.pop(0)
606 return self._qmp.pull_event(wait=wait)
608 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
610 Poll for queued QMP events and return a list of dicts
612 events = self._qmp.get_events(wait=wait)
613 events.extend(self._events)
615 self._qmp.clear_events()
619 def event_match(event: Any, match: Optional[Any]) -> bool:
621 Check if an event matches optional match criteria.
623 The match criteria takes the form of a matching subdict. The event is
624 checked to be a superset of the subdict, recursively, with matching
625 values whenever the subdict values are not None.
627 This has a limitation that you cannot explicitly check for None values.
629 Examples, with the subdict queries on the left:
630 - None matches any object.
631 - {"foo": None} matches {"foo": {"bar": 1}}
632 - {"foo": None} matches {"foo": 5}
633 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
634 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
642 if not QEMUMachine.event_match(event[key], match[key]):
648 # either match or event wasn't iterable (not a dict)
649 return bool(match == event)
651 def event_wait(self, name: str,
652 timeout: float = 60.0,
653 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
655 event_wait waits for and returns a named event from QMP with a timeout.
657 name: The event to wait for.
658 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
659 match: Optional match criteria. See event_match for details.
661 return self.events_wait([(name, match)], timeout)
663 def events_wait(self,
664 events: Sequence[Tuple[str, Any]],
665 timeout: float = 60.0) -> Optional[QMPMessage]:
667 events_wait waits for and returns a single named event from QMP.
668 In the case of multiple qualifying events, this function returns the
671 :param events: A sequence of (name, match_criteria) tuples.
672 The match criteria are optional and may be None.
673 See event_match for details.
674 :param timeout: Optional timeout, in seconds.
675 See QEMUMonitorProtocol.pull_event.
677 :raise QMPTimeoutError: If timeout was non-zero and no matching events
679 :return: A QMP event matching the filter criteria.
680 If timeout was 0 and no event matched, None.
682 def _match(event: QMPMessage) -> bool:
683 for name, match in events:
684 if event['event'] == name and self.event_match(event, match):
688 event: Optional[QMPMessage]
690 # Search cached events
691 for event in self._events:
693 self._events.remove(event)
696 # Poll for new events
698 event = self._qmp.pull_event(wait=timeout)
700 # NB: None is only returned when timeout is false-ish.
701 # Timeouts raise QMPTimeoutError instead!
705 self._events.append(event)
709 def get_log(self) -> Optional[str]:
711 After self.shutdown or failed qemu execution, this returns the output
716 def add_args(self, *args: str) -> None:
718 Adds to the list of extra arguments to be given to the QEMU binary
720 self._args.extend(args)
722 def set_machine(self, machine_type: str) -> None:
724 Sets the machine type
726 If set, the machine type will be added to the base arguments
727 of the resulting QEMU command line.
729 self._machine = machine_type
731 def set_console(self,
732 device_type: Optional[str] = None,
733 console_index: int = 0) -> None:
735 Sets the device type for a console device
737 If set, the console device and a backing character device will
738 be added to the base arguments of the resulting QEMU command
741 This is a convenience method that will either use the provided
742 device type, or default to a "-serial chardev:console" command
745 The actual setting of command line arguments will be be done at
746 machine launch time, as it depends on the temporary directory
749 @param device_type: the device type, such as "isa-serial". If
750 None is given (the default value) a "-serial
751 chardev:console" command line argument will
752 be used instead, resorting to the machine's
754 @param console_index: the index of the console device to use.
755 If not zero, the command line will create
756 'index - 1' consoles and connect them to
757 the 'null' backing character device.
759 self._console_set = True
760 self._console_device_type = device_type
761 self._console_index = console_index
764 def console_socket(self) -> socket.socket:
766 Returns a socket connected to the console
768 if self._console_socket is None:
769 self._console_socket = console_socket.ConsoleSocket(
770 self._console_address,
771 file=self._console_log_path,
772 drain=self._drain_console)
773 return self._console_socket
776 def temp_dir(self) -> str:
778 Returns a temporary directory to be used for this machine
780 if self._temp_dir is None:
781 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
782 dir=self._base_temp_dir)
783 return self._temp_dir
786 def log_dir(self) -> str:
788 Returns a directory to be used for writing logs
790 if self._log_dir is None: