def run(test, params, env):
    """
    Test hotplug/hotunplug of vcpu device.

    1) Boot up guest w/o vcpu device.
    2) Hot plug/unplug vcpu devices and check successfully or not. (qemu side)
    3) Check if the number of CPUs in guest changes accordingly. (guest side)
    4) Do sub test after hot plug/unplug.
    5) Recheck the number of CPUs in guest.
    6) Check the CPU topology of guest. (if all vcpu plugged)

    :param test:   QEMU test object.
    :param params: Dictionary with the test parameters.
    :param env:    Dictionary with test environment.
    """
    def check_guest_cpu_count():
        if not utils_misc.wait_for(
                lambda: cpu_utils.check_if_vm_vcpus_match_qemu(vm),
                verify_wait_timeout, first=sleep_after_change):
            test.fail("Actual number of guest CPUs is not equal to expected")

    def sub_hotunplug():
        error_context.context("Hotunplug vcpu devices after vcpu %s"
                              % hotpluggable_test, logging.info)
        for plugged_dev in pluggable_vcpu_dev[::-1]:
            try:
                vm.hotunplug_vcpu_device(plugged_dev)
            except VMDeviceCheckError:
                if not vm.is_paused():
                    raise
                logging.warning("%s can not be unplugged directly because "
                                "guest is paused, will check again after "
                                "resume", plugged_dev)
                vm.params["vcpu_enable_%s" % plugged_dev] = "no"

    def sub_reboot():
        error_context.context("Reboot guest after vcpu %s"
                              % hotpluggable_test, logging.info)
        vm.reboot(session=session, method=params["reboot_method"],
                  timeout=login_timeout)

    def sub_shutdown():
        error_context.context("Shutdown guest after vcpu %s"
                              % hotpluggable_test, logging.info)
        shutdown_method = params["shutdown_method"]
        if shutdown_method == "shell":
            session.sendline(params["shutdown_command"])
            error_context.context("waiting VM to go down (guest shell cmd)",
                                  logging.info)
        elif shutdown_method == "system_powerdown":
            vm.monitor.system_powerdown()
            error_context.context("waiting VM to go down (qemu monitor cmd)",
                                  logging.info)
        if not vm.wait_for_shutdown(360):
            test.fail("Guest refuses to go down after vcpu %s"
                      % hotpluggable_test)

    def sub_migrate():
        sub_migrate_reboot = sub_reboot
        sub_migrate_hotunplug = sub_hotunplug
        error_context.context("Migrate guest after vcpu %s"
                              % hotpluggable_test, logging.info)
        vm.migrate()
        vm.verify_alive()
        sub_test_after_migrate = params.objects("sub_test_after_migrate")
        while sub_test_after_migrate:
            check_guest_cpu_count()
            sub_test = sub_test_after_migrate.pop(0)
            error_context.context("%s after migration completed" % sub_test)
            eval("sub_migrate_%s" % sub_test)()

    def sub_online_offline():
        error_context.context("Offline then online guest CPUs after vcpu %s"
                              % hotpluggable_test, logging.info)
        cpu_ids = list(current_guest_cpu_ids - guest_cpu_ids)
        cpu_ids.sort()
        cmd = "echo %d > /sys/devices/system/cpu/cpu%d/online"
        try:
            for cpu_id in cpu_ids[::-1]:
                session.cmd(cmd % (0, cpu_id))
            if not cpu_utils.check_if_vm_vcpu_match(cpu_count_before_test, vm):
                test.fail(
                    "Actual number of guest CPUs is not equal to expected")
            for cpu_id in cpu_ids:
                session.cmd(cmd % (1, cpu_id))
        except ShellCmdError as err:
            logging.error(str(err))
            test.error("Failed to change the CPU state on guest.")

    def sub_pause_resume():
        error_context.context("Pause guest to hotunplug all vcpu devices",
                              logging.info)
        vm.pause()
        sub_hotunplug()
        error_context.context("Resume guest after hotunplug")
        vm.resume()

    login_timeout = params.get_numeric("login_timeout", 360)
    sleep_after_change = params.get_numeric("sleep_after_cpu_change", 30)
    os_type = params["os_type"]
    hotpluggable_test = params["hotpluggable_test"]
    verify_wait_timeout = params.get_numeric("verify_wait_timeout", 60)
    sub_test_type = params.get("sub_test_type")
    check_cpu_topology = params.get_boolean("check_cpu_topology", True)

    vm = env.get_vm(params["main_vm"])
    maxcpus = vm.cpuinfo.maxcpus
    if not params.objects("vcpu_devices"):
        vcpus_count = (vm.cpuinfo.threads if
                       params["machine_type"].startswith("pseries") else 1)
        pluggable_cpus = vm.cpuinfo.maxcpus // vcpus_count // 2
        params["vcpu_devices"] = " ".join(["vcpu%d" % (count + 1) for count in
                                           range(pluggable_cpus)])
        vm.destroy()
        if len(params.objects("vcpu_devices")) < 2:
            test.cancel("Insufficient maxcpus for multi-CPU hotplug")
        params["paused_after_start_vm"] = "no"
        vm.create(params=params)

    if vm.is_paused():
        vm.resume()
    vcpu_devices = params.objects("vcpu_devices")
    vm.verify_alive()
    session = vm.wait_for_login(timeout=login_timeout)
    cpu_count_before_test = vm.get_cpu_count()
    guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)

    error_context.context("Check the number of guest CPUs after startup",
                          logging.info)
    if not cpu_utils.check_if_vm_vcpus_match_qemu(vm):
        test.error("The number of guest CPUs is not equal to the qemu command "
                   "line configuration")

    if hotpluggable_test == "hotplug":
        pluggable_vcpu_dev = vcpu_devices
    else:
        pluggable_vcpu_dev = vcpu_devices[::-1]

    if params.get_boolean("workaround_need"):
        win_wora.modify_driver(params, session)

    if params.get("pause_vm_before_hotplug", "no") == "yes":
        error_context.context("Pause guest before %s" % hotpluggable_test,
                              logging.info)
        vm.pause()

    error_context.context("%s all vcpu devices" % hotpluggable_test,
                          logging.info)
    for vcpu_dev in pluggable_vcpu_dev:
        getattr(vm, "%s_vcpu_device" % hotpluggable_test)(vcpu_dev)
    if vm.is_paused():
        error_context.context("Resume guest after %s" % hotpluggable_test,
                              logging.info)
        vm.resume()

    check_guest_cpu_count()
    current_guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)

    if sub_test_type:
        eval("sub_%s" % sub_test_type)()
        # Close old session since guest maybe dead/reboot
        if session:
            session.close()

    if vm.is_alive():
        session = vm.wait_for_login(timeout=login_timeout)
        check_guest_cpu_count()
        if vm.get_cpu_count() == maxcpus and check_cpu_topology:
            if not cpu_utils.check_if_vm_vcpu_topology_match(session, os_type,
                                                             vm.cpuinfo):
                session.close()
                test.fail("CPU topology of guest is inconsistent with "
                          "expectations.")
