qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

python/qemu: split QEMUMachine out from underneath __init__.py

It's not obvious that something named __init__.py actually houses
important code that isn't relevant to python packaging glue. Move the
QEMUMachine and related error classes out into their own module.

Adjust users to the new import location.

Signed-off-by: John Snow <jsnow@redhat.com>
Message-Id: <20190627212816.27298-2-jsnow@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>

authored by

John Snow and committed by
Eduardo Habkost
abf0bf99 08aa87f5

+540 -519
+1 -501
python/qemu/__init__.py
··· 12 12 # Based on qmp.py. 13 13 # 14 14 15 - import errno 16 15 import logging 17 16 import os 18 - import subprocess 19 - import re 20 - import shutil 21 - import socket 22 - import tempfile 23 17 24 18 from . import qmp 25 - 19 + from . import machine 26 20 27 21 LOG = logging.getLogger(__name__) 28 22 ··· 39 33 if target_arch != ADDITIONAL_ARCHES.get(host_arch): 40 34 return False 41 35 return os.access("/dev/kvm", os.R_OK | os.W_OK) 42 - 43 - 44 - class QEMUMachineError(Exception): 45 - """ 46 - Exception called when an error in QEMUMachine happens. 47 - """ 48 - 49 - 50 - class QEMUMachineAddDeviceError(QEMUMachineError): 51 - """ 52 - Exception raised when a request to add a device can not be fulfilled 53 - 54 - The failures are caused by limitations, lack of information or conflicting 55 - requests on the QEMUMachine methods. This exception does not represent 56 - failures reported by the QEMU binary itself. 57 - """ 58 - 59 - class MonitorResponseError(qmp.QMPError): 60 - """ 61 - Represents erroneous QMP monitor reply 62 - """ 63 - def __init__(self, reply): 64 - try: 65 - desc = reply["error"]["desc"] 66 - except KeyError: 67 - desc = reply 68 - super(MonitorResponseError, self).__init__(desc) 69 - self.reply = reply 70 - 71 - 72 - class QEMUMachine(object): 73 - """ 74 - A QEMU VM 75 - 76 - Use this object as a context manager to ensure the QEMU process terminates:: 77 - 78 - with VM(binary) as vm: 79 - ... 80 - # vm is guaranteed to be shut down here 81 - """ 82 - 83 - def __init__(self, binary, args=None, wrapper=None, name=None, 84 - test_dir="/var/tmp", monitor_address=None, 85 - socket_scm_helper=None): 86 - ''' 87 - Initialize a QEMUMachine 88 - 89 - @param binary: path to the qemu binary 90 - @param args: list of extra arguments 91 - @param wrapper: list of arguments used as prefix to qemu binary 92 - @param name: prefix for socket and log file names (default: qemu-PID) 93 - @param test_dir: where to create socket and log file 94 - @param monitor_address: address for QMP monitor 95 - @param socket_scm_helper: helper program, required for send_fd_scm() 96 - @note: Qemu process is not started until launch() is used. 97 - ''' 98 - if args is None: 99 - args = [] 100 - if wrapper is None: 101 - wrapper = [] 102 - if name is None: 103 - name = "qemu-%d" % os.getpid() 104 - self._name = name 105 - self._monitor_address = monitor_address 106 - self._vm_monitor = None 107 - self._qemu_log_path = None 108 - self._qemu_log_file = None 109 - self._popen = None 110 - self._binary = binary 111 - self._args = list(args) # Force copy args in case we modify them 112 - self._wrapper = wrapper 113 - self._events = [] 114 - self._iolog = None 115 - self._socket_scm_helper = socket_scm_helper 116 - self._qmp = None 117 - self._qemu_full_args = None 118 - self._test_dir = test_dir 119 - self._temp_dir = None 120 - self._launched = False 121 - self._machine = None 122 - self._console_set = False 123 - self._console_device_type = None 124 - self._console_address = None 125 - self._console_socket = None 126 - 127 - # just in case logging wasn't configured by the main script: 128 - logging.basicConfig() 129 - 130 - def __enter__(self): 131 - return self 132 - 133 - def __exit__(self, exc_type, exc_val, exc_tb): 134 - self.shutdown() 135 - return False 136 - 137 - # This can be used to add an unused monitor instance. 138 - def add_monitor_null(self): 139 - self._args.append('-monitor') 140 - self._args.append('null') 141 - 142 - def add_fd(self, fd, fdset, opaque, opts=''): 143 - """ 144 - Pass a file descriptor to the VM 145 - """ 146 - options = ['fd=%d' % fd, 147 - 'set=%d' % fdset, 148 - 'opaque=%s' % opaque] 149 - if opts: 150 - options.append(opts) 151 - 152 - # This did not exist before 3.4, but since then it is 153 - # mandatory for our purpose 154 - if hasattr(os, 'set_inheritable'): 155 - os.set_inheritable(fd, True) 156 - 157 - self._args.append('-add-fd') 158 - self._args.append(','.join(options)) 159 - return self 160 - 161 - # Exactly one of fd and file_path must be given. 162 - # (If it is file_path, the helper will open that file and pass its 163 - # own fd) 164 - def send_fd_scm(self, fd=None, file_path=None): 165 - # In iotest.py, the qmp should always use unix socket. 166 - assert self._qmp.is_scm_available() 167 - if self._socket_scm_helper is None: 168 - raise QEMUMachineError("No path to socket_scm_helper set") 169 - if not os.path.exists(self._socket_scm_helper): 170 - raise QEMUMachineError("%s does not exist" % 171 - self._socket_scm_helper) 172 - 173 - # This did not exist before 3.4, but since then it is 174 - # mandatory for our purpose 175 - if hasattr(os, 'set_inheritable'): 176 - os.set_inheritable(self._qmp.get_sock_fd(), True) 177 - if fd is not None: 178 - os.set_inheritable(fd, True) 179 - 180 - fd_param = ["%s" % self._socket_scm_helper, 181 - "%d" % self._qmp.get_sock_fd()] 182 - 183 - if file_path is not None: 184 - assert fd is None 185 - fd_param.append(file_path) 186 - else: 187 - assert fd is not None 188 - fd_param.append(str(fd)) 189 - 190 - devnull = open(os.path.devnull, 'rb') 191 - proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, 192 - stderr=subprocess.STDOUT, close_fds=False) 193 - output = proc.communicate()[0] 194 - if output: 195 - LOG.debug(output) 196 - 197 - return proc.returncode 198 - 199 - @staticmethod 200 - def _remove_if_exists(path): 201 - """ 202 - Remove file object at path if it exists 203 - """ 204 - try: 205 - os.remove(path) 206 - except OSError as exception: 207 - if exception.errno == errno.ENOENT: 208 - return 209 - raise 210 - 211 - def is_running(self): 212 - return self._popen is not None and self._popen.poll() is None 213 - 214 - def exitcode(self): 215 - if self._popen is None: 216 - return None 217 - return self._popen.poll() 218 - 219 - def get_pid(self): 220 - if not self.is_running(): 221 - return None 222 - return self._popen.pid 223 - 224 - def _load_io_log(self): 225 - if self._qemu_log_path is not None: 226 - with open(self._qemu_log_path, "r") as iolog: 227 - self._iolog = iolog.read() 228 - 229 - def _base_args(self): 230 - if isinstance(self._monitor_address, tuple): 231 - moncdev = "socket,id=mon,host=%s,port=%s" % ( 232 - self._monitor_address[0], 233 - self._monitor_address[1]) 234 - else: 235 - moncdev = 'socket,id=mon,path=%s' % self._vm_monitor 236 - args = ['-chardev', moncdev, 237 - '-mon', 'chardev=mon,mode=control', 238 - '-display', 'none', '-vga', 'none'] 239 - if self._machine is not None: 240 - args.extend(['-machine', self._machine]) 241 - if self._console_set: 242 - self._console_address = os.path.join(self._temp_dir, 243 - self._name + "-console.sock") 244 - chardev = ('socket,id=console,path=%s,server,nowait' % 245 - self._console_address) 246 - args.extend(['-chardev', chardev]) 247 - if self._console_device_type is None: 248 - args.extend(['-serial', 'chardev:console']) 249 - else: 250 - device = '%s,chardev=console' % self._console_device_type 251 - args.extend(['-device', device]) 252 - return args 253 - 254 - def _pre_launch(self): 255 - self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) 256 - if self._monitor_address is not None: 257 - self._vm_monitor = self._monitor_address 258 - else: 259 - self._vm_monitor = os.path.join(self._temp_dir, 260 - self._name + "-monitor.sock") 261 - self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") 262 - self._qemu_log_file = open(self._qemu_log_path, 'wb') 263 - 264 - self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, 265 - server=True) 266 - 267 - def _post_launch(self): 268 - self._qmp.accept() 269 - 270 - def _post_shutdown(self): 271 - if self._qemu_log_file is not None: 272 - self._qemu_log_file.close() 273 - self._qemu_log_file = None 274 - 275 - self._qemu_log_path = None 276 - 277 - if self._console_socket is not None: 278 - self._console_socket.close() 279 - self._console_socket = None 280 - 281 - if self._temp_dir is not None: 282 - shutil.rmtree(self._temp_dir) 283 - self._temp_dir = None 284 - 285 - def launch(self): 286 - """ 287 - Launch the VM and make sure we cleanup and expose the 288 - command line/output in case of exception 289 - """ 290 - 291 - if self._launched: 292 - raise QEMUMachineError('VM already launched') 293 - 294 - self._iolog = None 295 - self._qemu_full_args = None 296 - try: 297 - self._launch() 298 - self._launched = True 299 - except: 300 - self.shutdown() 301 - 302 - LOG.debug('Error launching VM') 303 - if self._qemu_full_args: 304 - LOG.debug('Command: %r', ' '.join(self._qemu_full_args)) 305 - if self._iolog: 306 - LOG.debug('Output: %r', self._iolog) 307 - raise 308 - 309 - def _launch(self): 310 - """ 311 - Launch the VM and establish a QMP connection 312 - """ 313 - devnull = open(os.path.devnull, 'rb') 314 - self._pre_launch() 315 - self._qemu_full_args = (self._wrapper + [self._binary] + 316 - self._base_args() + self._args) 317 - LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) 318 - self._popen = subprocess.Popen(self._qemu_full_args, 319 - stdin=devnull, 320 - stdout=self._qemu_log_file, 321 - stderr=subprocess.STDOUT, 322 - shell=False, 323 - close_fds=False) 324 - self._post_launch() 325 - 326 - def wait(self): 327 - """ 328 - Wait for the VM to power off 329 - """ 330 - self._popen.wait() 331 - self._qmp.close() 332 - self._load_io_log() 333 - self._post_shutdown() 334 - 335 - def shutdown(self): 336 - """ 337 - Terminate the VM and clean up 338 - """ 339 - if self.is_running(): 340 - try: 341 - self._qmp.cmd('quit') 342 - self._qmp.close() 343 - except: 344 - self._popen.kill() 345 - self._popen.wait() 346 - 347 - self._load_io_log() 348 - self._post_shutdown() 349 - 350 - exitcode = self.exitcode() 351 - if exitcode is not None and exitcode < 0: 352 - msg = 'qemu received signal %i: %s' 353 - if self._qemu_full_args: 354 - command = ' '.join(self._qemu_full_args) 355 - else: 356 - command = '' 357 - LOG.warn(msg, -exitcode, command) 358 - 359 - self._launched = False 360 - 361 - def qmp(self, cmd, conv_keys=True, **args): 362 - """ 363 - Invoke a QMP command and return the response dict 364 - """ 365 - qmp_args = dict() 366 - for key, value in args.items(): 367 - if conv_keys: 368 - qmp_args[key.replace('_', '-')] = value 369 - else: 370 - qmp_args[key] = value 371 - 372 - return self._qmp.cmd(cmd, args=qmp_args) 373 - 374 - def command(self, cmd, conv_keys=True, **args): 375 - """ 376 - Invoke a QMP command. 377 - On success return the response dict. 378 - On failure raise an exception. 379 - """ 380 - reply = self.qmp(cmd, conv_keys, **args) 381 - if reply is None: 382 - raise qmp.QMPError("Monitor is closed") 383 - if "error" in reply: 384 - raise MonitorResponseError(reply) 385 - return reply["return"] 386 - 387 - def get_qmp_event(self, wait=False): 388 - """ 389 - Poll for one queued QMP events and return it 390 - """ 391 - if len(self._events) > 0: 392 - return self._events.pop(0) 393 - return self._qmp.pull_event(wait=wait) 394 - 395 - def get_qmp_events(self, wait=False): 396 - """ 397 - Poll for queued QMP events and return a list of dicts 398 - """ 399 - events = self._qmp.get_events(wait=wait) 400 - events.extend(self._events) 401 - del self._events[:] 402 - self._qmp.clear_events() 403 - return events 404 - 405 - @staticmethod 406 - def event_match(event, match=None): 407 - """ 408 - Check if an event matches optional match criteria. 409 - 410 - The match criteria takes the form of a matching subdict. The event is 411 - checked to be a superset of the subdict, recursively, with matching 412 - values whenever the subdict values are not None. 413 - 414 - This has a limitation that you cannot explicitly check for None values. 415 - 416 - Examples, with the subdict queries on the left: 417 - - None matches any object. 418 - - {"foo": None} matches {"foo": {"bar": 1}} 419 - - {"foo": None} matches {"foo": 5} 420 - - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}} 421 - - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}} 422 - """ 423 - if match is None: 424 - return True 425 - 426 - try: 427 - for key in match: 428 - if key in event: 429 - if not QEMUMachine.event_match(event[key], match[key]): 430 - return False 431 - else: 432 - return False 433 - return True 434 - except TypeError: 435 - # either match or event wasn't iterable (not a dict) 436 - return match == event 437 - 438 - def event_wait(self, name, timeout=60.0, match=None): 439 - """ 440 - event_wait waits for and returns a named event from QMP with a timeout. 441 - 442 - name: The event to wait for. 443 - timeout: QEMUMonitorProtocol.pull_event timeout parameter. 444 - match: Optional match criteria. See event_match for details. 445 - """ 446 - return self.events_wait([(name, match)], timeout) 447 - 448 - def events_wait(self, events, timeout=60.0): 449 - """ 450 - events_wait waits for and returns a named event from QMP with a timeout. 451 - 452 - events: a sequence of (name, match_criteria) tuples. 453 - The match criteria are optional and may be None. 454 - See event_match for details. 455 - timeout: QEMUMonitorProtocol.pull_event timeout parameter. 456 - """ 457 - def _match(event): 458 - for name, match in events: 459 - if (event['event'] == name and 460 - self.event_match(event, match)): 461 - return True 462 - return False 463 - 464 - # Search cached events 465 - for event in self._events: 466 - if _match(event): 467 - self._events.remove(event) 468 - return event 469 - 470 - # Poll for new events 471 - while True: 472 - event = self._qmp.pull_event(wait=timeout) 473 - if _match(event): 474 - return event 475 - self._events.append(event) 476 - 477 - return None 478 - 479 - def get_log(self): 480 - """ 481 - After self.shutdown or failed qemu execution, this returns the output 482 - of the qemu process. 483 - """ 484 - return self._iolog 485 - 486 - def add_args(self, *args): 487 - """ 488 - Adds to the list of extra arguments to be given to the QEMU binary 489 - """ 490 - self._args.extend(args) 491 - 492 - def set_machine(self, machine_type): 493 - """ 494 - Sets the machine type 495 - 496 - If set, the machine type will be added to the base arguments 497 - of the resulting QEMU command line. 498 - """ 499 - self._machine = machine_type 500 - 501 - def set_console(self, device_type=None): 502 - """ 503 - Sets the device type for a console device 504 - 505 - If set, the console device and a backing character device will 506 - be added to the base arguments of the resulting QEMU command 507 - line. 508 - 509 - This is a convenience method that will either use the provided 510 - device type, or default to a "-serial chardev:console" command 511 - line argument. 512 - 513 - The actual setting of command line arguments will be be done at 514 - machine launch time, as it depends on the temporary directory 515 - to be created. 516 - 517 - @param device_type: the device type, such as "isa-serial". If 518 - None is given (the default value) a "-serial 519 - chardev:console" command line argument will 520 - be used instead, resorting to the machine's 521 - default device type. 522 - """ 523 - self._console_set = True 524 - self._console_device_type = device_type 525 - 526 - @property 527 - def console_socket(self): 528 - """ 529 - Returns a socket connected to the console 530 - """ 531 - if self._console_socket is None: 532 - self._console_socket = socket.socket(socket.AF_UNIX, 533 - socket.SOCK_STREAM) 534 - self._console_socket.connect(self._console_address) 535 - return self._console_socket
+520
python/qemu/machine.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 <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 + 15 + import errno 16 + import logging 17 + import os 18 + import subprocess 19 + import re 20 + import shutil 21 + import socket 22 + import tempfile 23 + 24 + from . import qmp 25 + 26 + LOG = logging.getLogger(__name__) 27 + 28 + class QEMUMachineError(Exception): 29 + """ 30 + Exception called when an error in QEMUMachine happens. 31 + """ 32 + 33 + 34 + class QEMUMachineAddDeviceError(QEMUMachineError): 35 + """ 36 + Exception raised when a request to add a device can not be fulfilled 37 + 38 + The failures are caused by limitations, lack of information or conflicting 39 + requests on the QEMUMachine methods. This exception does not represent 40 + failures reported by the QEMU binary itself. 41 + """ 42 + 43 + 44 + class MonitorResponseError(qmp.QMPError): 45 + """ 46 + Represents erroneous QMP monitor reply 47 + """ 48 + def __init__(self, reply): 49 + try: 50 + desc = reply["error"]["desc"] 51 + except KeyError: 52 + desc = reply 53 + super(MonitorResponseError, self).__init__(desc) 54 + self.reply = reply 55 + 56 + 57 + class QEMUMachine(object): 58 + """ 59 + A QEMU VM 60 + 61 + Use this object as a context manager to ensure the QEMU process terminates:: 62 + 63 + with VM(binary) as vm: 64 + ... 65 + # vm is guaranteed to be shut down here 66 + """ 67 + 68 + def __init__(self, binary, args=None, wrapper=None, name=None, 69 + test_dir="/var/tmp", monitor_address=None, 70 + socket_scm_helper=None): 71 + ''' 72 + Initialize a QEMUMachine 73 + 74 + @param binary: path to the qemu binary 75 + @param args: list of extra arguments 76 + @param wrapper: list of arguments used as prefix to qemu binary 77 + @param name: prefix for socket and log file names (default: qemu-PID) 78 + @param test_dir: where to create socket and log file 79 + @param monitor_address: address for QMP monitor 80 + @param socket_scm_helper: helper program, required for send_fd_scm() 81 + @note: Qemu process is not started until launch() is used. 82 + ''' 83 + if args is None: 84 + args = [] 85 + if wrapper is None: 86 + wrapper = [] 87 + if name is None: 88 + name = "qemu-%d" % os.getpid() 89 + self._name = name 90 + self._monitor_address = monitor_address 91 + self._vm_monitor = None 92 + self._qemu_log_path = None 93 + self._qemu_log_file = None 94 + self._popen = None 95 + self._binary = binary 96 + self._args = list(args) # Force copy args in case we modify them 97 + self._wrapper = wrapper 98 + self._events = [] 99 + self._iolog = None 100 + self._socket_scm_helper = socket_scm_helper 101 + self._qmp = None 102 + self._qemu_full_args = None 103 + self._test_dir = test_dir 104 + self._temp_dir = None 105 + self._launched = False 106 + self._machine = None 107 + self._console_set = False 108 + self._console_device_type = None 109 + self._console_address = None 110 + self._console_socket = None 111 + 112 + # just in case logging wasn't configured by the main script: 113 + logging.basicConfig() 114 + 115 + def __enter__(self): 116 + return self 117 + 118 + def __exit__(self, exc_type, exc_val, exc_tb): 119 + self.shutdown() 120 + return False 121 + 122 + # This can be used to add an unused monitor instance. 123 + def add_monitor_null(self): 124 + self._args.append('-monitor') 125 + self._args.append('null') 126 + 127 + def add_fd(self, fd, fdset, opaque, opts=''): 128 + """ 129 + Pass a file descriptor to the VM 130 + """ 131 + options = ['fd=%d' % fd, 132 + 'set=%d' % fdset, 133 + 'opaque=%s' % opaque] 134 + if opts: 135 + options.append(opts) 136 + 137 + # This did not exist before 3.4, but since then it is 138 + # mandatory for our purpose 139 + if hasattr(os, 'set_inheritable'): 140 + os.set_inheritable(fd, True) 141 + 142 + self._args.append('-add-fd') 143 + self._args.append(','.join(options)) 144 + return self 145 + 146 + # Exactly one of fd and file_path must be given. 147 + # (If it is file_path, the helper will open that file and pass its 148 + # own fd) 149 + def send_fd_scm(self, fd=None, file_path=None): 150 + # In iotest.py, the qmp should always use unix socket. 151 + assert self._qmp.is_scm_available() 152 + if self._socket_scm_helper is None: 153 + raise QEMUMachineError("No path to socket_scm_helper set") 154 + if not os.path.exists(self._socket_scm_helper): 155 + raise QEMUMachineError("%s does not exist" % 156 + self._socket_scm_helper) 157 + 158 + # This did not exist before 3.4, but since then it is 159 + # mandatory for our purpose 160 + if hasattr(os, 'set_inheritable'): 161 + os.set_inheritable(self._qmp.get_sock_fd(), True) 162 + if fd is not None: 163 + os.set_inheritable(fd, True) 164 + 165 + fd_param = ["%s" % self._socket_scm_helper, 166 + "%d" % self._qmp.get_sock_fd()] 167 + 168 + if file_path is not None: 169 + assert fd is None 170 + fd_param.append(file_path) 171 + else: 172 + assert fd is not None 173 + fd_param.append(str(fd)) 174 + 175 + devnull = open(os.path.devnull, 'rb') 176 + proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, 177 + stderr=subprocess.STDOUT, close_fds=False) 178 + output = proc.communicate()[0] 179 + if output: 180 + LOG.debug(output) 181 + 182 + return proc.returncode 183 + 184 + @staticmethod 185 + def _remove_if_exists(path): 186 + """ 187 + Remove file object at path if it exists 188 + """ 189 + try: 190 + os.remove(path) 191 + except OSError as exception: 192 + if exception.errno == errno.ENOENT: 193 + return 194 + raise 195 + 196 + def is_running(self): 197 + return self._popen is not None and self._popen.poll() is None 198 + 199 + def exitcode(self): 200 + if self._popen is None: 201 + return None 202 + return self._popen.poll() 203 + 204 + def get_pid(self): 205 + if not self.is_running(): 206 + return None 207 + return self._popen.pid 208 + 209 + def _load_io_log(self): 210 + if self._qemu_log_path is not None: 211 + with open(self._qemu_log_path, "r") as iolog: 212 + self._iolog = iolog.read() 213 + 214 + def _base_args(self): 215 + if isinstance(self._monitor_address, tuple): 216 + moncdev = "socket,id=mon,host=%s,port=%s" % ( 217 + self._monitor_address[0], 218 + self._monitor_address[1]) 219 + else: 220 + moncdev = 'socket,id=mon,path=%s' % self._vm_monitor 221 + args = ['-chardev', moncdev, 222 + '-mon', 'chardev=mon,mode=control', 223 + '-display', 'none', '-vga', 'none'] 224 + if self._machine is not None: 225 + args.extend(['-machine', self._machine]) 226 + if self._console_set: 227 + self._console_address = os.path.join(self._temp_dir, 228 + self._name + "-console.sock") 229 + chardev = ('socket,id=console,path=%s,server,nowait' % 230 + self._console_address) 231 + args.extend(['-chardev', chardev]) 232 + if self._console_device_type is None: 233 + args.extend(['-serial', 'chardev:console']) 234 + else: 235 + device = '%s,chardev=console' % self._console_device_type 236 + args.extend(['-device', device]) 237 + return args 238 + 239 + def _pre_launch(self): 240 + self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) 241 + if self._monitor_address is not None: 242 + self._vm_monitor = self._monitor_address 243 + else: 244 + self._vm_monitor = os.path.join(self._temp_dir, 245 + self._name + "-monitor.sock") 246 + self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") 247 + self._qemu_log_file = open(self._qemu_log_path, 'wb') 248 + 249 + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, 250 + server=True) 251 + 252 + def _post_launch(self): 253 + self._qmp.accept() 254 + 255 + def _post_shutdown(self): 256 + if self._qemu_log_file is not None: 257 + self._qemu_log_file.close() 258 + self._qemu_log_file = None 259 + 260 + self._qemu_log_path = None 261 + 262 + if self._console_socket is not None: 263 + self._console_socket.close() 264 + self._console_socket = None 265 + 266 + if self._temp_dir is not None: 267 + shutil.rmtree(self._temp_dir) 268 + self._temp_dir = None 269 + 270 + def launch(self): 271 + """ 272 + Launch the VM and make sure we cleanup and expose the 273 + command line/output in case of exception 274 + """ 275 + 276 + if self._launched: 277 + raise QEMUMachineError('VM already launched') 278 + 279 + self._iolog = None 280 + self._qemu_full_args = None 281 + try: 282 + self._launch() 283 + self._launched = True 284 + except: 285 + self.shutdown() 286 + 287 + LOG.debug('Error launching VM') 288 + if self._qemu_full_args: 289 + LOG.debug('Command: %r', ' '.join(self._qemu_full_args)) 290 + if self._iolog: 291 + LOG.debug('Output: %r', self._iolog) 292 + raise 293 + 294 + def _launch(self): 295 + """ 296 + Launch the VM and establish a QMP connection 297 + """ 298 + devnull = open(os.path.devnull, 'rb') 299 + self._pre_launch() 300 + self._qemu_full_args = (self._wrapper + [self._binary] + 301 + self._base_args() + self._args) 302 + LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) 303 + self._popen = subprocess.Popen(self._qemu_full_args, 304 + stdin=devnull, 305 + stdout=self._qemu_log_file, 306 + stderr=subprocess.STDOUT, 307 + shell=False, 308 + close_fds=False) 309 + self._post_launch() 310 + 311 + def wait(self): 312 + """ 313 + Wait for the VM to power off 314 + """ 315 + self._popen.wait() 316 + self._qmp.close() 317 + self._load_io_log() 318 + self._post_shutdown() 319 + 320 + def shutdown(self): 321 + """ 322 + Terminate the VM and clean up 323 + """ 324 + if self.is_running(): 325 + try: 326 + self._qmp.cmd('quit') 327 + self._qmp.close() 328 + except: 329 + self._popen.kill() 330 + self._popen.wait() 331 + 332 + self._load_io_log() 333 + self._post_shutdown() 334 + 335 + exitcode = self.exitcode() 336 + if exitcode is not None and exitcode < 0: 337 + msg = 'qemu received signal %i: %s' 338 + if self._qemu_full_args: 339 + command = ' '.join(self._qemu_full_args) 340 + else: 341 + command = '' 342 + LOG.warn(msg, -exitcode, command) 343 + 344 + self._launched = False 345 + 346 + def qmp(self, cmd, conv_keys=True, **args): 347 + """ 348 + Invoke a QMP command and return the response dict 349 + """ 350 + qmp_args = dict() 351 + for key, value in args.items(): 352 + if conv_keys: 353 + qmp_args[key.replace('_', '-')] = value 354 + else: 355 + qmp_args[key] = value 356 + 357 + return self._qmp.cmd(cmd, args=qmp_args) 358 + 359 + def command(self, cmd, conv_keys=True, **args): 360 + """ 361 + Invoke a QMP command. 362 + On success return the response dict. 363 + On failure raise an exception. 364 + """ 365 + reply = self.qmp(cmd, conv_keys, **args) 366 + if reply is None: 367 + raise qmp.QMPError("Monitor is closed") 368 + if "error" in reply: 369 + raise MonitorResponseError(reply) 370 + return reply["return"] 371 + 372 + def get_qmp_event(self, wait=False): 373 + """ 374 + Poll for one queued QMP events and return it 375 + """ 376 + if len(self._events) > 0: 377 + return self._events.pop(0) 378 + return self._qmp.pull_event(wait=wait) 379 + 380 + def get_qmp_events(self, wait=False): 381 + """ 382 + Poll for queued QMP events and return a list of dicts 383 + """ 384 + events = self._qmp.get_events(wait=wait) 385 + events.extend(self._events) 386 + del self._events[:] 387 + self._qmp.clear_events() 388 + return events 389 + 390 + @staticmethod 391 + def event_match(event, match=None): 392 + """ 393 + Check if an event matches optional match criteria. 394 + 395 + The match criteria takes the form of a matching subdict. The event is 396 + checked to be a superset of the subdict, recursively, with matching 397 + values whenever the subdict values are not None. 398 + 399 + This has a limitation that you cannot explicitly check for None values. 400 + 401 + Examples, with the subdict queries on the left: 402 + - None matches any object. 403 + - {"foo": None} matches {"foo": {"bar": 1}} 404 + - {"foo": None} matches {"foo": 5} 405 + - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}} 406 + - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}} 407 + """ 408 + if match is None: 409 + return True 410 + 411 + try: 412 + for key in match: 413 + if key in event: 414 + if not QEMUMachine.event_match(event[key], match[key]): 415 + return False 416 + else: 417 + return False 418 + return True 419 + except TypeError: 420 + # either match or event wasn't iterable (not a dict) 421 + return match == event 422 + 423 + def event_wait(self, name, timeout=60.0, match=None): 424 + """ 425 + event_wait waits for and returns a named event from QMP with a timeout. 426 + 427 + name: The event to wait for. 428 + timeout: QEMUMonitorProtocol.pull_event timeout parameter. 429 + match: Optional match criteria. See event_match for details. 430 + """ 431 + return self.events_wait([(name, match)], timeout) 432 + 433 + def events_wait(self, events, timeout=60.0): 434 + """ 435 + events_wait waits for and returns a named event from QMP with a timeout. 436 + 437 + events: a sequence of (name, match_criteria) tuples. 438 + The match criteria are optional and may be None. 439 + See event_match for details. 440 + timeout: QEMUMonitorProtocol.pull_event timeout parameter. 441 + """ 442 + def _match(event): 443 + for name, match in events: 444 + if (event['event'] == name and 445 + self.event_match(event, match)): 446 + return True 447 + return False 448 + 449 + # Search cached events 450 + for event in self._events: 451 + if _match(event): 452 + self._events.remove(event) 453 + return event 454 + 455 + # Poll for new events 456 + while True: 457 + event = self._qmp.pull_event(wait=timeout) 458 + if _match(event): 459 + return event 460 + self._events.append(event) 461 + 462 + return None 463 + 464 + def get_log(self): 465 + """ 466 + After self.shutdown or failed qemu execution, this returns the output 467 + of the qemu process. 468 + """ 469 + return self._iolog 470 + 471 + def add_args(self, *args): 472 + """ 473 + Adds to the list of extra arguments to be given to the QEMU binary 474 + """ 475 + self._args.extend(args) 476 + 477 + def set_machine(self, machine_type): 478 + """ 479 + Sets the machine type 480 + 481 + If set, the machine type will be added to the base arguments 482 + of the resulting QEMU command line. 483 + """ 484 + self._machine = machine_type 485 + 486 + def set_console(self, device_type=None): 487 + """ 488 + Sets the device type for a console device 489 + 490 + If set, the console device and a backing character device will 491 + be added to the base arguments of the resulting QEMU command 492 + line. 493 + 494 + This is a convenience method that will either use the provided 495 + device type, or default to a "-serial chardev:console" command 496 + line argument. 497 + 498 + The actual setting of command line arguments will be be done at 499 + machine launch time, as it depends on the temporary directory 500 + to be created. 501 + 502 + @param device_type: the device type, such as "isa-serial". If 503 + None is given (the default value) a "-serial 504 + chardev:console" command line argument will 505 + be used instead, resorting to the machine's 506 + default device type. 507 + """ 508 + self._console_set = True 509 + self._console_device_type = device_type 510 + 511 + @property 512 + def console_socket(self): 513 + """ 514 + Returns a socket connected to the console 515 + """ 516 + if self._console_socket is None: 517 + self._console_socket = socket.socket(socket.AF_UNIX, 518 + socket.SOCK_STREAM) 519 + self._console_socket.connect(self._console_address) 520 + return self._console_socket
+1 -1
python/qemu/qtest.py
··· 14 14 import socket 15 15 import os 16 16 17 - from . import QEMUMachine 17 + from .machine import QEMUMachine 18 18 19 19 20 20 class QEMUQtestProtocol(object):
+1 -1
scripts/device-crash-test
··· 36 36 from itertools import chain 37 37 38 38 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) 39 - from qemu import QEMUMachine 39 + from qemu.machine import QEMUMachine 40 40 41 41 logger = logging.getLogger('device-crash-test') 42 42 dbg = logger.debug
+1 -1
scripts/render_block_graph.py
··· 25 25 from graphviz import Digraph 26 26 27 27 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) 28 - from qemu import MonitorResponseError 28 + from qemu.machine import MonitorResponseError 29 29 30 30 31 31 def perm(arr):
+1 -1
tests/acceptance/avocado_qemu/__init__.py
··· 17 17 SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') 18 18 sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) 19 19 20 - from qemu import QEMUMachine 20 + from qemu.machine import QEMUMachine 21 21 22 22 def is_readable_executable_file(path): 23 23 return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK)
+1 -1
tests/acceptance/virtio_version.py
··· 12 12 import os 13 13 14 14 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 15 - from qemu import QEMUMachine 15 + from qemu.machine import QEMUMachine 16 16 from avocado_qemu import Test 17 17 18 18 # Virtio Device IDs:
+11 -11
tests/migration/guestperf/engine.py
··· 30 30 31 31 sys.path.append(os.path.join(os.path.dirname(__file__), 32 32 '..', '..', '..', 'python')) 33 - import qemu 33 + from qemu.machine import QEMUMachine 34 34 35 35 36 36 class Engine(object): ··· 386 386 dstmonaddr = "/var/tmp/qemu-dst-%d-monitor.sock" % os.getpid() 387 387 srcmonaddr = "/var/tmp/qemu-src-%d-monitor.sock" % os.getpid() 388 388 389 - src = qemu.QEMUMachine(self._binary, 390 - args=self._get_src_args(hardware), 391 - wrapper=self._get_src_wrapper(hardware), 392 - name="qemu-src-%d" % os.getpid(), 393 - monitor_address=srcmonaddr) 389 + src = QEMUMachine(self._binary, 390 + args=self._get_src_args(hardware), 391 + wrapper=self._get_src_wrapper(hardware), 392 + name="qemu-src-%d" % os.getpid(), 393 + monitor_address=srcmonaddr) 394 394 395 - dst = qemu.QEMUMachine(self._binary, 396 - args=self._get_dst_args(hardware, uri), 397 - wrapper=self._get_dst_wrapper(hardware), 398 - name="qemu-dst-%d" % os.getpid(), 399 - monitor_address=dstmonaddr) 395 + dst = QEMUMachine(self._binary, 396 + args=self._get_dst_args(hardware, uri), 397 + wrapper=self._get_dst_wrapper(hardware), 398 + name="qemu-dst-%d" % os.getpid(), 399 + monitor_address=dstmonaddr) 400 400 401 401 try: 402 402 src.launch()
+1 -1
tests/qemu-iotests/235
··· 25 25 26 26 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 27 27 28 - from qemu import QEMUMachine 28 + from qemu.machine import QEMUMachine 29 29 30 30 # Note: 31 31 # This test was added to check that mirror dead-lock was fixed (see previous
+2 -1
tests/vm/basevm.py
··· 18 18 import time 19 19 import datetime 20 20 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 21 - from qemu import QEMUMachine, kvm_available 21 + from qemu import kvm_available 22 + from qemu.machine import QEMUMachine 22 23 import subprocess 23 24 import hashlib 24 25 import optparse