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,nowait' %
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(dir=self
._test
_dir
)
307 self
._qemu
_log
_path
= os
.path
.join(self
._temp
_dir
, self
._name
+ ".log")
308 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
310 if self
._console
_set
:
311 self
._remove
_files
.append(self
._console
_address
)
314 if self
._remove
_monitor
_sockfile
:
315 assert isinstance(self
._monitor
_address
, str)
316 self
._remove
_files
.append(self
._monitor
_address
)
317 self
._qmp
_connection
= qmp
.QEMUMonitorProtocol(
318 self
._monitor
_address
,
323 def _post_launch(self
) -> None:
324 if self
._qmp
_connection
:
327 def _post_shutdown(self
) -> None:
329 Called to cleanup the VM instance after the process has exited.
330 May also be called after a failed launch.
332 # Comprehensive reset for the failed launch case:
333 self
._early
_cleanup
()
335 if self
._qmp
_connection
:
337 self
._qmp
_connection
= None
341 if self
._qemu
_log
_file
is not None:
342 self
._qemu
_log
_file
.close()
343 self
._qemu
_log
_file
= None
345 self
._qemu
_log
_path
= None
347 if self
._temp
_dir
is not None:
348 shutil
.rmtree(self
._temp
_dir
)
349 self
._temp
_dir
= None
351 while len(self
._remove
_files
) > 0:
352 self
._remove
_if
_exists
(self
._remove
_files
.pop())
354 exitcode
= self
.exitcode()
355 if (exitcode
is not None and exitcode
< 0
356 and not (self
._user
_killed
and exitcode
== -signal
.SIGKILL
)):
357 msg
= 'qemu received signal %i; command: "%s"'
358 if self
._qemu
_full
_args
:
359 command
= ' '.join(self
._qemu
_full
_args
)
362 LOG
.warning(msg
, -int(exitcode
), command
)
364 self
._user
_killed
= False
365 self
._launched
= False
367 def launch(self
) -> None:
369 Launch the VM and make sure we cleanup and expose the
370 command line/output in case of exception
374 raise QEMUMachineError('VM already launched')
377 self
._qemu
_full
_args
= ()
380 self
._launched
= True
382 self
._post
_shutdown
()
384 LOG
.debug('Error launching VM')
385 if self
._qemu
_full
_args
:
386 LOG
.debug('Command: %r', ' '.join(self
._qemu
_full
_args
))
388 LOG
.debug('Output: %r', self
._iolog
)
391 def _launch(self
) -> None:
393 Launch the VM and establish a QMP connection
395 devnull
= open(os
.path
.devnull
, 'rb')
397 self
._qemu
_full
_args
= tuple(
403 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
404 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
406 stdout
=self
._qemu
_log
_file
,
407 stderr
=subprocess
.STDOUT
,
412 def _early_cleanup(self
) -> None:
414 Perform any cleanup that needs to happen before the VM exits.
416 May be invoked by both soft and hard shutdown in failover scenarios.
417 Called additionally by _post_shutdown for comprehensive cleanup.
419 # If we keep the console socket open, we may deadlock waiting
420 # for QEMU to exit, while QEMU is waiting for the socket to
422 if self
._console
_socket
is not None:
423 self
._console
_socket
.close()
424 self
._console
_socket
= None
426 def _hard_shutdown(self
) -> None:
428 Perform early cleanup, kill the VM, and wait for it to terminate.
430 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
431 waiting for the QEMU process to terminate.
433 self
._early
_cleanup
()
435 self
._subp
.wait(timeout
=60)
437 def _soft_shutdown(self
, timeout
: Optional
[int],
438 has_quit
: bool = False) -> None:
440 Perform early cleanup, attempt to gracefully shut down the VM, and wait
443 :param timeout: Timeout in seconds for graceful shutdown.
444 A value of None is an infinite wait.
445 :param has_quit: When True, don't attempt to issue 'quit' QMP command
447 :raise ConnectionReset: On QMP communication errors
448 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
449 the QEMU process to terminate.
451 self
._early
_cleanup
()
453 if self
._qmp
_connection
:
455 # Might raise ConnectionReset
456 self
._qmp
.cmd('quit')
458 # May raise subprocess.TimeoutExpired
459 self
._subp
.wait(timeout
=timeout
)
461 def _do_shutdown(self
, timeout
: Optional
[int],
462 has_quit
: bool = False) -> None:
464 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
466 :param timeout: Timeout in seconds for graceful shutdown.
467 A value of None is an infinite wait.
468 :param has_quit: When True, don't attempt to issue 'quit' QMP command
470 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
471 The inner exception will likely be ConnectionReset or
472 subprocess.TimeoutExpired. In rare cases, non-graceful termination
473 may result in its own exceptions, likely subprocess.TimeoutExpired.
476 self
._soft
_shutdown
(timeout
, has_quit
)
477 except Exception as exc
:
478 self
._hard
_shutdown
()
479 raise AbnormalShutdown("Could not perform graceful shutdown") \
482 def shutdown(self
, has_quit
: bool = False,
484 timeout
: Optional
[int] = 30) -> None:
486 Terminate the VM (gracefully if possible) and perform cleanup.
487 Cleanup will always be performed.
489 If the VM has not yet been launched, or shutdown(), wait(), or kill()
490 have already been called, this method does nothing.
492 :param has_quit: When true, do not attempt to issue 'quit' QMP command.
493 :param hard: When true, do not attempt graceful shutdown, and
494 suppress the SIGKILL warning log message.
495 :param timeout: Optional timeout in seconds for graceful shutdown.
496 Default 30 seconds, A `None` value is an infinite wait.
498 if not self
._launched
:
503 self
._user
_killed
= True
504 self
._hard
_shutdown
()
506 self
._do
_shutdown
(timeout
, has_quit
)
508 self
._post
_shutdown
()
510 def kill(self
) -> None:
512 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
514 self
.shutdown(hard
=True)
516 def wait(self
, timeout
: Optional
[int] = 30) -> None:
518 Wait for the VM to power off and perform post-shutdown cleanup.
520 :param timeout: Optional timeout in seconds. Default 30 seconds.
521 A value of `None` is an infinite wait.
523 self
.shutdown(has_quit
=True, timeout
=timeout
)
525 def set_qmp_monitor(self
, enabled
: bool = True) -> None:
529 @param enabled: if False, qmp monitor options will be removed from
530 the base arguments of the resulting QEMU command
531 line. Default is True.
532 @note: call this function before launch().
534 self
._qmp
_set
= enabled
537 def _qmp(self
) -> qmp
.QEMUMonitorProtocol
:
538 if self
._qmp
_connection
is None:
539 raise QEMUMachineError("Attempt to access QMP with no connection")
540 return self
._qmp
_connection
543 def _qmp_args(cls
, _conv_keys
: bool = True, **args
: Any
) -> Dict
[str, Any
]:
545 for key
, value
in args
.items():
547 qmp_args
[key
.replace('_', '-')] = value
549 qmp_args
[key
] = value
552 def qmp(self
, cmd
: str,
553 conv_keys
: bool = True,
554 **args
: Any
) -> QMPMessage
:
556 Invoke a QMP command and return the response dict
558 qmp_args
= self
._qmp
_args
(conv_keys
, **args
)
559 return self
._qmp
.cmd(cmd
, args
=qmp_args
)
561 def command(self
, cmd
: str,
562 conv_keys
: bool = True,
563 **args
: Any
) -> QMPReturnValue
:
565 Invoke a QMP command.
566 On success return the response dict.
567 On failure raise an exception.
569 qmp_args
= self
._qmp
_args
(conv_keys
, **args
)
570 return self
._qmp
.command(cmd
, **qmp_args
)
572 def get_qmp_event(self
, wait
: bool = False) -> Optional
[QMPMessage
]:
574 Poll for one queued QMP events and return it
577 return self
._events
.pop(0)
578 return self
._qmp
.pull_event(wait
=wait
)
580 def get_qmp_events(self
, wait
: bool = False) -> List
[QMPMessage
]:
582 Poll for queued QMP events and return a list of dicts
584 events
= self
._qmp
.get_events(wait
=wait
)
585 events
.extend(self
._events
)
587 self
._qmp
.clear_events()
591 def event_match(event
: Any
, match
: Optional
[Any
]) -> bool:
593 Check if an event matches optional match criteria.
595 The match criteria takes the form of a matching subdict. The event is
596 checked to be a superset of the subdict, recursively, with matching
597 values whenever the subdict values are not None.
599 This has a limitation that you cannot explicitly check for None values.
601 Examples, with the subdict queries on the left:
602 - None matches any object.
603 - {"foo": None} matches {"foo": {"bar": 1}}
604 - {"foo": None} matches {"foo": 5}
605 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
606 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
614 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
620 # either match or event wasn't iterable (not a dict)
621 return bool(match
== event
)
623 def event_wait(self
, name
: str,
624 timeout
: float = 60.0,
625 match
: Optional
[QMPMessage
] = None) -> Optional
[QMPMessage
]:
627 event_wait waits for and returns a named event from QMP with a timeout.
629 name: The event to wait for.
630 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
631 match: Optional match criteria. See event_match for details.
633 return self
.events_wait([(name
, match
)], timeout
)
635 def events_wait(self
,
636 events
: Sequence
[Tuple
[str, Any
]],
637 timeout
: float = 60.0) -> Optional
[QMPMessage
]:
639 events_wait waits for and returns a single named event from QMP.
640 In the case of multiple qualifying events, this function returns the
643 :param events: A sequence of (name, match_criteria) tuples.
644 The match criteria are optional and may be None.
645 See event_match for details.
646 :param timeout: Optional timeout, in seconds.
647 See QEMUMonitorProtocol.pull_event.
649 :raise QMPTimeoutError: If timeout was non-zero and no matching events
651 :return: A QMP event matching the filter criteria.
652 If timeout was 0 and no event matched, None.
654 def _match(event
: QMPMessage
) -> bool:
655 for name
, match
in events
:
656 if event
['event'] == name
and self
.event_match(event
, match
):
660 event
: Optional
[QMPMessage
]
662 # Search cached events
663 for event
in self
._events
:
665 self
._events
.remove(event
)
668 # Poll for new events
670 event
= self
._qmp
.pull_event(wait
=timeout
)
672 # NB: None is only returned when timeout is false-ish.
673 # Timeouts raise QMPTimeoutError instead!
677 self
._events
.append(event
)
681 def get_log(self
) -> Optional
[str]:
683 After self.shutdown or failed qemu execution, this returns the output
688 def add_args(self
, *args
: str) -> None:
690 Adds to the list of extra arguments to be given to the QEMU binary
692 self
._args
.extend(args
)
694 def set_machine(self
, machine_type
: str) -> None:
696 Sets the machine type
698 If set, the machine type will be added to the base arguments
699 of the resulting QEMU command line.
701 self
._machine
= machine_type
703 def set_console(self
,
704 device_type
: Optional
[str] = None,
705 console_index
: int = 0) -> None:
707 Sets the device type for a console device
709 If set, the console device and a backing character device will
710 be added to the base arguments of the resulting QEMU command
713 This is a convenience method that will either use the provided
714 device type, or default to a "-serial chardev:console" command
717 The actual setting of command line arguments will be be done at
718 machine launch time, as it depends on the temporary directory
721 @param device_type: the device type, such as "isa-serial". If
722 None is given (the default value) a "-serial
723 chardev:console" command line argument will
724 be used instead, resorting to the machine's
726 @param console_index: the index of the console device to use.
727 If not zero, the command line will create
728 'index - 1' consoles and connect them to
729 the 'null' backing character device.
731 self
._console
_set
= True
732 self
._console
_device
_type
= device_type
733 self
._console
_index
= console_index
736 def console_socket(self
) -> socket
.socket
:
738 Returns a socket connected to the console
740 if self
._console
_socket
is None:
741 self
._console
_socket
= console_socket
.ConsoleSocket(
742 self
._console
_address
,
743 file=self
._console
_log
_path
,
744 drain
=self
._drain
_console
)
745 return self
._console
_socket