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