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.
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):
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()
94 self._monitor_address = monitor_address
95 self._vm_monitor = None
96 self._qemu_log_path = None
97 self._qemu_log_file = None
100 self._args = list(args) # Force copy args in case we modify them
101 self._wrapper = wrapper
104 self._socket_scm_helper = socket_scm_helper
106 self._qemu_full_args = None
107 self._test_dir = test_dir
108 self._temp_dir = None
109 self._launched = False
111 self._console_set = False
112 self._console_device_type = None
113 self._console_address = None
114 self._console_socket = None
116 # just in case logging wasn't configured by the main script:
117 logging.basicConfig()
122 def __exit__(self, exc_type, exc_val, exc_tb):
126 def add_monitor_null(self):
128 This can be used to add an unused monitor instance.
130 self._args.append('-monitor')
131 self._args.append('null')
133 def add_fd(self, fd, fdset, opaque, opts=''):
135 Pass a file descriptor to the VM
137 options = ['fd=%d' % fd,
139 'opaque=%s' % opaque]
143 # This did not exist before 3.4, but since then it is
144 # mandatory for our purpose
145 if hasattr(os, 'set_inheritable'):
146 os.set_inheritable(fd, True)
148 self._args.append('-add-fd')
149 self._args.append(','.join(options))
152 def send_fd_scm(self, fd=None, file_path=None):
154 Send an fd or file_path to socket_scm_helper.
156 Exactly one of fd and file_path must be given.
157 If it is file_path, the helper will open that file and pass its own fd.
159 # In iotest.py, the qmp should always use unix socket.
160 assert self._qmp.is_scm_available()
161 if self._socket_scm_helper is None:
162 raise QEMUMachineError("No path to socket_scm_helper set")
163 if not os.path.exists(self._socket_scm_helper):
164 raise QEMUMachineError("%s does not exist" %
165 self._socket_scm_helper)
167 # This did not exist before 3.4, but since then it is
168 # mandatory for our purpose
169 if hasattr(os, 'set_inheritable'):
170 os.set_inheritable(self._qmp.get_sock_fd(), True)
172 os.set_inheritable(fd, True)
174 fd_param = ["%s" % self._socket_scm_helper,
175 "%d" % self._qmp.get_sock_fd()]
177 if file_path is not None:
179 fd_param.append(file_path)
181 assert fd is not None
182 fd_param.append(str(fd))
184 devnull = open(os.path.devnull, 'rb')
185 proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
186 stderr=subprocess.STDOUT, close_fds=False)
187 output = proc.communicate()[0]
191 return proc.returncode
194 def _remove_if_exists(path):
196 Remove file object at path if it exists
200 except OSError as exception:
201 if exception.errno == errno.ENOENT:
205 def is_running(self):
206 """Returns true if the VM is running."""
207 return self._popen is not None and self._popen.poll() is None
210 """Returns the exit code if possible, or None."""
211 if self._popen is None:
213 return self._popen.poll()
216 """Returns the PID of the running process, or None."""
217 if not self.is_running():
219 return self._popen.pid
221 def _load_io_log(self):
222 if self._qemu_log_path is not None:
223 with open(self._qemu_log_path, "r") as iolog:
224 self._iolog = iolog.read()
226 def _base_args(self):
227 if isinstance(self._monitor_address, tuple):
228 moncdev = "socket,id=mon,host=%s,port=%s" % (
229 self._monitor_address[0],
230 self._monitor_address[1])
232 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
233 args = ['-chardev', moncdev,
234 '-mon', 'chardev=mon,mode=control',
235 '-display', 'none', '-vga', 'none']
236 if self._machine is not None:
237 args.extend(['-machine', self._machine])
238 if self._console_set:
239 self._console_address = os.path.join(self._temp_dir,
240 self._name + "-console.sock")
241 chardev = ('socket,id=console,path=%s,server,nowait' %
242 self._console_address)
243 args.extend(['-chardev', chardev])
244 if self._console_device_type is None:
245 args.extend(['-serial', 'chardev:console'])
247 device = '%s,chardev=console' % self._console_device_type
248 args.extend(['-device', device])
251 def _pre_launch(self):
252 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
253 if self._monitor_address is not None:
254 self._vm_monitor = self._monitor_address
256 self._vm_monitor = os.path.join(self._temp_dir,
257 self._name + "-monitor.sock")
258 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
259 self._qemu_log_file = open(self._qemu_log_path, 'wb')
261 self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
264 def _post_launch(self):
267 def _post_shutdown(self):
268 if self._qemu_log_file is not None:
269 self._qemu_log_file.close()
270 self._qemu_log_file = None
272 self._qemu_log_path = None
274 if self._console_socket is not None:
275 self._console_socket.close()
276 self._console_socket = None
278 if self._temp_dir is not None:
279 shutil.rmtree(self._temp_dir)
280 self._temp_dir = None
284 Launch the VM and make sure we cleanup and expose the
285 command line/output in case of exception
289 raise QEMUMachineError('VM already launched')
292 self._qemu_full_args = None
295 self._launched = True
299 LOG.debug('Error launching VM')
300 if self._qemu_full_args:
301 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
303 LOG.debug('Output: %r', self._iolog)
308 Launch the VM and establish a QMP connection
310 devnull = open(os.path.devnull, 'rb')
312 self._qemu_full_args = (self._wrapper + [self._binary] +
313 self._base_args() + self._args)
314 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
315 self._popen = subprocess.Popen(self._qemu_full_args,
317 stdout=self._qemu_log_file,
318 stderr=subprocess.STDOUT,
325 Wait for the VM to power off
330 self._post_shutdown()
334 Terminate the VM and clean up
336 if self.is_running():
338 self._qmp.cmd('quit')
345 self._post_shutdown()
347 exitcode = self.exitcode()
348 if exitcode is not None and exitcode < 0:
349 msg = 'qemu received signal %i: %s'
350 if self._qemu_full_args:
351 command = ' '.join(self._qemu_full_args)
354 LOG.warning(msg, -exitcode, command)
356 self._launched = False
358 def qmp(self, cmd, conv_keys=True, **args):
360 Invoke a QMP command and return the response dict
363 for key, value in args.items():
365 qmp_args[key.replace('_', '-')] = value
367 qmp_args[key] = value
369 return self._qmp.cmd(cmd, args=qmp_args)
371 def command(self, cmd, conv_keys=True, **args):
373 Invoke a QMP command.
374 On success return the response dict.
375 On failure raise an exception.
377 reply = self.qmp(cmd, conv_keys, **args)
379 raise qmp.QMPError("Monitor is closed")
381 raise MonitorResponseError(reply)
382 return reply["return"]
384 def get_qmp_event(self, wait=False):
386 Poll for one queued QMP events and return it
389 return self._events.pop(0)
390 return self._qmp.pull_event(wait=wait)
392 def get_qmp_events(self, wait=False):
394 Poll for queued QMP events and return a list of dicts
396 events = self._qmp.get_events(wait=wait)
397 events.extend(self._events)
399 self._qmp.clear_events()
403 def event_match(event, match=None):
405 Check if an event matches optional match criteria.
407 The match criteria takes the form of a matching subdict. The event is
408 checked to be a superset of the subdict, recursively, with matching
409 values whenever the subdict values are not None.
411 This has a limitation that you cannot explicitly check for None values.
413 Examples, with the subdict queries on the left:
414 - None matches any object.
415 - {"foo": None} matches {"foo": {"bar": 1}}
416 - {"foo": None} matches {"foo": 5}
417 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
418 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
426 if not QEMUMachine.event_match(event[key], match[key]):
432 # either match or event wasn't iterable (not a dict)
433 return match == event
435 def event_wait(self, name, timeout=60.0, match=None):
437 event_wait waits for and returns a named event from QMP with a timeout.
439 name: The event to wait for.
440 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
441 match: Optional match criteria. See event_match for details.
443 return self.events_wait([(name, match)], timeout)
445 def events_wait(self, events, timeout=60.0):
447 events_wait waits for and returns a named event from QMP with a timeout.
449 events: a sequence of (name, match_criteria) tuples.
450 The match criteria are optional and may be None.
451 See event_match for details.
452 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
455 for name, match in events:
456 if event['event'] == name and self.event_match(event, match):
460 # Search cached events
461 for event in self._events:
463 self._events.remove(event)
466 # Poll for new events
468 event = self._qmp.pull_event(wait=timeout)
471 self._events.append(event)
477 After self.shutdown or failed qemu execution, this returns the output
482 def add_args(self, *args):
484 Adds to the list of extra arguments to be given to the QEMU binary
486 self._args.extend(args)
488 def set_machine(self, machine_type):
490 Sets the machine type
492 If set, the machine type will be added to the base arguments
493 of the resulting QEMU command line.
495 self._machine = machine_type
497 def set_console(self, device_type=None):
499 Sets the device type for a console device
501 If set, the console device and a backing character device will
502 be added to the base arguments of the resulting QEMU command
505 This is a convenience method that will either use the provided
506 device type, or default to a "-serial chardev:console" command
509 The actual setting of command line arguments will be be done at
510 machine launch time, as it depends on the temporary directory
513 @param device_type: the device type, such as "isa-serial". If
514 None is given (the default value) a "-serial
515 chardev:console" command line argument will
516 be used instead, resorting to the machine's
519 self._console_set = True
520 self._console_device_type = device_type
523 def console_socket(self):
525 Returns a socket connected to the console
527 if self._console_socket is None:
528 self._console_socket = socket.socket(socket.AF_UNIX,
530 self._console_socket.connect(self._console_address)
531 return self._console_socket