vnc: fix memory leak when vnc disconnect
[qemu/ar7.git] / python / qemu / machine.py
blob128a3d1dc28082623bb52ee46718e90b998b193f
1 """
2 QEMU machine module:
4 The machine module primarily provides the QEMUMachine class,
5 which provides facilities for managing the lifetime of a QEMU VM.
6 """
8 # Copyright (C) 2015-2016 Red Hat Inc.
9 # Copyright (C) 2012 IBM Corp.
11 # Authors:
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.
17 # Based on qmp.py.
20 import errno
21 import logging
22 import os
23 import subprocess
24 import shutil
25 import socket
26 import tempfile
28 from . import qmp
30 LOG = logging.getLogger(__name__)
32 class QEMUMachineError(Exception):
33 """
34 Exception called when an error in QEMUMachine happens.
35 """
38 class QEMUMachineAddDeviceError(QEMUMachineError):
39 """
40 Exception raised when a request to add a device can not be fulfilled
42 The failures are caused by limitations, lack of information or conflicting
43 requests on the QEMUMachine methods. This exception does not represent
44 failures reported by the QEMU binary itself.
45 """
48 class MonitorResponseError(qmp.QMPError):
49 """
50 Represents erroneous QMP monitor reply
51 """
52 def __init__(self, reply):
53 try:
54 desc = reply["error"]["desc"]
55 except KeyError:
56 desc = reply
57 super(MonitorResponseError, self).__init__(desc)
58 self.reply = reply
61 class QEMUMachine(object):
62 """
63 A QEMU VM
65 Use this object as a context manager to ensure the QEMU process terminates::
67 with VM(binary) as vm:
68 ...
69 # vm is guaranteed to be shut down here
70 """
72 def __init__(self, binary, args=None, wrapper=None, name=None,
73 test_dir="/var/tmp", monitor_address=None,
74 socket_scm_helper=None):
75 '''
76 Initialize a QEMUMachine
78 @param binary: path to the qemu binary
79 @param args: list of extra arguments
80 @param wrapper: list of arguments used as prefix to qemu binary
81 @param name: prefix for socket and log file names (default: qemu-PID)
82 @param test_dir: where to create socket and log file
83 @param monitor_address: address for QMP monitor
84 @param socket_scm_helper: helper program, required for send_fd_scm()
85 @note: Qemu process is not started until launch() is used.
86 '''
87 if args is None:
88 args = []
89 if wrapper is None:
90 wrapper = []
91 if name is None:
92 name = "qemu-%d" % os.getpid()
93 self._name = name
94 self._monitor_address = monitor_address
95 self._vm_monitor = None
96 self._qemu_log_path = None
97 self._qemu_log_file = None
98 self._popen = None
99 self._binary = binary
100 self._args = list(args) # Force copy args in case we modify them
101 self._wrapper = wrapper
102 self._events = []
103 self._iolog = None
104 self._socket_scm_helper = socket_scm_helper
105 self._qmp = None
106 self._qemu_full_args = None
107 self._test_dir = test_dir
108 self._temp_dir = None
109 self._launched = False
110 self._machine = None
111 self._console_set = False
112 self._console_device_type = None
113 self._console_address = None
114 self._console_socket = None
116 # just in case logging wasn't configured by the main script:
117 logging.basicConfig()
119 def __enter__(self):
120 return self
122 def __exit__(self, exc_type, exc_val, exc_tb):
123 self.shutdown()
124 return False
126 def add_monitor_null(self):
128 This can be used to add an unused monitor instance.
130 self._args.append('-monitor')
131 self._args.append('null')
133 def add_fd(self, fd, fdset, opaque, opts=''):
135 Pass a file descriptor to the VM
137 options = ['fd=%d' % fd,
138 'set=%d' % fdset,
139 'opaque=%s' % opaque]
140 if opts:
141 options.append(opts)
143 # This did not exist before 3.4, but since then it is
144 # mandatory for our purpose
145 if hasattr(os, 'set_inheritable'):
146 os.set_inheritable(fd, True)
148 self._args.append('-add-fd')
149 self._args.append(','.join(options))
150 return self
152 def send_fd_scm(self, fd=None, file_path=None):
154 Send an fd or file_path to socket_scm_helper.
156 Exactly one of fd and file_path must be given.
157 If it is file_path, the helper will open that file and pass its own fd.
159 # In iotest.py, the qmp should always use unix socket.
160 assert self._qmp.is_scm_available()
161 if self._socket_scm_helper is None:
162 raise QEMUMachineError("No path to socket_scm_helper set")
163 if not os.path.exists(self._socket_scm_helper):
164 raise QEMUMachineError("%s does not exist" %
165 self._socket_scm_helper)
167 # This did not exist before 3.4, but since then it is
168 # mandatory for our purpose
169 if hasattr(os, 'set_inheritable'):
170 os.set_inheritable(self._qmp.get_sock_fd(), True)
171 if fd is not None:
172 os.set_inheritable(fd, True)
174 fd_param = ["%s" % self._socket_scm_helper,
175 "%d" % self._qmp.get_sock_fd()]
177 if file_path is not None:
178 assert fd is None
179 fd_param.append(file_path)
180 else:
181 assert fd is not None
182 fd_param.append(str(fd))
184 devnull = open(os.path.devnull, 'rb')
185 proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
186 stderr=subprocess.STDOUT, close_fds=False)
187 output = proc.communicate()[0]
188 if output:
189 LOG.debug(output)
191 return proc.returncode
193 @staticmethod
194 def _remove_if_exists(path):
196 Remove file object at path if it exists
198 try:
199 os.remove(path)
200 except OSError as exception:
201 if exception.errno == errno.ENOENT:
202 return
203 raise
205 def is_running(self):
206 """Returns true if the VM is running."""
207 return self._popen is not None and self._popen.poll() is None
209 def exitcode(self):
210 """Returns the exit code if possible, or None."""
211 if self._popen is None:
212 return None
213 return self._popen.poll()
215 def get_pid(self):
216 """Returns the PID of the running process, or None."""
217 if not self.is_running():
218 return None
219 return self._popen.pid
221 def _load_io_log(self):
222 if self._qemu_log_path is not None:
223 with open(self._qemu_log_path, "r") as iolog:
224 self._iolog = iolog.read()
226 def _base_args(self):
227 if isinstance(self._monitor_address, tuple):
228 moncdev = "socket,id=mon,host=%s,port=%s" % (
229 self._monitor_address[0],
230 self._monitor_address[1])
231 else:
232 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
233 args = ['-chardev', moncdev,
234 '-mon', 'chardev=mon,mode=control',
235 '-display', 'none', '-vga', 'none']
236 if self._machine is not None:
237 args.extend(['-machine', self._machine])
238 if self._console_set:
239 self._console_address = os.path.join(self._temp_dir,
240 self._name + "-console.sock")
241 chardev = ('socket,id=console,path=%s,server,nowait' %
242 self._console_address)
243 args.extend(['-chardev', chardev])
244 if self._console_device_type is None:
245 args.extend(['-serial', 'chardev:console'])
246 else:
247 device = '%s,chardev=console' % self._console_device_type
248 args.extend(['-device', device])
249 return args
251 def _pre_launch(self):
252 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
253 if self._monitor_address is not None:
254 self._vm_monitor = self._monitor_address
255 else:
256 self._vm_monitor = os.path.join(self._temp_dir,
257 self._name + "-monitor.sock")
258 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
259 self._qemu_log_file = open(self._qemu_log_path, 'wb')
261 self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
262 server=True)
264 def _post_launch(self):
265 self._qmp.accept()
267 def _post_shutdown(self):
268 if self._qemu_log_file is not None:
269 self._qemu_log_file.close()
270 self._qemu_log_file = None
272 self._qemu_log_path = None
274 if self._console_socket is not None:
275 self._console_socket.close()
276 self._console_socket = None
278 if self._temp_dir is not None:
279 shutil.rmtree(self._temp_dir)
280 self._temp_dir = None
282 def launch(self):
284 Launch the VM and make sure we cleanup and expose the
285 command line/output in case of exception
288 if self._launched:
289 raise QEMUMachineError('VM already launched')
291 self._iolog = None
292 self._qemu_full_args = None
293 try:
294 self._launch()
295 self._launched = True
296 except:
297 self.shutdown()
299 LOG.debug('Error launching VM')
300 if self._qemu_full_args:
301 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
302 if self._iolog:
303 LOG.debug('Output: %r', self._iolog)
304 raise
306 def _launch(self):
308 Launch the VM and establish a QMP connection
310 devnull = open(os.path.devnull, 'rb')
311 self._pre_launch()
312 self._qemu_full_args = (self._wrapper + [self._binary] +
313 self._base_args() + self._args)
314 LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
315 self._popen = subprocess.Popen(self._qemu_full_args,
316 stdin=devnull,
317 stdout=self._qemu_log_file,
318 stderr=subprocess.STDOUT,
319 shell=False,
320 close_fds=False)
321 self._post_launch()
323 def wait(self):
325 Wait for the VM to power off
327 self._popen.wait()
328 self._qmp.close()
329 self._load_io_log()
330 self._post_shutdown()
332 def shutdown(self, has_quit=False):
334 Terminate the VM and clean up
336 if self.is_running():
337 try:
338 if not has_quit:
339 self._qmp.cmd('quit')
340 self._qmp.close()
341 except:
342 self._popen.kill()
343 self._popen.wait()
345 self._load_io_log()
346 self._post_shutdown()
348 exitcode = self.exitcode()
349 if exitcode is not None and exitcode < 0:
350 msg = 'qemu received signal %i: %s'
351 if self._qemu_full_args:
352 command = ' '.join(self._qemu_full_args)
353 else:
354 command = ''
355 LOG.warning(msg, -exitcode, command)
357 self._launched = False
359 def qmp(self, cmd, conv_keys=True, **args):
361 Invoke a QMP command and return the response dict
363 qmp_args = dict()
364 for key, value in args.items():
365 if conv_keys:
366 qmp_args[key.replace('_', '-')] = value
367 else:
368 qmp_args[key] = value
370 return self._qmp.cmd(cmd, args=qmp_args)
372 def command(self, cmd, conv_keys=True, **args):
374 Invoke a QMP command.
375 On success return the response dict.
376 On failure raise an exception.
378 reply = self.qmp(cmd, conv_keys, **args)
379 if reply is None:
380 raise qmp.QMPError("Monitor is closed")
381 if "error" in reply:
382 raise MonitorResponseError(reply)
383 return reply["return"]
385 def get_qmp_event(self, wait=False):
387 Poll for one queued QMP events and return it
389 if self._events:
390 return self._events.pop(0)
391 return self._qmp.pull_event(wait=wait)
393 def get_qmp_events(self, wait=False):
395 Poll for queued QMP events and return a list of dicts
397 events = self._qmp.get_events(wait=wait)
398 events.extend(self._events)
399 del self._events[:]
400 self._qmp.clear_events()
401 return events
403 @staticmethod
404 def event_match(event, match=None):
406 Check if an event matches optional match criteria.
408 The match criteria takes the form of a matching subdict. The event is
409 checked to be a superset of the subdict, recursively, with matching
410 values whenever the subdict values are not None.
412 This has a limitation that you cannot explicitly check for None values.
414 Examples, with the subdict queries on the left:
415 - None matches any object.
416 - {"foo": None} matches {"foo": {"bar": 1}}
417 - {"foo": None} matches {"foo": 5}
418 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
419 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
421 if match is None:
422 return True
424 try:
425 for key in match:
426 if key in event:
427 if not QEMUMachine.event_match(event[key], match[key]):
428 return False
429 else:
430 return False
431 return True
432 except TypeError:
433 # either match or event wasn't iterable (not a dict)
434 return match == event
436 def event_wait(self, name, timeout=60.0, match=None):
438 event_wait waits for and returns a named event from QMP with a timeout.
440 name: The event to wait for.
441 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
442 match: Optional match criteria. See event_match for details.
444 return self.events_wait([(name, match)], timeout)
446 def events_wait(self, events, timeout=60.0):
448 events_wait waits for and returns a named event from QMP with a timeout.
450 events: a sequence of (name, match_criteria) tuples.
451 The match criteria are optional and may be None.
452 See event_match for details.
453 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
455 def _match(event):
456 for name, match in events:
457 if event['event'] == name and self.event_match(event, match):
458 return True
459 return False
461 # Search cached events
462 for event in self._events:
463 if _match(event):
464 self._events.remove(event)
465 return event
467 # Poll for new events
468 while True:
469 event = self._qmp.pull_event(wait=timeout)
470 if _match(event):
471 return event
472 self._events.append(event)
474 return None
476 def get_log(self):
478 After self.shutdown or failed qemu execution, this returns the output
479 of the qemu process.
481 return self._iolog
483 def add_args(self, *args):
485 Adds to the list of extra arguments to be given to the QEMU binary
487 self._args.extend(args)
489 def set_machine(self, machine_type):
491 Sets the machine type
493 If set, the machine type will be added to the base arguments
494 of the resulting QEMU command line.
496 self._machine = machine_type
498 def set_console(self, device_type=None):
500 Sets the device type for a console device
502 If set, the console device and a backing character device will
503 be added to the base arguments of the resulting QEMU command
504 line.
506 This is a convenience method that will either use the provided
507 device type, or default to a "-serial chardev:console" command
508 line argument.
510 The actual setting of command line arguments will be be done at
511 machine launch time, as it depends on the temporary directory
512 to be created.
514 @param device_type: the device type, such as "isa-serial". If
515 None is given (the default value) a "-serial
516 chardev:console" command line argument will
517 be used instead, resorting to the machine's
518 default device type.
520 self._console_set = True
521 self._console_device_type = device_type
523 @property
524 def console_socket(self):
526 Returns a socket connected to the console
528 if self._console_socket is None:
529 self._console_socket = socket.socket(socket.AF_UNIX,
530 socket.SOCK_STREAM)
531 self._console_socket.connect(self._console_address)
532 return self._console_socket