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.
27 from typing
import Optional
, Type
28 from types
import TracebackType
32 LOG
= logging
.getLogger(__name__
)
35 class QEMUMachineError(Exception):
37 Exception called when an error in QEMUMachine happens.
41 class QEMUMachineAddDeviceError(QEMUMachineError
):
43 Exception raised when a request to add a device can not be fulfilled
45 The failures are caused by limitations, lack of information or conflicting
46 requests on the QEMUMachine methods. This exception does not represent
47 failures reported by the QEMU binary itself.
51 class MonitorResponseError(qmp
.QMPError
):
53 Represents erroneous QMP monitor reply
55 def __init__(self
, reply
):
57 desc
= reply
["error"]["desc"]
60 super().__init
__(desc
)
68 Use this object as a context manager to ensure
69 the QEMU process terminates::
71 with VM(binary) as vm:
73 # vm is guaranteed to be shut down here
76 def __init__(self
, binary
, args
=None, wrapper
=None, name
=None,
77 test_dir
="/var/tmp", monitor_address
=None,
78 socket_scm_helper
=None, sock_dir
=None):
80 Initialize a QEMUMachine
82 @param binary: path to the qemu binary
83 @param args: list of extra arguments
84 @param wrapper: list of arguments used as prefix to qemu binary
85 @param name: prefix for socket and log file names (default: qemu-PID)
86 @param test_dir: where to create socket and log file
87 @param monitor_address: address for QMP monitor
88 @param socket_scm_helper: helper program, required for send_fd_scm()
89 @note: Qemu process is not started until launch() is used.
96 name
= "qemu-%d" % os
.getpid()
100 self
._monitor
_address
= monitor_address
101 self
._vm
_monitor
= None
102 self
._qemu
_log
_path
= None
103 self
._qemu
_log
_file
= None
105 self
._binary
= binary
106 self
._args
= list(args
) # Force copy args in case we modify them
107 self
._wrapper
= wrapper
110 self
._socket
_scm
_helper
= socket_scm_helper
111 self
._qmp
_set
= True # Enable QMP monitor by default.
113 self
._qemu
_full
_args
= None
114 self
._test
_dir
= test_dir
115 self
._temp
_dir
= None
116 self
._sock
_dir
= sock_dir
117 self
._launched
= False
119 self
._console
_index
= 0
120 self
._console
_set
= False
121 self
._console
_device
_type
= None
122 self
._console
_address
= None
123 self
._console
_socket
= None
124 self
._remove
_files
= []
130 exc_type
: Optional
[Type
[BaseException
]],
131 exc_val
: Optional
[BaseException
],
132 exc_tb
: Optional
[TracebackType
]) -> None:
135 def add_monitor_null(self
):
137 This can be used to add an unused monitor instance.
139 self
._args
.append('-monitor')
140 self
._args
.append('null')
142 def add_fd(self
, fd
, fdset
, opaque
, opts
=''):
144 Pass a file descriptor to the VM
146 options
= ['fd=%d' % fd
,
148 'opaque=%s' % opaque
]
152 # This did not exist before 3.4, but since then it is
153 # mandatory for our purpose
154 if hasattr(os
, 'set_inheritable'):
155 os
.set_inheritable(fd
, True)
157 self
._args
.append('-add-fd')
158 self
._args
.append(','.join(options
))
161 def send_fd_scm(self
, fd
=None, file_path
=None):
163 Send an fd or file_path to socket_scm_helper.
165 Exactly one of fd and file_path must be given.
166 If it is file_path, the helper will open that file and pass its own fd.
168 # In iotest.py, the qmp should always use unix socket.
169 assert self
._qmp
.is_scm_available()
170 if self
._socket
_scm
_helper
is None:
171 raise QEMUMachineError("No path to socket_scm_helper set")
172 if not os
.path
.exists(self
._socket
_scm
_helper
):
173 raise QEMUMachineError("%s does not exist" %
174 self
._socket
_scm
_helper
)
176 # This did not exist before 3.4, but since then it is
177 # mandatory for our purpose
178 if hasattr(os
, 'set_inheritable'):
179 os
.set_inheritable(self
._qmp
.get_sock_fd(), True)
181 os
.set_inheritable(fd
, True)
183 fd_param
= ["%s" % self
._socket
_scm
_helper
,
184 "%d" % self
._qmp
.get_sock_fd()]
186 if file_path
is not None:
188 fd_param
.append(file_path
)
190 assert fd
is not None
191 fd_param
.append(str(fd
))
193 devnull
= open(os
.path
.devnull
, 'rb')
194 proc
= subprocess
.Popen(
195 fd_param
, stdin
=devnull
, stdout
=subprocess
.PIPE
,
196 stderr
=subprocess
.STDOUT
, close_fds
=False
198 output
= proc
.communicate()[0]
202 return proc
.returncode
205 def _remove_if_exists(path
):
207 Remove file object at path if it exists
211 except OSError as exception
:
212 if exception
.errno
== errno
.ENOENT
:
216 def is_running(self
):
217 """Returns true if the VM is running."""
218 return self
._popen
is not None and self
._popen
.poll() is None
221 """Returns the exit code if possible, or None."""
222 if self
._popen
is None:
224 return self
._popen
.poll()
227 """Returns the PID of the running process, or None."""
228 if not self
.is_running():
230 return self
._popen
.pid
232 def _load_io_log(self
):
233 if self
._qemu
_log
_path
is not None:
234 with
open(self
._qemu
_log
_path
, "r") as iolog
:
235 self
._iolog
= iolog
.read()
237 def _base_args(self
):
238 args
= ['-display', 'none', '-vga', 'none']
240 if isinstance(self
._monitor
_address
, tuple):
241 moncdev
= "socket,id=mon,host=%s,port=%s" % (
242 self
._monitor
_address
[0],
243 self
._monitor
_address
[1])
245 moncdev
= 'socket,id=mon,path=%s' % self
._vm
_monitor
246 args
.extend(['-chardev', moncdev
, '-mon',
247 'chardev=mon,mode=control'])
248 if self
._machine
is not None:
249 args
.extend(['-machine', self
._machine
])
250 for _
in range(self
._console
_index
):
251 args
.extend(['-serial', 'null'])
252 if self
._console
_set
:
253 self
._console
_address
= os
.path
.join(self
._sock
_dir
,
254 self
._name
+ "-console.sock")
255 self
._remove
_files
.append(self
._console
_address
)
256 chardev
= ('socket,id=console,path=%s,server,nowait' %
257 self
._console
_address
)
258 args
.extend(['-chardev', chardev
])
259 if self
._console
_device
_type
is None:
260 args
.extend(['-serial', 'chardev:console'])
262 device
= '%s,chardev=console' % self
._console
_device
_type
263 args
.extend(['-device', device
])
266 def _pre_launch(self
):
267 self
._temp
_dir
= tempfile
.mkdtemp(dir=self
._test
_dir
)
268 self
._qemu
_log
_path
= os
.path
.join(self
._temp
_dir
, self
._name
+ ".log")
269 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
272 if self
._monitor
_address
is not None:
273 self
._vm
_monitor
= self
._monitor
_address
275 self
._vm
_monitor
= os
.path
.join(self
._sock
_dir
,
276 self
._name
+ "-monitor.sock")
277 self
._remove
_files
.append(self
._vm
_monitor
)
278 self
._qmp
= qmp
.QEMUMonitorProtocol(self
._vm
_monitor
, server
=True,
281 def _post_launch(self
):
285 def _post_shutdown(self
):
286 if self
._qemu
_log
_file
is not None:
287 self
._qemu
_log
_file
.close()
288 self
._qemu
_log
_file
= None
290 self
._qemu
_log
_path
= None
292 if self
._temp
_dir
is not None:
293 shutil
.rmtree(self
._temp
_dir
)
294 self
._temp
_dir
= None
296 while len(self
._remove
_files
) > 0:
297 self
._remove
_if
_exists
(self
._remove
_files
.pop())
301 Launch the VM and make sure we cleanup and expose the
302 command line/output in case of exception
306 raise QEMUMachineError('VM already launched')
309 self
._qemu
_full
_args
= None
312 self
._launched
= True
316 LOG
.debug('Error launching VM')
317 if self
._qemu
_full
_args
:
318 LOG
.debug('Command: %r', ' '.join(self
._qemu
_full
_args
))
320 LOG
.debug('Output: %r', self
._iolog
)
325 Launch the VM and establish a QMP connection
327 devnull
= open(os
.path
.devnull
, 'rb')
329 self
._qemu
_full
_args
= (self
._wrapper
+ [self
._binary
] +
330 self
._base
_args
() + self
._args
)
331 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
332 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
334 stdout
=self
._qemu
_log
_file
,
335 stderr
=subprocess
.STDOUT
,
342 Wait for the VM to power off
348 self
._post
_shutdown
()
350 def shutdown(self
, has_quit
=False, hard
=False):
352 Terminate the VM and clean up
354 # If we keep the console socket open, we may deadlock waiting
355 # for QEMU to exit, while QEMU is waiting for the socket to
357 if self
._console
_socket
is not None:
358 self
._console
_socket
.close()
359 self
._console
_socket
= None
361 if self
.is_running():
367 self
._qmp
.cmd('quit')
369 self
._popen
.wait(timeout
=3)
375 self
._post
_shutdown
()
377 exitcode
= self
.exitcode()
378 if exitcode
is not None and exitcode
< 0 and \
379 not (exitcode
== -9 and hard
):
380 msg
= 'qemu received signal %i: %s'
381 if self
._qemu
_full
_args
:
382 command
= ' '.join(self
._qemu
_full
_args
)
385 LOG
.warning(msg
, -int(exitcode
), command
)
387 self
._launched
= False
390 self
.shutdown(hard
=True)
392 def set_qmp_monitor(self
, enabled
=True):
396 @param enabled: if False, qmp monitor options will be removed from
397 the base arguments of the resulting QEMU command
398 line. Default is True.
399 @note: call this function before launch().
404 self
._qmp
_set
= False
407 def qmp(self
, cmd
, conv_keys
=True, **args
):
409 Invoke a QMP command and return the response dict
412 for key
, value
in args
.items():
414 qmp_args
[key
.replace('_', '-')] = value
416 qmp_args
[key
] = value
418 return self
._qmp
.cmd(cmd
, args
=qmp_args
)
420 def command(self
, cmd
, conv_keys
=True, **args
):
422 Invoke a QMP command.
423 On success return the response dict.
424 On failure raise an exception.
426 reply
= self
.qmp(cmd
, conv_keys
, **args
)
428 raise qmp
.QMPError("Monitor is closed")
430 raise MonitorResponseError(reply
)
431 return reply
["return"]
433 def get_qmp_event(self
, wait
=False):
435 Poll for one queued QMP events and return it
438 return self
._events
.pop(0)
439 return self
._qmp
.pull_event(wait
=wait
)
441 def get_qmp_events(self
, wait
=False):
443 Poll for queued QMP events and return a list of dicts
445 events
= self
._qmp
.get_events(wait
=wait
)
446 events
.extend(self
._events
)
448 self
._qmp
.clear_events()
452 def event_match(event
, match
=None):
454 Check if an event matches optional match criteria.
456 The match criteria takes the form of a matching subdict. The event is
457 checked to be a superset of the subdict, recursively, with matching
458 values whenever the subdict values are not None.
460 This has a limitation that you cannot explicitly check for None values.
462 Examples, with the subdict queries on the left:
463 - None matches any object.
464 - {"foo": None} matches {"foo": {"bar": 1}}
465 - {"foo": None} matches {"foo": 5}
466 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
467 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
475 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
481 # either match or event wasn't iterable (not a dict)
482 return match
== event
484 def event_wait(self
, name
, timeout
=60.0, match
=None):
486 event_wait waits for and returns a named event from QMP with a timeout.
488 name: The event to wait for.
489 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
490 match: Optional match criteria. See event_match for details.
492 return self
.events_wait([(name
, match
)], timeout
)
494 def events_wait(self
, events
, timeout
=60.0):
496 events_wait waits for and returns a named event
497 from QMP with a timeout.
499 events: a sequence of (name, match_criteria) tuples.
500 The match criteria are optional and may be None.
501 See event_match for details.
502 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
505 for name
, match
in events
:
506 if event
['event'] == name
and self
.event_match(event
, match
):
510 # Search cached events
511 for event
in self
._events
:
513 self
._events
.remove(event
)
516 # Poll for new events
518 event
= self
._qmp
.pull_event(wait
=timeout
)
521 self
._events
.append(event
)
527 After self.shutdown or failed qemu execution, this returns the output
532 def add_args(self
, *args
):
534 Adds to the list of extra arguments to be given to the QEMU binary
536 self
._args
.extend(args
)
538 def set_machine(self
, machine_type
):
540 Sets the machine type
542 If set, the machine type will be added to the base arguments
543 of the resulting QEMU command line.
545 self
._machine
= machine_type
547 def set_console(self
, device_type
=None, console_index
=0):
549 Sets the device type for a console device
551 If set, the console device and a backing character device will
552 be added to the base arguments of the resulting QEMU command
555 This is a convenience method that will either use the provided
556 device type, or default to a "-serial chardev:console" command
559 The actual setting of command line arguments will be be done at
560 machine launch time, as it depends on the temporary directory
563 @param device_type: the device type, such as "isa-serial". If
564 None is given (the default value) a "-serial
565 chardev:console" command line argument will
566 be used instead, resorting to the machine's
568 @param console_index: the index of the console device to use.
569 If not zero, the command line will create
570 'index - 1' consoles and connect them to
571 the 'null' backing character device.
573 self
._console
_set
= True
574 self
._console
_device
_type
= device_type
575 self
._console
_index
= console_index
578 def console_socket(self
):
580 Returns a socket connected to the console
582 if self
._console
_socket
is None:
583 self
._console
_socket
= socket
.socket(socket
.AF_UNIX
,
585 self
._console
_socket
.connect(self
._console
_address
)
586 return self
._console
_socket