Exemple #2
0
def run(test, params, env):
    """
    Test hotplug/hotunplug of vcpu device.

    1) Boot up guest w/o vcpu device.
    2) Hot plug/unplug vcpu devices and check successfully or not. (qemu side)
    3) Check if the number of CPUs in guest changes accordingly. (guest side)
    4) Do sub test after hot plug/unplug.
    5) Recheck the number of CPUs in guest.
    6) Check the CPU topology of guest. (if all vcpu plugged)

    :param test:   QEMU test object.
    :param params: Dictionary with the test parameters.
    :param env:    Dictionary with test environment.
    """
    def check_guest_cpu_count(expected_count):
        if not utils_misc.wait_for(
                lambda: vm.get_cpu_count() == expected_count,
                verify_wait_timeout,
                first=sleep_after_change):
            logging.error(
                "CPU quantity mismatched! Guest got %s but the"
                " expected is %s", vm.get_cpu_count(), expected_count)
            test.fail("Actual number of guest CPUs is not equal to expected")
        logging.info("CPU quantity matched: %s", expected_count)

    def sub_hotunplug():
        error_context.context(
            "Hotunplug vcpu devices after vcpu %s" % hotpluggable_test,
            logging.info)
        for plugged_dev in pluggable_vcpu_dev[::-1]:
            try:
                vm.hotunplug_vcpu_device(plugged_dev)
            except VMDeviceCheckError:
                if not vm.is_paused():
                    raise
                logging.warning(
                    "%s can not be unplugged directly because "
                    "guest is paused, will check again after "
                    "resume", plugged_dev)

    def sub_reboot():
        error_context.context("Reboot guest after vcpu %s" % hotpluggable_test,
                              logging.info)
        vm.reboot(session=session,
                  method=params["reboot_method"],
                  timeout=login_timeout)

    def sub_shutdown():
        error_context.context(
            "Shutdown guest after vcpu %s" % hotpluggable_test, logging.info)
        shutdown_method = params["shutdown_method"]
        if shutdown_method == "shell":
            session.sendline(params["shutdown_command"])
            error_context.context("waiting VM to go down (guest shell cmd)",
                                  logging.info)
        elif shutdown_method == "system_powerdown":
            vm.monitor.system_powerdown()
            error_context.context("waiting VM to go down (qemu monitor cmd)",
                                  logging.info)
        if not vm.wait_for_shutdown(360):
            test.fail("Guest refuses to go down after vcpu %s" %
                      hotpluggable_test)

    def sub_migrate():
        sub_migrate_reboot = sub_reboot
        sub_migrate_hotunplug = sub_hotunplug
        error_context.context(
            "Migrate guest after vcpu %s" % hotpluggable_test, logging.info)
        vm.migrate()
        vm.verify_alive()
        sub_test_after_migrate = params.objects("sub_test_after_migrate")
        while sub_test_after_migrate:
            check_guest_cpu_count(expected_vcpus)
            sub_test = sub_test_after_migrate.pop(0)
            error_context.context("%s after migration completed" % sub_test)
            eval("sub_migrate_%s" % sub_test)()

    def sub_online_offline():
        error_context.context(
            "Offline then online guest CPUs after vcpu %s" % hotpluggable_test,
            logging.info)
        cpu_ids = list(current_guest_cpu_ids - guest_cpu_ids)
        cpu_ids.sort()
        cmd = "echo %d > /sys/devices/system/cpu/cpu%d/online"
        try:
            for cpu_id in cpu_ids[::-1]:
                session.cmd(cmd % (0, cpu_id))
            check_guest_cpu_count(smp)
            for cpu_id in cpu_ids:
                session.cmd(cmd % (1, cpu_id))
        except ShellCmdError as err:
            logging.error(str(err))
            test.error("Failed to change the CPU state on guest.")

    def sub_pause_resume():
        error_context.context("Pause guest to hotunplug all vcpu devices",
                              logging.info)
        vm.pause()
        sub_hotunplug()
        error_context.context("Resume guest after hotunplug")
        vm.resume()

    login_timeout = params.get_numeric("login_timeout", 360)
    sleep_after_change = params.get_numeric("sleep_after_cpu_change", 30)
    os_type = params["os_type"]
    hotpluggable_test = params["hotpluggable_test"]
    verify_wait_timeout = params.get_numeric("verify_wait_timeout", 60)
    sub_test_type = params.get("sub_test_type")
    check_cpu_topology = params.get_boolean("check_cpu_topology", True)

    vm = env.get_vm(params["main_vm"])
    vm.verify_alive()
    session = vm.wait_for_login(timeout=login_timeout)
    smp = vm.cpuinfo.smp
    maxcpus = vm.cpuinfo.maxcpus
    vcpus_count = vm.params.get_numeric("vcpus_count", 1)
    vcpu_devices = params.objects("vcpu_devices")
    guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)

    if hotpluggable_test == "hotplug":
        pluggable_vcpu_dev = vcpu_devices
        pluggable_vcpu = vcpus_count * len(pluggable_vcpu_dev)
    else:
        pluggable_vcpu_dev = vcpu_devices[::-1]
        pluggable_vcpu = -(vcpus_count * len(pluggable_vcpu_dev))
    expected_vcpus = vm.get_cpu_count() + pluggable_vcpu

    if params.get("pause_vm_before_hotplug", "no") == "yes":
        error_context.context("Pause guest before %s" % hotpluggable_test,
                              logging.info)
        vm.pause()
    error_context.context("%s all vcpu devices" % hotpluggable_test,
                          logging.info)
    for vcpu_dev in pluggable_vcpu_dev:
        getattr(vm, "%s_vcpu_device" % hotpluggable_test)(vcpu_dev)
    if vm.is_paused():
        error_context.context("Resume guest after %s" % hotpluggable_test,
                              logging.info)
        vm.resume()

    check_guest_cpu_count(expected_vcpus)
    current_guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)

    if sub_test_type:
        eval("sub_%s" % sub_test_type)()
        # Close old session since guest maybe dead/reboot
        if session:
            session.close()
        if ("hotunplug" in params.objects("sub_test_after_migrate")
                or sub_test_type == "pause_resume"):
            expected_vcpus -= pluggable_vcpu

    if vm.is_alive():
        session = vm.wait_for_login(timeout=login_timeout)
        check_guest_cpu_count(expected_vcpus)
        if expected_vcpus == maxcpus and check_cpu_topology:
            if not cpu_utils.check_guest_cpu_topology(session, os_type,
                                                      vm.cpuinfo):
                session.close()
                test.fail("CPU topology of guest is inconsistent with "
                          "expectations.")
