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 the remote via SCM_RIGHTS.
218 Exactly one of fd and file_path must be given. If it is
219 file_path, the file will be opened read-only and the new file
220 descriptor will be sent to the remote.
222 if file_path is not None:
224 with open(file_path, "rb") as passfile:
225 fd = passfile.fileno()
226 self._qmp.send_fd_scm(fd)
228 assert fd is not None
229 self._qmp.send_fd_scm(fd)
234 def _remove_if_exists(path: str) -> None:
236 Remove file object at path if it exists
240 except OSError as exception:
241 if exception.errno == errno.ENOENT:
245 def is_running(self) -> bool:
246 """Returns true if the VM is running."""
247 return self._popen is not None and self._popen.poll() is None
250 def _subp(self) -> 'subprocess.Popen[bytes]':
251 if self._popen is None:
252 raise QEMUMachineError('Subprocess pipe not present')
255 def exitcode(self) -> Optional[int]:
256 """Returns the exit code if possible, or None."""
257 if self._popen is None:
259 return self._popen.poll()
261 def get_pid(self) -> Optional[int]:
262 """Returns the PID of the running process, or None."""
263 if not self.is_running():
265 return self._subp.pid
267 def _load_io_log(self) -> None:
268 # Assume that the output encoding of QEMU's terminal output is
269 # defined by our locale. If indeterminate, allow open() to fall
270 # back to the platform default.
271 _, encoding = locale.getlocale()
272 if self._qemu_log_path is not None:
273 with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
274 self._iolog = iolog.read()
277 def _base_args(self) -> List[str]:
278 args = ['-display', 'none', '-vga', 'none']
281 if isinstance(self._monitor_address, tuple):
282 moncdev = "socket,id=mon,host={},port={}".format(
283 *self._monitor_address
286 moncdev = f"socket,id=mon,path={self._monitor_address}"
287 args.extend(['-chardev', moncdev, '-mon',
288 'chardev=mon,mode=control'])
290 if self._machine is not None:
291 args.extend(['-machine', self._machine])
292 for _ in range(self._console_index):
293 args.extend(['-serial', 'null'])
294 if self._console_set:
295 chardev = ('socket,id=console,path=%s,server=on,wait=off' %
296 self._console_address)
297 args.extend(['-chardev', chardev])
298 if self._console_device_type is None:
299 args.extend(['-serial', 'chardev:console'])
301 device = '%s,chardev=console' % self._console_device_type
302 args.extend(['-device', device])
306 def args(self) -> List[str]:
307 """Returns the list of arguments given to the QEMU binary."""
310 def _pre_launch(self) -> None:
311 if self._console_set:
312 self._remove_files.append(self._console_address)
315 if self._remove_monitor_sockfile:
316 assert 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')
330 def _post_launch(self) -> None:
331 if self._qmp_connection:
332 self._qmp.accept(self._qmp_timer)
334 def _close_qemu_log_file(self) -> None:
335 if self._qemu_log_file is not None:
336 self._qemu_log_file.close()
337 self._qemu_log_file = None
339 def _post_shutdown(self) -> None:
341 Called to cleanup the VM instance after the process has exited.
342 May also be called after a failed launch.
344 # Comprehensive reset for the failed launch case:
345 self._early_cleanup()
347 if self._qmp_connection:
349 self._qmp_connection = None
351 self._close_qemu_log_file()
355 self._qemu_log_path = None
357 if self._temp_dir is not None:
358 shutil.rmtree(self._temp_dir)
359 self._temp_dir = None
361 while len(self._remove_files) > 0:
362 self._remove_if_exists(self._remove_files.pop())
364 exitcode = self.exitcode()
365 if (exitcode is not None and exitcode < 0
366 and not (self._user_killed and exitcode == -signal.SIGKILL)):
367 msg = 'qemu received signal %i; command: "%s"'
368 if self._qemu_full_args:
369 command = ' '.join(self._qemu_full_args)
372 LOG.warning(msg, -int(exitcode), command)
374 self._user_killed = False
375 self._launched = False
377 def launch(self) -> None:
379 Launch the VM and make sure we cleanup and expose the
380 command line/output in case of exception
384 raise QEMUMachineError('VM already launched')
387 self._qemu_full_args = ()
390 self._launched = True
392 self._post_shutdown()
394 LOG.debug('Error launching VM')
395 if self._qemu_full_args:
396 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
398 LOG.debug('Output: %r', self._iolog)
401 def _launch(self) -> None:
403 Launch the VM and establish a QMP connection
406 self._qemu_full_args = tuple(
412 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
414 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
415 # pylint: disable=consider-using-with
416 self._popen = subprocess.Popen(self._qemu_full_args,
417 stdin=subprocess.DEVNULL,
418 stdout=self._qemu_log_file,
419 stderr=subprocess.STDOUT,
424 def _early_cleanup(self) -> None:
426 Perform any cleanup that needs to happen before the VM exits.
428 May be invoked by both soft and hard shutdown in failover scenarios.
429 Called additionally by _post_shutdown for comprehensive cleanup.
431 # If we keep the console socket open, we may deadlock waiting
432 # for QEMU to exit, while QEMU is waiting for the socket to
434 if self._console_socket is not None:
435 self._console_socket.close()
436 self._console_socket = None
438 def _hard_shutdown(self) -> None:
440 Perform early cleanup, kill the VM, and wait for it to terminate.
442 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
443 waiting for the QEMU process to terminate.
445 self._early_cleanup()
447 self._subp.wait(timeout=60)
449 def _soft_shutdown(self, timeout: Optional[int],
450 has_quit: bool = False) -> None:
452 Perform early cleanup, attempt to gracefully shut down the VM, and wait
455 :param timeout: Timeout in seconds for graceful shutdown.
456 A value of None is an infinite wait.
457 :param has_quit: When True, don't attempt to issue 'quit' QMP command
459 :raise ConnectionReset: On QMP communication errors
460 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
461 the QEMU process to terminate.
463 self._early_cleanup()
465 if self._qmp_connection:
467 # Might raise ConnectionReset
468 self._qmp.cmd('quit')
470 # May raise subprocess.TimeoutExpired
471 self._subp.wait(timeout=timeout)
473 def _do_shutdown(self, timeout: Optional[int],
474 has_quit: bool = False) -> None:
476 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
478 :param timeout: Timeout in seconds for graceful shutdown.
479 A value of None is an infinite wait.
480 :param has_quit: When True, don't attempt to issue 'quit' QMP command
482 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
483 The inner exception will likely be ConnectionReset or
484 subprocess.TimeoutExpired. In rare cases, non-graceful termination
485 may result in its own exceptions, likely subprocess.TimeoutExpired.
488 self._soft_shutdown(timeout, has_quit)
489 except Exception as exc:
490 self._hard_shutdown()
491 raise AbnormalShutdown("Could not perform graceful shutdown") \
494 def shutdown(self, has_quit: bool = False,
496 timeout: Optional[int] = 30) -> None:
498 Terminate the VM (gracefully if possible) and perform cleanup.
499 Cleanup will always be performed.
501 If the VM has not yet been launched, or shutdown(), wait(), or kill()
502 have already been called, this method does nothing.
504 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
505 :param hard: When true, do not attempt graceful shutdown, and
506 suppress the SIGKILL warning log message.
507 :param timeout: Optional timeout in seconds for graceful shutdown.
508 Default 30 seconds, A `None` value is an infinite wait.
510 if not self._launched:
515 self._user_killed = True
516 self._hard_shutdown()
518 self._do_shutdown(timeout, has_quit)
520 self._post_shutdown()
522 def kill(self) -> None:
524 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
526 self.shutdown(hard=True)
528 def wait(self, timeout: Optional[int] = 30) -> None:
530 Wait for the VM to power off and perform post-shutdown cleanup.
532 :param timeout: Optional timeout in seconds. Default 30 seconds.
533 A value of `None` is an infinite wait.
535 self.shutdown(has_quit=True, timeout=timeout)
537 def set_qmp_monitor(self, enabled: bool = True) -> None:
541 @param enabled: if False, qmp monitor options will be removed from
542 the base arguments of the resulting QEMU command
543 line. Default is True.
545 .. note:: Call this function before launch().
547 self._qmp_set = enabled
550 def _qmp(self) -> QEMUMonitorProtocol:
551 if self._qmp_connection is None:
552 raise QEMUMachineError("Attempt to access QMP with no connection")
553 return self._qmp_connection
556 def _qmp_args(cls, conv_keys: bool,
557 args: Dict[str, Any]) -> Dict[str, object]:
559 return {k.replace('_', '-'): v for k, v in args.items()}
563 def qmp(self, cmd: str,
564 args_dict: Optional[Dict[str, object]] = None,
565 conv_keys: Optional[bool] = None,
566 **args: Any) -> QMPMessage:
568 Invoke a QMP command and return the response dict
570 if args_dict is not None:
572 assert conv_keys is None
576 if conv_keys is None:
579 qmp_args = self._qmp_args(conv_keys, args)
580 return self._qmp.cmd(cmd, args=qmp_args)
582 def command(self, cmd: str,
583 conv_keys: bool = True,
584 **args: Any) -> QMPReturnValue:
586 Invoke a QMP command.
587 On success return the response dict.
588 On failure raise an exception.
590 qmp_args = self._qmp_args(conv_keys, args)
591 return self._qmp.command(cmd, **qmp_args)
593 def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
595 Poll for one queued QMP events and return it
598 return self._events.pop(0)
599 return self._qmp.pull_event(wait=wait)
601 def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
603 Poll for queued QMP events and return a list of dicts
605 events = self._qmp.get_events(wait=wait)
606 events.extend(self._events)
611 def event_match(event: Any, match: Optional[Any]) -> bool:
613 Check if an event matches optional match criteria.
615 The match criteria takes the form of a matching subdict. The event is
616 checked to be a superset of the subdict, recursively, with matching
617 values whenever the subdict values are not None.
619 This has a limitation that you cannot explicitly check for None values.
621 Examples, with the subdict queries on the left:
622 - None matches any object.
623 - {"foo": None} matches {"foo": {"bar": 1}}
624 - {"foo": None} matches {"foo": 5}
625 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
626 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
634 if not QEMUMachine.event_match(event[key], match[key]):
640 # either match or event wasn't iterable (not a dict)
641 return bool(match == event)
643 def event_wait(self, name: str,
644 timeout: float = 60.0,
645 match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
647 event_wait waits for and returns a named event from QMP with a timeout.
649 name: The event to wait for.
650 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
651 match: Optional match criteria. See event_match for details.
653 return self.events_wait([(name, match)], timeout)
655 def events_wait(self,
656 events: Sequence[Tuple[str, Any]],
657 timeout: float = 60.0) -> Optional[QMPMessage]:
659 events_wait waits for and returns a single named event from QMP.
660 In the case of multiple qualifying events, this function returns the
663 :param events: A sequence of (name, match_criteria) tuples.
664 The match criteria are optional and may be None.
665 See event_match for details.
666 :param timeout: Optional timeout, in seconds.
667 See QEMUMonitorProtocol.pull_event.
669 :raise QMPTimeoutError: If timeout was non-zero and no matching events
671 :return: A QMP event matching the filter criteria.
672 If timeout was 0 and no event matched, None.
674 def _match(event: QMPMessage) -> bool:
675 for name, match in events:
676 if event['event'] == name and self.event_match(event, match):
680 event: Optional[QMPMessage]
682 # Search cached events
683 for event in self._events:
685 self._events.remove(event)
688 # Poll for new events
690 event = self._qmp.pull_event(wait=timeout)
692 # NB: None is only returned when timeout is false-ish.
693 # Timeouts raise QMPTimeoutError instead!
697 self._events.append(event)
701 def get_log(self) -> Optional[str]:
703 After self.shutdown or failed qemu execution, this returns the output
708 def add_args(self, *args: str) -> None:
710 Adds to the list of extra arguments to be given to the QEMU binary
712 self._args.extend(args)
714 def set_machine(self, machine_type: str) -> None:
716 Sets the machine type
718 If set, the machine type will be added to the base arguments
719 of the resulting QEMU command line.
721 self._machine = machine_type
723 def set_console(self,
724 device_type: Optional[str] = None,
725 console_index: int = 0) -> None:
727 Sets the device type for a console device
729 If set, the console device and a backing character device will
730 be added to the base arguments of the resulting QEMU command
733 This is a convenience method that will either use the provided
734 device type, or default to a "-serial chardev:console" command
737 The actual setting of command line arguments will be be done at
738 machine launch time, as it depends on the temporary directory
741 @param device_type: the device type, such as "isa-serial". If
742 None is given (the default value) a "-serial
743 chardev:console" command line argument will
744 be used instead, resorting to the machine's
746 @param console_index: the index of the console device to use.
747 If not zero, the command line will create
748 'index - 1' consoles and connect them to
749 the 'null' backing character device.
751 self._console_set = True
752 self._console_device_type = device_type
753 self._console_index = console_index
756 def console_socket(self) -> socket.socket:
758 Returns a socket connected to the console
760 if self._console_socket is None:
761 self._console_socket = console_socket.ConsoleSocket(
762 self._console_address,
763 file=self._console_log_path,
764 drain=self._drain_console)
765 return self._console_socket
768 def temp_dir(self) -> str:
770 Returns a temporary directory to be used for this machine
772 if self._temp_dir is None:
773 self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
774 dir=self._base_temp_dir)
775 return self._temp_dir
778 def log_dir(self) -> str:
780 Returns a directory to be used for writing logs
782 if self._log_dir is None: