pci_hotplug.py 13.7 KB
Newer Older
L
Lucas Meneghel Rodrigues 已提交
1 2 3
import re
import logging
import string
4
from autotest.client.shared import error
5
from virttest import utils_misc, aexpect, storage, utils_test, data_dir, arch
6 7


8
@error.context_aware
9
def run(test, params, env):
10 11 12 13
    """
    Test hotplug of PCI devices.

    (Elements between [] are configurable test parameters)
14
    1) PCI add one/multi device (NIC / block) with(or without) repeat
15 16 17 18 19
    2) Compare output of monitor command 'info pci'.
    3) Compare output of guest command [reference_cmd].
    4) Verify whether pci_model is shown in [pci_find_cmd].
    5) Check whether the newly added PCI device works fine.
    6) PCI delete the device, verify whether could remove the PCI device.
20
    7) reboot VM after guest wakeup form S3/S4 status (Optional Step).
21

L
Lucas Meneghel Rodrigues 已提交
22 23 24
    :param test:   QEMU test object.
    :param params: Dictionary with the test parameters.
    :param env:    Dictionary with test environment.
25
    """
26 27 28 29 30
    # Select an image file
    def find_image(pci_num):
        image_params = params.object_params("%s" % img_list[pci_num + 1])
        o = storage.get_image_filename(image_params, data_dir.get_data_dir())
        return o
31

32 33 34
    def pci_add_nic(pci_num):
        pci_add_cmd = "pci_add pci_addr=auto nic model=%s" % pci_model
        return pci_add(pci_add_cmd)
35

36 37 38 39 40
    def pci_add_block(pci_num):
        image_filename = find_image(pci_num)
        pci_add_cmd = ("pci_add pci_addr=auto storage file=%s,if=%s" %
                       (image_filename, pci_model))
        return pci_add(pci_add_cmd)
41

42 43 44
    def pci_add(pci_add_cmd):
        error.context("Adding pci device with command 'pci_add'")
        add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False)
L
Lucas Meneghel Rodrigues 已提交
45
        pci_info.append(['', '', add_output, pci_model])
46

47
        if "OK domain" not in add_output:
48 49 50
            raise error.TestFail("Add PCI device failed. "
                                 "Monitor command is: %s, Output: %r" %
                                 (pci_add_cmd, add_output))
L
Lucas Meneghel Rodrigues 已提交
51
        return vm.monitor.info("pci")
52

53 54
    def is_supported_device(dev):
        # Probe qemu to verify what is the supported syntax for PCI hotplug
55
        cmd_output = vm.monitor.human_monitor_cmd("?")
56 57 58 59 60 61 62 63
        if len(re.findall("\ndevice_add", cmd_output)) > 0:
            cmd_type = "device_add"
        elif len(re.findall("\npci_add", cmd_output)) > 0:
            cmd_type = "pci_add"
        else:
            raise error.TestError("Unknow version of qemu")

        # Probe qemu for a list of supported devices
64
        probe_output = vm.monitor.human_monitor_cmd("%s ?" % cmd_type)
65 66 67 68 69 70 71
        devices_supported = [j.strip('"') for j in
                             re.findall('\"[a-z|0-9|\-|\_|\,|\.]*\"',
                                        probe_output, re.MULTILINE)]
        logging.debug("QEMU reported the following supported devices for "
                      "PCI hotplug: %s", devices_supported)
        return (dev in devices_supported)

72
    def verify_supported_device(dev):
73
        if not is_supported_device(dev):
74
            raise error.TestError("%s doesn't support device: %s" %
75 76
                                  (cmd_type, dev))

77
    def device_add_nic(pci_num, queues=1):
78 79 80 81 82 83 84 85 86
        device_id = pci_type + "-" + utils_misc.generate_random_id()
        pci_info.append([device_id, device_id])

        pci_model = params.get("pci_model")
        if pci_model == "virtio":
            pci_model = "virtio-net-pci"
        verify_supported_device(pci_model)
        pci_add_cmd = "device_add id=%s,driver=%s" % (pci_info[pci_num][1],
                                                      pci_model)
87 88
        if queues > 1 and "virtio" in pci_model:
            pci_add_cmd += ",mq=on"
89 90
        return device_add(pci_num, pci_add_cmd)

91
    def device_add_block(pci_num, queues=1):
92 93 94
        device_id = pci_type + "-" + utils_misc.generate_random_id()
        pci_info.append([device_id, device_id])

L
Lucas Meneghel Rodrigues 已提交
95
        image_format = params.get("image_format_%s" % img_list[pci_num + 1])
96 97 98 99 100 101 102 103 104 105 106
        if not image_format:
            image_format = params.get("image_format", "qcow2")
        image_filename = find_image(pci_num)

        pci_model = params.get("pci_model")
        controller_model = None
        if pci_model == "virtio":
            pci_model = "virtio-blk-pci"

        if pci_model == "scsi":
            pci_model = "scsi-disk"
