]> Git Repo - qemu.git/blob - scripts/qemu.py
hw/hppa/dino: mask out lower 2 bits of PCI config addr
[qemu.git] / scripts / qemu.py
1 # QEMU library
2 #
3 # Copyright (C) 2015-2016 Red Hat Inc.
4 # Copyright (C) 2012 IBM Corp.
5 #
6 # Authors:
7 #  Fam Zheng <[email protected]>
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
15 import errno
16 import logging
17 import os
18 import subprocess
19 import qmp.qmp
20 import re
21 import shutil
22 import socket
23 import tempfile
24
25
26 LOG = logging.getLogger(__name__)
27
28 # Mapping host architecture to any additional architectures it can
29 # support which often includes its 32 bit cousin.
30 ADDITIONAL_ARCHES = {
31     "x86_64" : "i386",
32     "aarch64" : "armhf"
33 }
34
35 def kvm_available(target_arch=None):
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
40     return os.access("/dev/kvm", os.R_OK | os.W_OK)
41
42
43 #: Maps machine types to the preferred console device types
44 CONSOLE_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
54 class QEMUMachineError(Exception):
55     """
56     Exception called when an error in QEMUMachine happens.
57     """
58
59
60 class 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
69 class MonitorResponseError(qmp.qmp.QMPError):
70     """
71     Represents erroneous QMP monitor reply
72     """
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
82 class QEMUMachine(object):
83     """
84     A QEMU VM
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
91     """
92
93     def __init__(self, binary, args=None, wrapper=None, name=None,
94                  test_dir="/var/tmp", monitor_address=None,
95                  socket_scm_helper=None):
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
105         @param socket_scm_helper: helper program, required for send_fd_scm()
106         @note: Qemu process is not started until launch() is used.
107         '''
108         if args is None:
109             args = []
110         if wrapper is None:
111             wrapper = []
112         if name is None:
113             name = "qemu-%d" % os.getpid()
114         self._name = name
115         self._monitor_address = monitor_address
116         self._vm_monitor = None
117         self._qemu_log_path = None
118         self._qemu_log_file = None
119         self._popen = None
120         self._binary = binary
121         self._args = list(args)     # Force copy args in case we modify them
122         self._wrapper = wrapper
123         self._events = []
124         self._iolog = None
125         self._socket_scm_helper = socket_scm_helper
126         self._qmp = None
127         self._qemu_full_args = None
128         self._test_dir = test_dir
129         self._temp_dir = None
130         self._launched = False
131         self._machine = None
132         self._console_device_type = None
133         self._console_address = None
134         self._console_socket = None
135
136         # just in case logging wasn't configured by the main script:
137         logging.basicConfig()
138
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
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=''):
153         """
154         Pass a file descriptor to the VM
155         """
156         options = ['fd=%d' % fd,
157                    'set=%d' % fdset,
158                    'opaque=%s' % opaque]
159         if opts:
160             options.append(opts)
161
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
167         self._args.append('-add-fd')
168         self._args.append(','.join(options))
169         return self
170
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):
175         # In iotest.py, the qmp should always use unix socket.
176         assert self._qmp.is_scm_available()
177         if self._socket_scm_helper is None:
178             raise QEMUMachineError("No path to socket_scm_helper set")
179         if not os.path.exists(self._socket_scm_helper):
180             raise QEMUMachineError("%s does not exist" %
181                                    self._socket_scm_helper)
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
190         fd_param = ["%s" % self._socket_scm_helper,
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
200         devnull = open(os.path.devnull, 'rb')
201         proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
202                                 stderr=subprocess.STDOUT, close_fds=False)
203         output = proc.communicate()[0]
204         if output:
205             LOG.debug(output)
206
207         return proc.returncode
208
209     @staticmethod
210     def _remove_if_exists(path):
211         """
212         Remove file object at path if it exists
213         """
214         try:
215             os.remove(path)
216         except OSError as exception:
217             if exception.errno == errno.ENOENT:
218                 return
219             raise
220
221     def is_running(self):
222         return self._popen is not None and self._popen.poll() is None
223
224     def exitcode(self):
225         if self._popen is None:
226             return None
227         return self._popen.poll()
228
229     def get_pid(self):
230         if not self.is_running():
231             return None
232         return self._popen.pid
233
234     def _load_io_log(self):
235         if self._qemu_log_path is not None:
236             with open(self._qemu_log_path, "r") as iolog:
237                 self._iolog = iolog.read()
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:
245             moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
246         args = ['-chardev', moncdev,
247                 '-mon', 'chardev=mon,mode=control',
248                 '-display', 'none', '-vga', 'none']
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
259
260     def _pre_launch(self):
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,
271                                                 server=True)
272
273     def _post_launch(self):
274         self._qmp.accept()
275
276     def _post_shutdown(self):
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
283         if self._console_socket is not None:
284             self._console_socket.close()
285             self._console_socket = None
286
287         if self._temp_dir is not None:
288             shutil.rmtree(self._temp_dir)
289             self._temp_dir = None
290
291     def launch(self):
292         """
293         Launch the VM and make sure we cleanup and expose the
294         command line/output in case of exception
295         """
296
297         if self._launched:
298             raise QEMUMachineError('VM already launched')
299
300         self._iolog = None
301         self._qemu_full_args = None
302         try:
303             self._launch()
304             self._launched = True
305         except:
306             self.shutdown()
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)
313             raise
314
315     def _launch(self):
316         """
317         Launch the VM and establish a QMP connection
318         """
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,
327                                        shell=False,
328                                        close_fds=False)
329         self._post_launch()
330
331     def wait(self):
332         """
333         Wait for the VM to power off
334         """
335         self._popen.wait()
336         self._qmp.close()
337         self._load_io_log()
338         self._post_shutdown()
339
340     def shutdown(self):
341         """
342         Terminate the VM and clean up
343         """
344         if self.is_running():
345             try:
346                 self._qmp.cmd('quit')
347                 self._qmp.close()
348             except:
349                 self._popen.kill()
350             self._popen.wait()
351
352         self._load_io_log()
353         self._post_shutdown()
354
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 = ''
362             LOG.warn(msg, -exitcode, command)
363
364         self._launched = False
365
366     def qmp(self, cmd, conv_keys=True, **args):
367         """
368         Invoke a QMP command and return the response dict
369         """
370         qmp_args = dict()
371         for key, value in args.items():
372             if conv_keys:
373                 qmp_args[key.replace('_', '-')] = value
374             else:
375                 qmp_args[key] = value
376
377         return self._qmp.cmd(cmd, args=qmp_args)
378
379     def command(self, cmd, conv_keys=True, **args):
380         """
381         Invoke a QMP command.
382         On success return the response dict.
383         On failure raise an exception.
384         """
385         reply = self.qmp(cmd, conv_keys, **args)
386         if reply is None:
387             raise qmp.qmp.QMPError("Monitor is closed")
388         if "error" in reply:
389             raise MonitorResponseError(reply)
390         return reply["return"]
391
392     def get_qmp_event(self, wait=False):
393         """
394         Poll for one queued QMP events and return it
395         """
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):
401         """
402         Poll for queued QMP events and return a list of dicts
403         """
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):
411         """
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}}
419         """
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
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):
452         """
453         After self.shutdown or failed qemu execution, this returns the output
454         of the qemu process.
455         """
456         return self._iolog
457
458     def add_args(self, *args):
459         """
460         Adds to the list of extra arguments to be given to the QEMU binary
461         """
462         self._args.extend(args)
463
464     def set_machine(self, machine_type):
465         """
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.
470         """
471         self._machine = machine_type
472
473     def set_console(self, device_type=None):
474         """
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.
492         """
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
This page took 0.053871 seconds and 4 git commands to generate.