blob: 6e3b0e677193299ffa45306b2f6350389256a8e1 [file] [log] [blame]
Daniel P. Berrange66613972016-07-20 14:23:10 +01001# 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
Amador Pahim4738b0a2017-09-01 13:28:18 +020016import logging
Daniel P. Berrange66613972016-07-20 14:23:10 +010017import os
Daniel P. Berrange66613972016-07-20 14:23:10 +010018import subprocess
19import qmp.qmp
Cleber Rosa22dea9d2018-05-30 14:41:55 -040020import re
Amador Pahimaf99fa92018-01-22 21:50:28 +010021import shutil
Cleber Rosa22dea9d2018-05-30 14:41:55 -040022import socket
Amador Pahimaf99fa92018-01-22 21:50:28 +010023import tempfile
Daniel P. Berrange66613972016-07-20 14:23:10 +010024
25
Amador Pahim4738b0a2017-09-01 13:28:18 +020026LOG = logging.getLogger(__name__)
27
28
Philippe Mathieu-Daudéb59b82e2018-10-13 02:40:26 +020029def kvm_available(target_arch=None):
Philippe Mathieu-Daudé67a52f32018-10-13 02:40:31 +020030 if target_arch and target_arch != os.uname()[4]:
31 return False
Philippe Mathieu-Daudéb59b82e2018-10-13 02:40:26 +020032 return os.access("/dev/kvm", os.R_OK | os.W_OK)
33
34
Cleber Rosa22dea9d2018-05-30 14:41:55 -040035#: Maps machine types to the preferred console device types
36CONSOLE_DEV_TYPES = {
37 r'^clipper$': 'isa-serial',
38 r'^malta': 'isa-serial',
39 r'^(pc.*|q35.*|isapc)$': 'isa-serial',
40 r'^(40p|powernv|prep)$': 'isa-serial',
41 r'^pseries.*': 'spapr-vty',
42 r'^s390-ccw-virtio.*': 'sclpconsole',
43 }
44
45
Amador Pahim4738b0a2017-09-01 13:28:18 +020046class QEMUMachineError(Exception):
47 """
48 Exception called when an error in QEMUMachine happens.
49 """
50
51
Cleber Rosa22dea9d2018-05-30 14:41:55 -040052class QEMUMachineAddDeviceError(QEMUMachineError):
53 """
54 Exception raised when a request to add a device can not be fulfilled
55
56 The failures are caused by limitations, lack of information or conflicting
57 requests on the QEMUMachine methods. This exception does not represent
58 failures reported by the QEMU binary itself.
59 """
60
Lukáš Doktora004e242017-08-18 16:26:08 +020061class MonitorResponseError(qmp.qmp.QMPError):
Cleber Rosae301e652018-10-04 12:18:51 -040062 """
Lukáš Doktora004e242017-08-18 16:26:08 +020063 Represents erroneous QMP monitor reply
Cleber Rosae301e652018-10-04 12:18:51 -040064 """
Lukáš Doktora004e242017-08-18 16:26:08 +020065 def __init__(self, reply):
66 try:
67 desc = reply["error"]["desc"]
68 except KeyError:
69 desc = reply
70 super(MonitorResponseError, self).__init__(desc)
71 self.reply = reply
72
73
Daniel P. Berrange66613972016-07-20 14:23:10 +010074class QEMUMachine(object):
Cleber Rosae301e652018-10-04 12:18:51 -040075 """
76 A QEMU VM
Stefan Hajnoczid792bc32017-08-24 08:22:00 +010077
78 Use this object as a context manager to ensure the QEMU process terminates::
79
80 with VM(binary) as vm:
81 ...
82 # vm is guaranteed to be shut down here
Cleber Rosae301e652018-10-04 12:18:51 -040083 """
Daniel P. Berrange66613972016-07-20 14:23:10 +010084
Lukáš Doktor2782fc52017-08-18 16:26:05 +020085 def __init__(self, binary, args=None, wrapper=None, name=None,
Lukáš Doktor2d853c72017-08-18 16:26:04 +020086 test_dir="/var/tmp", monitor_address=None,
Eduardo Habkost1a6d3752017-10-05 14:20:13 -030087 socket_scm_helper=None):
Lukáš Doktor2d853c72017-08-18 16:26:04 +020088 '''
89 Initialize a QEMUMachine
90
91 @param binary: path to the qemu binary
92 @param args: list of extra arguments
93 @param wrapper: list of arguments used as prefix to qemu binary
94 @param name: prefix for socket and log file names (default: qemu-PID)
95 @param test_dir: where to create socket and log file
96 @param monitor_address: address for QMP monitor
Cleber Rosaa5a98622018-10-04 12:18:52 -040097 @param socket_scm_helper: helper program, required for send_fd_scm()
Lukáš Doktor2d853c72017-08-18 16:26:04 +020098 @note: Qemu process is not started until launch() is used.
99 '''
Lukáš Doktor2782fc52017-08-18 16:26:05 +0200100 if args is None:
101 args = []
102 if wrapper is None:
103 wrapper = []
Daniel P. Berrange66613972016-07-20 14:23:10 +0100104 if name is None:
105 name = "qemu-%d" % os.getpid()
Amador Pahimaf99fa92018-01-22 21:50:28 +0100106 self._name = name
Daniel P. Berrange66613972016-07-20 14:23:10 +0100107 self._monitor_address = monitor_address
Amador Pahimaf99fa92018-01-22 21:50:28 +0100108 self._vm_monitor = None
109 self._qemu_log_path = None
110 self._qemu_log_file = None
Daniel P. Berrange66613972016-07-20 14:23:10 +0100111 self._popen = None
112 self._binary = binary
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200113 self._args = list(args) # Force copy args in case we modify them
Daniel P. Berrange66613972016-07-20 14:23:10 +0100114 self._wrapper = wrapper
115 self._events = []
116 self._iolog = None
Daniel P. Berrange4c44b4a2016-07-26 17:16:07 +0100117 self._socket_scm_helper = socket_scm_helper
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200118 self._qmp = None
Amador Pahimdab91d92017-09-01 13:28:20 +0200119 self._qemu_full_args = None
Amador Pahimaf99fa92018-01-22 21:50:28 +0100120 self._test_dir = test_dir
121 self._temp_dir = None
Amador Pahim156dc7b2018-01-22 21:50:33 +0100122 self._launched = False
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400123 self._machine = None
124 self._console_device_type = None
125 self._console_address = None
126 self._console_socket = None
Daniel P. Berrange66613972016-07-20 14:23:10 +0100127
Eduardo Habkost58103142017-09-21 13:22:34 -0300128 # just in case logging wasn't configured by the main script:
Eduardo Habkost1a6d3752017-10-05 14:20:13 -0300129 logging.basicConfig()
Eduardo Habkost58103142017-09-21 13:22:34 -0300130
Stefan Hajnoczid792bc32017-08-24 08:22:00 +0100131 def __enter__(self):
132 return self
133
134 def __exit__(self, exc_type, exc_val, exc_tb):
135 self.shutdown()
136 return False
137
Daniel P. Berrange66613972016-07-20 14:23:10 +0100138 # This can be used to add an unused monitor instance.
139 def add_monitor_telnet(self, ip, port):
140 args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
141 self._args.append('-monitor')
142 self._args.append(args)
143
144 def add_fd(self, fd, fdset, opaque, opts=''):
Cleber Rosae301e652018-10-04 12:18:51 -0400145 """
146 Pass a file descriptor to the VM
147 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100148 options = ['fd=%d' % fd,
149 'set=%d' % fdset,
150 'opaque=%s' % opaque]
151 if opts:
152 options.append(opts)
153
Max Reitzbf43b292018-10-22 14:53:04 +0100154 # This did not exist before 3.4, but since then it is
155 # mandatory for our purpose
156 if hasattr(os, 'set_inheritable'):
157 os.set_inheritable(fd, True)
158
Daniel P. Berrange66613972016-07-20 14:23:10 +0100159 self._args.append('-add-fd')
160 self._args.append(','.join(options))
161 return self
162
Max Reitzbf43b292018-10-22 14:53:04 +0100163 # Exactly one of fd and file_path must be given.
164 # (If it is file_path, the helper will open that file and pass its
165 # own fd)
166 def send_fd_scm(self, fd=None, file_path=None):
Daniel P. Berrange66613972016-07-20 14:23:10 +0100167 # In iotest.py, the qmp should always use unix socket.
168 assert self._qmp.is_scm_available()
Daniel P. Berrange4c44b4a2016-07-26 17:16:07 +0100169 if self._socket_scm_helper is None:
Amador Pahim4738b0a2017-09-01 13:28:18 +0200170 raise QEMUMachineError("No path to socket_scm_helper set")
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200171 if not os.path.exists(self._socket_scm_helper):
Amador Pahim4738b0a2017-09-01 13:28:18 +0200172 raise QEMUMachineError("%s does not exist" %
173 self._socket_scm_helper)
Max Reitzbf43b292018-10-22 14:53:04 +0100174
175 # This did not exist before 3.4, but since then it is
176 # mandatory for our purpose
177 if hasattr(os, 'set_inheritable'):
178 os.set_inheritable(self._qmp.get_sock_fd(), True)
179 if fd is not None:
180 os.set_inheritable(fd, True)
181
Daniel P. Berrange4c44b4a2016-07-26 17:16:07 +0100182 fd_param = ["%s" % self._socket_scm_helper,
Max Reitzbf43b292018-10-22 14:53:04 +0100183 "%d" % self._qmp.get_sock_fd()]
184
185 if file_path is not None:
186 assert fd is None
187 fd_param.append(file_path)
188 else:
189 assert fd is not None
190 fd_param.append(str(fd))
191
Amador Pahim63e0ba52017-09-01 13:28:19 +0200192 devnull = open(os.path.devnull, 'rb')
Amador Pahim4738b0a2017-09-01 13:28:18 +0200193 proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
Max Reitzbf43b292018-10-22 14:53:04 +0100194 stderr=subprocess.STDOUT, close_fds=False)
Amador Pahim4738b0a2017-09-01 13:28:18 +0200195 output = proc.communicate()[0]
196 if output:
197 LOG.debug(output)
198
199 return proc.returncode
Daniel P. Berrange66613972016-07-20 14:23:10 +0100200
201 @staticmethod
202 def _remove_if_exists(path):
Cleber Rosae301e652018-10-04 12:18:51 -0400203 """
204 Remove file object at path if it exists
205 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100206 try:
207 os.remove(path)
208 except OSError as exception:
209 if exception.errno == errno.ENOENT:
210 return
211 raise
212
Eduardo Habkost37bbcd52017-05-26 15:11:58 -0300213 def is_running(self):
Amador Pahim17589ca2018-01-22 21:50:31 +0100214 return self._popen is not None and self._popen.poll() is None
Eduardo Habkost37bbcd52017-05-26 15:11:58 -0300215
Eduardo Habkostb2b8d982017-05-26 15:11:59 -0300216 def exitcode(self):
217 if self._popen is None:
218 return None
Amador Pahim17589ca2018-01-22 21:50:31 +0100219 return self._popen.poll()
Eduardo Habkostb2b8d982017-05-26 15:11:59 -0300220
Daniel P. Berrange66613972016-07-20 14:23:10 +0100221 def get_pid(self):
Eduardo Habkost37bbcd52017-05-26 15:11:58 -0300222 if not self.is_running():
Daniel P. Berrange66613972016-07-20 14:23:10 +0100223 return None
224 return self._popen.pid
225
226 def _load_io_log(self):
Amador Pahim04a963b2018-01-22 21:50:30 +0100227 if self._qemu_log_path is not None:
228 with open(self._qemu_log_path, "r") as iolog:
229 self._iolog = iolog.read()
Daniel P. Berrange66613972016-07-20 14:23:10 +0100230
231 def _base_args(self):
232 if isinstance(self._monitor_address, tuple):
233 moncdev = "socket,id=mon,host=%s,port=%s" % (
234 self._monitor_address[0],
235 self._monitor_address[1])
236 else:
Amador Pahimaf99fa92018-01-22 21:50:28 +0100237 moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400238 args = ['-chardev', moncdev,
Daniel P. Berrange66613972016-07-20 14:23:10 +0100239 '-mon', 'chardev=mon,mode=control',
240 '-display', 'none', '-vga', 'none']
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400241 if self._machine is not None:
242 args.extend(['-machine', self._machine])
243 if self._console_device_type is not None:
244 self._console_address = os.path.join(self._temp_dir,
245 self._name + "-console.sock")
246 chardev = ('socket,id=console,path=%s,server,nowait' %
247 self._console_address)
248 device = '%s,chardev=console' % self._console_device_type
249 args.extend(['-chardev', chardev, '-device', device])
250 return args
Daniel P. Berrange66613972016-07-20 14:23:10 +0100251
252 def _pre_launch(self):
Amador Pahimaf99fa92018-01-22 21:50:28 +0100253 self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
254 if self._monitor_address is not None:
255 self._vm_monitor = self._monitor_address
256 else:
257 self._vm_monitor = os.path.join(self._temp_dir,
258 self._name + "-monitor.sock")
259 self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
260 self._qemu_log_file = open(self._qemu_log_path, 'wb')
261
262 self._qmp = qmp.qmp.QEMUMonitorProtocol(self._vm_monitor,
Eduardo Habkost09177652017-10-05 14:20:12 -0300263 server=True)
Daniel P. Berrange66613972016-07-20 14:23:10 +0100264
265 def _post_launch(self):
266 self._qmp.accept()
267
268 def _post_shutdown(self):
Amador Pahimaf99fa92018-01-22 21:50:28 +0100269 if self._qemu_log_file is not None:
270 self._qemu_log_file.close()
271 self._qemu_log_file = None
272
273 self._qemu_log_path = None
274
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400275 if self._console_socket is not None:
276 self._console_socket.close()
277 self._console_socket = None
278
Amador Pahimaf99fa92018-01-22 21:50:28 +0100279 if self._temp_dir is not None:
280 shutil.rmtree(self._temp_dir)
281 self._temp_dir = None
Daniel P. Berrange66613972016-07-20 14:23:10 +0100282
283 def launch(self):
Amador Pahimd301bcc2018-01-22 21:50:29 +0100284 """
285 Launch the VM and make sure we cleanup and expose the
286 command line/output in case of exception
287 """
Amador Pahim156dc7b2018-01-22 21:50:33 +0100288
289 if self._launched:
290 raise QEMUMachineError('VM already launched')
291
Amador Pahimb92a0012017-09-01 13:28:21 +0200292 self._iolog = None
Amador Pahimdab91d92017-09-01 13:28:20 +0200293 self._qemu_full_args = None
Daniel P. Berrange66613972016-07-20 14:23:10 +0100294 try:
Amador Pahimd301bcc2018-01-22 21:50:29 +0100295 self._launch()
Amador Pahim156dc7b2018-01-22 21:50:33 +0100296 self._launched = True
Daniel P. Berrange66613972016-07-20 14:23:10 +0100297 except:
Amador Pahimc58b5352018-01-22 21:50:32 +0100298 self.shutdown()
Amador Pahimb92a0012017-09-01 13:28:21 +0200299
300 LOG.debug('Error launching VM')
301 if self._qemu_full_args:
302 LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
303 if self._iolog:
304 LOG.debug('Output: %r', self._iolog)
Daniel P. Berrange66613972016-07-20 14:23:10 +0100305 raise
306
Amador Pahimd301bcc2018-01-22 21:50:29 +0100307 def _launch(self):
Cleber Rosae301e652018-10-04 12:18:51 -0400308 """
309 Launch the VM and establish a QMP connection
310 """
Amador Pahimd301bcc2018-01-22 21:50:29 +0100311 devnull = open(os.path.devnull, 'rb')
312 self._pre_launch()
313 self._qemu_full_args = (self._wrapper + [self._binary] +
314 self._base_args() + self._args)
315 self._popen = subprocess.Popen(self._qemu_full_args,
316 stdin=devnull,
317 stdout=self._qemu_log_file,
318 stderr=subprocess.STDOUT,
Max Reitzbf43b292018-10-22 14:53:04 +0100319 shell=False,
320 close_fds=False)
Amador Pahimd301bcc2018-01-22 21:50:29 +0100321 self._post_launch()
322
Fam Zheng22491a22017-09-05 10:11:51 +0800323 def wait(self):
Cleber Rosae301e652018-10-04 12:18:51 -0400324 """
325 Wait for the VM to power off
326 """
Fam Zheng22491a22017-09-05 10:11:51 +0800327 self._popen.wait()
328 self._qmp.close()
329 self._load_io_log()
330 self._post_shutdown()
331
Daniel P. Berrange66613972016-07-20 14:23:10 +0100332 def shutdown(self):
Cleber Rosae301e652018-10-04 12:18:51 -0400333 """
334 Terminate the VM and clean up
335 """
Eduardo Habkost37bbcd52017-05-26 15:11:58 -0300336 if self.is_running():
Daniel P. Berrange66613972016-07-20 14:23:10 +0100337 try:
338 self._qmp.cmd('quit')
339 self._qmp.close()
340 except:
341 self._popen.kill()
Amador Pahimdab91d92017-09-01 13:28:20 +0200342 self._popen.wait()
Daniel P. Berrange66613972016-07-20 14:23:10 +0100343
Amador Pahim04a963b2018-01-22 21:50:30 +0100344 self._load_io_log()
345 self._post_shutdown()
Daniel P. Berrange66613972016-07-20 14:23:10 +0100346
Amador Pahimdab91d92017-09-01 13:28:20 +0200347 exitcode = self.exitcode()
348 if exitcode is not None and exitcode < 0:
349 msg = 'qemu received signal %i: %s'
350 if self._qemu_full_args:
351 command = ' '.join(self._qemu_full_args)
352 else:
353 command = ''
354 LOG.warn(msg, exitcode, command)
355
Amador Pahim156dc7b2018-01-22 21:50:33 +0100356 self._launched = False
357
Daniel P. Berrange66613972016-07-20 14:23:10 +0100358 def qmp(self, cmd, conv_keys=True, **args):
Cleber Rosae301e652018-10-04 12:18:51 -0400359 """
360 Invoke a QMP command and return the response dict
361 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100362 qmp_args = dict()
Eduardo Habkostfb2e1cc2018-03-12 15:55:01 -0300363 for key, value in args.items():
Daniel P. Berrange66613972016-07-20 14:23:10 +0100364 if conv_keys:
Lukáš Doktor41f714b2017-08-18 16:26:07 +0200365 qmp_args[key.replace('_', '-')] = value
Daniel P. Berrange66613972016-07-20 14:23:10 +0100366 else:
Lukáš Doktor7f33ca72017-08-18 16:26:06 +0200367 qmp_args[key] = value
Daniel P. Berrange66613972016-07-20 14:23:10 +0100368
369 return self._qmp.cmd(cmd, args=qmp_args)
370
371 def command(self, cmd, conv_keys=True, **args):
Cleber Rosae301e652018-10-04 12:18:51 -0400372 """
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200373 Invoke a QMP command.
374 On success return the response dict.
375 On failure raise an exception.
Cleber Rosae301e652018-10-04 12:18:51 -0400376 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100377 reply = self.qmp(cmd, conv_keys, **args)
378 if reply is None:
Lukáš Doktora004e242017-08-18 16:26:08 +0200379 raise qmp.qmp.QMPError("Monitor is closed")
Daniel P. Berrange66613972016-07-20 14:23:10 +0100380 if "error" in reply:
Lukáš Doktora004e242017-08-18 16:26:08 +0200381 raise MonitorResponseError(reply)
Daniel P. Berrange66613972016-07-20 14:23:10 +0100382 return reply["return"]
383
384 def get_qmp_event(self, wait=False):
Cleber Rosae301e652018-10-04 12:18:51 -0400385 """
386 Poll for one queued QMP events and return it
387 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100388 if len(self._events) > 0:
389 return self._events.pop(0)
390 return self._qmp.pull_event(wait=wait)
391
392 def get_qmp_events(self, wait=False):
Cleber Rosae301e652018-10-04 12:18:51 -0400393 """
394 Poll for queued QMP events and return a list of dicts
395 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100396 events = self._qmp.get_events(wait=wait)
397 events.extend(self._events)
398 del self._events[:]
399 self._qmp.clear_events()
400 return events
401
402 def event_wait(self, name, timeout=60.0, match=None):
Cleber Rosae301e652018-10-04 12:18:51 -0400403 """
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200404 Wait for specified timeout on named event in QMP; optionally filter
405 results by match.
406
407 The 'match' is checked to be a recursive subset of the 'event'; skips
408 branch processing on match's value None
409 {"foo": {"bar": 1}} matches {"foo": None}
410 {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
Cleber Rosae301e652018-10-04 12:18:51 -0400411 """
Daniel P. Berrange4c44b4a2016-07-26 17:16:07 +0100412 def event_match(event, match=None):
413 if match is None:
414 return True
415
416 for key in match:
417 if key in event:
418 if isinstance(event[key], dict):
419 if not event_match(event[key], match[key]):
420 return False
421 elif event[key] != match[key]:
422 return False
423 else:
424 return False
425
426 return True
427
Daniel P. Berrange66613972016-07-20 14:23:10 +0100428 # Search cached events
429 for event in self._events:
430 if (event['event'] == name) and event_match(event, match):
431 self._events.remove(event)
432 return event
433
434 # Poll for new events
435 while True:
436 event = self._qmp.pull_event(wait=timeout)
437 if (event['event'] == name) and event_match(event, match):
438 return event
439 self._events.append(event)
440
441 return None
442
443 def get_log(self):
Cleber Rosae301e652018-10-04 12:18:51 -0400444 """
Lukáš Doktor2d853c72017-08-18 16:26:04 +0200445 After self.shutdown or failed qemu execution, this returns the output
446 of the qemu process.
Cleber Rosae301e652018-10-04 12:18:51 -0400447 """
Daniel P. Berrange66613972016-07-20 14:23:10 +0100448 return self._iolog
Cleber Rosa572a8242018-05-30 14:41:53 -0400449
450 def add_args(self, *args):
Cleber Rosae301e652018-10-04 12:18:51 -0400451 """
Cleber Rosa572a8242018-05-30 14:41:53 -0400452 Adds to the list of extra arguments to be given to the QEMU binary
Cleber Rosae301e652018-10-04 12:18:51 -0400453 """
Cleber Rosa572a8242018-05-30 14:41:53 -0400454 self._args.extend(args)
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400455
456 def set_machine(self, machine_type):
Cleber Rosae301e652018-10-04 12:18:51 -0400457 """
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400458 Sets the machine type
459
460 If set, the machine type will be added to the base arguments
461 of the resulting QEMU command line.
Cleber Rosae301e652018-10-04 12:18:51 -0400462 """
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400463 self._machine = machine_type
464
465 def set_console(self, device_type=None):
Cleber Rosae301e652018-10-04 12:18:51 -0400466 """
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400467 Sets the device type for a console device
468
469 If set, the console device and a backing character device will
470 be added to the base arguments of the resulting QEMU command
471 line.
472
473 This is a convenience method that will either use the provided
474 device type, of if not given, it will used the device type set
475 on CONSOLE_DEV_TYPES.
476
477 The actual setting of command line arguments will be be done at
478 machine launch time, as it depends on the temporary directory
479 to be created.
480
481 @param device_type: the device type, such as "isa-serial"
482 @raises: QEMUMachineAddDeviceError if the device type is not given
483 and can not be determined.
Cleber Rosae301e652018-10-04 12:18:51 -0400484 """
Cleber Rosa22dea9d2018-05-30 14:41:55 -0400485 if device_type is None:
486 if self._machine is None:
487 raise QEMUMachineAddDeviceError("Can not add a console device:"
488 " QEMU instance without a "
489 "defined machine type")
490 for regex, device in CONSOLE_DEV_TYPES.items():
491 if re.match(regex, self._machine):
492 device_type = device
493 break
494 if device_type is None:
495 raise QEMUMachineAddDeviceError("Can not add a console device:"
496 " no matching console device "
497 "type definition")
498 self._console_device_type = device_type
499
500 @property
501 def console_socket(self):
502 """
503 Returns a socket connected to the console
504 """
505 if self._console_socket is None:
506 self._console_socket = socket.socket(socket.AF_UNIX,
507 socket.SOCK_STREAM)
508 self._console_socket.connect(self._console_address)
509 return self._console_socket