107 108 109 110
            if arch.ARCH == 'ppc64':
                controller_model = "spapr-vscsi"
            else:
                controller_model = "lsi53c895a"
111 112 113 114 115 116 117 118 119 120 121 122 123 124
            verify_supported_device(controller_model)
            controller_id = "controller-" + device_id
            controller_add_cmd = ("device_add %s,id=%s" %
                                  (controller_model, controller_id))
            error.context("Adding SCSI controller.")
            vm.monitor.send_args_cmd(controller_add_cmd)

        verify_supported_device(pci_model)
        if drive_cmd_type == "drive_add":
            driver_add_cmd = ("%s auto file=%s,if=none,format=%s,id=%s" %
                              (drive_cmd_type, image_filename, image_format,
                               pci_info[pci_num][0]))
        elif drive_cmd_type == "__com.redhat_drive_add":
            driver_add_cmd = ("%s file=%s,format=%s,id=%s" %
125 126
                              (drive_cmd_type, image_filename, image_format,
                               pci_info[pci_num][0]))
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
        # add driver.
        error.context("Adding driver.")
        vm.monitor.send_args_cmd(driver_add_cmd, convert=False)

        pci_add_cmd = ("device_add id=%s,driver=%s,drive=%s" %
                       (pci_info[pci_num][1], pci_model, pci_info[pci_num][0]))
        return device_add(pci_num, pci_add_cmd)

    def device_add(pci_num, pci_add_cmd):
        error.context("Adding pci device with command 'device_add'")
        if vm.monitor.protocol == 'qmp':
            add_output = vm.monitor.send_args_cmd(pci_add_cmd)
        else:
            add_output = vm.monitor.send_args_cmd(pci_add_cmd, convert=False)
        pci_info[pci_num].append(add_output)
        pci_info[pci_num].append(pci_model)
143

144
        after_add = vm.monitor.info("pci")
C
Cong Li 已提交
145
        if pci_info[pci_num][1] not in str(after_add):
146 147
            logging.error("Could not find matched id in monitor:"
                          " %s" % pci_info[pci_num][1])
148 149
            raise error.TestFail("Add device failed. Monitor command is: %s"
                                 ". Output: %r" % (pci_add_cmd, add_output))
150
        return after_add
151

152
    # Hot add a pci device
153
    def add_device(pci_num, queues=1):
154 155 156 157 158 159 160 161 162 163 164 165
        info_pci_ref = vm.monitor.info("pci")
        reference = session.cmd_output(reference_cmd)

        try:
            # get function for adding device.
            add_fuction = local_functions["%s_%s" % (cmd_type, pci_type)]
        except Exception:
            raise error.TestError("No function for adding '%s' dev with '%s'" %
                                  (pci_type, cmd_type))
        after_add = None
        if add_fuction:
            # Do add pci device.
166
            after_add = add_fuction(pci_num, queues)
167 168 169 170 171 172 173 174 175 176 177 178 179 180

        try:
            # Define a helper function to compare the output
            def _new_shown():
                o = session.cmd_output(reference_cmd)
                return o != reference

            # Define a helper function to catch PCI device string
            def _find_pci():
                output = session.cmd_output(params.get("find_pci_cmd"))
                output = map(string.strip, output.splitlines())
                ref = map(string.strip, reference.splitlines())
                output = [_ for _ in output if _ not in ref]
                output = "\n".join(output)
181
                if re.search(params.get("match_string"), output, re.I | re.M):
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
                    return True
                return False

            error.context("Start checking new added device")
            # Compare the output of 'info pci'
            if after_add == info_pci_ref:
                raise error.TestFail("No new PCI device shown after executing "
                                     "monitor command: 'info pci'")

            secs = int(params.get("wait_secs_for_hook_up"))
            if not utils_misc.wait_for(_new_shown, test_timeout, secs, 3):
                raise error.TestFail("No new device shown in output of command "
                                     "executed inside the guest: %s" %
                                     reference_cmd)

            if not utils_misc.wait_for(_find_pci, test_timeout, 3, 3):
                raise error.TestFail("PCI %s %s device not found in guest. "
                                     "Command was: %s" %
                                     (pci_model, pci_type,
                                      params.get("find_pci_cmd")))

            # Test the newly added device
            try:
L
Lucas Meneghel Rodrigues 已提交
205
                session.cmd(params.get("pci_test_cmd") % (pci_num + 1))
206 207 208 209 210 211 212 213 214 215 216
            except aexpect.ShellError, e:
                raise error.TestFail("Check for %s device failed after PCI "
                                     "hotplug. Output: %r" % (pci_type, e.output))

        except Exception:
            pci_del(pci_num, ignore_failure=True)
            raise

    # Hot delete a pci device
    def pci_del(pci_num, ignore_failure=False):
        def _device_removed():
217
            after_del = vm.monitor.info("pci")
