]> git.proxmox.com Git - mirror_qemu.git/blame - python/qemu/__init__.py
QEMUMachine: add events_wait method
[mirror_qemu.git] / python / qemu / __init__.py
CommitLineData
66613974
DB
1# QEMU library
2#
3# Copyright (C) 2015-2016 Red Hat Inc.
4# Copyright (C) 2012 IBM Corp.
5#
6# Authors:
7# Fam Zheng <famz@redhat.com>
8#
9# This work is licensed under the terms of the GNU GPL, version 2. See
10# the COPYING file in the top-level directory.
11#
12# Based on qmp.py.
13#
14
15import errno
4738b0a8 16import logging
66613974 17import os
66613974 18import subprocess
22dea9db 19import re
af99fa9f 20import shutil
22dea9db 21import socket
af99fa9f 22import tempfile
66613974 23
8f8fd9ed
CR
24from . import qmp
25
66613974 26
4738b0a8
AP
27LOG = logging.getLogger(__name__)
28
2d4e4c01
AB
29# Mapping host architecture to any additional architectures it can
30# support which often includes its 32 bit cousin.
31ADDITIONAL_ARCHES = {
32 "x86_64" : "i386",
33 "aarch64" : "armhf"
34}
4738b0a8 35
b59b82ed 36def kvm_available(target_arch=None):
2d4e4c01
AB
37 host_arch = os.uname()[4]
38 if target_arch and target_arch != host_arch:
39 if target_arch != ADDITIONAL_ARCHES.get(host_arch):
40 return False
b59b82ed
PMD
41 return os.access("/dev/kvm", os.R_OK | os.W_OK)
42
43
4738b0a8
AP
44class QEMUMachineError(Exception):
45 """
46 Exception called when an error in QEMUMachine happens.
47 """
48
49
22dea9db
CR
50class QEMUMachineAddDeviceError(QEMUMachineError):
51 """
52 Exception raised when a request to add a device can not be fulfilled
53
54 The failures are caused by limitations, lack of information or conflicting
55 requests on the QEMUMachine methods. This exception does not represent
56 failures reported by the QEMU binary itself.
57 """
58
8f8fd9ed 59class MonitorResponseError(qmp.QMPError):
e301e65c 60 """
a004e249 61 Represents erroneous QMP monitor reply
e301e65c 62 """
a004e249
LD
63 def __init__(self, reply):
64 try:
65 desc = reply["error"]["desc"]
66 except KeyError:
67 desc = reply
68 super(MonitorResponseError, self).__init__(desc)
69 self.reply = reply
70
71
66613974 72class QEMUMachine(object):
e301e65c
CR
73 """
74 A QEMU VM
d792bc38
SH
75
76 Use this object as a context manager to ensure the QEMU process terminates::
77
78 with VM(binary) as vm:
79 ...
80 # vm is guaranteed to be shut down here
e301e65c 81 """
66613974 82
2782fc51 83 def __init__(self, binary, args=None, wrapper=None, name=None,
2d853c70 84 test_dir="/var/tmp", monitor_address=None,
1a6d3757 85 socket_scm_helper=None):
2d853c70
LD
86 '''
87 Initialize a QEMUMachine
88
89 @param binary: path to the qemu binary
90 @param args: list of extra arguments
91 @param wrapper: list of arguments used as prefix to qemu binary
92 @param name: prefix for socket and log file names (default: qemu-PID)
93 @param test_dir: where to create socket and log file
94 @param monitor_address: address for QMP monitor
a5a98620 95 @param socket_scm_helper: helper program, required for send_fd_scm()
2d853c70
LD
96 @note: Qemu process is not started until launch() is used.
97 '''
2782fc51
LD
98 if args is None:
99 args = []
100 if wrapper is None:
101 wrapper = []
66613974
DB
102 if name is None:
103 name = "qemu-%d" % os.getpid()
af99fa9f 104 self._name = name
66613974 105 self._monitor_address = monitor_address
af99fa9f
AP
106 self._vm_monitor = None
107 self._qemu_log_path = None
108 self._qemu_log_file = None
66613974
DB
109 self._popen = None
110 self._binary = binary
2d853c70 111 self._args = list(args) # Force copy args in case we modify them
66613974
DB
112 self._wrapper = wrapper
113 self._events = []
114 self._iolog = None
4c44b4a4 115 self._socket_scm_helper = socket_scm_helper
2d853c70 116 self._qmp = None
dab91d9a 117 self._qemu_full_args = None
af99fa9f
AP
118 self._test_dir = test_dir
119 self._temp_dir = None
156dc7b1 120 self._launched = False
22dea9db 121 self._machine = None
123990ad 122 self._console_set = False
22dea9db
CR
123 self._console_device_type = None
124 self._console_address = None
125 self._console_socket = None
66613974 126
5810314e 127 # just in case logging wasn't configured by the main script:
1a6d3757 128 logging.basicConfig()
5810314e 129
d792bc38
SH
130 def __enter__(self):
131 return self
132
133 def __exit__(self, exc_type, exc_val, exc_tb):
134 self.shutdown()
135 return False
136
66613974 137 # This can be used to add an unused monitor instance.
e35792b6 138 def add_monitor_null(self):
66613974 139 self._args.append('-monitor')
e35792b6 140 self._args.append('null')
66613974
DB
141
142 def add_fd(self, fd, fdset, opaque, opts=''):
e301e65c
CR
143 """
144 Pass a file descriptor to the VM
145 """
66613974
DB
146 options = ['fd=%d' % fd,
147 'set=%d' % fdset,
148 'opaque=%s' % opaque]
149 if opts:
150 options.append(opts)
151
bf43b29d
HR
152 # This did not exist before 3.4, but since then it is
153 # mandatory for our purpose
154 if hasattr(os, 'set_inheritable'):
155 os.set_inheritable(fd, True)
156
66613974
DB
157 self._args.append('-add-fd')
158 self._args.append(','.join(options))
159 return self
160
bf43b29d
HR
161 # Exactly one of fd and file_path must be given.
162 # (If it is file_path, the helper will open that file and pass its
163 # own fd)
164 def send_fd_scm(self, fd=None, file_path=None):
66613974
DB
165 # In iotest.py, the qmp should always use unix socket.
166 assert self._qmp.is_scm_available()
4c44b4a4 167 if self._socket_scm_helper is None:
4738b0a8 168 raise QEMUMachineError("No path to socket_scm_helper set")
2d853c70 169 if not os.path.exists(self._socket_scm_helper):
4738b0a8
AP
170 raise QEMUMachineError("%s does not exist" %
171 self._socket_scm_helper)
bf43b29d
HR
172
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)
179
4c44b4a4 180 fd_param = ["%s" % self._socket_scm_helper,
bf43b29d
HR
181 "%d" % self._qmp.get_sock_fd()]
182
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))
189
63e0ba55 190 devnull = open(os.path.devnull, 'rb')
4738b0a8 191 proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
bf43b29d 192 stderr=subprocess.STDOUT, close_fds=False)
4738b0a8
AP
193 output = proc.communicate()[0]
194 if output:
195 LOG.debug(output)
196
197 return proc.returncode
66613974
DB
198
199 @staticmethod
200 def _remove_if_exists(path):
e301e65c
CR
201 """
202 Remove file object at path if it exists
203 """
66613974
DB
204 try:
205 os.remove(path)
206 except OSError as exception:
207 if exception.errno == errno.ENOENT:
208 return
209 raise
210
37bbcd57 211 def is_running(self):
17589cae 212 return self._popen is not None and self._popen.poll() is None
37bbcd57 213
b2b8d986
EH
214 def exitcode(self):
215 if self._popen is None:
216 return None
17589cae 217 return self._popen.poll()
b2b8d986 218
66613974 219 def get_pid(self):
37bbcd57 220 if not self.is_running():
66613974
DB
221 return None
222 return self._popen.pid
223
224 def _load_io_log(self):
04a963b4
AP
225 if self._qemu_log_path is not None:
226 with open(self._qemu_log_path, "r") as iolog:
227 self._iolog = iolog.read()
66613974
DB
228
229 def _base_args(self):
230 if isinstance(self._monitor_address, tuple):
231 moncdev = "socket,id=mon,host=%s,port=%s" % (
232 self._monitor_address[0],
233 self._monitor_address[1])
234 else:
af99fa9f 235 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
22dea9db 236 args = ['-chardev', moncdev,
66613974
DB
237 '-mon', 'chardev=mon,mode=control',
238 '-display', 'none', '-vga', 'none']
22dea9db
CR
239 if self._machine is not None:
240 args.extend(['-machine', self._machine])
123990ad 241 if self._console_set:
22dea9db
CR
242 self._console_address = os.path.join(self._temp_dir,
243 self._name + "-console.sock")
244 chardev = ('socket,id=console,path=%s,server,nowait' %
245 self._console_address)
123990ad
CR
246 args.extend(['-chardev', chardev])
247 if self._console_device_type is None:
248 args.extend(['-serial', 'chardev:console'])
249 else:
250 device = '%s,chardev=console' % self._console_device_type
251 args.extend(['-device', device])
22dea9db 252 return args
66613974
DB
253
254 def _pre_launch(self):
af99fa9f
AP
255 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
256 if self._monitor_address is not None:
257 self._vm_monitor = self._monitor_address
258 else:
259 self._vm_monitor = os.path.join(self._temp_dir,
260 self._name + "-monitor.sock")
261 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
262 self._qemu_log_file = open(self._qemu_log_path, 'wb')
263
8f8fd9ed
CR
264 self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
265 server=True)
66613974
DB
266
267 def _post_launch(self):
268 self._qmp.accept()
269
270 def _post_shutdown(self):
af99fa9f
AP
271 if self._qemu_log_file is not None:
272 self._qemu_log_file.close()
273 self._qemu_log_file = None
274
275 self._qemu_log_path = None
276
22dea9db
CR
277 if self._console_socket is not None:
278 self._console_socket.close()
279 self._console_socket = None
280
af99fa9f
AP
281 if self._temp_dir is not None:
282 shutil.rmtree(self._temp_dir)
283 self._temp_dir = None
66613974
DB
284
285 def launch(self):
d301bccf
AP
286 """
287 Launch the VM and make sure we cleanup and expose the
288 command line/output in case of exception
289 """
156dc7b1
AP
290
291 if self._launched:
292 raise QEMUMachineError('VM already launched')
293
b92a0011 294 self._iolog = None
dab91d9a 295 self._qemu_full_args = None
66613974 296 try:
d301bccf 297 self._launch()
156dc7b1 298 self._launched = True
66613974 299 except:
c58b535f 300 self.shutdown()
b92a0011
AP
301
302 LOG.debug('Error launching VM')
303 if self._qemu_full_args:
304 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
305 if self._iolog:
306 LOG.debug('Output: %r', self._iolog)
66613974
DB
307 raise
308
d301bccf 309 def _launch(self):
e301e65c
CR
310 """
311 Launch the VM and establish a QMP connection
312 """
d301bccf
AP
313 devnull = open(os.path.devnull, 'rb')
314 self._pre_launch()
315 self._qemu_full_args = (self._wrapper + [self._binary] +
316 self._base_args() + self._args)
10314fb0 317 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
d301bccf
AP
318 self._popen = subprocess.Popen(self._qemu_full_args,
319 stdin=devnull,
320 stdout=self._qemu_log_file,
321 stderr=subprocess.STDOUT,
bf43b29d
HR
322 shell=False,
323 close_fds=False)
d301bccf
AP
324 self._post_launch()
325
22491a2f 326 def wait(self):
e301e65c
CR
327 """
328 Wait for the VM to power off
329 """
22491a2f
FZ
330 self._popen.wait()
331 self._qmp.close()
332 self._load_io_log()
333 self._post_shutdown()
334
66613974 335 def shutdown(self):
e301e65c
CR
336 """
337 Terminate the VM and clean up
338 """
37bbcd57 339 if self.is_running():
66613974
DB
340 try:
341 self._qmp.cmd('quit')
342 self._qmp.close()
343 except:
344 self._popen.kill()
dab91d9a 345 self._popen.wait()
66613974 346
04a963b4
AP
347 self._load_io_log()
348 self._post_shutdown()
66613974 349
dab91d9a
AP
350 exitcode = self.exitcode()
351 if exitcode is not None and exitcode < 0:
352 msg = 'qemu received signal %i: %s'
353 if self._qemu_full_args:
354 command = ' '.join(self._qemu_full_args)
355 else:
356 command = ''
0cabc8f1 357 LOG.warn(msg, -exitcode, command)
dab91d9a 358
156dc7b1
AP
359 self._launched = False
360
66613974 361 def qmp(self, cmd, conv_keys=True, **args):
e301e65c
CR
362 """
363 Invoke a QMP command and return the response dict
364 """
66613974 365 qmp_args = dict()
fb2e1cc6 366 for key, value in args.items():
66613974 367 if conv_keys:
41f714b1 368 qmp_args[key.replace('_', '-')] = value
66613974 369 else:
7f33ca78 370 qmp_args[key] = value
66613974
DB
371
372 return self._qmp.cmd(cmd, args=qmp_args)
373
374 def command(self, cmd, conv_keys=True, **args):
e301e65c 375 """
2d853c70
LD
376 Invoke a QMP command.
377 On success return the response dict.
378 On failure raise an exception.
e301e65c 379 """
66613974
DB
380 reply = self.qmp(cmd, conv_keys, **args)
381 if reply is None:
8f8fd9ed 382 raise qmp.QMPError("Monitor is closed")
66613974 383 if "error" in reply:
a004e249 384 raise MonitorResponseError(reply)
66613974
DB
385 return reply["return"]
386
387 def get_qmp_event(self, wait=False):
e301e65c
CR
388 """
389 Poll for one queued QMP events and return it
390 """
66613974
DB
391 if len(self._events) > 0:
392 return self._events.pop(0)
393 return self._qmp.pull_event(wait=wait)
394
395 def get_qmp_events(self, wait=False):
e301e65c
CR
396 """
397 Poll for queued QMP events and return a list of dicts
398 """
66613974
DB
399 events = self._qmp.get_events(wait=wait)
400 events.extend(self._events)
401 del self._events[:]
402 self._qmp.clear_events()
403 return events
404
f6f4b3f0
JS
405 @staticmethod
406 def event_match(event, match=None):
e301e65c 407 """
f6f4b3f0 408 Check if an event matches optional match criteria.
2d853c70 409
f6f4b3f0
JS
410 The match criteria takes the form of a matching subdict. The event is
411 checked to be a superset of the subdict, recursively, with matching
412 values whenever those values are not None.
413
414 Examples, with the subdict queries on the left:
415 - None matches any object.
416 - {"foo": None} matches {"foo": {"bar": 1}}
417 - {"foo": {"baz": None}} does not match {"foo": {"bar": 1}}
418 - {"foo": {"baz": 2}} matches {"foo": {"bar": 1, "baz": 2}}
e301e65c 419 """
f6f4b3f0
JS
420 if match is None:
421 return True
422
423 for key in match:
424 if key in event:
425 if isinstance(event[key], dict):
426 if not QEMUMachine.event_match(event[key], match[key]):
4c44b4a4 427 return False
f6f4b3f0 428 elif event[key] != match[key]:
4c44b4a4 429 return False
f6f4b3f0
JS
430 else:
431 return False
432 return True
4c44b4a4 433
f6f4b3f0
JS
434 def event_wait(self, name, timeout=60.0, match=None):
435 """
436 event_wait waits for and returns a named event from QMP with a timeout.
437
438 name: The event to wait for.
439 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
440 match: Optional match criteria. See event_match for details.
441 """
442 return self.events_wait([(name, match)], timeout)
443
444 def events_wait(self, events, timeout=60.0):
445 """
446 events_wait waits for and returns a named event from QMP with a timeout.
447
448 events: a sequence of (name, match_criteria) tuples.
449 The match criteria are optional and may be None.
450 See event_match for details.
451 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
452 """
453 def _match(event):
454 for name, match in events:
455 if (event['event'] == name and
456 self.event_match(event, match)):
457 return True
458 return False
4c44b4a4 459
66613974
DB
460 # Search cached events
461 for event in self._events:
f6f4b3f0 462 if _match(event):
66613974
DB
463 self._events.remove(event)
464 return event
465
466 # Poll for new events
467 while True:
468 event = self._qmp.pull_event(wait=timeout)
f6f4b3f0 469 if _match(event):
66613974
DB
470 return event
471 self._events.append(event)
472
473 return None
474
475 def get_log(self):
e301e65c 476 """
2d853c70
LD
477 After self.shutdown or failed qemu execution, this returns the output
478 of the qemu process.
e301e65c 479 """
66613974 480 return self._iolog
572a8243
CR
481
482 def add_args(self, *args):
e301e65c 483 """
572a8243 484 Adds to the list of extra arguments to be given to the QEMU binary
e301e65c 485 """
572a8243 486 self._args.extend(args)
22dea9db
CR
487
488 def set_machine(self, machine_type):
e301e65c 489 """
22dea9db
CR
490 Sets the machine type
491
492 If set, the machine type will be added to the base arguments
493 of the resulting QEMU command line.
e301e65c 494 """
22dea9db
CR
495 self._machine = machine_type
496
497 def set_console(self, device_type=None):
e301e65c 498 """
22dea9db
CR
499 Sets the device type for a console device
500
501 If set, the console device and a backing character device will
502 be added to the base arguments of the resulting QEMU command
503 line.
504
505 This is a convenience method that will either use the provided
123990ad
CR
506 device type, or default to a "-serial chardev:console" command
507 line argument.
22dea9db
CR
508
509 The actual setting of command line arguments will be be done at
510 machine launch time, as it depends on the temporary directory
511 to be created.
512
123990ad
CR
513 @param device_type: the device type, such as "isa-serial". If
514 None is given (the default value) a "-serial
515 chardev:console" command line argument will
516 be used instead, resorting to the machine's
517 default device type.
518 """
519 self._console_set = True
22dea9db
CR
520 self._console_device_type = device_type
521
522 @property
523 def console_socket(self):
524 """
525 Returns a socket connected to the console
526 """
527 if self._console_socket is None:
528 self._console_socket = socket.socket(socket.AF_UNIX,
529 socket.SOCK_STREAM)
530 self._console_socket.connect(self._console_address)
531 return self._console_socket