]>
git.proxmox.com Git - mirror_qemu.git/blob - python/qemu/machine.py
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.
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.
27 from typing
import Optional
, Type
28 from types
import TracebackType
29 from qemu
.console_socket
import ConsoleSocket
33 LOG
= logging
.getLogger(__name__
)
36 class QEMUMachineError(Exception):
38 Exception called when an error in QEMUMachine happens.
42 class QEMUMachineAddDeviceError(QEMUMachineError
):
44 Exception raised when a request to add a device can not be fulfilled
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.
52 class MonitorResponseError(qmp
.QMPError
):
54 Represents erroneous QMP monitor reply
56 def __init__(self
, reply
):
58 desc
= reply
["error"]["desc"]
61 super().__init
__(desc
)
69 Use this object as a context manager to ensure
70 the QEMU process terminates::
72 with VM(binary) as vm:
74 # vm is guaranteed to be shut down here
77 def __init__(self
, binary
, args
=None, wrapper
=None, name
=None,
78 test_dir
="/var/tmp", monitor_address
=None,
79 socket_scm_helper
=None, sock_dir
=None,
80 drain_console
=False, console_log
=None):
82 Initialize a QEMUMachine
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()
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
94 @note: Qemu process is not started until launch() is used.
101 name
= "qemu-%d" % os
.getpid()
105 self
._monitor
_address
= monitor_address
106 self
._vm
_monitor
= None
107 self
._qemu
_log
_path
= None
108 self
._qemu
_log
_file
= None
110 self
._binary
= binary
111 self
._args
= list(args
) # Force copy args in case we modify them
112 self
._wrapper
= wrapper
115 self
._socket
_scm
_helper
= socket_scm_helper
116 self
._qmp
_set
= True # Enable QMP monitor by default.
118 self
._qemu
_full
_args
= None
119 self
._test
_dir
= test_dir
120 self
._temp
_dir
= None
121 self
._sock
_dir
= sock_dir
122 self
._launched
= False
124 self
._console
_index
= 0
125 self
._console
_set
= False
126 self
._console
_device
_type
= None
127 self
._console
_address
= None
128 self
._console
_socket
= None
129 self
._remove
_files
= []
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
135 self
._drain
_console
= drain_console
141 exc_type
: Optional
[Type
[BaseException
]],
142 exc_val
: Optional
[BaseException
],
143 exc_tb
: Optional
[TracebackType
]) -> None:
146 def add_monitor_null(self
):
148 This can be used to add an unused monitor instance.
150 self
._args
.append('-monitor')
151 self
._args
.append('null')
153 def add_fd(self
, fd
, fdset
, opaque
, opts
=''):
155 Pass a file descriptor to the VM
157 options
= ['fd=%d' % fd
,
159 'opaque=%s' % opaque
]
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)
168 self
._args
.append('-add-fd')
169 self
._args
.append(','.join(options
))
172 def send_fd_scm(self
, fd
=None, file_path
=None):
174 Send an fd or file_path to socket_scm_helper.
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.
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
)
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)
192 os
.set_inheritable(fd
, True)
194 fd_param
= ["%s" % self
._socket
_scm
_helper
,
195 "%d" % self
._qmp
.get_sock_fd()]
197 if file_path
is not None:
199 fd_param
.append(file_path
)
201 assert fd
is not None
202 fd_param
.append(str(fd
))
204 devnull
= open(os
.path
.devnull
, 'rb')
205 proc
= subprocess
.Popen(
206 fd_param
, stdin
=devnull
, stdout
=subprocess
.PIPE
,
207 stderr
=subprocess
.STDOUT
, close_fds
=False
209 output
= proc
.communicate()[0]
213 return proc
.returncode
216 def _remove_if_exists(path
):
218 Remove file object at path if it exists
222 except OSError as exception
:
223 if exception
.errno
== errno
.ENOENT
:
227 def is_running(self
):
228 """Returns true if the VM is running."""
229 return self
._popen
is not None and self
._popen
.poll() is None
232 """Returns the exit code if possible, or None."""
233 if self
._popen
is None:
235 return self
._popen
.poll()
238 """Returns the PID of the running process, or None."""
239 if not self
.is_running():
241 return self
._popen
.pid
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()
248 def _base_args(self
):
249 args
= ['-display', 'none', '-vga', 'none']
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])
256 moncdev
= 'socket,id=mon,path=%s' % self
._vm
_monitor
257 args
.extend(['-chardev', moncdev
, '-mon',
258 'chardev=mon,mode=control'])
259 if self
._machine
is not None:
260 args
.extend(['-machine', self
._machine
])
261 for _
in range(self
._console
_index
):
262 args
.extend(['-serial', 'null'])
263 if self
._console
_set
:
264 self
._console
_address
= os
.path
.join(self
._sock
_dir
,
265 self
._name
+ "-console.sock")
266 self
._remove
_files
.append(self
._console
_address
)
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'])
273 device
= '%s,chardev=console' % self
._console
_device
_type
274 args
.extend(['-device', device
])
277 def _pre_launch(self
):
278 self
._temp
_dir
= tempfile
.mkdtemp(dir=self
._test
_dir
)
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')
283 if self
._monitor
_address
is not None:
284 self
._vm
_monitor
= self
._monitor
_address
286 self
._vm
_monitor
= os
.path
.join(self
._sock
_dir
,
287 self
._name
+ "-monitor.sock")
288 self
._remove
_files
.append(self
._vm
_monitor
)
289 self
._qmp
= qmp
.QEMUMonitorProtocol(self
._vm
_monitor
, server
=True,
292 def _post_launch(self
):
296 def _post_shutdown(self
):
303 if self
._qemu
_log
_file
is not None:
304 self
._qemu
_log
_file
.close()
305 self
._qemu
_log
_file
= None
307 self
._qemu
_log
_path
= None
309 if self
._temp
_dir
is not None:
310 shutil
.rmtree(self
._temp
_dir
)
311 self
._temp
_dir
= None
313 while len(self
._remove
_files
) > 0:
314 self
._remove
_if
_exists
(self
._remove
_files
.pop())
316 exitcode
= self
.exitcode()
317 if exitcode
is not None and exitcode
< 0:
318 msg
= 'qemu received signal %i; command: "%s"'
319 if self
._qemu
_full
_args
:
320 command
= ' '.join(self
._qemu
_full
_args
)
323 LOG
.warning(msg
, -int(exitcode
), command
)
325 self
._launched
= False
329 Launch the VM and make sure we cleanup and expose the
330 command line/output in case of exception
334 raise QEMUMachineError('VM already launched')
337 self
._qemu
_full
_args
= None
340 self
._launched
= True
344 LOG
.debug('Error launching VM')
345 if self
._qemu
_full
_args
:
346 LOG
.debug('Command: %r', ' '.join(self
._qemu
_full
_args
))
348 LOG
.debug('Output: %r', self
._iolog
)
353 Launch the VM and establish a QMP connection
355 devnull
= open(os
.path
.devnull
, 'rb')
357 self
._qemu
_full
_args
= (self
._wrapper
+ [self
._binary
] +
358 self
._base
_args
() + self
._args
)
359 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
360 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
362 stdout
=self
._qemu
_log
_file
,
363 stderr
=subprocess
.STDOUT
,
368 def _early_cleanup(self
) -> None:
370 Perform any cleanup that needs to happen before the VM exits.
372 # If we keep the console socket open, we may deadlock waiting
373 # for QEMU to exit, while QEMU is waiting for the socket to
375 if self
._console
_socket
is not None:
376 self
._console
_socket
.close()
377 self
._console
_socket
= None
381 Wait for the VM to power off
383 self
._early
_cleanup
()
385 self
._post
_shutdown
()
387 def shutdown(self
, has_quit
=False, hard
=False):
389 Terminate the VM and clean up
391 self
._early
_cleanup
()
393 if self
.is_running():
399 self
._qmp
.cmd('quit')
400 self
._popen
.wait(timeout
=3)
405 self
._post
_shutdown
()
408 self
.shutdown(hard
=True)
410 def set_qmp_monitor(self
, enabled
=True):
414 @param enabled: if False, qmp monitor options will be removed from
415 the base arguments of the resulting QEMU command
416 line. Default is True.
417 @note: call this function before launch().
422 self
._qmp
_set
= False
425 def qmp(self
, cmd
, conv_keys
=True, **args
):
427 Invoke a QMP command and return the response dict
430 for key
, value
in args
.items():
432 qmp_args
[key
.replace('_', '-')] = value
434 qmp_args
[key
] = value
436 return self
._qmp
.cmd(cmd
, args
=qmp_args
)
438 def command(self
, cmd
, conv_keys
=True, **args
):
440 Invoke a QMP command.
441 On success return the response dict.
442 On failure raise an exception.
444 reply
= self
.qmp(cmd
, conv_keys
, **args
)
446 raise qmp
.QMPError("Monitor is closed")
448 raise MonitorResponseError(reply
)
449 return reply
["return"]
451 def get_qmp_event(self
, wait
=False):
453 Poll for one queued QMP events and return it
456 return self
._events
.pop(0)
457 return self
._qmp
.pull_event(wait
=wait
)
459 def get_qmp_events(self
, wait
=False):
461 Poll for queued QMP events and return a list of dicts
463 events
= self
._qmp
.get_events(wait
=wait
)
464 events
.extend(self
._events
)
466 self
._qmp
.clear_events()
470 def event_match(event
, match
=None):
472 Check if an event matches optional match criteria.
474 The match criteria takes the form of a matching subdict. The event is
475 checked to be a superset of the subdict, recursively, with matching
476 values whenever the subdict values are not None.
478 This has a limitation that you cannot explicitly check for None values.
480 Examples, with the subdict queries on the left:
481 - None matches any object.
482 - {"foo": None} matches {"foo": {"bar": 1}}
483 - {"foo": None} matches {"foo": 5}
484 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
485 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
493 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
499 # either match or event wasn't iterable (not a dict)
500 return match
== event
502 def event_wait(self
, name
, timeout
=60.0, match
=None):
504 event_wait waits for and returns a named event from QMP with a timeout.
506 name: The event to wait for.
507 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
508 match: Optional match criteria. See event_match for details.
510 return self
.events_wait([(name
, match
)], timeout
)
512 def events_wait(self
, events
, timeout
=60.0):
514 events_wait waits for and returns a named event
515 from QMP with a timeout.
517 events: a sequence of (name, match_criteria) tuples.
518 The match criteria are optional and may be None.
519 See event_match for details.
520 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
523 for name
, match
in events
:
524 if event
['event'] == name
and self
.event_match(event
, match
):
528 # Search cached events
529 for event
in self
._events
:
531 self
._events
.remove(event
)
534 # Poll for new events
536 event
= self
._qmp
.pull_event(wait
=timeout
)
539 self
._events
.append(event
)
545 After self.shutdown or failed qemu execution, this returns the output
550 def add_args(self
, *args
):
552 Adds to the list of extra arguments to be given to the QEMU binary
554 self
._args
.extend(args
)
556 def set_machine(self
, machine_type
):
558 Sets the machine type
560 If set, the machine type will be added to the base arguments
561 of the resulting QEMU command line.
563 self
._machine
= machine_type
565 def set_console(self
, device_type
=None, console_index
=0):
567 Sets the device type for a console device
569 If set, the console device and a backing character device will
570 be added to the base arguments of the resulting QEMU command
573 This is a convenience method that will either use the provided
574 device type, or default to a "-serial chardev:console" command
577 The actual setting of command line arguments will be be done at
578 machine launch time, as it depends on the temporary directory
581 @param device_type: the device type, such as "isa-serial". If
582 None is given (the default value) a "-serial
583 chardev:console" command line argument will
584 be used instead, resorting to the machine's
586 @param console_index: the index of the console device to use.
587 If not zero, the command line will create
588 'index - 1' consoles and connect them to
589 the 'null' backing character device.
591 self
._console
_set
= True
592 self
._console
_device
_type
= device_type
593 self
._console
_index
= console_index
596 def console_socket(self
):
598 Returns a socket connected to the console
600 if self
._console
_socket
is None:
601 if self
._drain
_console
:
602 self
._console
_socket
= ConsoleSocket(self
._console
_address
,
603 file=self
._console
_log
_path
)
605 self
._console
_socket
= socket
.socket(socket
.AF_UNIX
,
607 self
._console
_socket
.connect(self
._console
_address
)
608 return self
._console
_socket