|
| 1 | +import time |
| 2 | + |
| 3 | +from virttest import error_context, utils_disk, utils_misc |
| 4 | +from virttest.qemu_monitor import QMPCmdError |
| 5 | + |
| 6 | + |
| 7 | +@error_context.context_aware |
| 8 | +def run(test, params, env): |
| 9 | + """ |
| 10 | + Hotplug a LUKS device with a detached header to a running VM (Linux or Windows), |
| 11 | + do IO, then unplug and verify. |
| 12 | + Steps: |
| 13 | + 1. Boot the VM (images are assumed to be created by the framework) |
| 14 | + 2. Hotplug the LUKS device with QMP (detached header) |
| 15 | + 3. Do read/write IO in the guest |
| 16 | + 4. Unplug and verify |
| 17 | + 5. Clean up images |
| 18 | + """ |
| 19 | + |
| 20 | + vm = env.get_vm(params["main_vm"]) |
| 21 | + vm.verify_alive() |
| 22 | + login_timeout = int(params.get("login_timeout", 360)) |
| 23 | + disk_op_cmd = params.get("disk_op_cmd") |
| 24 | + disk_op_timeout = int(params.get("disk_op_timeout", 360)) |
| 25 | + luks_secret = params.get("image_secret_header", "redhat") |
| 26 | + luks_header = params.get("luks_header_img", "test-header.img") |
| 27 | + luks_payload = params.get("luks_payload_img", "test-payload.img") |
| 28 | + os_type = params.get("os_type", "linux").lower() |
| 29 | + windows = os_type == "windows" |
| 30 | + |
| 31 | + # 1. Boot VM |
| 32 | + session = vm.wait_for_login(timeout=login_timeout) |
| 33 | + if windows: |
| 34 | + disks_before = set(session.cmd("wmic diskdrive get index").split()[1:]) |
| 35 | + else: |
| 36 | + disks_before = set(utils_misc.list_linux_guest_disks(session)) |
| 37 | + session.close() |
| 38 | + |
| 39 | + # 2. QMP hotplug sequence |
| 40 | + try: |
| 41 | + # blockdev-add for payload |
| 42 | + vm.monitor.cmd( |
| 43 | + "blockdev-add", |
| 44 | + { |
| 45 | + "node-name": "libvirt-1-storage", |
| 46 | + "driver": "file", |
| 47 | + "filename": luks_payload, |
| 48 | + }, |
| 49 | + ) |
| 50 | + # blockdev-add for header |
| 51 | + vm.monitor.cmd( |
| 52 | + "blockdev-add", |
| 53 | + { |
| 54 | + "node-name": "libvirt-2-storage", |
| 55 | + "driver": "file", |
| 56 | + "filename": luks_header, |
| 57 | + }, |
| 58 | + ) |
| 59 | + # object-add secret |
| 60 | + vm.monitor.cmd( |
| 61 | + "object-add", |
| 62 | + { |
| 63 | + "qom-type": "secret", |
| 64 | + "id": "libvirt-2-storage-secret0", |
| 65 | + "data": luks_secret, |
| 66 | + }, |
| 67 | + ) |
| 68 | + # blockdev-add for raw |
| 69 | + vm.monitor.cmd( |
| 70 | + "blockdev-add", |
| 71 | + { |
| 72 | + "node-name": "libvirt-1-format", |
| 73 | + "driver": "raw", |
| 74 | + "file": "libvirt-1-storage", |
| 75 | + }, |
| 76 | + ) |
| 77 | + # blockdev-add for luks |
| 78 | + vm.monitor.cmd( |
| 79 | + "blockdev-add", |
| 80 | + { |
| 81 | + "node-name": "libvirt-2-format", |
| 82 | + "driver": "luks", |
| 83 | + "file": "libvirt-1-format", |
| 84 | + "header": "libvirt-2-storage", |
| 85 | + "key-secret": "libvirt-2-storage-secret0", |
| 86 | + }, |
| 87 | + ) |
| 88 | + # device_add |
| 89 | + vm.monitor.cmd( |
| 90 | + "device_add", |
| 91 | + { |
| 92 | + "num-queues": "1", |
| 93 | + "driver": "virtio-blk-pci", |
| 94 | + "drive": "libvirt-2-format", |
| 95 | + "id": "virtio-disk2", |
| 96 | + }, |
| 97 | + ) |
| 98 | + except QMPCmdError as e: |
| 99 | + test.fail("QMP hotplug failed: %s" % e) |
| 100 | + except Exception as e: |
| 101 | + test.fail("QMP hotplug failed: %s" % e) |
| 102 | + |
| 103 | + # 3. IO in guest |
| 104 | + session = vm.wait_for_login(timeout=login_timeout) |
| 105 | + time.sleep(5) # Wait for device to appear |
| 106 | + if windows: |
| 107 | + disks_after = set(session.cmd("wmic diskdrive get index").split()[1:]) |
| 108 | + new_disks = list(disks_after - disks_before) |
| 109 | + if not new_disks: |
| 110 | + test.fail("No new disk detected after hotplug!") |
| 111 | + new_disk = new_disks[0] |
| 112 | + error_context.context( |
| 113 | + "New disk detected (Windows index): %s" % new_disk, test.log.info |
| 114 | + ) |
| 115 | + # Format the disk if needed and get drive letter |
| 116 | + disk_index = params.objects("disk_index") |
| 117 | + disk_letter = params.objects("disk_letter") |
| 118 | + drive_letters = [] |
| 119 | + if disk_index and disk_letter: |
| 120 | + idx = 0 |
| 121 | + utils_misc.format_windows_disk(session, disk_index[idx], disk_letter[idx]) |
| 122 | + drive_letters.append(disk_letter[idx]) |
| 123 | + drive_letter = drive_letters[0] |
| 124 | + else: |
| 125 | + # Try to auto format and get letter |
| 126 | + drive_letter = utils_disk.configure_empty_windows_disk( |
| 127 | + session, new_disk, params.get("luks_payload_size", "5G") |
| 128 | + )[0] |
| 129 | + test_cmd = disk_op_cmd % (drive_letter, drive_letter) |
| 130 | + test_cmd = utils_misc.set_winutils_letter(session, test_cmd) |
| 131 | + else: |
| 132 | + disks_after = set(utils_misc.list_linux_guest_disks(session)) |
| 133 | + new_disks = list(disks_after - disks_before) |
| 134 | + if not new_disks: |
| 135 | + test.fail("No new disk detected after hotplug!") |
| 136 | + new_disk = new_disks[0] |
| 137 | + error_context.context("New disk detected: %s" % new_disk, test.log.info) |
| 138 | + test_cmd = disk_op_cmd % (new_disk, new_disk) |
| 139 | + try: |
| 140 | + session.cmd(test_cmd, timeout=disk_op_timeout) |
| 141 | + except Exception as e: |
| 142 | + test.fail(f"IO on hotplugged disk failed: {e}") |
| 143 | + session.close() |
| 144 | + |
| 145 | + # 4. Unplug |
| 146 | + try: |
| 147 | + vm.monitor.cmd("device_del", {"id": "virtio-disk2"}) |
| 148 | + # Wait for disk to disappear |
| 149 | + session = vm.wait_for_login(timeout=login_timeout) |
| 150 | + |
| 151 | + def disk_gone(): |
| 152 | + if windows: |
| 153 | + return ( |
| 154 | + new_disk not in session.cmd("wmic diskdrive get index").split()[1:] |
| 155 | + ) |
| 156 | + else: |
| 157 | + return new_disk not in utils_misc.list_linux_guest_disks(session) |
| 158 | + |
| 159 | + utils_misc.wait_for(disk_gone, 60, step=2) |
| 160 | + session.close() |
| 161 | + except Exception as e: |
| 162 | + test.fail(f"Unplug failed: {e}") |
0 commit comments