def run(test, params, env):
    """
    Test hotplug vcpu devices and execute stress test.

    1) Boot up guest without vcpu device.
    2) Hotplug vcpu devices and check successfully or not. (qemu side)
    3) Check if the number of CPUs in guest changes accordingly. (guest side)
    4) Execute stress test on all hotplugged vcpu devices
    5) Hotunplug vcpu devices during stress test
    6) Recheck the number of CPUs in guest.

    :param test:   QEMU test object.
    :param params: Dictionary with the test parameters.
    :param env:    Dictionary with test environment.
    """
    def heavyload_install():
        if session.cmd_status(test_installed_cmd) != 0:
            logging.warning("Could not find installed heavyload in guest, will"
                            " install it via winutils.iso ")
            winutil_drive = utils_misc.get_winutils_vol(session)
            if not winutil_drive:
                test.cancel("WIN_UTILS CDROM not found.")
            install_cmd = params["install_cmd"] % winutil_drive
            session.cmd(install_cmd)

    os_type = params["os_type"]
    vm_arch_name = params.get('vm_arch_name', arch.ARCH)
    login_timeout = params.get_numeric("login_timeout", 360)
    stress_duration = params.get_numeric("stress_duration", 180)
    verify_wait_timeout = params.get_numeric("verify_wait_timeout", 60)
    vcpu_devices = params.objects("vcpu_devices")
    vcpus_count = params.get_numeric("vcpus_count", 1)
    pluggable_count = len(vcpu_devices) * vcpus_count

    vm = env.get_vm(params["main_vm"])
    vm.verify_alive()
    session = vm.wait_for_login(timeout=login_timeout)
    if not cpu_utils.check_if_vm_vcpu_match(vm.cpuinfo.smp, vm):
        test.error("The number of guest CPUs is not equal to the qemu command "
                   "line configuration")

    cpu_count_before_test = vm.get_cpu_count()
    expected_count = pluggable_count + cpu_count_before_test
    guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)
    for vcpu_dev in vcpu_devices:
        error_context.context("Hotplug vcpu device: %s" % vcpu_dev,
                              logging.info)
        vm.hotplug_vcpu_device(vcpu_dev)
    if not utils_misc.wait_for(
            lambda: cpu_utils.check_if_vm_vcpu_match(expected_count, vm),
            verify_wait_timeout):
        test.fail("Actual number of guest CPUs is not equal to expected")

    if os_type == "linux":
        stress_args = params["stress_args"]
        stress_tool = cpu_utils.VMStressBinding(vm,
                                                params,
                                                stress_args=stress_args)
        current_guest_cpu_ids = cpu_utils.get_guest_cpu_ids(session, os_type)
        plugged_cpu_ids = list(current_guest_cpu_ids - guest_cpu_ids)
        plugged_cpu_ids.sort()
        for cpu_id in plugged_cpu_ids:
            error_context.context(
                "Run stress on vCPU(%d) inside guest." % cpu_id, logging.info)
            stress_tool.load_stress_tool(cpu_id)
        error_context.context(
            "Successfully launched stress sessions, execute "
            "stress test for %d seconds" % stress_duration, logging.info)
        time.sleep(stress_duration)
        if utils_package.package_install("sysstat", session):
            error_context.context("Check usage of guest CPUs", logging.info)
            mpstat_cmd = "mpstat 1 5 -P %s | cat" % ",".join(
                map(str, plugged_cpu_ids))
            mpstat_out = session.cmd_output(mpstat_cmd)
            cpu_stat = dict(
                re.findall(r"Average:\s+(\d+)\s+(\d+\.\d+)", mpstat_out, re.M))
            for cpu_id in plugged_cpu_ids:
                cpu_usage_rate = float(cpu_stat[str(cpu_id)])
                if cpu_usage_rate < 50:
                    test.error("Stress test on vCPU(%s) failed, usage rate: "
                               "%.2f%%" % (cpu_id, cpu_usage_rate))
                logging.info("Usage rate of vCPU(%s) is: %.2f%%", cpu_id,
                             cpu_usage_rate)
        if not vm_arch_name.startswith("s390"):
            for vcpu_dev in vcpu_devices:
                error_context.context("Hotunplug vcpu device: %s" % vcpu_dev,
                                      logging.info)
                vm.hotunplug_vcpu_device(vcpu_dev)
                # Drift the running stress task to other vCPUs
                time.sleep(random.randint(5, 10))
            if not cpu_utils.check_if_vm_vcpu_match(cpu_count_before_test, vm):
                test.fail("Actual number of guest CPUs is not equal to "
                          "expected")
        stress_tool.unload_stress()
        stress_tool.clean()
    else:
        install_path = params["install_path"]
        test_installed_cmd = 'dir "%s" | findstr /I heavyload' % install_path
        heavyload_install()
        error_context.context("Run heavyload inside guest.", logging.info)
        heavyload_bin = r'"%s\heavyload.exe" ' % install_path
        heavyload_options = [
            "/CPU %d" % expected_count,
            "/DURATION %d" % (stress_duration // 60), "/AUTOEXIT", "/START"
        ]
        start_cmd = heavyload_bin + " ".join(heavyload_options)
        stress_tool = BackgroundTest(
            session.cmd, (start_cmd, stress_duration, stress_duration))
        stress_tool.start()
        if not utils_misc.wait_for(
                stress_tool.is_alive, verify_wait_timeout, first=5):
            test.error("Failed to start heavyload process.")
        stress_tool.join(stress_duration)

    session.close()