218 219 220 221 222 223 224 225 226 227
            return after_del != before_del

        before_del = vm.monitor.info("pci")
        if cmd_type == "pci_add":
            slot_id = int(pci_info[pci_num][2].split(",")[2].split()[1])
            cmd = "pci_del pci_addr=%s" % hex(slot_id)
            vm.monitor.send_args_cmd(cmd, convert=False)
        elif cmd_type == "device_add":
            cmd = "device_del id=%s" % pci_info[pci_num][1]
            vm.monitor.send_args_cmd(cmd)
228

229
        if (not utils_misc.wait_for(_device_removed, test_timeout, 0, 1)
L
Lucas Meneghel Rodrigues 已提交
230
                and not ignore_failure):
231 232
            raise error.TestFail("Failed to hot remove PCI device: %s. "
                                 "Monitor command: %s" %
233 234 235 236 237 238
                                 (pci_info[pci_num][3], cmd))

    vm = env.get_vm(params["main_vm"])
    vm.verify_alive()
    timeout = int(params.get("login_timeout", 360))
    session = vm.wait_for_login(timeout=timeout)
239

240
    test_timeout = int(params.get("hotplug_timeout", 360))
241 242 243 244
    reference_cmd = params["reference_cmd"]
    # Test if it is nic or block
    pci_type = params["pci_type"]
    pci_model = params["pci_model"]
245

246 247 248 249 250 251 252 253 254 255 256
    # Modprobe the module if specified in config file
    module = params.get("modprobe_module")
    if module:
        session.cmd("modprobe %s" % module)

    # check monitor type
    qemu_binary = params.get("qemu_binary", "/usr/libexec/qemu-kvm")
    qemu_binary = utils_misc.get_path(test.bindir, qemu_binary)
    # Probe qemu to verify what is the supported syntax for PCI hotplug
    if vm.monitor.protocol == 'qmp':
        cmd_output = vm.monitor.info("commands")
257
    else:
258 259
        cmd_output = vm.monitor.human_monitor_cmd("help", debug=False)

260
    cmd_type = utils_misc.find_substring(str(cmd_output), "device_add",
261 262 263 264 265 266 267 268
                                         "pci_add")
    if not cmd_output:
        raise error.TestError("Could find a suitable method for hotplugging"
                              " device in this version of qemu")

    # Determine syntax of drive hotplug
    # __com.redhat_drive_add == qemu-kvm-0.12 on RHEL 6
    # drive_add == qemu-kvm-0.13 onwards
269
    drive_cmd_type = utils_misc.find_substring(str(cmd_output),
L
Lucas Meneghel Rodrigues 已提交
270
                                               "__com.redhat_drive_add", "drive_add")
271 272 273 274 275 276 277
    if not drive_cmd_type:
        raise error.TestError("Could find a suitable method for hotplugging"
                              " drive in this version of qemu")

    local_functions = locals()

    pci_num_range = int(params.get("pci_num"))
278
    queues = int(params.get("queues", 1))
279 280
    rp_times = int(params.get("repeat_times"))
    img_list = params.get("images").split()
281
    context_msg = "Running sub test '%s' %s"
282 283 284 285 286 287 288 289 290
    for j in range(rp_times):
        # pci_info is a list of list.
        # each element 'i' has 4 members:
        # pci_info[i][0] == device drive id, only used for device_add
        # pci_info[i][1] == device id, only used for device_add
        # pci_info[i][2] == output of device add command
        # pci_info[i][3] == device module name.
        pci_info = []
        for pci_num in xrange(pci_num_range):
291 292 293 294 295 296
            sub_type = params.get("sub_type_before_plug")
            if sub_type:
                error.context(context_msg % (sub_type, "before hotplug"),
                              logging.info)
                utils_test.run_virt_sub_test(test, params, env, sub_type)

297 298
            error.context("Start hot-adding pci device, repeat %d" % j,
                          logging.info)
299
            add_device(pci_num, queues)
300 301 302 303 304 305

            sub_type = params.get("sub_type_after_plug")
            if sub_type:
                error.context(context_msg % (sub_type, "after hotplug"),
                              logging.info)
                utils_test.run_virt_sub_test(test, params, env, sub_type)
306
        for pci_num in xrange(pci_num_range):
307 308 309 310 311 312
            sub_type = params.get("sub_type_before_unplug")
            if sub_type:
                error.context(context_msg % (sub_type, "before hotunplug"),
                              logging.info)
                utils_test.run_virt_sub_test(test, params, env, sub_type)

313 314
            error.context("start hot-deleting pci device, repeat %d" % j,
                          logging.info)
315
            pci_del(-(pci_num + 1))
316 317 318 319 320 321

            sub_type = params.get("sub_type_after_unplug")
            if sub_type:
                error.context(context_msg % (sub_type, "after hotunplug"),
                              logging.info)
                utils_test.run_virt_sub_test(test, params, env, sub_type)
322 323 324

    if params.get("reboot_vm", "no") == "yes":
        vm.reboot()