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.
30 LOG
= logging
.getLogger(__name__
)
32 class QEMUMachineError(Exception):
34 Exception called when an error in QEMUMachine happens.
38 class QEMUMachineAddDeviceError(QEMUMachineError
):
40 Exception raised when a request to add a device can not be fulfilled
42 The failures are caused by limitations, lack of information or conflicting
43 requests on the QEMUMachine methods. This exception does not represent
44 failures reported by the QEMU binary itself.
48 class MonitorResponseError(qmp
.QMPError
):
50 Represents erroneous QMP monitor reply
52 def __init__(self
, reply
):
54 desc
= reply
["error"]["desc"]
57 super(MonitorResponseError
, self
).__init
__(desc
)
61 class QEMUMachine(object):
65 Use this object as a context manager to ensure the QEMU process terminates::
67 with VM(binary) as vm:
69 # vm is guaranteed to be shut down here
72 def __init__(self
, binary
, args
=None, wrapper
=None, name
=None,
73 test_dir
="/var/tmp", monitor_address
=None,
74 socket_scm_helper
=None, sock_dir
=None):
76 Initialize a QEMUMachine
78 @param binary: path to the qemu binary
79 @param args: list of extra arguments
80 @param wrapper: list of arguments used as prefix to qemu binary
81 @param name: prefix for socket and log file names (default: qemu-PID)
82 @param test_dir: where to create socket and log file
83 @param monitor_address: address for QMP monitor
84 @param socket_scm_helper: helper program, required for send_fd_scm()
85 @note: Qemu process is not started until launch() is used.
92 name
= "qemu-%d" % os
.getpid()
96 self
._monitor
_address
= monitor_address
97 self
._vm
_monitor
= None
98 self
._qemu
_log
_path
= None
99 self
._qemu
_log
_file
= None
101 self
._binary
= binary
102 self
._args
= list(args
) # Force copy args in case we modify them
103 self
._wrapper
= wrapper
106 self
._socket
_scm
_helper
= socket_scm_helper
107 self
._qmp
_set
= True # Enable QMP monitor by default.
109 self
._qemu
_full
_args
= None
110 self
._test
_dir
= test_dir
111 self
._temp
_dir
= None
112 self
._sock
_dir
= sock_dir
113 self
._launched
= False
115 self
._console
_index
= 0
116 self
._console
_set
= False
117 self
._console
_device
_type
= None
118 self
._console
_address
= None
119 self
._console
_socket
= None
120 self
._remove
_files
= []
122 # just in case logging wasn't configured by the main script:
123 logging
.basicConfig()
128 def __exit__(self
, exc_type
, exc_val
, exc_tb
):
132 def add_monitor_null(self
):
134 This can be used to add an unused monitor instance.
136 self
._args
.append('-monitor')
137 self
._args
.append('null')
139 def add_fd(self
, fd
, fdset
, opaque
, opts
=''):
141 Pass a file descriptor to the VM
143 options
= ['fd=%d' % fd
,
145 'opaque=%s' % opaque
]
149 # This did not exist before 3.4, but since then it is
150 # mandatory for our purpose
151 if hasattr(os
, 'set_inheritable'):
152 os
.set_inheritable(fd
, True)
154 self
._args
.append('-add-fd')
155 self
._args
.append(','.join(options
))
158 def send_fd_scm(self
, fd
=None, file_path
=None):
160 Send an fd or file_path to socket_scm_helper.
162 Exactly one of fd and file_path must be given.
163 If it is file_path, the helper will open that file and pass its own fd.
165 # In iotest.py, the qmp should always use unix socket.
166 assert self
._qmp
.is_scm_available()
167 if self
._socket
_scm
_helper
is None:
168 raise QEMUMachineError("No path to socket_scm_helper set")
169 if not os
.path
.exists(self
._socket
_scm
_helper
):
170 raise QEMUMachineError("%s does not exist" %
171 self
._socket
_scm
_helper
)
173 # This did not exist before 3.4, but since then it is
174 # mandatory for our purpose
175 if hasattr(os
, 'set_inheritable'):
176 os
.set_inheritable(self
._qmp
.get_sock_fd(), True)
178 os
.set_inheritable(fd
, True)
180 fd_param
= ["%s" % self
._socket
_scm
_helper
,
181 "%d" % self
._qmp
.get_sock_fd()]
183 if file_path
is not None:
185 fd_param
.append(file_path
)
187 assert fd
is not None
188 fd_param
.append(str(fd
))
190 devnull
= open(os
.path
.devnull
, 'rb')
191 proc
= subprocess
.Popen(fd_param
, stdin
=devnull
, stdout
=subprocess
.PIPE
,
192 stderr
=subprocess
.STDOUT
, close_fds
=False)
193 output
= proc
.communicate()[0]
197 return proc
.returncode
200 def _remove_if_exists(path
):
202 Remove file object at path if it exists
206 except OSError as exception
:
207 if exception
.errno
== errno
.ENOENT
:
211 def is_running(self
):
212 """Returns true if the VM is running."""
213 return self
._popen
is not None and self
._popen
.poll() is None
216 """Returns the exit code if possible, or None."""
217 if self
._popen
is None:
219 return self
._popen
.poll()
222 """Returns the PID of the running process, or None."""
223 if not self
.is_running():
225 return self
._popen
.pid
227 def _load_io_log(self
):
228 if self
._qemu
_log
_path
is not None:
229 with
open(self
._qemu
_log
_path
, "r") as iolog
:
230 self
._iolog
= iolog
.read()
232 def _base_args(self
):
233 args
= ['-display', 'none', '-vga', 'none']
235 if isinstance(self
._monitor
_address
, tuple):
236 moncdev
= "socket,id=mon,host=%s,port=%s" % (
237 self
._monitor
_address
[0],
238 self
._monitor
_address
[1])
240 moncdev
= 'socket,id=mon,path=%s' % self
._vm
_monitor
241 args
.extend(['-chardev', moncdev
, '-mon',
242 'chardev=mon,mode=control'])
243 if self
._machine
is not None:
244 args
.extend(['-machine', self
._machine
])
245 for i
in range(self
._console
_index
):
246 args
.extend(['-serial', 'null'])
247 if self
._console
_set
:
248 self
._console
_address
= os
.path
.join(self
._sock
_dir
,
249 self
._name
+ "-console.sock")
250 self
._remove
_files
.append(self
._console
_address
)
251 chardev
= ('socket,id=console,path=%s,server,nowait' %
252 self
._console
_address
)
253 args
.extend(['-chardev', chardev
])
254 if self
._console
_device
_type
is None:
255 args
.extend(['-serial', 'chardev:console'])
257 device
= '%s,chardev=console' % self
._console
_device
_type
258 args
.extend(['-device', device
])
261 def _pre_launch(self
):
262 self
._temp
_dir
= tempfile
.mkdtemp(dir=self
._test
_dir
)
263 self
._qemu
_log
_path
= os
.path
.join(self
._temp
_dir
, self
._name
+ ".log")
264 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
267 if self
._monitor
_address
is not None:
268 self
._vm
_monitor
= self
._monitor
_address
270 self
._vm
_monitor
= os
.path
.join(self
._sock
_dir
,
271 self
._name
+ "-monitor.sock")
272 self
._remove
_files
.append(self
._vm
_monitor
)
273 self
._qmp
= qmp
.QEMUMonitorProtocol(self
._vm
_monitor
, server
=True)
275 def _post_launch(self
):
279 def _post_shutdown(self
):
280 if self
._qemu
_log
_file
is not None:
281 self
._qemu
_log
_file
.close()
282 self
._qemu
_log
_file
= None
284 self
._qemu
_log
_path
= None
286 if self
._temp
_dir
is not None:
287 shutil
.rmtree(self
._temp
_dir
)
288 self
._temp
_dir
= None
290 while len(self
._remove
_files
) > 0:
291 self
._remove
_if
_exists
(self
._remove
_files
.pop())
295 Launch the VM and make sure we cleanup and expose the
296 command line/output in case of exception
300 raise QEMUMachineError('VM already launched')
303 self
._qemu
_full
_args
= None
306 self
._launched
= True
310 LOG
.debug('Error launching VM')
311 if self
._qemu
_full
_args
:
312 LOG
.debug('Command: %r', ' '.join(self
._qemu
_full
_args
))
314 LOG
.debug('Output: %r', self
._iolog
)
319 Launch the VM and establish a QMP connection
321 devnull
= open(os
.path
.devnull
, 'rb')
323 self
._qemu
_full
_args
= (self
._wrapper
+ [self
._binary
] +
324 self
._base
_args
() + self
._args
)
325 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
326 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
328 stdout
=self
._qemu
_log
_file
,
329 stderr
=subprocess
.STDOUT
,
336 Wait for the VM to power off
342 self
._post
_shutdown
()
344 def shutdown(self
, has_quit
=False):
346 Terminate the VM and clean up
348 # If we keep the console socket open, we may deadlock waiting
349 # for QEMU to exit, while QEMU is waiting for the socket to
351 if self
._console
_socket
is not None:
352 self
._console
_socket
.close()
353 self
._console
_socket
= None
355 if self
.is_running():
359 self
._qmp
.cmd('quit')
366 self
._post
_shutdown
()
368 exitcode
= self
.exitcode()
369 if exitcode
is not None and exitcode
< 0:
370 msg
= 'qemu received signal %i: %s'
371 if self
._qemu
_full
_args
:
372 command
= ' '.join(self
._qemu
_full
_args
)
375 LOG
.warning(msg
, -exitcode
, command
)
377 self
._launched
= False
379 def set_qmp_monitor(self
, enabled
=True):
383 @param enabled: if False, qmp monitor options will be removed from
384 the base arguments of the resulting QEMU command
385 line. Default is True.
386 @note: call this function before launch().
391 self
._qmp
_set
= False
394 def qmp(self
, cmd
, conv_keys
=True, **args
):
396 Invoke a QMP command and return the response dict
399 for key
, value
in args
.items():
401 qmp_args
[key
.replace('_', '-')] = value
403 qmp_args
[key
] = value
405 return self
._qmp
.cmd(cmd
, args
=qmp_args
)
407 def command(self
, cmd
, conv_keys
=True, **args
):
409 Invoke a QMP command.
410 On success return the response dict.
411 On failure raise an exception.
413 reply
= self
.qmp(cmd
, conv_keys
, **args
)
415 raise qmp
.QMPError("Monitor is closed")
417 raise MonitorResponseError(reply
)
418 return reply
["return"]
420 def get_qmp_event(self
, wait
=False):
422 Poll for one queued QMP events and return it
425 return self
._events
.pop(0)
426 return self
._qmp
.pull_event(wait
=wait
)
428 def get_qmp_events(self
, wait
=False):
430 Poll for queued QMP events and return a list of dicts
432 events
= self
._qmp
.get_events(wait
=wait
)
433 events
.extend(self
._events
)
435 self
._qmp
.clear_events()
439 def event_match(event
, match
=None):
441 Check if an event matches optional match criteria.
443 The match criteria takes the form of a matching subdict. The event is
444 checked to be a superset of the subdict, recursively, with matching
445 values whenever the subdict values are not None.
447 This has a limitation that you cannot explicitly check for None values.
449 Examples, with the subdict queries on the left:
450 - None matches any object.
451 - {"foo": None} matches {"foo": {"bar": 1}}
452 - {"foo": None} matches {"foo": 5}
453 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
454 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
462 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
468 # either match or event wasn't iterable (not a dict)
469 return match
== event
471 def event_wait(self
, name
, timeout
=60.0, match
=None):
473 event_wait waits for and returns a named event from QMP with a timeout.
475 name: The event to wait for.
476 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
477 match: Optional match criteria. See event_match for details.
479 return self
.events_wait([(name
, match
)], timeout
)
481 def events_wait(self
, events
, timeout
=60.0):
483 events_wait waits for and returns a named event from QMP with a timeout.
485 events: a sequence of (name, match_criteria) tuples.
486 The match criteria are optional and may be None.
487 See event_match for details.
488 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
491 for name
, match
in events
:
492 if event
['event'] == name
and self
.event_match(event
, match
):
496 # Search cached events
497 for event
in self
._events
:
499 self
._events
.remove(event
)
502 # Poll for new events
504 event
= self
._qmp
.pull_event(wait
=timeout
)
507 self
._events
.append(event
)
513 After self.shutdown or failed qemu execution, this returns the output
518 def add_args(self
, *args
):
520 Adds to the list of extra arguments to be given to the QEMU binary
522 self
._args
.extend(args
)
524 def set_machine(self
, machine_type
):
526 Sets the machine type
528 If set, the machine type will be added to the base arguments
529 of the resulting QEMU command line.
531 self
._machine
= machine_type
533 def set_console(self
, device_type
=None, console_index
=0):
535 Sets the device type for a console device
537 If set, the console device and a backing character device will
538 be added to the base arguments of the resulting QEMU command
541 This is a convenience method that will either use the provided
542 device type, or default to a "-serial chardev:console" command
545 The actual setting of command line arguments will be be done at
546 machine launch time, as it depends on the temporary directory
549 @param device_type: the device type, such as "isa-serial". If
550 None is given (the default value) a "-serial
551 chardev:console" command line argument will
552 be used instead, resorting to the machine's
554 @param console_index: the index of the console device to use.
555 If not zero, the command line will create
556 'index - 1' consoles and connect them to
557 the 'null' backing character device.
559 self
._console
_set
= True
560 self
._console
_device
_type
= device_type
561 self
._console
_index
= console_index
564 def console_socket(self
):
566 Returns a socket connected to the console
568 if self
._console
_socket
is None:
569 self
._console
_socket
= socket
.socket(socket
.AF_UNIX
,
571 self
._console
_socket
.connect(self
._console
_address
)
572 return self
._console
_socket