From 9b48ccc76a3f678b921bdf94ef33539305e1b2f8 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Oct 25 2021 17:29:02 +0000 Subject: [PATCH 1/2] add TEST_SKIP_MISSING_DEVICE If you are using the same provision.fmf on multiple systems, the qemu on some systems may not support all of the specified devices. For example, qemu on many platforms does not support NVMe. By default, the script will issue an error if you attempt to use an unsupported device. Use this flag to skip missing devices, with a warning. --- diff --git a/README.md b/README.md index 5f00184..1134b09 100644 --- a/README.md +++ b/README.md @@ -215,5 +215,13 @@ cause terrible performance with `ssh` and especially with Ansible. You can use `--sshd-usedns-no` or set `TEST_SSHD_USEDNS_NO=True` to configure the VM to use `UseDNS no` instead. +## TEST_SKIP_MISSING_DEVICE + +If you are using the same provision.fmf on multiple systems, the qemu on some +systems may not support all of the specified devices. For example, qemu on many +platforms does not support NVMe. By default, the script will issue an error if +you attempt to use an unsupported device. Use this flag to skip missing devices, +with a warning. + [1]: https://fedoraproject.org/wiki/CI/Metadata [2]: http://fmf.readthedocs.io/ diff --git a/inventory/standard-inventory-qcow2 b/inventory/standard-inventory-qcow2 index 2120723..a1a7458 100755 --- a/inventory/standard-inventory-qcow2 +++ b/inventory/standard-inventory-qcow2 @@ -101,7 +101,7 @@ class AdditionalDrives(object): _tempfiles = list() @classmethod - def generate(cls): + def generate(cls, nvme_support, skip_missing_device): """Generate sparse files and return drive qemu options Returns ------- @@ -127,9 +127,14 @@ class AdditionalDrives(object): if interface is None or interface.lower() == 'virtio': result += ["-drive", "file=%s,media=disk,if=virtio" % drive_file.name] elif interface.lower() == 'nvme': - result += ["-device", "nvme,drive=nvme%s,serial=def%s" % (dev_id, dev_id)] - result += ["-drive", "file=%s,media=disk,if=none,id=nvme%s" % (drive_file.name, dev_id)] - dev_id += 1 + if nvme_support: + result += ["-device", "nvme,drive=nvme%s,serial=def%s" % (dev_id, dev_id)] + result += ["-drive", "file=%s,media=disk,if=none,id=nvme%s" % (drive_file.name, dev_id)] + dev_id += 1 + elif not skip_missing_device: + raise Exception("Cannot use device of size '%d' - no NVMe support on this platform" % size) + else: + logger.warn("NVMe drive of size '%d' will be skipped - no NVMe support on this platform", size) elif interface.lower() == 'scsi': if not scsi_device_exists: result += ["-device", "virtio-scsi-pci,id=scsi0"] @@ -329,7 +334,7 @@ def fmf_get(path, default=None): return value -def start_qemu(image, cloudinit, portrange=(2222, 5555)): +def start_qemu(image, cloudinit, skip_missing_device, portrange=(2222, 5555)): for _ in range(10): port = random.randint(*portrange) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -422,6 +427,22 @@ def start_qemu(image, cloudinit, portrange=(2222, 5555)): pass if virtio_rng: logger.info("qemu-kvm is using %s device" % virtio_rng[1]) + # check for nvme support + cmd_tmpl = "echo quit | %s -device %%s -S -monitor stdio" % ( + " ".join([qemu_path] + qemu_common_params) + ) + nvme_support = False + try: + subprocess.check_call( + cmd_tmpl % "nvme", + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=True + ) + nvme_support = True + except subprocess.CalledProcessError: + pass + # Assemble QEMU command with its parameters: qemu_cmd = [ qemu_path @@ -451,7 +472,7 @@ def start_qemu(image, cloudinit, portrange=(2222, 5555)): # Log all traffic received from the guest to log_quest "-chardev", "file,id=pts2,path=" + log_guest ] - qemu_cmd += AdditionalDrives.generate() + qemu_cmd += AdditionalDrives.generate(nvme_support, skip_missing_device) if diagnose: qemu_cmd += ["-vnc", DEF_HOST + ":1,to=4095"] # Launch QEMU: @@ -510,7 +531,7 @@ def inv_host(opts, image, hostalias): log = None for _ in range(0, 5): try: - proc, port, log = start_qemu(image, cloudinit) + proc, port, log = start_qemu(image, cloudinit, opts.skip_missing_device) break except subprocess.CalledProcessError as cpe: time.sleep(1) @@ -674,6 +695,14 @@ def help_sshd_usedns_no(): --sshd-usedns-no to configure the VM to use 'UseDNS no' instead.""" +def help_skip_missing_device(): + return """If you are using the same provision.fmf on multiple systems, the + qemu on some systems may not support all of the specified devices. For + example, qemu on many platforms does not support NVMe. By default, the + script will issue an error if you attempt to use an unsupported device. + Use this flag to skip missing devices, with a warning.""" + + def main(): global logger global diagnose @@ -704,6 +733,7 @@ def main(): parser.add_argument("--use-basename", default=bool(distutils.util.strtobool(os.environ.get("TEST_USE_BASENAME", "False"))), action="store_true", help=help_hostalias()) parser.add_argument("--hostalias", default=shlex.split(os.environ.get("TEST_HOSTALIASES", "")), action="append", help=help_hostalias()) parser.add_argument("--sshd-usedns-no", default=bool(distutils.util.strtobool(os.environ.get("TEST_SSHD_USEDNS_NO", "False"))), action="store_true", help=help_sshd_usedns_no()) + parser.add_argument("--skip-missing-device", default=bool(distutils.util.strtobool(os.environ.get("TEST_SKIP_MISSING_DEVICE", "False"))), action="store_true", help=help_skip_missing_device()) parser.add_argument("subjects", nargs="*", default=shlex.split(os.environ.get("TEST_SUBJECTS", ""))) opts = parser.parse_args() # Send logs to common logfile for all default provisioners. From 7a49b4931c731fd5a0f85bbbbbd40536d4b5f8c2 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Oct 26 2021 15:34:43 +0000 Subject: [PATCH 2/2] use supported_devices instead of nvme_support --- diff --git a/inventory/standard-inventory-qcow2 b/inventory/standard-inventory-qcow2 index a1a7458..ad3a2f7 100755 --- a/inventory/standard-inventory-qcow2 +++ b/inventory/standard-inventory-qcow2 @@ -101,7 +101,7 @@ class AdditionalDrives(object): _tempfiles = list() @classmethod - def generate(cls, nvme_support, skip_missing_device): + def generate(cls, supported_devices, skip_missing_device): """Generate sparse files and return drive qemu options Returns ------- @@ -127,7 +127,7 @@ class AdditionalDrives(object): if interface is None or interface.lower() == 'virtio': result += ["-drive", "file=%s,media=disk,if=virtio" % drive_file.name] elif interface.lower() == 'nvme': - if nvme_support: + if 'nvme' in supported_devices: result += ["-device", "nvme,drive=nvme%s,serial=def%s" % (dev_id, dev_id)] result += ["-drive", "file=%s,media=disk,if=none,id=nvme%s" % (drive_file.name, dev_id)] dev_id += 1 @@ -136,12 +136,17 @@ class AdditionalDrives(object): else: logger.warn("NVMe drive of size '%d' will be skipped - no NVMe support on this platform", size) elif interface.lower() == 'scsi': - if not scsi_device_exists: - result += ["-device", "virtio-scsi-pci,id=scsi0"] - scsi_device_exists = True - result += ["-device", "scsi-hd,drive=drive%s,bus=scsi0.0" % dev_id] - result += ["-drive", "if=none,file=%s,id=drive%s" % (drive_file.name, dev_id)] - dev_id += 1 + if 'scsi' in supported_devices: + if not scsi_device_exists: + result += ["-device", "virtio-scsi-pci,id=scsi0"] + scsi_device_exists = True + result += ["-device", "scsi-hd,drive=drive%s,bus=scsi0.0" % dev_id] + result += ["-drive", "if=none,file=%s,id=drive%s" % (drive_file.name, dev_id)] + dev_id += 1 + elif not skip_missing_device: + raise Exception("Cannot use device of size '%d' - no SCSI support on this platform" % size) + else: + logger.warn("SCSI drive of size '%d' will be skipped - no SCSI support on this platform", size) usb_drives = fmf_get(['qemu', 'usb_drive'], list()) if usb_drives: @@ -427,21 +432,24 @@ def start_qemu(image, cloudinit, skip_missing_device, portrange=(2222, 5555)): pass if virtio_rng: logger.info("qemu-kvm is using %s device" % virtio_rng[1]) - # check for nvme support + # check for supported devices + # - key is the name used in provision.fmf + # - value is the name used by qemu + supported_devices = {"nvme": "nvme", + "scsi": "virtio-scsi-pci"} cmd_tmpl = "echo quit | %s -device %%s -S -monitor stdio" % ( " ".join([qemu_path] + qemu_common_params) ) - nvme_support = False - try: - subprocess.check_call( - cmd_tmpl % "nvme", - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - shell=True - ) - nvme_support = True - except subprocess.CalledProcessError: - pass + for name, qemu_name in list(supported_devices.items()): + try: + subprocess.check_call( + cmd_tmpl % qemu_name, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=True + ) + except subprocess.CalledProcessError: + del supported_devices[name] # Assemble QEMU command with its parameters: qemu_cmd = [ @@ -472,7 +480,7 @@ def start_qemu(image, cloudinit, skip_missing_device, portrange=(2222, 5555)): # Log all traffic received from the guest to log_quest "-chardev", "file,id=pts2,path=" + log_guest ] - qemu_cmd += AdditionalDrives.generate(nvme_support, skip_missing_device) + qemu_cmd += AdditionalDrives.generate(supported_devices, skip_missing_device) if diagnose: qemu_cmd += ["-vnc", DEF_HOST + ":1,to=4095"] # Launch QEMU: