]> git.proxmox.com Git - mirror_qemu.git/blame - python/qemu/machine.py
tests/acceptance: Don't test reboot on cubieboard
[mirror_qemu.git] / python / qemu / machine.py
CommitLineData
306dfcd6
JS
1"""
2QEMU machine module:
3
4The machine module primarily provides the QEMUMachine class,
5which provides facilities for managing the lifetime of a QEMU VM.
6"""
7
abf0bf99
JS
8# Copyright (C) 2015-2016 Red Hat Inc.
9# Copyright (C) 2012 IBM Corp.
10#
11# Authors:
12# Fam Zheng <famz@redhat.com>
13#
14# This work is licensed under the terms of the GNU GPL, version 2. See
15# the COPYING file in the top-level directory.
16#
17# Based on qmp.py.
18#
19
20import errno
21import logging
22import os
23import subprocess
abf0bf99
JS
24import shutil
25import socket
26import tempfile
1dda0404
JS
27from typing import Optional, Type
28from types import TracebackType
0fc8f660 29from qemu.console_socket import ConsoleSocket
abf0bf99
JS
30
31from . import qmp
32
33LOG = logging.getLogger(__name__)
34
8dfac2ed 35
abf0bf99
JS
36class QEMUMachineError(Exception):
37 """
38 Exception called when an error in QEMUMachine happens.
39 """
40
41
42class QEMUMachineAddDeviceError(QEMUMachineError):
43 """
44 Exception raised when a request to add a device can not be fulfilled
45
46 The failures are caused by limitations, lack of information or conflicting
47 requests on the QEMUMachine methods. This exception does not represent
48 failures reported by the QEMU binary itself.
49 """
50
51
52class MonitorResponseError(qmp.QMPError):
53 """
54 Represents erroneous QMP monitor reply
55 """
56 def __init__(self, reply):
57 try:
58 desc = reply["error"]["desc"]
59 except KeyError:
60 desc = reply
3797dbcb 61 super().__init__(desc)
abf0bf99
JS
62 self.reply = reply
63
64
9b8ccd6d 65class QEMUMachine:
abf0bf99
JS
66 """
67 A QEMU VM
68
8dfac2ed
JS
69 Use this object as a context manager to ensure
70 the QEMU process terminates::
abf0bf99
JS
71
72 with VM(binary) as vm:
73 ...
74 # vm is guaranteed to be shut down here
75 """
76
77 def __init__(self, binary, args=None, wrapper=None, name=None,
78 test_dir="/var/tmp", monitor_address=None,
0fc8f660
RF
79 socket_scm_helper=None, sock_dir=None,
80 drain_console=False, console_log=None):
abf0bf99
JS
81 '''
82 Initialize a QEMUMachine
83
84 @param binary: path to the qemu binary
85 @param args: list of extra arguments
86 @param wrapper: list of arguments used as prefix to qemu binary
87 @param name: prefix for socket and log file names (default: qemu-PID)
88 @param test_dir: where to create socket and log file
89 @param monitor_address: address for QMP monitor
90 @param socket_scm_helper: helper program, required for send_fd_scm()
0fc8f660
RF
91 @param sock_dir: where to create socket (overrides test_dir for sock)
92 @param console_log: (optional) path to console log file
93 @param drain_console: (optional) True to drain console socket to buffer
abf0bf99
JS
94 @note: Qemu process is not started until launch() is used.
95 '''
96 if args is None:
97 args = []
98 if wrapper is None:
99 wrapper = []
100 if name is None:
101 name = "qemu-%d" % os.getpid()
32558ce7
HR
102 if sock_dir is None:
103 sock_dir = test_dir
abf0bf99
JS
104 self._name = name
105 self._monitor_address = monitor_address
106 self._vm_monitor = None
107 self._qemu_log_path = None
108 self._qemu_log_file = None
109 self._popen = None
110 self._binary = binary
111 self._args = list(args) # Force copy args in case we modify them
112 self._wrapper = wrapper
113 self._events = []
114 self._iolog = None
115 self._socket_scm_helper = socket_scm_helper
74b56bb5 116 self._qmp_set = True # Enable QMP monitor by default.
abf0bf99
JS
117 self._qmp = None
118 self._qemu_full_args = None
119 self._test_dir = test_dir
120 self._temp_dir = None
32558ce7 121 self._sock_dir = sock_dir
abf0bf99
JS
122 self._launched = False
123 self._machine = None
746f244d 124 self._console_index = 0
abf0bf99
JS
125 self._console_set = False
126 self._console_device_type = None
127 self._console_address = None
128 self._console_socket = None
32558ce7 129 self._remove_files = []
0fc8f660
RF
130 self._console_log_path = console_log
131 if self._console_log_path:
132 # In order to log the console, buffering needs to be enabled.
133 self._drain_console = True
134 else:
135 self._drain_console = drain_console
abf0bf99 136
abf0bf99
JS
137 def __enter__(self):
138 return self
139
1dda0404
JS
140 def __exit__(self,
141 exc_type: Optional[Type[BaseException]],
142 exc_val: Optional[BaseException],
143 exc_tb: Optional[TracebackType]) -> None:
abf0bf99 144 self.shutdown()
abf0bf99 145
abf0bf99 146 def add_monitor_null(self):
306dfcd6
JS
147 """
148 This can be used to add an unused monitor instance.
149 """
abf0bf99
JS
150 self._args.append('-monitor')
151 self._args.append('null')
152
153 def add_fd(self, fd, fdset, opaque, opts=''):
154 """
155 Pass a file descriptor to the VM
156 """
157 options = ['fd=%d' % fd,
158 'set=%d' % fdset,
159 'opaque=%s' % opaque]
160 if opts:
161 options.append(opts)
162
163 # This did not exist before 3.4, but since then it is
164 # mandatory for our purpose
165 if hasattr(os, 'set_inheritable'):
166 os.set_inheritable(fd, True)
167
168 self._args.append('-add-fd')
169 self._args.append(','.join(options))
170 return self
171
abf0bf99 172 def send_fd_scm(self, fd=None, file_path=None):
306dfcd6
JS
173 """
174 Send an fd or file_path to socket_scm_helper.
175
176 Exactly one of fd and file_path must be given.
177 If it is file_path, the helper will open that file and pass its own fd.
178 """
abf0bf99
JS
179 # In iotest.py, the qmp should always use unix socket.
180 assert self._qmp.is_scm_available()
181 if self._socket_scm_helper is None:
182 raise QEMUMachineError("No path to socket_scm_helper set")
183 if not os.path.exists(self._socket_scm_helper):
184 raise QEMUMachineError("%s does not exist" %
185 self._socket_scm_helper)
186
187 # This did not exist before 3.4, but since then it is
188 # mandatory for our purpose
189 if hasattr(os, 'set_inheritable'):
190 os.set_inheritable(self._qmp.get_sock_fd(), True)
191 if fd is not None:
192 os.set_inheritable(fd, True)
193
194 fd_param = ["%s" % self._socket_scm_helper,
195 "%d" % self._qmp.get_sock_fd()]
196
197 if file_path is not None:
198 assert fd is None
199 fd_param.append(file_path)
200 else:
201 assert fd is not None
202 fd_param.append(str(fd))
203
204 devnull = open(os.path.devnull, 'rb')
8dfac2ed
JS
205 proc = subprocess.Popen(
206 fd_param, stdin=devnull, stdout=subprocess.PIPE,
207 stderr=subprocess.STDOUT, close_fds=False
208 )
abf0bf99
JS
209 output = proc.communicate()[0]
210 if output:
211 LOG.debug(output)
212
213 return proc.returncode
214
215 @staticmethod
216 def _remove_if_exists(path):
217 """
218 Remove file object at path if it exists
219 """
220 try:
221 os.remove(path)
222 except OSError as exception:
223 if exception.errno == errno.ENOENT:
224 return
225 raise
226
227 def is_running(self):
306dfcd6 228 """Returns true if the VM is running."""
abf0bf99
JS
229 return self._popen is not None and self._popen.poll() is None
230
231 def exitcode(self):
306dfcd6 232 """Returns the exit code if possible, or None."""
abf0bf99
JS
233 if self._popen is None:
234 return None
235 return self._popen.poll()
236
237 def get_pid(self):
306dfcd6 238 """Returns the PID of the running process, or None."""
abf0bf99
JS
239 if not self.is_running():
240 return None
241 return self._popen.pid
242
243 def _load_io_log(self):
244 if self._qemu_log_path is not None:
245 with open(self._qemu_log_path, "r") as iolog:
246 self._iolog = iolog.read()
247
248 def _base_args(self):
74b56bb5
WSM
249 args = ['-display', 'none', '-vga', 'none']
250 if self._qmp_set:
251 if isinstance(self._monitor_address, tuple):
252 moncdev = "socket,id=mon,host=%s,port=%s" % (
253 self._monitor_address[0],
254 self._monitor_address[1])
255 else:
256 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
257 args.extend(['-chardev', moncdev, '-mon',
258 'chardev=mon,mode=control'])
abf0bf99
JS
259 if self._machine is not None:
260 args.extend(['-machine', self._machine])
9b8ccd6d 261 for _ in range(self._console_index):
746f244d 262 args.extend(['-serial', 'null'])
abf0bf99 263 if self._console_set:
32558ce7 264 self._console_address = os.path.join(self._sock_dir,
abf0bf99 265 self._name + "-console.sock")
32558ce7 266 self._remove_files.append(self._console_address)
abf0bf99
JS
267 chardev = ('socket,id=console,path=%s,server,nowait' %
268 self._console_address)
269 args.extend(['-chardev', chardev])
270 if self._console_device_type is None:
271 args.extend(['-serial', 'chardev:console'])
272 else:
273 device = '%s,chardev=console' % self._console_device_type
274 args.extend(['-device', device])
275 return args
276
277 def _pre_launch(self):
278 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
abf0bf99
JS
279 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
280 self._qemu_log_file = open(self._qemu_log_path, 'wb')
281
74b56bb5
WSM
282 if self._qmp_set:
283 if self._monitor_address is not None:
284 self._vm_monitor = self._monitor_address
285 else:
286 self._vm_monitor = os.path.join(self._sock_dir,
287 self._name + "-monitor.sock")
288 self._remove_files.append(self._vm_monitor)
566054a0
OV
289 self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True,
290 nickname=self._name)
abf0bf99
JS
291
292 def _post_launch(self):
74b56bb5
WSM
293 if self._qmp:
294 self._qmp.accept()
abf0bf99
JS
295
296 def _post_shutdown(self):
a3842cb0
JS
297 """
298 Called to cleanup the VM instance after the process has exited.
299 May also be called after a failed launch.
300 """
301 # Comprehensive reset for the failed launch case:
302 self._early_cleanup()
303
671940e6
JS
304 if self._qmp:
305 self._qmp.close()
306 self._qmp = None
307
14661d93
JS
308 self._load_io_log()
309
abf0bf99
JS
310 if self._qemu_log_file is not None:
311 self._qemu_log_file.close()
312 self._qemu_log_file = None
313
314 self._qemu_log_path = None
315
abf0bf99
JS
316 if self._temp_dir is not None:
317 shutil.rmtree(self._temp_dir)
318 self._temp_dir = None
319
32558ce7
HR
320 while len(self._remove_files) > 0:
321 self._remove_if_exists(self._remove_files.pop())
322
14661d93
JS
323 exitcode = self.exitcode()
324 if exitcode is not None and exitcode < 0:
325 msg = 'qemu received signal %i; command: "%s"'
326 if self._qemu_full_args:
327 command = ' '.join(self._qemu_full_args)
328 else:
329 command = ''
330 LOG.warning(msg, -int(exitcode), command)
331
332 self._launched = False
333
abf0bf99
JS
334 def launch(self):
335 """
336 Launch the VM and make sure we cleanup and expose the
337 command line/output in case of exception
338 """
339
340 if self._launched:
341 raise QEMUMachineError('VM already launched')
342
343 self._iolog = None
344 self._qemu_full_args = None
345 try:
346 self._launch()
347 self._launched = True
348 except:
a3842cb0 349 self._post_shutdown()
abf0bf99
JS
350
351 LOG.debug('Error launching VM')
352 if self._qemu_full_args:
353 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
354 if self._iolog:
355 LOG.debug('Output: %r', self._iolog)
356 raise
357
358 def _launch(self):
359 """
360 Launch the VM and establish a QMP connection
361 """
362 devnull = open(os.path.devnull, 'rb')
363 self._pre_launch()
364 self._qemu_full_args = (self._wrapper + [self._binary] +
365 self._base_args() + self._args)
366 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
367 self._popen = subprocess.Popen(self._qemu_full_args,
368 stdin=devnull,
369 stdout=self._qemu_log_file,
370 stderr=subprocess.STDOUT,
371 shell=False,
372 close_fds=False)
373 self._post_launch()
374
e2c97f16
JS
375 def _early_cleanup(self) -> None:
376 """
377 Perform any cleanup that needs to happen before the VM exits.
a3842cb0
JS
378
379 Called additionally by _post_shutdown for comprehensive cleanup.
e2c97f16
JS
380 """
381 # If we keep the console socket open, we may deadlock waiting
382 # for QEMU to exit, while QEMU is waiting for the socket to
383 # become writeable.
384 if self._console_socket is not None:
385 self._console_socket.close()
386 self._console_socket = None
387
c9b3045b
JS
388 def shutdown(self, has_quit: bool = False,
389 hard: bool = False,
390 timeout: Optional[int] = 3) -> None:
abf0bf99
JS
391 """
392 Terminate the VM and clean up
393 """
a3842cb0
JS
394 if not self._launched:
395 return
396
e2c97f16 397 self._early_cleanup()
08580967 398
abf0bf99 399 if self.is_running():
e0e925a6
VSO
400 if hard:
401 self._popen.kill()
402 elif self._qmp:
74b56bb5
WSM
403 try:
404 if not has_quit:
405 self._qmp.cmd('quit')
c9b3045b 406 self._popen.wait(timeout=timeout)
74b56bb5
WSM
407 except:
408 self._popen.kill()
c9b3045b 409 self._popen.wait(timeout=timeout)
abf0bf99 410
abf0bf99
JS
411 self._post_shutdown()
412
e0e925a6
VSO
413 def kill(self):
414 self.shutdown(hard=True)
415
89528059
JS
416 def wait(self, timeout: Optional[int] = None) -> None:
417 """
418 Wait for the VM to power off and perform post-shutdown cleanup.
419
420 :param timeout: Optional timeout in seconds.
421 Default None, an infinite wait.
422 """
423 self.shutdown(has_quit=True, timeout=timeout)
424
74b56bb5
WSM
425 def set_qmp_monitor(self, enabled=True):
426 """
427 Set the QMP monitor.
428
429 @param enabled: if False, qmp monitor options will be removed from
430 the base arguments of the resulting QEMU command
431 line. Default is True.
432 @note: call this function before launch().
433 """
434 if enabled:
435 self._qmp_set = True
436 else:
437 self._qmp_set = False
438 self._qmp = None
439
abf0bf99
JS
440 def qmp(self, cmd, conv_keys=True, **args):
441 """
442 Invoke a QMP command and return the response dict
443 """
444 qmp_args = dict()
445 for key, value in args.items():
446 if conv_keys:
447 qmp_args[key.replace('_', '-')] = value
448 else:
449 qmp_args[key] = value
450
451 return self._qmp.cmd(cmd, args=qmp_args)
452
453 def command(self, cmd, conv_keys=True, **args):
454 """
455 Invoke a QMP command.
456 On success return the response dict.
457 On failure raise an exception.
458 """
459 reply = self.qmp(cmd, conv_keys, **args)
460 if reply is None:
461 raise qmp.QMPError("Monitor is closed")
462 if "error" in reply:
463 raise MonitorResponseError(reply)
464 return reply["return"]
465
466 def get_qmp_event(self, wait=False):
467 """
468 Poll for one queued QMP events and return it
469 """
306dfcd6 470 if self._events:
abf0bf99
JS
471 return self._events.pop(0)
472 return self._qmp.pull_event(wait=wait)
473
474 def get_qmp_events(self, wait=False):
475 """
476 Poll for queued QMP events and return a list of dicts
477 """
478 events = self._qmp.get_events(wait=wait)
479 events.extend(self._events)
480 del self._events[:]
481 self._qmp.clear_events()
482 return events
483
484 @staticmethod
485 def event_match(event, match=None):
486 """
487 Check if an event matches optional match criteria.
488
489 The match criteria takes the form of a matching subdict. The event is
490 checked to be a superset of the subdict, recursively, with matching
491 values whenever the subdict values are not None.
492
493 This has a limitation that you cannot explicitly check for None values.
494
495 Examples, with the subdict queries on the left:
496 - None matches any object.
497 - {"foo": None} matches {"foo": {"bar": 1}}
498 - {"foo": None} matches {"foo": 5}
499 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
500 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
501 """
502 if match is None:
503 return True
504
505 try:
506 for key in match:
507 if key in event:
508 if not QEMUMachine.event_match(event[key], match[key]):
509 return False
510 else:
511 return False
512 return True
513 except TypeError:
514 # either match or event wasn't iterable (not a dict)
515 return match == event
516
517 def event_wait(self, name, timeout=60.0, match=None):
518 """
519 event_wait waits for and returns a named event from QMP with a timeout.
520
521 name: The event to wait for.
522 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
523 match: Optional match criteria. See event_match for details.
524 """
525 return self.events_wait([(name, match)], timeout)
526
527 def events_wait(self, events, timeout=60.0):
528 """
8dfac2ed
JS
529 events_wait waits for and returns a named event
530 from QMP with a timeout.
abf0bf99
JS
531
532 events: a sequence of (name, match_criteria) tuples.
533 The match criteria are optional and may be None.
534 See event_match for details.
535 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
536 """
537 def _match(event):
538 for name, match in events:
306dfcd6 539 if event['event'] == name and self.event_match(event, match):
abf0bf99
JS
540 return True
541 return False
542
543 # Search cached events
544 for event in self._events:
545 if _match(event):
546 self._events.remove(event)
547 return event
548
549 # Poll for new events
550 while True:
551 event = self._qmp.pull_event(wait=timeout)
552 if _match(event):
553 return event
554 self._events.append(event)
555
556 return None
557
558 def get_log(self):
559 """
560 After self.shutdown or failed qemu execution, this returns the output
561 of the qemu process.
562 """
563 return self._iolog
564
565 def add_args(self, *args):
566 """
567 Adds to the list of extra arguments to be given to the QEMU binary
568 """
569 self._args.extend(args)
570
571 def set_machine(self, machine_type):
572 """
573 Sets the machine type
574
575 If set, the machine type will be added to the base arguments
576 of the resulting QEMU command line.
577 """
578 self._machine = machine_type
579
746f244d 580 def set_console(self, device_type=None, console_index=0):
abf0bf99
JS
581 """
582 Sets the device type for a console device
583
584 If set, the console device and a backing character device will
585 be added to the base arguments of the resulting QEMU command
586 line.
587
588 This is a convenience method that will either use the provided
589 device type, or default to a "-serial chardev:console" command
590 line argument.
591
592 The actual setting of command line arguments will be be done at
593 machine launch time, as it depends on the temporary directory
594 to be created.
595
596 @param device_type: the device type, such as "isa-serial". If
597 None is given (the default value) a "-serial
598 chardev:console" command line argument will
599 be used instead, resorting to the machine's
600 default device type.
746f244d
PMD
601 @param console_index: the index of the console device to use.
602 If not zero, the command line will create
603 'index - 1' consoles and connect them to
604 the 'null' backing character device.
abf0bf99
JS
605 """
606 self._console_set = True
607 self._console_device_type = device_type
746f244d 608 self._console_index = console_index
abf0bf99
JS
609
610 @property
611 def console_socket(self):
612 """
613 Returns a socket connected to the console
614 """
615 if self._console_socket is None:
0fc8f660
RF
616 if self._drain_console:
617 self._console_socket = ConsoleSocket(self._console_address,
618 file=self._console_log_path)
619 else:
620 self._console_socket = socket.socket(socket.AF_UNIX,
621 socket.SOCK_STREAM)
622 self._console_socket.connect(self._console_address)
abf0bf99 623 return self._console_socket