docs: add Orange Pi PC document
[qemu/ar7.git] / python / qemu / machine.py
blob183d8f3d3894b201c8c512e32a833fb6a2d4634a
1 """
2 QEMU machine module:
4 The machine module primarily provides the QEMUMachine class,
5 which provides facilities for managing the lifetime of a QEMU VM.
6 """
8 # Copyright (C) 2015-2016 Red Hat Inc.
9 # Copyright (C) 2012 IBM Corp.
11 # Authors:
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.
17 # Based on qmp.py.
20 import errno
21 import logging
22 import os
23 import subprocess
24 import shutil
25 import socket
26 import tempfile
28 from . import qmp
30 LOG = logging.getLogger(__name__)
32 class QEMUMachineError(Exception):
33 """
34 Exception called when an error in QEMUMachine happens.
35 """
38 class QEMUMachineAddDeviceError(QEMUMachineError):
39 """
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.
45 """
48 class MonitorResponseError(qmp.QMPError):
49 """
50 Represents erroneous QMP monitor reply
51 """
52 def __init__(self, reply):
53 try:
54 desc = reply["error"]["desc"]
55 except KeyError:
56 desc = reply
57 super(MonitorResponseError, self).__init__(desc)
58 self.reply = reply
61 class QEMUMachine(object):
62 """
63 A QEMU VM
65 Use this object as a context manager to ensure the QEMU process terminates::
67 with VM(binary) as vm:
68 ...
69 # vm is guaranteed to be shut down here
70 """
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):
75 '''
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.
86 '''
87 if args is None:
88 args = []
89 if wrapper is None:
90 wrapper = []
91 if name is None:
92 name = "qemu-%d" % os.getpid()
93 if sock_dir is None:
94 sock_dir = test_dir
95 self._name = name
96 self._monitor_address = monitor_address
97 self._vm_monitor = None
98 self._qemu_log_path = None
99 self._qemu_log_file = None
100 self._popen = None
101 self._binary = binary
102 self._args = list(args) # Force copy args in case we modify them
103 self._wrapper = wrapper
104 self._events = []
105 self._iolog = None
106 self._socket_scm_helper = socket_scm_helper
107 self._qmp_set = True # Enable QMP monitor by default.
108 self._qmp = None
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
114 self._machine = None
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()
125 def __enter__(self):
126 return self
128 def __exit__(self, exc_type, exc_val, exc_tb):
129 self.shutdown()
130 return False
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,
144 'set=%d' % fdset,
145 'opaque=%s' % opaque]
146 if opts:
147 options.append(opts)
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))
156 return self
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)
177 if fd is not None:
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:
184 assert fd is None
185 fd_param.append(file_path)
186 else:
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]
194 if output:
195 LOG.debug(output)
197 return proc.returncode
199 @staticmethod
200 def _remove_if_exists(path):
202 Remove file object at path if it exists
204 try:
205 os.remove(path)
206 except OSError as exception:
207 if exception.errno == errno.ENOENT:
208 return
209 raise
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
215 def exitcode(self):
216 """Returns the exit code if possible, or None."""
217 if self._popen is None:
218 return None
219 return self._popen.poll()
221 def get_pid(self):
222 """Returns the PID of the running process, or None."""
223 if not self.is_running():
224 return None
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']
234 if self._qmp_set:
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])
239 else:
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'])
256 else:
257 device = '%s,chardev=console' % self._console_device_type
258 args.extend(['-device', device])
259 return args
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')
266 if self._qmp_set:
267 if self._monitor_address is not None:
268 self._vm_monitor = self._monitor_address
269 else:
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):
276 if self._qmp:
277 self._qmp.accept()
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())
293 def launch(self):
295 Launch the VM and make sure we cleanup and expose the
296 command line/output in case of exception
299 if self._launched:
300 raise QEMUMachineError('VM already launched')
302 self._iolog = None
303 self._qemu_full_args = None
304 try:
305 self._launch()
306 self._launched = True
307 except:
308 self.shutdown()
310 LOG.debug('Error launching VM')
311 if self._qemu_full_args:
312 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
313 if self._iolog:
314 LOG.debug('Output: %r', self._iolog)
315 raise
317 def _launch(self):
319 Launch the VM and establish a QMP connection
321 devnull = open(os.path.devnull, 'rb')
322 self._pre_launch()
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,
327 stdin=devnull,
328 stdout=self._qemu_log_file,
329 stderr=subprocess.STDOUT,
330 shell=False,
331 close_fds=False)
332 self._post_launch()
334 def wait(self):
336 Wait for the VM to power off
338 self._popen.wait()
339 if self._qmp:
340 self._qmp.close()
341 self._load_io_log()
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
350 # become writeable.
351 if self._console_socket is not None:
352 self._console_socket.close()
353 self._console_socket = None
355 if self.is_running():
356 if self._qmp:
357 try:
358 if not has_quit:
359 self._qmp.cmd('quit')
360 self._qmp.close()
361 except:
362 self._popen.kill()
363 self._popen.wait()
365 self._load_io_log()
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)
373 else:
374 command = ''
375 LOG.warning(msg, -exitcode, command)
377 self._launched = False
379 def set_qmp_monitor(self, enabled=True):
381 Set the QMP monitor.
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().
388 if enabled:
389 self._qmp_set = True
390 else:
391 self._qmp_set = False
392 self._qmp = None
394 def qmp(self, cmd, conv_keys=True, **args):
396 Invoke a QMP command and return the response dict
398 qmp_args = dict()
399 for key, value in args.items():
400 if conv_keys:
401 qmp_args[key.replace('_', '-')] = value
402 else:
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)
414 if reply is None:
415 raise qmp.QMPError("Monitor is closed")
416 if "error" in reply:
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
424 if self._events:
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)
434 del self._events[:]
435 self._qmp.clear_events()
436 return events
438 @staticmethod
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}}
456 if match is None:
457 return True
459 try:
460 for key in match:
461 if key in event:
462 if not QEMUMachine.event_match(event[key], match[key]):
463 return False
464 else:
465 return False
466 return True
467 except TypeError:
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.
490 def _match(event):
491 for name, match in events:
492 if event['event'] == name and self.event_match(event, match):
493 return True
494 return False
496 # Search cached events
497 for event in self._events:
498 if _match(event):
499 self._events.remove(event)
500 return event
502 # Poll for new events
503 while True:
504 event = self._qmp.pull_event(wait=timeout)
505 if _match(event):
506 return event
507 self._events.append(event)
509 return None
511 def get_log(self):
513 After self.shutdown or failed qemu execution, this returns the output
514 of the qemu process.
516 return self._iolog
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
539 line.
541 This is a convenience method that will either use the provided
542 device type, or default to a "-serial chardev:console" command
543 line argument.
545 The actual setting of command line arguments will be be done at
546 machine launch time, as it depends on the temporary directory
547 to be created.
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
553 default device type.
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
563 @property
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,
570 socket.SOCK_STREAM)
571 self._console_socket.connect(self._console_address)
572 return self._console_socket