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