]> Git Repo - qemu.git/blob - scripts/qemu.py
Merge remote-tracking branch 'kraxel/tags/pull-vga-20170511-1' into staging
[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 string
17 import os
18 import sys
19 import subprocess
20 import qmp.qmp
21
22
23 class QEMUMachine(object):
24     '''A QEMU VM'''
25
26     def __init__(self, binary, args=[], wrapper=[], name=None, test_dir="/var/tmp",
27                  monitor_address=None, socket_scm_helper=None, debug=False):
28         if name is None:
29             name = "qemu-%d" % os.getpid()
30         if monitor_address is None:
31             monitor_address = os.path.join(test_dir, name + "-monitor.sock")
32         self._monitor_address = monitor_address
33         self._qemu_log_path = os.path.join(test_dir, name + ".log")
34         self._popen = None
35         self._binary = binary
36         self._args = list(args) # Force copy args in case we modify them
37         self._wrapper = wrapper
38         self._events = []
39         self._iolog = None
40         self._socket_scm_helper = socket_scm_helper
41         self._debug = debug
42
43     # This can be used to add an unused monitor instance.
44     def add_monitor_telnet(self, ip, port):
45         args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
46         self._args.append('-monitor')
47         self._args.append(args)
48
49     def add_fd(self, fd, fdset, opaque, opts=''):
50         '''Pass a file descriptor to the VM'''
51         options = ['fd=%d' % fd,
52                    'set=%d' % fdset,
53                    'opaque=%s' % opaque]
54         if opts:
55             options.append(opts)
56
57         self._args.append('-add-fd')
58         self._args.append(','.join(options))
59         return self
60
61     def send_fd_scm(self, fd_file_path):
62         # In iotest.py, the qmp should always use unix socket.
63         assert self._qmp.is_scm_available()
64         if self._socket_scm_helper is None:
65             print >>sys.stderr, "No path to socket_scm_helper set"
66             return -1
67         if os.path.exists(self._socket_scm_helper) == False:
68             print >>sys.stderr, "%s does not exist" % self._socket_scm_helper
69             return -1
70         fd_param = ["%s" % self._socket_scm_helper,
71                     "%d" % self._qmp.get_sock_fd(),
72                     "%s" % fd_file_path]
73         devnull = open('/dev/null', 'rb')
74         p = subprocess.Popen(fd_param, stdin=devnull, stdout=sys.stdout,
75                              stderr=sys.stderr)
76         return p.wait()
77
78     @staticmethod
79     def _remove_if_exists(path):
80         '''Remove file object at path if it exists'''
81         try:
82             os.remove(path)
83         except OSError as exception:
84             if exception.errno == errno.ENOENT:
85                 return
86             raise
87
88     def get_pid(self):
89         if not self._popen:
90             return None
91         return self._popen.pid
92
93     def _load_io_log(self):
94         with open(self._qemu_log_path, "r") as fh:
95             self._iolog = fh.read()
96
97     def _base_args(self):
98         if isinstance(self._monitor_address, tuple):
99             moncdev = "socket,id=mon,host=%s,port=%s" % (
100                 self._monitor_address[0],
101                 self._monitor_address[1])
102         else:
103             moncdev = 'socket,id=mon,path=%s' % self._monitor_address
104         return ['-chardev', moncdev,
105                 '-mon', 'chardev=mon,mode=control',
106                 '-display', 'none', '-vga', 'none']
107
108     def _pre_launch(self):
109         self._qmp = qmp.qmp.QEMUMonitorProtocol(self._monitor_address, server=True,
110                                                 debug=self._debug)
111
112     def _post_launch(self):
113         self._qmp.accept()
114
115     def _post_shutdown(self):
116         if not isinstance(self._monitor_address, tuple):
117             self._remove_if_exists(self._monitor_address)
118         self._remove_if_exists(self._qemu_log_path)
119
120     def launch(self):
121         '''Launch the VM and establish a QMP connection'''
122         devnull = open('/dev/null', 'rb')
123         qemulog = open(self._qemu_log_path, 'wb')
124         try:
125             self._pre_launch()
126             args = self._wrapper + [self._binary] + self._base_args() + self._args
127             self._popen = subprocess.Popen(args, stdin=devnull, stdout=qemulog,
128                                            stderr=subprocess.STDOUT, shell=False)
129             self._post_launch()
130         except:
131             if self._popen:
132                 self._popen.kill()
133             self._load_io_log()
134             self._post_shutdown()
135             self._popen = None
136             raise
137
138     def shutdown(self):
139         '''Terminate the VM and clean up'''
140         if not self._popen is None:
141             try:
142                 self._qmp.cmd('quit')
143                 self._qmp.close()
144             except:
145                 self._popen.kill()
146
147             exitcode = self._popen.wait()
148             if exitcode < 0:
149                 sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode, ' '.join(self._args)))
150             self._load_io_log()
151             self._post_shutdown()
152             self._popen = None
153
154     underscore_to_dash = string.maketrans('_', '-')
155     def qmp(self, cmd, conv_keys=True, **args):
156         '''Invoke a QMP command and return the result dict'''
157         qmp_args = dict()
158         for k in args.keys():
159             if conv_keys:
160                 qmp_args[k.translate(self.underscore_to_dash)] = args[k]
161             else:
162                 qmp_args[k] = args[k]
163
164         return self._qmp.cmd(cmd, args=qmp_args)
165
166     def command(self, cmd, conv_keys=True, **args):
167         reply = self.qmp(cmd, conv_keys, **args)
168         if reply is None:
169             raise Exception("Monitor is closed")
170         if "error" in reply:
171             raise Exception(reply["error"]["desc"])
172         return reply["return"]
173
174     def get_qmp_event(self, wait=False):
175         '''Poll for one queued QMP events and return it'''
176         if len(self._events) > 0:
177             return self._events.pop(0)
178         return self._qmp.pull_event(wait=wait)
179
180     def get_qmp_events(self, wait=False):
181         '''Poll for queued QMP events and return a list of dicts'''
182         events = self._qmp.get_events(wait=wait)
183         events.extend(self._events)
184         del self._events[:]
185         self._qmp.clear_events()
186         return events
187
188     def event_wait(self, name, timeout=60.0, match=None):
189         # Test if 'match' is a recursive subset of 'event'
190         def event_match(event, match=None):
191             if match is None:
192                 return True
193
194             for key in match:
195                 if key in event:
196                     if isinstance(event[key], dict):
197                         if not event_match(event[key], match[key]):
198                             return False
199                     elif event[key] != match[key]:
200                         return False
201                 else:
202                     return False
203
204             return True
205
206         # Search cached events
207         for event in self._events:
208             if (event['event'] == name) and event_match(event, match):
209                 self._events.remove(event)
210                 return event
211
212         # Poll for new events
213         while True:
214             event = self._qmp.pull_event(wait=timeout)
215             if (event['event'] == name) and event_match(event, match):
216                 return event
217             self._events.append(event)
218
219         return None
220
221     def get_log(self):
222         return self._iolog
This page took 0.034354 seconds and 4 git commands to generate.