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