Python queue:

* migration acceptance test fix
 * introduce pylintrc & flake8 config
 * various cleanups (Python3, style)
 * vm-test can set QEMU_LOCAL=1 to use locally built binaries
 * refactored BootLinuxBase & LinuxKernelTest acceptance classes
 
 151323210
 693157969
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl7T3U8ACgkQ4+MsLN6t
 wN4JnhAAif0Lw06lEl+ZksD0d4YLyBp079BNJEUWvflsivHY9RIn4e+eXdL3Q8m9
 roBzlsV71C7Ufbp26LFthcnvLaq0JH7RhBUVUoQAI7XZ46HAr3KRyOBJQP7LCr5N
 4Z97y8hqfSdchwpYbxkEbPy58caCRIneqIvg0sp8XuyXpDpVDqP11rXTg4fgqi7i
 1+D1yjr+wgaa7Vvf4sYzOw4D5zD2Mh+zMyDFI9d7yajs/4RH9k+iZteV7baLRQ5Q
 xkC0yqHDGp+uzEF4mk+5VUiZDvDUUxnkuFYKc6mFcahKzhrxLpEsvhnPFZ+vr4ib
 1DDmSr6ihf37wBzowHgAkmTwAiGmVEobu/2h93JXJesWw0TKRT74w1ftZKEIY1v4
 1Hka38gV0LULOAOjiy+aKNJqpJ/eipds94MvllRLHCgbB4H9VKBd4ts6linn+xsM
 CUebvUOgiVzH+hYbLJ1EBLFhbsmQ+yvopbQtLIlyFpKTFhdE1dA3vfb9NV0iqfOL
 fxaP/WaibKEFF5H40H7Ro+H7cT2+hF8MyByBT6q/UzoDURkZxeswqd2ww2VcUw2M
 X6h3/Hzek8PtZ+md3G6Hb1mJccfBHElrSgXAjrZ0WLOy4ZV7Y+/QrE8ooJwIKGKZ
 NinXrUocDl8xfRNWjynImzqma5TdaLW5tOmx6yTSK1R3lQh2z7A=
 =7xHS
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd-gitlab/tags/python-next-20200531' into staging

Python queue:

* migration acceptance test fix
* introduce pylintrc & flake8 config
* various cleanups (Python3, style)
* vm-test can set QEMU_LOCAL=1 to use locally built binaries
* refactored BootLinuxBase & LinuxKernelTest acceptance classes

151323210
693157969

# gpg: Signature made Sun 31 May 2020 17:37:35 BST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/python-next-20200531: (25 commits)
  tests/acceptance: refactor boot_linux to allow code reuse
  tests/acceptance: refactor boot_linux_console test to allow code reuse
  tests/acceptance: allow console interaction with specific VMs
  tests/acceptance/migration.py: Wait for both sides
  tests/migration/guestperf: Use Python 3 interpreter
  tests/vm: allow wait_ssh() to specify command
  tests/vm: Add ability to select QEMU from current build
  tests/vm: Pass --debug through for vm-boot-ssh
  python/qemu/qtest: Check before accessing _qtest
  python/qemu/qmp: assert sockfile is not None
  python/qemu/qmp: use True/False for non/blocking modes
  python/qemu: Adjust traceback typing
  python/qemu: fix socket.makefile() typing
  python/qemu: remove Python2 style super() calls
  python/qemu: delint; add flake8 config
  python/qemu: delint and add pylintrc
  python/qemu/machine: remove logging configuration
  python/qemu/machine: add kill() method
  python: remove more instances of sys.version_info
  scripts/qmp: Fix shebang and imports
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-05-31 21:49:07 +01:00
commit b73f417aae
35 changed files with 317 additions and 178 deletions

2
python/qemu/.flake8 Normal file
View file

@ -0,0 +1,2 @@
[flake8]
extend-ignore = E722 # Pylint handles this, but smarter.

View file

@ -28,6 +28,7 @@ ADDITIONAL_ARCHES = {
"ppc64le": "ppc64", "ppc64le": "ppc64",
} }
def list_accel(qemu_bin): def list_accel(qemu_bin):
""" """
List accelerators enabled in the QEMU binary. List accelerators enabled in the QEMU binary.
@ -47,6 +48,7 @@ def list_accel(qemu_bin):
# Skip the first line which is the header. # Skip the first line which is the header.
return [acc.strip() for acc in out.splitlines()[1:]] return [acc.strip() for acc in out.splitlines()[1:]]
def kvm_available(target_arch=None, qemu_bin=None): def kvm_available(target_arch=None, qemu_bin=None):
""" """
Check if KVM is available using the following heuristic: Check if KVM is available using the following heuristic:
@ -69,6 +71,7 @@ def kvm_available(target_arch=None, qemu_bin=None):
return False return False
return True return True
def tcg_available(qemu_bin): def tcg_available(qemu_bin):
""" """
Check if TCG is available. Check if TCG is available.

View file

