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.
12 # Fam Zheng <famz@redhat.com>
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 . import console_socket
, qmp
42 from .qmp
import QMPMessage
, QMPReturnValue
, SocketAddrT
45 LOG
= logging
.getLogger(__name__
)
48 class QEMUMachineError(Exception):
50 Exception called when an error in QEMUMachine happens.
54 class QEMUMachineAddDeviceError(QEMUMachineError
):
56 Exception raised when a request to add a device can not be fulfilled
58 The failures are caused by limitations, lack of information or conflicting
59 requests on the QEMUMachine methods. This exception does not represent
60 failures reported by the QEMU binary itself.
64 class AbnormalShutdown(QEMUMachineError
):
66 Exception raised when a graceful shutdown was requested, but not performed.
74 Use this object as a context manager to ensure
75 the QEMU process terminates::
77 with VM(binary) as vm:
79 # vm is guaranteed to be shut down here
84 args
: Sequence
[str] = (),
85 wrapper
: Sequence
[str] = (),
86 name
: Optional
[str] = None,
87 test_dir
: str = "/var/tmp",
88 monitor_address
: Optional
[SocketAddrT
] = None,
89 socket_scm_helper
: Optional
[str] = None,
90 sock_dir
: Optional
[str] = None,
91 drain_console
: bool = False,
92 console_log
: Optional
[str] = None):
94 Initialize a QEMUMachine
96 @param binary: path to the qemu binary
97 @param args: list of extra arguments
98 @param wrapper: list of arguments used as prefix to qemu binary
99 @param name: prefix for socket and log file names (default: qemu-PID)
100 @param test_dir: where to create socket and log file
101 @param monitor_address: address for QMP monitor
102 @param socket_scm_helper: helper program, required for send_fd_scm()
103 @param sock_dir: where to create socket (overrides test_dir for sock)
104 @param drain_console: (optional) True to drain console socket to buffer
105 @param console_log: (optional) path to console log file
106 @note: Qemu process is not started until launch() is used.
108 # Direct user configuration
110 self
._binary
= binary
111 self
._args
= list(args
)
112 self
._wrapper
= wrapper
114 self
._name
= name
or "qemu-%d" % os
.getpid()
115 self
._test
_dir
= test_dir
116 self
._sock
_dir
= sock_dir
or self
._test
_dir
117 self
._socket
_scm
_helper
= socket_scm_helper
119 if monitor_address
is not None:
120 self
._monitor
_address
= monitor_address
121 self
._remove
_monitor
_sockfile
= False
123 self
._monitor
_address
= os
.path
.join(
124 self
._sock
_dir
, f
"{self._name}-monitor.sock"
126 self
._remove
_monitor
_sockfile
= True
128 self
._console
_log
_path
= console_log
129 if self
._console
_log
_path
:
130 # In order to log the console, buffering needs to be enabled.
131 self
._drain
_console
= True
133 self
._drain
_console
= drain_console
136 self
._qemu
_log
_path
: Optional
[str] = None
137 self
._qemu
_log
_file
: Optional
[BinaryIO
] = None
138 self
._popen
: Optional
['subprocess.Popen[bytes]'] = None
139 self
._events
: List
[QMPMessage
] = []
140 self
._iolog
: Optional
[str] = None
141 self
._qmp
_set
= True # Enable QMP monitor by default.
142 self
._qmp
_connection
: Optional
[qmp
.QEMUMonitorProtocol
] = None
143 self
._qemu
_full
_args
: Tuple
[str, ...] = ()
144 self
._temp
_dir
: Optional
[str] = None
145 self
._launched
= False
146 self
._machine
: Optional
[str] = None
147 self
._console
_index
= 0
148 self
._console
_set
= False
149 self
._console
_device
_type
: Optional
[str] = None
150 self
._console
_address
= os
.path
.join(
151 self
._sock
_dir
, f
"{self._name}-console.sock"
153 self
._console
_socket
: Optional
[socket
.socket
] = None
154 self
._remove
_files
: List
[str] = []
155 self
._user
_killed
= False
157 def __enter__(self
) -> 'QEMUMachine':
161 exc_type
: Optional
[Type
[BaseException
]],
162 exc_val
: Optional
[BaseException
],
163 exc_tb
: Optional
[TracebackType
]) -> None:
166 def add_monitor_null(self
) -> None:
168 This can be used to add an unused monitor instance.
170 self
._args
.append('-monitor')
171 self
._args
.append('null')
173 def add_fd(self
, fd
: int, fdset
: int,
174 opaque
: str, opts
: str = '') -> 'QEMUMachine':
176 Pass a file descriptor to the VM
178 options
= ['fd=%d' % fd
,
180 'opaque=%s' % opaque
]
184 # This did not exist before 3.4, but since then it is
185 # mandatory for our purpose
186 if hasattr(os
, 'set_inheritable'):
187 os
.set_inheritable(fd
, True)
189 self
._args
.append('-add-fd')
190 self
._args
.append(','.join(options
))
193 def send_fd_scm(self
, fd
: Optional
[int] = None,
194 file_path
: Optional
[str] = None) -> int:
196 Send an fd or file_path to socket_scm_helper.
198 Exactly one of fd and file_path must be given.
199 If it is file_path, the helper will open that file and pass its own fd.
201 # In iotest.py, the qmp should always use unix socket.
202 assert self
._qmp
.is_scm_available()
203 if self
._socket
_scm
_helper
is None:
204 raise QEMUMachineError("No path to socket_scm_helper set")
205 if not os
.path
.exists(self
._socket
_scm
_helper
):
206 raise QEMUMachineError("%s does not exist" %
207 self
._socket
_scm
_helper
)
209 # This did not exist before 3.4, but since then it is
210 # mandatory for our purpose
211 if hasattr(os
, 'set_inheritable'):
212 os
.set_inheritable(self
._qmp
.get_sock_fd(), True)
214 os
.set_inheritable(fd
, True)
216 fd_param
= ["%s" % self
._socket
_scm
_helper
,
217 "%d" % self
._qmp
.get_sock_fd()]
219 if file_path
is not None:
221 fd_param
.append(file_path
)
223 assert fd
is not None
224 fd_param
.append(str(fd
))
226 devnull
= open(os
.path
.devnull
, 'rb')
227 proc
= subprocess
.Popen(
228 fd_param
, stdin
=devnull
, stdout
=subprocess
.PIPE
,
229 stderr
=subprocess
.STDOUT
, close_fds
=False
231 output
= proc
.communicate()[0]
235 return proc
.returncode
238 def _remove_if_exists(path
: str) -> None:
240 Remove file object at path if it exists
244 except OSError as exception
:
245 if exception
.errno
== errno
.ENOENT
:
249 def is_running(self
) -> bool:
250 """Returns true if the VM is running."""
251 return self
._popen
is not None and self
._popen
.poll() is None
254 def _subp(self
) -> 'subprocess.Popen[bytes]':
255 if self
._popen
is None:
256 raise QEMUMachineError('Subprocess pipe not present')
259 def exitcode(self
) -> Optional
[int]:
260 """Returns the exit code if possible, or None."""
261 if self
._popen
is None:
263 return self
._popen
.poll()
265 def get_pid(self
) -> Optional
[int]:
266 """Returns the PID of the running process, or None."""
267 if not self
.is_running():
269 return self
._subp
.pid
271 def _load_io_log(self
) -> None:
272 if self
._qemu
_log
_path
is not None:
273 with
open(self
._qemu
_log
_path
, "r") 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
])
305 def _pre_launch(self
) -> None:
306 self
._temp
_dir
= tempfile
.mkdtemp(prefix
="qemu-machine-",
308 self
._qemu
_log
_path
= os
.path
.join(self
._temp
_dir
, self
._name
+ ".log")
309 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
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
= qmp
.QEMUMonitorProtocol(
319 self
._monitor
_address
,
324 def _post_launch(self
) -> None:
325 if self
._qmp
_connection
:
328 def _post_shutdown(self
) -> None:
330 Called to cleanup the VM instance after the process has exited.
331 May also be called after a failed launch.
333 # Comprehensive reset for the failed launch case:
334 self
._early
_cleanup
()
336 if self
._qmp
_connection
:
338 self
._qmp
_connection
= None
342 if self
._qemu
_log
_file
is not None:
343 self
._qemu
_log
_file
.close()
344 self
._qemu
_log
_file
= None
346 self
._qemu
_log
_path
= None
348 if self
._temp
_dir
is not None:
349 shutil
.rmtree(self
._temp
_dir
)
350 self
._temp
_dir
= None
352 while len(self
._remove
_files
) > 0:
353 self
._remove
_if
_exists
(self
._remove
_files
.pop())
355 exitcode
= self
.exitcode()
356 if (exitcode
is not None and exitcode
< 0
357 and not (self
._user
_killed
and exitcode
== -signal
.SIGKILL
)):
358 msg
= 'qemu received signal %i; command: "%s"'
359 if self
._qemu
_full
_args
:
360 command
= ' '.join(self
._qemu
_full
_args
)
363 LOG
.warning(msg
, -int(exitcode
), command
)
365 self
._user
_killed
= False
366 self
._launched
= False
368 def launch(self
) -> None:
370 Launch the VM and make sure we cleanup and expose the
371 command line/output in case of exception
375 raise QEMUMachineError('VM already launched')
378 self
._qemu
_full
_args
= ()
381 self
._launched
= True
383 self
._post
_shutdown
()
385 LOG
.debug('Error launching VM')
386 if self
._qemu
_full
_args
:
387 LOG
.debug('Command: %r', ' '.join(self
._qemu
_full
_args
))
389 LOG
.debug('Output: %r', self
._iolog
)
392 def _launch(self
) -> None:
394 Launch the VM and establish a QMP connection
396 devnull
= open(os
.path
.devnull
, 'rb')
398 self
._qemu
_full
_args
= tuple(
404 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
405 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
407 stdout
=self
._qemu
_log
_file
,
408 stderr
=subprocess
.STDOUT
,
413 def _early_cleanup(self
) -> None:
415 Perform any cleanup that needs to happen before the VM exits.
417 May be invoked by both soft and hard shutdown in failover scenarios.
418 Called additionally by _post_shutdown for comprehensive cleanup.
420 # If we keep the console socket open, we may deadlock waiting
421 # for QEMU to exit, while QEMU is waiting for the socket to
423 if self
._console
_socket
is not None:
424 self
._console
_socket
.close()
425 self
._console
_socket
= None
427 def _hard_shutdown(self
) -> None:
429 Perform early cleanup, kill the VM, and wait for it to terminate.
431 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
432 waiting for the QEMU process to terminate.
434 self
._early
_cleanup
()
436 self
._subp
.wait(timeout
=60)
438 def _soft_shutdown(self
, timeout
: Optional
[int],
439 has_quit
: bool = False) -> None:
441 Perform early cleanup, attempt to gracefully shut down the VM, and wait
444 :param timeout: Timeout in seconds for graceful shutdown.
445 A value of None is an infinite wait.
446 :param has_quit: When True, don't attempt to issue 'quit' QMP command
448 :raise ConnectionReset: On QMP communication errors
449 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
450 the QEMU process to terminate.
452 self
._early
_cleanup
()
454 if self
._qmp
_connection
:
456 # Might raise ConnectionReset
457 self
._qmp
.cmd('quit')
459 # May raise subprocess.TimeoutExpired
460 self
._subp
.wait(timeout
=timeout
)
462 def _do_shutdown(self
, timeout
: Optional
[int],
463 has_quit
: bool = False) -> None:
465 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
467 :param timeout: Timeout in seconds for graceful shutdown.
468 A value of None is an infinite wait.
469 :param has_quit: When True, don't attempt to issue 'quit' QMP command
471 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
472 The inner exception will likely be ConnectionReset or
473 subprocess.TimeoutExpired. In rare cases, non-graceful termination
474 may result in its own exceptions, likely subprocess.TimeoutExpired.
477 self
._soft
_shutdown
(timeout
, has_quit
)
478 except Exception as exc
:
479 self
._hard
_shutdown
()
480 raise AbnormalShutdown("Could not perform graceful shutdown") \
483 def shutdown(self
, has_quit
: bool = False,
485 timeout
: Optional
[int] = 30) -> None:
487 Terminate the VM (gracefully if possible) and perform cleanup.
488 Cleanup will always be performed.
490 If the VM has not yet been launched, or shutdown(), wait(), or kill()
491 have already been called, this method does nothing.
493 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
494 :param hard: When true, do not attempt graceful shutdown, and
495 suppress the SIGKILL warning log message.
496 :param timeout: Optional timeout in seconds for graceful shutdown.
497 Default 30 seconds, A `None` value is an infinite wait.
499 if not self
._launched
:
504 self
._user
_killed
= True
505 self
._hard
_shutdown
()
507 self
._do
_shutdown
(timeout
, has_quit
)
509 self
._post
_shutdown
()
511 def kill(self
) -> None:
513 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
515 self
.shutdown(hard
=True)
517 def wait(self
, timeout
: Optional
[int] = 30) -> None:
519 Wait for the VM to power off and perform post-shutdown cleanup.
521 :param timeout: Optional timeout in seconds. Default 30 seconds.
522 A value of `None` is an infinite wait.
524 self
.shutdown(has_quit
=True, timeout
=timeout
)
526 def set_qmp_monitor(self
, enabled
: bool = True) -> None:
530 @param enabled: if False, qmp monitor options will be removed from
531 the base arguments of the resulting QEMU command
532 line. Default is True.
533 @note: call this function before launch().
535 self
._qmp
_set
= enabled
538 def _qmp(self
) -> qmp
.QEMUMonitorProtocol
:
539 if self
._qmp
_connection
is None:
540 raise QEMUMachineError("Attempt to access QMP with no connection")
541 return self
._qmp
_connection
544 def _qmp_args(cls
, _conv_keys
: bool = True, **args
: Any
) -> Dict
[str, Any
]:
546 for key
, value
in args
.items():
548 qmp_args
[key
.replace('_', '-')] = value
550 qmp_args
[key
] = value
553 def qmp(self
, cmd
: str,
554 conv_keys
: bool = True,
555 **args
: Any
) -> QMPMessage
:
557 Invoke a QMP command and return the response dict
559 qmp_args
= self
._qmp
_args
(conv_keys
, **args
)
560 return self
._qmp
.cmd(cmd
, args
=qmp_args
)
562 def command(self
, cmd
: str,
563 conv_keys
: bool = True,
564 **args
: Any
) -> QMPReturnValue
:
566 Invoke a QMP command.
567 On success return the response dict.
568 On failure raise an exception.
570 qmp_args
= self
._qmp
_args
(conv_keys
, **args
)
571 return self
._qmp
.command(cmd
, **qmp_args
)
573 def get_qmp_event(self
, wait
: bool = False) -> Optional
[QMPMessage
]:
575 Poll for one queued QMP events and return it
578 return self
._events
.pop(0)
579 return self
._qmp
.pull_event(wait
=wait
)
581 def get_qmp_events(self
, wait
: bool = False) -> List
[QMPMessage
]:
583 Poll for queued QMP events and return a list of dicts
585 events
= self
._qmp
.get_events(wait
=wait
)
586 events
.extend(self
._events
)
588 self
._qmp
.clear_events()
592 def event_match(event
: Any
, match
: Optional
[Any
]) -> bool:
594 Check if an event matches optional match criteria.
596 The match criteria takes the form of a matching subdict. The event is
597 checked to be a superset of the subdict, recursively, with matching
598 values whenever the subdict values are not None.
600 This has a limitation that you cannot explicitly check for None values.
602 Examples, with the subdict queries on the left:
603 - None matches any object.
604 - {"foo": None} matches {"foo": {"bar": 1}}
605 - {"foo": None} matches {"foo": 5}
606 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
607 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
615 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
621 # either match or event wasn't iterable (not a dict)
622 return bool(match
== event
)
624 def event_wait(self
, name
: str,
625 timeout
: float = 60.0,
626 match
: Optional
[QMPMessage
] = None) -> Optional
[QMPMessage
]:
628 event_wait waits for and returns a named event from QMP with a timeout.
630 name: The event to wait for.
631 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
632 match: Optional match criteria. See event_match for details.
634 return self
.events_wait([(name
, match
)], timeout
)
636 def events_wait(self
,
637 events
: Sequence
[Tuple
[str, Any
]],
638 timeout
: float = 60.0) -> Optional
[QMPMessage
]:
640 events_wait waits for and returns a single named event from QMP.
641 In the case of multiple qualifying events, this function returns the
644 :param events: A sequence of (name, match_criteria) tuples.
645 The match criteria are optional and may be None.
646 See event_match for details.
647 :param timeout: Optional timeout, in seconds.
648 See QEMUMonitorProtocol.pull_event.
650 :raise QMPTimeoutError: If timeout was non-zero and no matching events
652 :return: A QMP event matching the filter criteria.
653 If timeout was 0 and no event matched, None.
655 def _match(event
: QMPMessage
) -> bool:
656 for name
, match
in events
:
657 if event
['event'] == name
and self
.event_match(event
, match
):
661 event
: Optional
[QMPMessage
]
663 # Search cached events
664 for event
in self
._events
:
666 self
._events
.remove(event
)
669 # Poll for new events
671 event
= self
._qmp
.pull_event(wait
=timeout
)
673 # NB: None is only returned when timeout is false-ish.
674 # Timeouts raise QMPTimeoutError instead!
678 self
._events
.append(event
)
682 def get_log(self
) -> Optional
[str]:
684 After self.shutdown or failed qemu execution, this returns the output
689 def add_args(self
, *args
: str) -> None:
691 Adds to the list of extra arguments to be given to the QEMU binary
693 self
._args
.extend(args
)
695 def set_machine(self
, machine_type
: str) -> None:
697 Sets the machine type
699 If set, the machine type will be added to the base arguments
700 of the resulting QEMU command line.
702 self
._machine
= machine_type
704 def set_console(self
,
705 device_type
: Optional
[str] = None,
706 console_index
: int = 0) -> None:
708 Sets the device type for a console device
710 If set, the console device and a backing character device will
711 be added to the base arguments of the resulting QEMU command
714 This is a convenience method that will either use the provided
715 device type, or default to a "-serial chardev:console" command
718 The actual setting of command line arguments will be be done at
719 machine launch time, as it depends on the temporary directory
722 @param device_type: the device type, such as "isa-serial". If
723 None is given (the default value) a "-serial
724 chardev:console" command line argument will
725 be used instead, resorting to the machine's
727 @param console_index: the index of the console device to use.
728 If not zero, the command line will create
729 'index - 1' consoles and connect them to
730 the 'null' backing character device.
732 self
._console
_set
= True
733 self
._console
_device
_type
= device_type
734 self
._console
_index
= console_index
737 def console_socket(self
) -> socket
.socket
:
739 Returns a socket connected to the console
741 if self
._console
_socket
is None:
742 self
._console
_socket
= console_socket
.ConsoleSocket(
743 self
._console
_address
,
744 file=self
._console
_log
_path
,
745 drain
=self
._drain
_console
)
746 return self
._console
_socket