From 14022d56ef37199333025f55eb394b7ce8ef673d Mon Sep 17 00:00:00 2001 From: Andrei Stepanov Date: May 07 2019 10:58:30 +0000 Subject: Merge #341 `inventory/standard-inventory-qcow2: Support running on multiple architectures` --- diff --git a/inventory/standard-inventory-qcow2 b/inventory/standard-inventory-qcow2 index 0e413d9..f6e23d3 100755 --- a/inventory/standard-inventory-qcow2 +++ b/inventory/standard-inventory-qcow2 @@ -22,6 +22,7 @@ import random import logging import argparse import tempfile +import platform import functools import subprocess import distutils.util @@ -137,12 +138,19 @@ def print_bad_inventory(): sys.stdout.write(json.dumps(bad_inv, indent=4, separators=(',', ': '))) +# See https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python/377028#377028 def which(executable, default=None): - for path in os.environ['PATH'].split(os.pathsep): - path = path.strip('"') - fpath = os.path.join(path, executable) - if os.path.isfile(fpath) and os.access(fpath, os.X_OK): - return fpath + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + fpath, _ = os.path.split(executable) + if fpath: + if is_exe(executable): + return executable + else: + for path in os.environ['PATH'].split(os.pathsep): + exe_file = os.path.join(path, executable) + if is_exe(exe_file): + return exe_file return default @@ -284,22 +292,106 @@ def start_qemu(image, cloudinit, portrange=(2222, 5555)): # Parameters from FMF: param_m = str(fmf_get(['qemu', 'm'], "1024")) param_net_nic_model = str(fmf_get(['qemu', 'net_nic', 'model'], 'virtio')) - # Use -cpu host and -smp by default. - path_lookups = ["/usr/bin/qemu-system-x86_64", "/usr/libexec/qemu-kvm"] - for qemu_path in path_lookups: - if os.path.exists(qemu_path): + # QEMU -M param. + # Most architectures do not need a -M flag, defaults are fine + qemu_M_param = [] + # List of firmwares QEMU needs to work. + qemu_firmwares = [] + # Common QEMU parameters used both to run VM and virtio-rng probing. + qemu_common_params = [ + # Pass through CPU model of host + "-cpu", "host", + # Enable KVM full virtualization support + "-enable-kvm", + # Do not display video output + "-display", "none", + # Disable VGA card to avoid crash on ppc with PR + # https://lists.gnu.org/archive/html/qemu-devel/2018-05/msg07070.html + "-vga", "none" + ] + # Add platform specific settings: + if platform.machine() == "aarch64": + # Emulate the same generic interrupt controller as the host system has + qemu_M_param = ["-M", "virt,gic_version=host"] + # Add AAVMF firmware (without this, qemu-kvm will not work on ARM) + qemu_firmwares.extend([ + "-drive", "%s,%s,%s,%s,%s" % ( + "file=/usr/share/AAVMF/AAVMF_CODE.fd", + "if=pflash", + "format=raw", + "unit=0", + "readonly=on" + ) + ]) + # Include -M param and firmwares to common params: + qemu_common_params.extend(qemu_M_param) + qemu_common_params.extend(qemu_firmwares) + # Lookup for qemu: + qemu_env = os.environ.get("QEMU_CMD") + if qemu_env: + path_lookups = [qemu_env] + else: + path_lookups = [] + path_lookups.extend([ + "qemu-kvm", "/usr/libexec/qemu-kvm", "/usr/bin/qemu-system-x86_64" + ]) + for qemu_cmd in path_lookups: + qemu_path = which(qemu_cmd) + if qemu_path: break + # Try to probe virtio-rng device: # virtio-rng-pci: https://wiki.qemu.org/Features/VirtIORNG - qemu_cmd = [qemu_path, - "-cpu", "host", "-smp", get_qemu_smp_arg(), - "-m", param_m, image, "-enable-kvm", "-snapshot", "-cdrom", cloudinit, - "-net", "nic,model=%s" % param_net_nic_model, "-net", "user,hostfwd=tcp:127.0.0.3:{0}-:22".format(port), - "-device", "virtio-rng-pci", "-rtc", "base=utc", - "-device", "isa-serial,chardev=pts2", "-chardev", "file,id=pts2,path=" + log_guest, - "-display", "none"] + virtio_rng = [] + cmd_tmpl = "echo quit | %s -device %%s -S -monitor stdio" % ( + " ".join([qemu_path] + qemu_common_params) + ) + for x in ["virtio-rng", "virtio-rng-pci", "virtio-rng-ccw"]: + try: + subprocess.check_call( + cmd_tmpl % x, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=True + ) + virtio_rng = ["-device", x] + break + except subprocess.CalledProcessError: + pass + if virtio_rng: + logger.info("qemu-kvm is using %s device" % virtio_rng[1]) + # Assemble QEMU command with its parameters: + qemu_cmd = [ + qemu_path + # Add common parameters + ] + qemu_common_params + [ + # Simulate SMP system with get_qemu_smp_arg() CPUs + "-smp", get_qemu_smp_arg(), + # Set startup RAM size + "-m", param_m, + # Add image with RHEL as drive (if=virtio is important for newer + # RHEL 8 images) + "-drive", "file={0},if=virtio".format(image), + # Write to temporary files instead of disk image files + "-snapshot", + # Use `cloudinit` as CD-ROM image + "-cdrom", cloudinit, + # Configure/create an on-board (or machine default) NIC + "-net", "nic,model=%s" % param_net_nic_model, + # Configure a host network backend + "-net", "user,hostfwd=tcp:127.0.0.3:{0}-:22".format(port) + # Add a source of randomness + ] + virtio_rng + [ + # Let the RTC start at the current UTC + "-rtc", "base=utc", + # Connect the virtual serial port with pts2 + "-serial", "chardev:pts2", + # Log all traffic received from the guest to log_quest + "-chardev", "file,id=pts2,path=" + log_guest + ] qemu_cmd += AdditionalDrives.generate() if diagnose: qemu_cmd += ["-vnc", DEF_HOST + ":1,to=4095"] + # Launch QEMU: qemu_proc = subprocess.Popen(qemu_cmd, stdout=open(log_qemu, 'a'), stderr=subprocess.STDOUT) time.sleep(5) if qemu_proc and diagnose: @@ -350,7 +442,7 @@ def inv_host(image): if proc is None: raise RuntimeError("Could not launch VM for qcow2 image" " '{0}':{1}".format(image, cpe.output)) - for _ in range(0, 30): + for _ in range(0, 600): try: # The variables variables = {