@ -24,11 +24,14 @@ import subprocess
import shutil import shutil
import socket import socket
import tempfile import tempfile
from typing import Optional, Type
from types import TracebackType
from . import qmp from . import qmp
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class QEMUMachineError(Exception): class QEMUMachineError(Exception):
""" """
Exception called when an error in QEMUMachine happens. Exception called when an error in QEMUMachine happens.
@ -54,15 +57,16 @@ class MonitorResponseError(qmp.QMPError):
desc = reply["error"]["desc"] desc = reply["error"]["desc"]
except KeyError: except KeyError:
desc = reply desc = reply
super(MonitorResponseError, self).__init__(desc) super().__init__(desc)
self.reply = reply self.reply = reply
class QEMUMachine(object): class QEMUMachine:
""" """
A QEMU VM A QEMU VM
Use this object as a context manager to ensure the QEMU process terminates:: Use this object as a context manager to ensure
the QEMU process terminates::
with VM(binary) as vm: with VM(binary) as vm:
... ...
@ -119,15 +123,14 @@ class QEMUMachine(object):
self._console_socket = None self._console_socket = None
self._remove_files = [] self._remove_files = []
# just in case logging wasn't configured by the main script:
logging.basicConfig()
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType]) -> None:
self.shutdown() self.shutdown()
return False
def add_monitor_null(self): def add_monitor_null(self):
""" """
@ -188,8 +191,10 @@ class QEMUMachine(object):
fd_param.append(str(fd)) fd_param.append(str(fd))
devnull = open(os.path.devnull, 'rb') devnull = open(os.path.devnull, 'rb')
proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, proc = subprocess.Popen(
stderr=subprocess.STDOUT, close_fds=False) fd_param, stdin=devnull, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, close_fds=False
)
output = proc.communicate()[0] output = proc.communicate()[0]
if output: if output:
LOG.debug(output) LOG.debug(output)
@ -242,7 +247,7 @@ class QEMUMachine(object):
'chardev=mon,mode=control']) 'chardev=mon,mode=control'])
if self._machine is not None: if self._machine is not None:
args.extend(['-machine', self._machine]) args.extend(['-machine', self._machine])
for i in range(self._console_index): for _ in range(self._console_index):
args.extend(['-serial', 'null']) args.extend(['-serial', 'null'])
if self._console_set: if self._console_set:
self._console_address = os.path.join(self._sock_dir, self._console_address = os.path.join(self._sock_dir,
@ -342,7 +347,7 @@ class QEMUMachine(object):
self._load_io_log() self._load_io_log()
self._post_shutdown() self._post_shutdown()
def shutdown(self, has_quit=False): def shutdown(self, has_quit=False, hard=False):
""" """
Terminate the VM and clean up Terminate the VM and clean up
""" """
@ -354,7 +359,9 @@ class QEMUMachine(object):
self._console_socket = None self._console_socket = None
if self.is_running(): if self.is_running():
if self._qmp: if hard:
self._popen.kill()
elif self._qmp:
try: try:
if not has_quit: if not has_quit:
self._qmp.cmd('quit') self._qmp.cmd('quit')
@ -368,16 +375,20 @@ class QEMUMachine(object):
self._post_shutdown() self._post_shutdown()
exitcode = self.exitcode() exitcode = self.exitcode()
if exitcode is not None and exitcode < 0: if exitcode is not None and exitcode < 0 and \
not (exitcode == -9 and hard):
msg = 'qemu received signal %i: %s' msg = 'qemu received signal %i: %s'
if self._qemu_full_args: if self._qemu_full_args:
command = ' '.join(self._qemu_full_args) command = ' '.join(self._qemu_full_args)
else: else:
command = '' command = ''
LOG.warning(msg, -exitcode, command) LOG.warning(msg, -int(exitcode), command)
self._launched = False self._launched = False
def kill(self):
self.shutdown(hard=True)
def set_qmp_monitor(self, enabled=True): def set_qmp_monitor(self, enabled=True):
""" """
Set the QMP monitor. Set the QMP monitor.
@ -482,7 +493,8 @@ class QEMUMachine(object):
def events_wait(self, events, timeout=60.0): def events_wait(self, events, timeout=60.0):
""" """
events_wait waits for and returns a named event from QMP with a timeout. events_wait waits for and returns a named event
from QMP with a timeout.
events: a sequence of (name, match_criteria) tuples. events: a sequence of (name, match_criteria) tuples.
The match criteria are optional and may be None. The match criteria are optional and may be None.

58
python/qemu/pylintrc Normal file
View file

@ -0,0 +1,58 @@
[MASTER]
[MESSAGES CONTROL]
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=too-many-arguments,
too-many-instance-attributes,
too-many-public-methods,
[REPORTS]
[REFACTORING]
[MISCELLANEOUS]
[LOGGING]
[BASIC]
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_,
fd,
[VARIABLES]
[STRING]
[SPELLING]
[FORMAT]
[SIMILARITIES]
# Ignore imports when computing similarities.
ignore-imports=yes
[TYPECHECK]
[CLASSES]
[IMPORTS]
[DESIGN]
[EXCEPTIONS]

View file

@ -11,6 +11,12 @@ import json
import errno import errno
import socket import socket
import logging import logging
from typing import (
Optional,
TextIO,
Type,
)
from types import TracebackType
class QMPError(Exception): class QMPError(Exception):
@ -61,7 +67,7 @@ class QEMUMonitorProtocol:
self.__events = [] self.__events = []
self.__address = address self.__address = address
self.__sock = self.__get_sock() self.__sock = self.__get_sock()
self.__sockfile = None self.__sockfile: Optional[TextIO] = None
self._nickname = nickname self._nickname = nickname
if self._nickname: if self._nickname:
self.logger = logging.getLogger('QMP').getChild(self._nickname) self.logger = logging.getLogger('QMP').getChild(self._nickname)
@ -88,6 +94,7 @@ class QEMUMonitorProtocol:
raise QMPCapabilitiesError raise QMPCapabilitiesError
def __json_read(self, only_event=False): def __json_read(self, only_event=False):
assert self.__sockfile is not None
while True: while True:
data = self.__sockfile.readline() data = self.__sockfile.readline()
if not data: if not data:
@ -114,14 +121,14 @@ class QEMUMonitorProtocol:
""" """
# Check for new events regardless and pull them into the cache: # Check for new events regardless and pull them into the cache:
self.__sock.setblocking(0) self.__sock.setblocking(False)
try: try:
self.__json_read() self.__json_read()
except OSError as err: except OSError as err:
if err.errno == errno.EAGAIN: if err.errno == errno.EAGAIN:
# No data available # No data available
pass pass
self.__sock.setblocking(1) self.__sock.setblocking(True)
# Wait for new events, if needed. # Wait for new events, if needed.
# if wait is 0.0, this means "no wait" and is also implicitly false. # if wait is 0.0, this means "no wait" and is also implicitly false.
@ -142,10 +149,14 @@ class QEMUMonitorProtocol:
# Implement context manager enter function. # Implement context manager enter function.
return self return self
def __exit__(self, exc_type, exc_value, exc_traceback): def __exit__(self,
# pylint: disable=duplicate-code
# see https://github.com/PyCQA/pylint/issues/3619
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType]) -> None:
# Implement context manager exit function. # Implement context manager exit function.
self.close() self.close()
return False
def connect(self, negotiate=True): def connect(self, negotiate=True):
""" """
@ -157,7 +168,7 @@ class QEMUMonitorProtocol:
@raise QMPCapabilitiesError if fails to negotiate capabilities @raise QMPCapabilitiesError if fails to negotiate capabilities
""" """
self.__sock.connect(self.__address) self.__sock.connect(self.__address)
self.__sockfile = self.__sock.makefile() self.__sockfile = self.__sock.makefile(mode='r')
if negotiate: if negotiate:
return self.__negotiate_capabilities() return self.__negotiate_capabilities()
return None return None
@ -168,8 +179,8 @@ class QEMUMonitorProtocol:
@param timeout: timeout in seconds (nonnegative float number, or @param timeout: timeout in seconds (nonnegative float number, or
None). The value passed will set the behavior of the None). The value passed will set the behavior of the
underneath QMP socket as described in [1]. Default value underneath QMP socket as described in [1].
is set to 15.0. Default value is set to 15.0.
@return QMP greeting dict @return QMP greeting dict
@raise OSError on socket connection errors @raise OSError on socket connection errors
@raise QMPConnectError if the greeting is not received @raise QMPConnectError if the greeting is not received
@ -180,7 +191,7 @@ class QEMUMonitorProtocol:
""" """
self.__sock.settimeout(timeout) self.__sock.settimeout(timeout)
self.__sock, _ = self.__sock.accept() self.__sock, _ = self.__sock.accept()
self.__sockfile = self.__sock.makefile() self.__sockfile = self.__sock.makefile(mode='r')
return self.__negotiate_capabilities() return self.__negotiate_capabilities()
def cmd_obj(self, qmp_cmd): def cmd_obj(self, qmp_cmd):

View file

@ -1,5 +1,11 @@
# QEMU qtest library """
# QEMU qtest library
qtest offers the QEMUQtestProtocol and QEMUQTestMachine classes, which
offer a connection to QEMU's qtest protocol socket, and a qtest-enabled
subclass of QEMUMachine, respectively.
"""
# Copyright (C) 2015 Red Hat Inc. # Copyright (C) 2015 Red Hat Inc.
# #
# Authors: # Authors:
@ -13,26 +19,29 @@
import socket import socket
import os import os
from typing import Optional, TextIO
from .machine import QEMUMachine from .machine import QEMUMachine
class QEMUQtestProtocol(object): class QEMUQtestProtocol:
def __init__(self, address, server=False):
""" """
Create a QEMUQtestProtocol object. QEMUQtestProtocol implements a connection to a qtest socket.
@param address: QEMU address, can be either a unix socket path (string) :param address: QEMU address, can be either a unix socket path (string)
or a tuple in the form ( address, port ) for a TCP or a tuple in the form ( address, port ) for a TCP
connection connection
@param server: server mode, listens on the socket (bool) :param server: server mode, listens on the socket (bool)
@raise socket.error on socket connection errors :raise socket.error: on socket connection errors
@note No connection is established, this is done by the connect() or
accept() methods .. note::
No conection is estabalished by __init__(), this is done
by the connect() or accept() methods.
""" """
def __init__(self, address, server=False):
self._address = address self._address = address
self._sock = self._get_sock() self._sock = self._get_sock()
self._sockfile = None self._sockfile: Optional[TextIO] = None
if server: if server:
self._sock.bind(self._address) self._sock.bind(self._address)
self._sock.listen(1) self._sock.listen(1)
@ -51,7 +60,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors @raise socket.error on socket connection errors
""" """
self._sock.connect(self._address) self._sock.connect(self._address)
self._sockfile = self._sock.makefile() self._sockfile = self._sock.makefile(mode='r')
def accept(self): def accept(self):
""" """
@ -60,7 +69,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors @raise socket.error on socket connection errors
""" """
self._sock, _ = self._sock.accept() self._sock, _ = self._sock.accept()
self._sockfile = self._sock.makefile() self._sockfile = self._sock.makefile(mode='r')
def cmd(self, qtest_cmd): def cmd(self, qtest_cmd):
""" """
@ -68,20 +77,27 @@ class QEMUQtestProtocol(object):
@param qtest_cmd: qtest command text to be sent @param qtest_cmd: qtest command text to be sent
""" """
assert self._sockfile is not None
self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
resp = self._sockfile.readline() resp = self._sockfile.readline()
return resp return resp
def close(self): def close(self):
"""Close this socket."""
self._sock.close() self._sock.close()
if self._sockfile:
self._sockfile.close() self._sockfile.close()
self._sockfile = None
def settimeout(self, timeout): def settimeout(self, timeout):
"""Set a timeout, in seconds."""
self._sock.settimeout(timeout) self._sock.settimeout(timeout)
class QEMUQtestMachine(QEMUMachine): class QEMUQtestMachine(QEMUMachine):
'''A QEMU VM''' """
A QEMU VM, with a qtest socket available.
"""
def __init__(self, binary, args=None, name=None, test_dir="/var/tmp", def __init__(self, binary, args=None, name=None, test_dir="/var/tmp",
socket_scm_helper=None, sock_dir=None): socket_scm_helper=None, sock_dir=None):
@ -89,31 +105,38 @@ class QEMUQtestMachine(QEMUMachine):
name = "qemu-%d" % os.getpid() name = "qemu-%d" % os.getpid()
if sock_dir is None: if sock_dir is None:
sock_dir = test_dir sock_dir = test_dir
super(QEMUQtestMachine, super().__init__(binary, args, name=name, test_dir=test_dir,
self).__init__(binary, args, name=name, test_dir=test_dir,
socket_scm_helper=socket_scm_helper, socket_scm_helper=socket_scm_helper,
sock_dir=sock_dir) sock_dir=sock_dir)
self._qtest = None self._qtest = None
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
def _base_args(self): def _base_args(self):
args = super(QEMUQtestMachine, self)._base_args() args = super()._base_args()
args.extend(['-qtest', 'unix:path=' + self._qtest_path, args.extend(['-qtest', 'unix:path=' + self._qtest_path,
'-accel', 'qtest']) '-accel', 'qtest'])
return args return args
def _pre_launch(self): def _pre_launch(self):
super(QEMUQtestMachine, self)._pre_launch() super()._pre_launch()
self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) self._qtest = QEMUQtestProtocol(self._qtest_path, server=True)
def _post_launch(self): def _post_launch(self) -> None:
super(QEMUQtestMachine, self)._post_launch() assert self._qtest is not None
super()._post_launch()
self._qtest.accept() self._qtest.accept()
def _post_shutdown(self): def _post_shutdown(self):
super(QEMUQtestMachine, self)._post_shutdown() super()._post_shutdown()
self._remove_if_exists(self._qtest_path) self._remove_if_exists(self._qtest_path)
def qtest(self, cmd): def qtest(self, cmd: str) -> str:
'''Send a qtest command to guest''' """
Send a qtest command to the guest.
:param cmd: qtest command to send
:return: qtest server response
"""
if self._qtest is None:
raise RuntimeError("qtest socket not available")
return self._qtest.cmd(cmd) return self._qtest.cmd(cmd)

View file

@ -25,11 +25,6 @@ import struct
import sys import sys
MIN_PYTHON = (3, 2)
if sys.version_info < MIN_PYTHON:
sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON)
def mkdir_p(path): def mkdir_p(path):
try: try:
os.makedirs(path) os.makedirs(path)

View file

@ -75,13 +75,6 @@ def output(*args):
output_fd.write(a) output_fd.write(a)
if sys.version_info >= (3, 4):
re_fullmatch = re.fullmatch
else:
def re_fullmatch(pat, str):
return re.match('^' + pat + '$', str)
def output_autogen(): def output_autogen():
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n') output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
@ -428,18 +421,18 @@ def parse_field(lineno, name, toks):
width = 0 width = 0
func = None func = None
for t in toks: for t in toks:
if re_fullmatch('!function=' + re_ident, t): if re.fullmatch('!function=' + re_ident, t):
if func: if func:
error(lineno, 'duplicate function') error(lineno, 'duplicate function')
func = t.split('=') func = t.split('=')
func = func[1] func = func[1]
continue continue
if re_fullmatch('[0-9]+:s[0-9]+', t): if re.fullmatch('[0-9]+:s[0-9]+', t):
# Signed field extract # Signed field extract
subtoks = t.split(':s') subtoks = t.split(':s')
sign = True sign = True
elif re_fullmatch('[0-9]+:[0-9]+', t): elif re.fullmatch('[0-9]+:[0-9]+', t):
# Unsigned field extract # Unsigned field extract
subtoks = t.split(':') subtoks = t.split(':')
sign = False sign = False
@ -488,11 +481,11 @@ def parse_arguments(lineno, name, toks):
flds = [] flds = []
extern = False extern = False
for t in toks: for t in toks:
if re_fullmatch('!extern', t): if re.fullmatch('!extern', t):
extern = True extern = True
anyextern = True anyextern = True
continue continue
if not re_fullmatch(re_ident, t): if not re.fullmatch(re_ident, t):
error(lineno, 'invalid argument set token "{0}"'.format(t)) error(lineno, 'invalid argument set token "{0}"'.format(t))
if t in flds: if t in flds:
error(lineno, 'duplicate argument "{0}"'.format(t)) error(lineno, 'duplicate argument "{0}"'.format(t))
@ -621,13 +614,13 @@ def parse_generic(lineno, is_format, name, toks):
continue continue
# 'Foo=%Bar' imports a field with a different name. # 'Foo=%Bar' imports a field with a different name.
if re_fullmatch(re_ident + '=%' + re_ident, t): if re.fullmatch(re_ident + '=%' + re_ident, t):
(fname, iname) = t.split('=%') (fname, iname) = t.split('=%')
flds = add_field_byname(lineno, flds, fname, iname) flds = add_field_byname(lineno, flds, fname, iname)
continue continue
# 'Foo=number' sets an argument field to a constant value # 'Foo=number' sets an argument field to a constant value
if re_fullmatch(re_ident + '=[+-]?[0-9]+', t): if re.fullmatch(re_ident + '=[+-]?[0-9]+', t):
(fname, value) = t.split('=') (fname, value) = t.split('=')
value = int(value) value = int(value)
flds = add_field(lineno, flds, fname, ConstField(value)) flds = add_field(lineno, flds, fname, ConstField(value))
@ -635,7 +628,7 @@ def parse_generic(lineno, is_format, name, toks):
# Pattern of 0s, 1s, dots and dashes indicate required zeros, # Pattern of 0s, 1s, dots and dashes indicate required zeros,
# required ones, or dont-cares. # required ones, or dont-cares.
if re_fullmatch('[01.-]+', t): if re.fullmatch('[01.-]+', t):
shift = len(t) shift = len(t)
fms = t.replace('0', '1') fms = t.replace('0', '1')
fms = fms.replace('.', '0') fms = fms.replace('.', '0')
@ -652,7 +645,7 @@ def parse_generic(lineno, is_format, name, toks):
fixedmask = (fixedmask << shift) | fms fixedmask = (fixedmask << shift) | fms
undefmask = (undefmask << shift) | ubm undefmask = (undefmask << shift) | ubm
# Otherwise, fieldname:fieldwidth # Otherwise, fieldname:fieldwidth
elif re_fullmatch(re_ident + ':s?[0-9]+', t): elif re.fullmatch(re_ident + ':s?[0-9]+', t):
(fname, flen) = t.split(':') (fname, flen) = t.split(':')
sign = False sign = False
if flen[0] == 's': if flen[0] == 's':

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# tool for querying VMX capabilities # tool for querying VMX capabilities
# #
@ -275,5 +275,6 @@ controls = [
), ),
] ]
if __name__ == '__main__':
for c in controls: for c in controls:
c.show() c.show()

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# Module information generator # Module information generator
# #
@ -80,6 +80,7 @@ def print_bottom(fheader):
#endif #endif
''') ''')
if __name__ == '__main__':
# First argument: output file # First argument: output file
# All other arguments: modules source files (.c) # All other arguments: modules source files (.c)
output_file = sys.argv[1] output_file = sys.argv[1]

View file

@ -1,5 +1,5 @@
#!/usr/bin/python #!/usr/bin/env python3
#
# GDB debugging support # GDB debugging support
# #
# Copyright 2012 Red Hat, Inc. and/or its affiliates # Copyright 2012 Red Hat, Inc. and/or its affiliates

View file

@ -1,5 +1,4 @@
#!/usr/bin/python #
# GDB debugging support # GDB debugging support
# #
# Copyright (c) 2015 Linaro Ltd # Copyright (c) 2015 Linaro Ltd

View file

@ -1,5 +1,4 @@
#!/usr/bin/python #
# GDB debugging support: aio/iohandler debug # GDB debugging support: aio/iohandler debug
# #
# Copyright (c) 2015 Red Hat, Inc. # Copyright (c) 2015 Red Hat, Inc.

View file

@ -1,5 +1,4 @@
#!/usr/bin/python #
# GDB debugging support # GDB debugging support
# #
# Copyright 2012 Red Hat, Inc. and/or its affiliates # Copyright 2012 Red Hat, Inc. and/or its affiliates

View file

@ -1,5 +1,4 @@
#!/usr/bin/python #
# GDB debugging support # GDB debugging support
# #
# Copyright 2012 Red Hat, Inc. and/or its affiliates # Copyright 2012 Red Hat, Inc. and/or its affiliates
@ -84,4 +83,3 @@ class MtreeCommand(gdb.Command):
while not isnull(subregion): while not isnull(subregion):
self.print_item(subregion, addr, level) self.print_item(subregion, addr, level)
subregion = subregion['subregions_link']['tqe_next'] subregion = subregion['subregions_link']['tqe_next']

View file

@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# GDB debugging support, TCG status # GDB debugging support, TCG status

View file

@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# GDB debugging support # GDB debugging support
# #

View file

@ -11,7 +11,9 @@
# See the COPYING file in the top-level directory. # See the COPYING file in the top-level directory.
import sys, os import sys, os
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
def print_response(rsp, prefix=[]): def print_response(rsp, prefix=[]):
if type(rsp) == list: if type(rsp) == list:

View file

@ -77,9 +77,6 @@ import re
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp from qemu import qmp
if sys.version_info[0] == 2:
input = raw_input
class QMPCompleter(list): class QMPCompleter(list):
def complete(self, text, state): def complete(self, text, state):
for cmd in self: for cmd in self:

View file

@ -15,7 +15,9 @@ import fuse, stat
from fuse import Fuse from fuse import Fuse
import os, posix import os, posix
from errno import * from errno import *
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
fuse.fuse_python_api = (0, 2) fuse.fuse_python_api = (0, 2)

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
## ##
# QEMU Object Model test tools # QEMU Object Model test tools
# #
@ -13,7 +13,9 @@
import sys import sys
import os import os
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:] cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None socket_path = None

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
## ##
# QEMU Object Model test tools # QEMU Object Model test tools
# #
@ -13,7 +13,9 @@
import sys import sys
import os import os
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:] cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None socket_path = None

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
## ##
# QEMU Object Model test tools # QEMU Object Model test tools
# #
@ -13,7 +13,9 @@
import sys import sys
import os import os
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:] cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None socket_path = None

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
## ##
# QEMU Object Model test tools # QEMU Object Model test tools
# #
@ -15,7 +15,9 @@
import sys import sys
import os import os
from qmp import QEMUMonitorProtocol
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:] cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None socket_path = None

View file

@ -69,13 +69,15 @@ def pick_default_qemu_bin(arch=None):
def _console_interaction(test, success_message, failure_message, def _console_interaction(test, success_message, failure_message,
send_string, keep_sending=False): send_string, keep_sending=False, vm=None):
assert not keep_sending or send_string assert not keep_sending or send_string
console = test.vm.console_socket.makefile() if vm is None:
vm = test.vm
console = vm.console_socket.makefile()
console_logger = logging.getLogger('console') console_logger = logging.getLogger('console')
while True: while True:
if send_string: if send_string:
test.vm.console_socket.sendall(send_string.encode()) vm.console_socket.sendall(send_string.encode())
if not keep_sending: if not keep_sending:
send_string = None # send only once send_string = None # send only once
msg = console.readline().strip() msg = console.readline().strip()
@ -115,7 +117,8 @@ def interrupt_interactive_console_until_pattern(test, success_message,
_console_interaction(test, success_message, failure_message, _console_interaction(test, success_message, failure_message,
interrupt_string, True) interrupt_string, True)
def wait_for_console_pattern(test, success_message, failure_message=None): def wait_for_console_pattern(test, success_message, failure_message=None,
vm=None):
""" """
Waits for messages to appear on the console, while logging the content Waits for messages to appear on the console, while logging the content
@ -125,7 +128,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None):
:param success_message: if this message appears, test succeeds :param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails :param failure_message: if this message appears, test fails
""" """
_console_interaction(test, success_message, failure_message, None) _console_interaction(test, success_message, failure_message, None, vm=vm)
def exec_command_and_wait_for_pattern(test, command, def exec_command_and_wait_for_pattern(test, command,
success_message, failure_message=None): success_message, failure_message=None):

View file

@ -26,22 +26,8 @@ KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM"
TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG"
class BootLinux(Test): class BootLinuxBase(Test):
""" def download_boot(self):
Boots a Linux system, checking for a successful initialization
"""
timeout = 900
chksum = None
def setUp(self):
super(BootLinux, self).setUp()
self.vm.add_args('-smp', '2')
self.vm.add_args('-m', '1024')
self.prepare_boot()
self.prepare_cloudinit()
def prepare_boot(self):
self.log.debug('Looking for and selecting a qemu-img binary to be ' self.log.debug('Looking for and selecting a qemu-img binary to be '
'used to create the bootable snapshot image') 'used to create the bootable snapshot image')
# If qemu-img has been built, use it, otherwise the system wide one # If qemu-img has been built, use it, otherwise the system wide one
@ -60,17 +46,17 @@ class BootLinux(Test):
if image_arch == 'ppc64': if image_arch == 'ppc64':
image_arch = 'ppc64le' image_arch = 'ppc64le'
try: try:
self.boot = vmimage.get( boot = vmimage.get(
'fedora', arch=image_arch, version='31', 'fedora', arch=image_arch, version='31',
checksum=self.chksum, checksum=self.chksum,
algorithm='sha256', algorithm='sha256',
cache_dir=self.cache_dirs[0], cache_dir=self.cache_dirs[0],
snapshot_dir=self.workdir) snapshot_dir=self.workdir)
self.vm.add_args('-drive', 'file=%s' % self.boot.path)
except: except:
self.cancel('Failed to download/prepare boot image') self.cancel('Failed to download/prepare boot image')
return boot.path
def prepare_cloudinit(self): def download_cloudinit(self):
self.log.info('Preparing cloudinit image') self.log.info('Preparing cloudinit image')
try: try:
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
@ -81,9 +67,32 @@ class BootLinux(Test):
# QEMU's hard coded usermode router address # QEMU's hard coded usermode router address
phone_home_host='10.0.2.2', phone_home_host='10.0.2.2',
phone_home_port=self.phone_home_port) phone_home_port=self.phone_home_port)
self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
except Exception: except Exception:
self.cancel('Failed to prepared cloudinit image') self.cancel('Failed to prepared cloudinit image')
return cloudinit_iso
class BootLinux(BootLinuxBase):
"""
Boots a Linux system, checking for a successful initialization
"""
timeout = 900
chksum = None
def setUp(self):
super(BootLinux, self).setUp()
self.vm.add_args('-smp', '2')
self.vm.add_args('-m', '1024')
self.prepare_boot()
self.prepare_cloudinit()
def prepare_boot(self):
path = self.download_boot()
self.vm.add_args('-drive', 'file=%s' % path)
def prepare_cloudinit(self):
cloudinit_iso = self.download_cloudinit()
self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
def launch_and_wait(self): def launch_and_wait(self):
self.vm.set_console() self.vm.set_console()

View file

@ -28,19 +28,13 @@ try:
except CmdNotFoundError: except CmdNotFoundError:
P7ZIP_AVAILABLE = False P7ZIP_AVAILABLE = False
class BootLinuxConsole(Test): class LinuxKernelTest(Test):
"""
Boots a Linux kernel and checks that the console is operational and the
kernel command line is properly passed from QEMU to the kernel
"""
timeout = 90
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
def wait_for_console_pattern(self, success_message): def wait_for_console_pattern(self, success_message, vm=None):
wait_for_console_pattern(self, success_message, wait_for_console_pattern(self, success_message,
failure_message='Kernel panic - not syncing') failure_message='Kernel panic - not syncing',
vm=vm)
def extract_from_deb(self, deb, path): def extract_from_deb(self, deb, path):
""" """
@ -79,6 +73,13 @@ class BootLinuxConsole(Test):
os.chdir(cwd) os.chdir(cwd)
return os.path.normpath(os.path.join(self.workdir, path)) return os.path.normpath(os.path.join(self.workdir, path))
class BootLinuxConsole(LinuxKernelTest):
"""
Boots a Linux kernel and checks that the console is operational and the
kernel command line is properly passed from QEMU to the kernel
"""
timeout = 90
def test_x86_64_pc(self): def test_x86_64_pc(self):
""" """
:avocado: tags=arch:x86_64 :avocado: tags=arch:x86_64

View file

@ -35,6 +35,10 @@ class Migration(Test):
timeout=self.timeout, timeout=self.timeout,
step=0.1, step=0.1,
args=(src_vm,)) args=(src_vm,))
wait.wait_for(self.migration_finished,
timeout=self.timeout,
step=0.1,
args=(dst_vm,))
self.assertEqual(src_vm.command('query-migrate')['status'], 'completed') self.assertEqual(src_vm.command('query-migrate')['status'], 'completed')
self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed') self.assertEqual(dst_vm.command('query-migrate')['status'], 'completed')
self.assertEqual(dst_vm.command('query-status')['status'], 'running') self.assertEqual(dst_vm.command('query-status')['status'], 'running')

View file

@ -258,12 +258,13 @@ class Docker(object):
return self._do_kill_instances(True) return self._do_kill_instances(True)
def _output(self, cmd, **kwargs): def _output(self, cmd, **kwargs):
if sys.version_info[1] >= 6: try:
return subprocess.check_output(self._command + cmd, return subprocess.check_output(self._command + cmd,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
encoding='utf-8', encoding='utf-8',
**kwargs) **kwargs)
else: except TypeError:
# 'encoding' argument was added in 3.6+
return subprocess.check_output(self._command + cmd, return subprocess.check_output(self._command + cmd,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
**kwargs).decode('utf-8') **kwargs).decode('utf-8')

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# Migration test batch comparison invokation # Migration test batch comparison invokation
# #

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# Migration test graph plotting command # Migration test graph plotting command
# #

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# Migration test direct invokation command # Migration test direct invokation command
# #

View file

@ -47,10 +47,7 @@ import sys
import socket import socket
import struct import struct
import collections import collections
if sys.version_info.major >= 3:
import configparser import configparser
else:
import ConfigParser as configparser
FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB

View file

@ -41,6 +41,7 @@ endif
@echo " J=[0..9]* - Override the -jN parameter for make commands" @echo " J=[0..9]* - Override the -jN parameter for make commands"
@echo " DEBUG=1 - Enable verbose output on host and interactive debugging" @echo " DEBUG=1 - Enable verbose output on host and interactive debugging"
@echo " V=1 - Enable verbose ouput on host and guest commands" @echo " V=1 - Enable verbose ouput on host and guest commands"
@echo " QEMU_LOCAL=1 - Use QEMU binary local to this build."
@echo " QEMU=/path/to/qemu - Change path to QEMU binary" @echo " QEMU=/path/to/qemu - Change path to QEMU binary"
@echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool"
@ -57,6 +58,7 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
$(PYTHON) $< \ $(PYTHON) $< \
$(if $(V)$(DEBUG), --debug) \ $(if $(V)$(DEBUG), --debug) \
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$@" \ --image "$@" \
--force \ --force \
--build-image $@, \ --build-image $@, \
@ -71,6 +73,7 @@ vm-build-%: $(IMAGES_DIR)/%.img
$(if $(DEBUG), --interactive) \ $(if $(DEBUG), --interactive) \
$(if $(J),--jobs $(J)) \ $(if $(J),--jobs $(J)) \
$(if $(V),--verbose) \ $(if $(V),--verbose) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$<" \ --image "$<" \
$(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \ $(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \
--snapshot \ --snapshot \
@ -91,6 +94,8 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img
$(call quiet-command, \ $(call quiet-command, \
$(PYTHON) $(SRC_PATH)/tests/vm/$* \ $(PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(J),--jobs $(J)) \ $(if $(J),--jobs $(J)) \
$(if $(V)$(DEBUG), --debug) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
--image "$<" \ --image "$<" \
--interactive \ --interactive \
false, \ false, \

View file

@ -61,9 +61,11 @@ class BaseVM(object):
# 4 is arbitrary, but greater than 2, # 4 is arbitrary, but greater than 2,
# since we found we need to wait more than twice as long. # since we found we need to wait more than twice as long.
tcg_ssh_timeout_multiplier = 4 tcg_ssh_timeout_multiplier = 4
def __init__(self, debug=False, vcpus=None, genisoimage=None): def __init__(self, debug=False, vcpus=None, genisoimage=None,
build_path=None):
self._guest = None self._guest = None
self._genisoimage = genisoimage self._genisoimage = genisoimage
self._build_path = build_path
self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
suffix=".tmp", suffix=".tmp",
dir=".")) dir="."))
@ -184,15 +186,15 @@ class BaseVM(object):
"-device", "virtio-blk,drive=drive0,bootindex=0"] "-device", "virtio-blk,drive=drive0,bootindex=0"]
args += self._data_args + extra_args args += self._data_args + extra_args
logging.debug("QEMU args: %s", " ".join(args)) logging.debug("QEMU args: %s", " ".join(args))
qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) qemu_path = get_qemu_path(self.arch, self._build_path)
guest = QEMUMachine(binary=qemu_bin, args=args) guest = QEMUMachine(binary=qemu_path, args=args)
guest.set_machine('pc') guest.set_machine('pc')
guest.set_console() guest.set_console()
try: try:
guest.launch() guest.launch()
except: except:
logging.error("Failed to launch QEMU, command line:") logging.error("Failed to launch QEMU, command line:")
logging.error(" ".join([qemu_bin] + args)) logging.error(" ".join([qemu_path] + args))
logging.error("Log:") logging.error("Log:")
logging.error(guest.get_log()) logging.error(guest.get_log())
logging.error("QEMU version >= 2.10 is required") logging.error("QEMU version >= 2.10 is required")
@ -318,24 +320,24 @@ class BaseVM(object):
def print_step(self, text): def print_step(self, text):
sys.stderr.write("### %s ...\n" % text) sys.stderr.write("### %s ...\n" % text)
def wait_ssh(self, wait_root=False, seconds=300): def wait_ssh(self, wait_root=False, seconds=300, cmd="exit 0"):
# Allow more time for VM to boot under TCG. # Allow more time for VM to boot under TCG.
if not kvm_available(self.arch): if not kvm_available(self.arch):
seconds *= self.tcg_ssh_timeout_multiplier seconds *= self.tcg_ssh_timeout_multiplier
starttime = datetime.datetime.now() starttime = datetime.datetime.now()
endtime = starttime + datetime.timedelta(seconds=seconds) endtime = starttime + datetime.timedelta(seconds=seconds)
guest_up = False cmd_success = False
while datetime.datetime.now() < endtime: while datetime.datetime.now() < endtime:
if wait_root and self.ssh_root("exit 0") == 0: if wait_root and self.ssh_root(cmd) == 0:
guest_up = True cmd_success = True
break break
elif self.ssh("exit 0") == 0: elif self.ssh(cmd) == 0:
guest_up = True cmd_success = True
break break
seconds = (endtime - datetime.datetime.now()).total_seconds() seconds = (endtime - datetime.datetime.now()).total_seconds()
logging.debug("%ds before timeout", seconds) logging.debug("%ds before timeout", seconds)
time.sleep(1) time.sleep(1)
if not guest_up: if not cmd_success:
raise Exception("Timeout while waiting for guest ssh") raise Exception("Timeout while waiting for guest ssh")
def shutdown(self): def shutdown(self):
@ -391,6 +393,19 @@ class BaseVM(object):
return os.path.join(cidir, "cloud-init.iso") return os.path.join(cidir, "cloud-init.iso")
def get_qemu_path(arch, build_path=None):
"""Fetch the path to the qemu binary."""
# If QEMU environment variable set, it takes precedence
if "QEMU" in os.environ:
qemu_path = os.environ["QEMU"]
elif build_path:
qemu_path = os.path.join(build_path, arch + "-softmmu")
qemu_path = os.path.join(qemu_path, "qemu-system-" + arch)
else:
# Default is to use system path for qemu.
qemu_path = "qemu-system-" + arch
return qemu_path
def parse_args(vmcls): def parse_args(vmcls):
def get_default_jobs(): def get_default_jobs():
@ -421,6 +436,9 @@ def parse_args(vmcls):
help="build QEMU from source in guest") help="build QEMU from source in guest")
parser.add_option("--build-target", parser.add_option("--build-target",
help="QEMU build target", default="check") help="QEMU build target", default="check")
parser.add_option("--build-path", default=None,
help="Path of build directory, "\
"for using build tree QEMU binary. ")
parser.add_option("--interactive", "-I", action="store_true", parser.add_option("--interactive", "-I", action="store_true",
help="Interactively run command") help="Interactively run command")
parser.add_option("--snapshot", "-s", action="store_true", parser.add_option("--snapshot", "-s", action="store_true",
@ -439,7 +457,7 @@ def main(vmcls):
logging.basicConfig(level=(logging.DEBUG if args.debug logging.basicConfig(level=(logging.DEBUG if args.debug
else logging.WARN)) else logging.WARN))
vm = vmcls(debug=args.debug, vcpus=args.jobs, vm = vmcls(debug=args.debug, vcpus=args.jobs,
genisoimage=args.genisoimage) genisoimage=args.genisoimage, build_path=args.build_path)
if args.build_image: if args.build_image:
if os.path.exists(args.image) and not args.force: if os.path.exists(args.image) and not args.force:
sys.stderr.writelines(["Image file exists: %s\n" % args.image, sys.stderr.writelines(["Image file exists: %s\n" % args.image,