def __init__(self, test, env, params, *args, **dargs): super(MigrationWithRng, self).__init__(test, env, params, *args, **dargs) self.hotplug = params.get("hotplug", "no") self.rng_source_mode = params.get("rng_source_mode") self.backend_type = params.get("backend_type") self.rng_model = params.get("rng_model", "virtio") self.backend_model = params.get("backend_model", "random") self.backend_dev = params.get("backend_dev", "") self.backend_source = params.get("backend_source", "") self.backend_protocol = params.get("backend_protocol") # Builtin backend model is only supported by libvirt>=6.2.0 # and qemu-kvm>=4.2.0 if (self.backend_model == "builtin" and (not libvirt_version.version_compare(6, 2, 0) or not utils_misc.compare_qemu_version(4, 2, 0))): self.test.cancel("backend model builtin is not supported" "by this hypervisor version") # Create the rng_xml to be set self.rng_xml = self._create_rng_xml() # Variable: background jobs to be killed in cleanup() self.bgjobs = []
def setup_max_vfs(): """ Setup for max_vfs case 1. Check test environment 2. Enable VFs 3. Create networks """ if not utils_misc.compare_qemu_version(4, 0, 0, False): test.cancel("This test is supported from qemu-kvm 4.0.0.") if len(pf_info) < 2: test.cancel("This test requires at least 2 PFs.") pf_id_list = get_pf_id_list(pf_info, driver) for pf_pci in pf_id_list: sriov_base.recover_vf(pf_pci, params) sriov_base.setup_vf(pf_pci, params) net_info = get_net_dict(pf_info) for pf_dev, net_name in net_info.items(): create_network(net_name, pf_dev, params)
def check_model_test_cmd_line(model_type, is_primary=None): """ Check whether the added video devices are shown in the qemu cmd line """ cmdline = open('/proc/%s/cmdline' % vm.get_pid()).read().replace("\x00", " ") logging.debug("the cmdline is: %s" % cmdline) # s390x only supports virtio s390x_pattern = r"-device.*virtio-gpu-ccw" # aarch64 only supports virtio aarch64_pattern = r"-device.*virtio-gpu-pci" if is_primary or is_primary is None: if model_type == "vga": pattern = r"-device.*VGA" else: pattern = r"-device.*%s-vga" % model_type if guest_arch == 's390x': pattern = s390x_pattern elif guest_arch == 'aarch64': pattern = aarch64_pattern if not re.search(pattern, cmdline): test.fail("Can not find the primary %s video device " "in qemu cmd line." % model_type) else: if model_type == "qxl": pattern = r"-device.*qxl," elif model_type == "virtio": pattern = r"-device.*virtio-gpu-pci" if with_packed: if utils_misc.compare_qemu_version(6, 2, 0, is_rhev=False): pattern = r"-device.*virtio-gpu-pci.*packed\W{1,2}%s" % "true" else: pattern = r"-device.*virtio-gpu-pci.*packed\W{1,2}%s" % driver_packed if guest_arch == 's390x': pattern = s390x_pattern if not re.search(pattern, cmdline): test.fail("Can not find the secondary %s video device " "in qemu cmd line." % model_type)
def run(test, params, env): """ Test the tpm virtual devices 1. prepare a guest with different tpm devices 2. check whether the guest can be started 3. check the xml and qemu cmd line, even swtpm for vtpm 4. check tpm usage in guest os """ # Tpm passthrough supported since libvirt 1.0.5. if not libvirt_version.version_compare(1, 0, 5): test.cancel("Tpm device is not supported " "on current libvirt version.") # Tpm passthrough supported since qemu 2.12.0-49. if not utils_misc.compare_qemu_version(2, 9, 0, is_rhev=False): test.cancel("Tpm device is not supported " "on current qemu version.") tpm_model = params.get("tpm_model") backend_type = params.get("backend_type") backend_version = params.get("backend_version") device_path = params.get("device_path") tpm_num = int(params.get("tpm_num", 1)) # After first start of vm with vtpm, do operations, check it still works vm_operate = params.get("vm_operate") # Sub-operation(e.g.domrename) under vm_operate(e.g.restart) vm_oprt = params.get("vm_oprt") secret_uuid = params.get("secret_uuid") secret_value = params.get("secret_value") # Change encryption state: from plain to encrypted, or reverse. encrypt_change = params.get("encrypt_change") secret_uuid = params.get("secret_uuid") prepare_secret = ("yes" == params.get("prepare_secret", "no")) remove_dev = ("yes" == params.get("remove_dev", "no")) multi_vms = ("yes" == params.get("multi_vms", "no")) # Remove swtpm state file rm_statefile = ("yes" == params.get("rm_statefile", "no")) test_suite = ("yes" == params.get("test_suite", "no")) restart_libvirtd = ("yes" == params.get("restart_libvirtd", "no")) no_backend = ("yes" == params.get("no_backend", "no")) status_error = ("yes" == params.get("status_error", "no")) err_msg = params.get("xml_errmsg", "") loader = params.get("loader", "") nvram = params.get("nvram", "") uefi_disk_url = params.get("uefi_disk_url", "") download_file_path = os.path.join(data_dir.get_tmp_dir(), "uefi_disk.qcow2") # Check tpm chip on host for passthrough testing if backend_type == "passthrough": dmesg_info = process.getoutput("dmesg|grep tpm -wi", shell=True) logging.debug("dmesg info about tpm:\n %s", dmesg_info) dmesg_error = re.search("No TPM chip found|TPM is disabled", dmesg_info) if dmesg_error: test.cancel(dmesg_error.group()) else: # Try to check host tpm chip version tpm_v = None if re.search("2.0 TPM", dmesg_info): tpm_v = "2.0" if not utils_package.package_install("tpm2-tools"): # package_install() return 'True' if succeed test.error("Failed to install tpm2-tools on host") else: if re.search("1.2 TPM", dmesg_info): tpm_v = "1.2" # If "1.2 TPM" or no version info in dmesg, try to test a tpm1.2 at first if not utils_package.package_install("tpm-tools"): test.error("Failed to install tpm-tools on host") # Check host env for vtpm testing elif backend_type == "emulator": if not utils_misc.compare_qemu_version(4, 0, 0, is_rhev=False): test.cancel("vtpm(emulator backend) is not supported " "on current qemu version.") # Install swtpm pkgs on host for vtpm emulation if not utils_package.package_install("swtpm*"): test.error("Failed to install swtpm swtpm-tools on host") def replace_os_disk(vm_xml, vm_name, nvram): """ Replace os(nvram) and disk(uefi) for x86 vtpm test :param vm_xml: current vm's xml :param vm_name: current vm name :param nvram: nvram file path of vm """ # Add loader, nvram in <os> nvram = nvram.replace("<VM_NAME>", vm_name) dict_os_attrs = {"loader_readonly": "yes", "secure": "yes", "loader_type": "pflash", "loader": loader, "nvram": nvram} vm_xml.set_os_attrs(**dict_os_attrs) logging.debug("Set smm=on in VMFeaturesXML") # Add smm in <features> features_xml = vm_xml.features features_xml.smm = "on" vm_xml.features = features_xml vm_xml.sync() # Replace disk with an uefi image if not utils_package.package_install("wget"): test.error("Failed to install wget on host") if uefi_disk_url.count("EXAMPLE"): test.error("Please provide the URL %s" % uefi_disk_url) else: download_cmd = ("wget %s -O %s" % (uefi_disk_url, download_file_path)) process.system(download_cmd, verbose=False, shell=True) vm = env.get_vm(vm_name) uefi_disk = {'disk_source_name': download_file_path} libvirt.set_vm_disk(vm, uefi_disk) vm_names = params.get("vms").split() vm_name = vm_names[0] vm = env.get_vm(vm_name) vm_xml = VMXML.new_from_inactive_dumpxml(vm_name) vm_xml_backup = vm_xml.copy() os_xml = getattr(vm_xml, "os") host_arch = platform.machine() if backend_type == "emulator" and host_arch == 'x86_64': if not utils_package.package_install("OVMF"): test.error("Failed to install OVMF or edk2-ovmf pkgs on host") if os_xml.xmltreefile.find('nvram') is None: replace_os_disk(vm_xml, vm_name, nvram) vm_xml = VMXML.new_from_inactive_dumpxml(vm_name) if vm.is_alive(): vm.destroy() vm2 = None if multi_vms: if len(vm_names) > 1: vm2_name = vm_names[1] vm2 = env.get_vm(vm2_name) vm2_xml = VMXML.new_from_inactive_dumpxml(vm2_name) vm2_xml_backup = vm2_xml.copy() else: # Clone additional vms if needed try: utils_path.find_command("virt-clone") except utils_path.CmdNotFoundError: if not utils_package.package_install(["virt-install"]): test.cancel("Failed to install virt-install on host") vm2_name = "vm2_" + utils_misc.generate_random_string(5) ret_clone = utils_libguestfs.virt_clone_cmd(vm_name, vm2_name, True, timeout=360, debug=True) if ret_clone.exit_status: test.error("Need more than one domains, but error occured when virt-clone.") vm2 = vm.clone(vm2_name) vm2_xml = VMXML.new_from_inactive_dumpxml(vm2_name) if vm2.is_alive(): vm2.destroy() service_mgr = service.ServiceManager() def check_dumpxml(vm_name): """ Check whether the added devices are shown in the guest xml :param vm_name: current vm name """ logging.info("------Checking guest dumpxml------") if tpm_model: pattern = '<tpm model="%s">' % tpm_model else: # The default tpm model is "tpm-tis" pattern = '<tpm model="tpm-tis">' # Check tpm model xml_after_adding_device = VMXML.new_from_dumpxml(vm_name) logging.debug("xml after add tpm dev is %s", xml_after_adding_device) if pattern not in astring.to_text(xml_after_adding_device): test.fail("Can not find the %s tpm device xml " "in the guest xml file." % tpm_model) # Check backend type pattern = '<backend type="%s"' % backend_type if pattern not in astring.to_text(xml_after_adding_device): test.fail("Can not find the %s backend type xml for tpm dev " "in the guest xml file." % backend_type) # Check backend version if backend_version: check_ver = backend_version if backend_version != 'none' else '2.0' pattern = '"emulator" version="%s"' % check_ver if pattern not in astring.to_text(xml_after_adding_device): test.fail("Can not find the %s backend version xml for tpm dev " "in the guest xml file." % check_ver) # Check device path if backend_type == "passthrough": pattern = '<device path="/dev/tpm0"' if pattern not in astring.to_text(xml_after_adding_device): test.fail("Can not find the %s device path xml for tpm dev " "in the guest xml file." % device_path) # Check encryption secret if prepare_secret: pattern = '<encryption secret="%s" />' % encryption_uuid if pattern not in astring.to_text(xml_after_adding_device): test.fail("Can not find the %s secret uuid xml for tpm dev " "in the guest xml file." % encryption_uuid) logging.info('------PASS on guest dumpxml check------') def check_qemu_cmd_line(vm, vm_name, domid): """ Check whether the added devices are shown in the qemu cmd line :param vm: current vm :param vm_name: current vm name :param domid: domain id for checking vtpm socket file """ logging.info("------Checking qemu cmd line------") if not vm.get_pid(): test.fail('VM pid file missing.') with open('/proc/%s/cmdline' % vm.get_pid()) as cmdline_file: cmdline = cmdline_file.read() logging.debug("Qemu cmd line info:\n %s", cmdline) # Check tpm model pattern_list = ["-device.%s" % tpm_model] # Check backend type if backend_type == "passthrough": dev_num = re.search(r"\d+", device_path).group() backend_segment = "id=tpm-tpm%s" % dev_num else: # emulator backend backend_segment = "id=tpm-tpm0,chardev=chrtpm" pattern_list.append("-tpmdev.%s,%s" % (backend_type, backend_segment)) # Check chardev socket for vtpm if backend_type == "emulator": pattern_list.append("-chardev.socket,id=chrtpm," "path=.*/run/libvirt/qemu/swtpm/%s-%s-swtpm.sock" % (domid, vm_name)) for pattern in pattern_list: if not re.search(pattern, cmdline): if not remove_dev: test.fail("Can not find the %s for tpm device " "in qemu cmd line." % pattern) elif remove_dev: test.fail("%s still exists after remove vtpm and restart" % pattern) logging.info("------PASS on qemu cmd line check------") def check_swtpm(domid, domuuid, vm_name): """ Check swtpm cmdline and files for vtpm. :param domid: domain id for checking vtpm files :param domuuid: domain uuid for checking vtpm state file :param vm_name: current vm name """ logging.info("------Checking swtpm cmdline and files------") # Check swtpm cmdline swtpm_pid = utils_misc.get_pid("%s-swtpm.pid" % vm_name) if not swtpm_pid: if not remove_dev: test.fail('swtpm pid file missing.') else: return elif remove_dev: test.fail('swtpm pid file still exists after remove vtpm and restart') with open('/proc/%s/cmdline' % swtpm_pid) as cmdline_file: cmdline = cmdline_file.read() logging.debug("Swtpm cmd line info:\n %s", cmdline) pattern_list = ["--daemon", "--ctrl", "--tpmstate", "--log", "--tpm2", "--pid"] if prepare_secret: pattern_list.extend(["--key", "--migration-key"]) for pattern in pattern_list: if not re.search(pattern, cmdline): test.fail("Can not find the %s for tpm device " "in swtpm cmd line." % pattern) # Check swtpm files file_list = ["/var/run/libvirt/qemu/swtpm/%s-%s-swtpm.sock" % (domid, vm_name)] file_list.append("/var/lib/libvirt/swtpm/%s/tpm2" % domuuid) file_list.append("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % vm_name) file_list.append("/var/run/libvirt/qemu/swtpm/%s-%s-swtpm.pid" % (domid, vm_name)) for swtpm_file in file_list: if not os.path.exists(swtpm_file): test.fail("Swtpm file: %s does not exist" % swtpm_file) logging.info("------PASS on Swtpm cmdline and files check------") def get_tpm2_tools_cmd(session=None): """ Get tpm2-tools pkg version and return corresponding getrandom cmd :session: guest console session :return: tpm2_getrandom cmd usage """ cmd = 'rpm -q tpm2-tools' get_v_tools = session.cmd(cmd) if session else process.run(cmd).stdout_text v_tools_list = get_v_tools.strip().split('-') if session: logging.debug("The tpm2-tools version is %s", v_tools_list[2]) v_tools = int(v_tools_list[2].split('.')[0]) return "tpm2_getrandom 8" if v_tools < 4 else "tpm2_getrandom -T device:/dev/tpm0 8 --hex" def get_host_tpm_bef(tpm_v): """ Test host tpm function and identify its real version before passthrough Since sometimes dmesg info doesn't include tpm msg, need use tpm-tool or tpm2-tools to try the function. :param tpm_v: host tpm version get from dmesg info :return: host tpm version """ logging.info("------Checking host tpm device before passthrough------") # Try tcsd tool for suspected tpm1.2 chip on host tpm_real_v = tpm_v if tpm_v != "2.0": if not service_mgr.start('tcsd'): # service_mgr.start() return 'True' if succeed if tpm_v == "1.2": test.fail("Host tcsd.serivce start failed") else: # Means tpm_v got nothing from dmesg, log failure here and # go to next 'if' to try tpm2.0 tools. logging.info("Host tcsd.serivce start failed") else: tpm_real_v = "1.2" logging.info("Host tpm version info:") result = process.run("tpm_version", ignore_status=False) logging.debug("[host]# tpm_version\n %s", result.stdout) time.sleep(2) service_mgr.stop('tcsd') if tpm_v != "1.2": # Try tpm2.0 tools if not utils_package.package_install("tpm2-tools"): test.error("Failed to install tpm2-tools on host") tpm2_getrandom_cmd = get_tpm2_tools_cmd() if process.run(tpm2_getrandom_cmd, ignore_status=True).exit_status: test.cancel("Both tcsd and tpm2-tools can not work, " "pls check your host tpm version and test env.") else: tpm_real_v = "2.0" logging.info("------PASS on host tpm device check------") return tpm_real_v def test_host_tpm_aft(tpm_real_v): """ Test host tpm function after passthrough :param tpm_real_v: host tpm real version indentified from testing """ logging.info("------Checking host tpm device after passthrough------") if tpm_real_v == "1.2": if service_mgr.start('tcsd'): time.sleep(2) service_mgr.stop('tcsd') test.fail("Host tpm should not work after passthrough to guest.") else: logging.info("Expected failure: Tpm is being used by guest.") elif tpm_real_v == "2.0": tpm2_getrandom_cmd = get_tpm2_tools_cmd() if not process.run(tpm2_getrandom_cmd, ignore_status=True).exit_status: test.fail("Host tpm should not work after passthrough to guest.") else: logging.info("Expected failure: Tpm is being used by guest.") logging.info("------PASS on host tpm device check------") def test_guest_tpm(expect_version, session, expect_fail): """ Test tpm function in guest :param expect_version: guest tpm version, as host version, or emulator specified :param session: Guest session to be tested :param expect_fail: guest tpm is expectedly fail to work """ logging.info("------Checking guest tpm device work------") if expect_version == "1.2": # Install tpm-tools and test by tcsd method if not utils_package.package_install(["tpm-tools"], session, 360): test.error("Failed to install tpm-tools package in guest") else: status, output = session.cmd_status_output("systemctl start tcsd") logging.debug("Command output: %s", output) if status: if expect_fail: test.cancel("tpm-crb passthrough only works with host tpm2.0, " "but your host tpm version is 1.2") else: test.fail("Failed to start tcsd.service in guest") else: dev_output = session.cmd_output("ls /dev/|grep tpm") logging.debug("Command output: %s", dev_output) status, output = session.cmd_status_output("tpm_version") logging.debug("Command output: %s", output) if status: test.fail("Guest tpm can not work") else: # If expect_version is tpm2.0, install and test by tpm2-tools if not utils_package.package_install(["tpm2-tools"], session, 360): test.error("Failed to install tpm2-tools package in guest") else: tpm2_getrandom_cmd = get_tpm2_tools_cmd(session) status1, output1 = session.cmd_status_output("ls /dev/|grep tpm") logging.debug("Command output: %s", output1) status2, output2 = session.cmd_status_output(tpm2_getrandom_cmd) logging.debug("Command output: %s", output2) if status1 or status2: if not expect_fail: test.fail("Guest tpm can not work") else: d_status, d_output = session.cmd_status_output("date") if d_status: test.fail("Guest OS doesn't work well") logging.debug("Command output: %s", d_output) elif expect_fail: test.fail("Expect fail but guest tpm still works") logging.info("------PASS on guest tpm device work check------") def run_test_suite_in_guest(session): """ Run kernel test suite for guest tpm. :param session: Guest session to be tested """ logging.info("------Checking kernel test suite for guest tpm------") boot_info = session.cmd('uname -r').strip().split('.') kernel_version = '.'.join(boot_info[:2]) # Download test suite per current guest kernel version parent_path = "https://cdn.kernel.org/pub/linux/kernel" if float(kernel_version) < 5.3: major_version = "5" file_version = "5.3" else: major_version = boot_info[0] file_version = kernel_version src_url = "%s/v%s.x/linux-%s.tar.xz" % (parent_path, major_version, file_version) download_cmd = "wget %s -O %s" % (src_url, "/root/linux.tar.xz") output = session.cmd_output(download_cmd, timeout=480) logging.debug("Command output: %s", output) # Install neccessary pkgs to build test suite if not utils_package.package_install(["tar", "make", "gcc", "rsync", "python2"], session, 360): test.fail("Failed to install specified pkgs in guest OS.") # Unzip the downloaded test suite status, output = session.cmd_status_output("tar xvJf /root/linux.tar.xz -C /root") if status: test.fail("Uzip failed: %s" % output) # Specify using python2 to run the test suite per supporting test_path = "/root/linux-%s/tools/testing/selftests" % file_version sed_cmd = "sed -i 's/python -m unittest/python2 -m unittest/g' %s/tpm2/test_*.sh" % test_path output = session.cmd_output(sed_cmd) logging.debug("Command output: %s", output) # Build and and run the .sh files of test suite status, output = session.cmd_status_output("make -C %s TARGETS=tpm2 run_tests" % test_path, timeout=360) logging.debug("Command output: %s", output) if status: test.fail("Failed to run test suite in guest OS.") for test_sh in ["test_smoke.sh", "test_space.sh"]: pattern = "ok .* selftests: tpm2: %s" % test_sh if not re.search(pattern, output) or ("not ok" in output): test.fail("test suite check failed.") logging.info("------PASS on kernel test suite check------") def reuse_by_vm2(tpm_dev): """ Try to add same tpm to a second guest, when it's being used by one guest. :param tpm_dev: tpm device to be added into guest xml """ logging.info("------Trying to add same tpm to a second domain------") vm2_xml.remove_all_device_by_type('tpm') vm2_xml.add_device(tpm_dev) vm2_xml.sync() ret = virsh.start(vm2_name, ignore_status=True, debug=True) if backend_type == "passthrough": if ret.exit_status: logging.info("Expected failure when try to passthrough a tpm" " that being used by another guest") return test.fail("Reuse a passthroughed tpm should not succeed.") elif ret.exit_status: # emulator backend test.fail("Vtpm for each guest should not interfere with each other") try: tpm_real_v = None sec_uuids = [] new_name = "" virsh_dargs = {"debug": True, "ignore_status": False} vm_xml.remove_all_device_by_type('tpm') tpm_dev = Tpm() if tpm_model: tpm_dev.tpm_model = tpm_model if not no_backend: backend = tpm_dev.Backend() if backend_type != 'none': backend.backend_type = backend_type if backend_type == "passthrough": tpm_real_v = get_host_tpm_bef(tpm_v) logging.debug("The host tpm real version is %s", tpm_real_v) if device_path: backend.device_path = device_path if backend_type == "emulator": if backend_version != 'none': backend.backend_version = backend_version if prepare_secret: auth_sec_dict = {"sec_ephemeral": "no", "sec_private": "yes", "sec_desc": "sample vTPM secret", "sec_usage": "vtpm", "sec_name": "VTPM_example"} encryption_uuid = libvirt.create_secret(auth_sec_dict) if secret_value != 'none': virsh.secret_set_value(encryption_uuid, "open sesame", encode=True, debug=True) sec_uuids.append(encryption_uuid) if encrypt_change != 'encrpt': # plain_to_encrypt will not add encryption on first start if secret_uuid == 'invalid': encryption_uuid = encryption_uuid[:-1] backend.encryption_secret = encryption_uuid if secret_uuid == "change": auth_sec_dict["sec_desc"] = "sample2 vTPM secret" auth_sec_dict["sec_name"] = "VTPM_example2" new_encryption_uuid = libvirt.create_secret(auth_sec_dict) virsh.secret_set_value(new_encryption_uuid, "open sesame", encode=True, debug=True) sec_uuids.append(new_encryption_uuid) if secret_uuid == 'nonexist': backend.encryption_secret = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" tpm_dev.backend = backend logging.debug("tpm dev xml to add is:\n %s", tpm_dev) for num in range(tpm_num): vm_xml.add_device(tpm_dev, True) ret = virsh.define(vm_xml.xml, ignore_status=True, debug=True) expected_match = "" if not err_msg: expected_match = "Domain .*%s.* defined from %s" % (vm_name, vm_xml.xml) libvirt.check_result(ret, err_msg, "", False, expected_match) if err_msg: # Stop test when get expected failure return if vm_operate != "restart": check_dumpxml(vm_name) # For default model, no need start guest to test if tpm_model: expect_fail = False try: vm.start() except VMStartError as detail: if secret_value == 'none' or secret_uuid == 'nonexist': logging.debug("Expected failure: %s", detail) return else: test.fail(detail) domuuid = vm.get_uuid() if vm_operate or restart_libvirtd: # Make sure OS works before vm operate or restart libvirtd session = vm.wait_for_login() test_guest_tpm("2.0", session, False) session.close() if restart_libvirtd: utils_libvirtd.libvirtd_restart() swtpm_statedir = "/var/lib/libvirt/swtpm/%s" % domuuid if vm_operate == "resume": virsh.suspend(vm_name, **virsh_dargs) time.sleep(3) virsh.resume(vm_name, **virsh_dargs) elif vm_operate == "snapshot": virsh.snapshot_create_as(vm_name, "sp1 --memspec file=/tmp/testvm_sp1", **virsh_dargs) elif vm_operate in ["restart", "create"]: vm.destroy() if vm_operate == "create": virsh.undefine(vm_name, options="--nvram", **virsh_dargs) if os.path.exists(swtpm_statedir): test.fail("Swtpm state dir: %s still exist after vm undefine" % swtpm_statedir) virsh.create(vm_xml.xml, **virsh_dargs) else: if vm_oprt == "domrename": new_name = "vm_" + utils_misc.generate_random_string(5) virsh.domrename(vm_name, new_name, **virsh_dargs) new_vm = libvirt_vm.VM(new_name, vm.params, vm.root_dir, vm.address_cache) vm = new_vm vm_name = new_name elif secret_value == 'change': logging.info("Changing secret value...") virsh.secret_set_value(encryption_uuid, "new sesame", encode=True, debug=True) elif not restart_libvirtd: # remove_dev or do other vm operations during restart vm_xml.remove_all_device_by_type('tpm') if secret_uuid == "change" or encrypt_change: # Change secret uuid, or change encrytion state:from plain to encrypted, or on the contrary if encrypt_change == 'plain': # Change from encrypted state to plain:redefine a tpm dev without encryption tpm_dev = Tpm() tpm_dev.tpm_model = tpm_model backend = tpm_dev.Backend() backend.backend_type = backend_type backend.backend_version = backend_version else: # Use a new secret's uuid if secret_uuid == "change": encryption_uuid = new_encryption_uuid backend.encryption_secret = encryption_uuid tpm_dev.backend = backend logging.debug("The new tpm dev xml to add for restart vm is:\n %s", tpm_dev) vm_xml.add_device(tpm_dev, True) if encrypt_change in ['encrpt', 'plain']: # Avoid sync() undefine removing the state file vm_xml.define() else: vm_xml.sync() if rm_statefile: swtpm_statefile = "%s/tpm2/tpm2-00.permall" % swtpm_statedir logging.debug("Removing state file: %s", swtpm_statefile) os.remove(swtpm_statefile) ret = virsh.start(vm_name, ignore_status=True, debug=True) libvirt.check_exit_status(ret, status_error) if status_error and ret.exit_status != 0: return if not remove_dev: check_dumpxml(vm_name) elif vm_operate == 'managedsave': virsh.managedsave(vm_name, **virsh_dargs) time.sleep(5) if secret_value == 'change': logging.info("Changing secret value...") virsh.secret_set_value(encryption_uuid, "new sesame", encode=True, debug=True) if rm_statefile: swtpm_statefile = "%s/tpm2/tpm2-00.permall" % swtpm_statedir logging.debug("Removing state file: %s", swtpm_statefile) os.remove(swtpm_statefile) ret = virsh.start(vm_name, ignore_status=True, debug=True) libvirt.check_exit_status(ret, status_error) if status_error and ret.exit_status != 0: return domid = vm.get_id() check_qemu_cmd_line(vm, vm_name, domid) if backend_type == "passthrough": if tpm_real_v == "1.2" and tpm_model == "tpm-crb": expect_fail = True expect_version = tpm_real_v test_host_tpm_aft(tpm_real_v) else: # emulator backend if remove_dev: expect_fail = True expect_version = backend_version check_swtpm(domid, domuuid, vm_name) session = vm.wait_for_login() if test_suite: run_test_suite_in_guest(session) else: test_guest_tpm(expect_version, session, expect_fail) session.close() if multi_vms: reuse_by_vm2(tpm_dev) if backend_type != "passthrough": #emulator backend check_dumpxml(vm2_name) domid = vm2.get_id() domuuid = vm2.get_uuid() check_qemu_cmd_line(vm2, vm2_name, domid) check_swtpm(domid, domuuid, vm2_name) session = vm2.wait_for_login() test_guest_tpm(backend_version, session, expect_fail) session.close() finally: # Remove renamed domain if it exists if new_name: virsh.remove_domain(new_name, "--nvram", debug=True) if os.path.exists("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % new_name): os.remove("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % new_name) # Remove snapshot if exists if vm_operate == "snapshot": snapshot_lists = virsh.snapshot_list(vm_name) if len(snapshot_lists) > 0: libvirt.clean_up_snapshots(vm_name, snapshot_lists) for snap in snapshot_lists: virsh.snapshot_delete(vm_name, snap, "--metadata") if os.path.exists("/tmp/testvm_sp1"): os.remove("/tmp/testvm_sp1") # Clear guest os if test_suite: session = vm.wait_for_login() logging.info("Removing dir /root/linux-*") output = session.cmd_output("rm -rf /root/linux-*") logging.debug("Command output:\n %s", output) session.close() if vm_operate == "create": vm.define(vm_xml.xml) vm_xml_backup.sync(options="--nvram --managed-save") # Remove swtpm log file in case of impact on later runs if os.path.exists("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % vm.name): os.remove("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % vm.name) for sec_uuid in set(sec_uuids): virsh.secret_undefine(sec_uuid, ignore_status=True, debug=True) if vm2: if len(vm_names) > 1: vm2_xml_backup.sync(options="--nvram") else: virsh.remove_domain(vm2_name, "--nvram --remove-all-storage", debug=True) if os.path.exists("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % vm2.name): os.remove("/var/log/swtpm/libvirt/qemu/%s-swtpm.log" % vm2.name)
def run(test, params, env): """ Test hpt resizing """ vm_name = params.get('main_vm') vm = env.get_vm(vm_name) status_error = 'yes' == params.get('status_error', 'no') error_msg = eval(params.get('error_msg', '[]')) hpt_attrs = eval(params.get('hpt_attrs', '{}')) hpt_order_path = params.get('hpt_order_path', '') cpu_attrs = eval(params.get('cpu_attrs', '{}')) numa_cell = eval(params.get('numa_cell', '{}')) hugepage = 'yes' == params.get('hugepage', 'no') maxpagesize = int(params.get('maxpagesize', 0)) check_hp = 'yes' == params.get('check_hp', 'no') qemu_check = params.get('qemu_check', '') skip_p8 = 'yes' == params.get('skip_p8', 'no') def set_hpt(vmxml, sync, **attrs): """ Set resizing value to vm xml :param vmxml: xml of vm to be manipulated :param sync: whether to sync vmxml after :param attrs: attrs to set to hpt xml """ if vmxml.xmltreefile.find('/features'): features_xml = vmxml.features else: features_xml = vm_xml.VMFeaturesXML() hpt_xml = vm_xml.VMFeaturesHptXML() for attr in attrs: setattr(hpt_xml, attr, attrs[attr]) features_xml.hpt = hpt_xml vmxml.features = features_xml logging.debug(vmxml) if sync: vmxml.sync() def set_cpu(vmxml, **attrs): """ Set cpu attrs for vmxml according to given attrs :param vmxml: xml of vm to be manipulated :param attrs: attrs to set to cpu xml """ if vmxml.xmltreefile.find('cpu'): cpu = vmxml.cpu else: cpu = vm_xml.VMCPUXML() if 'numa_cell' in attrs: cpu.xmltreefile.create_by_xpath('/numa') cpu.numa_cell = attrs['numa_cell'] for key in attrs: setattr(cpu, key, attrs[key]) vmxml.cpu = cpu vmxml.sync() def set_memory(vmxml): """ Set memory attributes in vm xml """ vmxml.max_mem_rt = int(params.get('max_mem_rt', 30670848)) vmxml.max_mem_rt_slots = int(params.get('max_mem_rt_slots', 16)) vmxml.max_mem_rt_unit = params.get('max_mem_rt_unit', 'KiB') logging.debug(numa_cell) if numa_cell: # Remove cpu topology to avoid that it doesn't match vcpu count if vmxml.get_cpu_topology(): new_cpu = vmxml.cpu new_cpu.del_topology() vmxml.cpu = new_cpu vmxml.vcpu = max([int(cell['cpus'][-1]) for cell in numa_cell]) + 1 vmxml.sync() def check_hpt_order(session, resizing=''): """ Return htp order in hpt_order file by default If 'resizing' is disabled, test updating htp_order """ if not hpt_order_path: test.cancel('No hpt order path provided.') hpt_order = session.cmd_output('cat %s' % hpt_order_path).strip() hpt_order = int(hpt_order) logging.info('Current hpt_order is %d', hpt_order) if resizing == 'disabled': cmd_result = session.cmd_status_output( 'echo %d > %s' % (hpt_order + 1, hpt_order_path)) result = process.CmdResult(stderr=cmd_result[1], exit_status=cmd_result[0]) libvirt.check_exit_status(result, True) libvirt.check_result(result, error_msg) return hpt_order def check_hp_in_vm(session, page_size): """ Check if hugepage size is correct inside vm :param session: the session of the running vm :param page_size: the expected pagesize to be checked inside vm """ expect = False if int(page_size) == 65536 else True meminfo = session.cmd_output('cat /proc/meminfo|grep Huge') logging.info('meminfo: \n%s', meminfo) pattern = 'Hugepagesize:\s+%d\s+kB' % int(page_size / 1024) logging.info('"%s" should %s be found in meminfo output', pattern, '' if expect else 'not') result = expect == bool(re.search(pattern, meminfo)) if not result: test.fail('meminfo output not meet expectation') # Check PAGE_SIZE in another way if not expect: conf_page_size = session.cmd_output('getconf PAGE_SIZE') logging.debug('Output of "getconf PAGE_SIZE": %s', conf_page_size) if int(conf_page_size) != int(page_size): test.fail( 'PAGE_SIZE not correct, should be %r, actually is %r' % (page_size, conf_page_size)) bk_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: arch = platform.machine() vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) resizing = hpt_attrs.get('resizing') # Test on ppc64le hosts if arch.lower() == 'ppc64le': cpu_arch = cpu.get_cpu_arch() logging.debug('cpu_arch is: %s', cpu_arch) if skip_p8 and cpu_arch == 'power8': test.cancel('This case is not for POWER8') if maxpagesize and not utils_misc.compare_qemu_version(3, 1, 0): test.cancel('Qemu version is too low, ' 'does not support maxpagesize setting') if maxpagesize == 16384 and cpu_arch == 'power9': test.cancel('Power9 does not support 16M pagesize.') set_hpt(vmxml, True, **hpt_attrs) if cpu_attrs or numa_cell: if numa_cell: cpu_attrs['numa_cell'] = numa_cell set_cpu(vmxml, **cpu_attrs) if hugepage: vm_mem = vmxml.max_mem host_hp_size = utils_memory.get_huge_page_size() # Make 100m extra memory just to be safe hp_count = max((vm_mem + 102400) // host_hp_size, 1200) vm_xml.VMXML.set_memoryBacking_tag(vm_name, hpgs=True) # Set up hugepage env mnt_source, hp_path, fstype = 'hugetlbfs', '/dev/hugepages', 'hugetlbfs' if not os.path.isdir(hp_path): process.run('mkdir %s' % hp_path, verbose=True) utils_memory.set_num_huge_pages(hp_count) if utils_misc.is_mounted(mnt_source, hp_path, fstype, verbose=True): utils_misc.umount(mnt_source, hp_path, fstype, verbose=True) utils_misc.mount(mnt_source, hp_path, fstype, verbose=True) # Restart libvirtd service to make sure mounted hugepage # be recognized utils_libvirtd.libvirtd_restart() if resizing == 'enabled': set_memory(vmxml) logging.debug('vmxml: \n%s', vmxml) # Start vm and check if start succeeds result = virsh.start(vm_name, debug=True) libvirt.check_exit_status(result, expect_error=status_error) # if vm is not suposed to start, terminate test if status_error: libvirt.check_result(result, error_msg) return libvirt.check_qemu_cmd_line(qemu_check) session = vm.wait_for_login() hpt_order = check_hpt_order(session, resizing) # Check hugepage inside vm if check_hp: check_hp_in_vm(session, maxpagesize * 1024) if resizing == 'enabled': mem_xml = utils_hotplug.create_mem_xml( tg_size=int(params.get('mem_size', 2048000)), tg_sizeunit=params.get('size_unit', 'KiB'), tg_node=int(params.get('mem_node', 0)), mem_model=params.get('mem_model', 'dimm')) logging.debug(mem_xml) # Attach memory device to the guest for 12 times # that will reach the maxinum memory limitation for i in range(12): virsh.attach_device(vm_name, mem_xml.xml, debug=True, ignore_status=False) xml_after_attach = vm_xml.VMXML.new_from_dumpxml(vm_name) logging.debug(xml_after_attach) # Check dumpxml of the guest, # check if each device has its alias for i in range(12): pattern = "alias\s+name=[\'\"]dimm%d[\'\"]" % i logging.debug('Searching for %s', pattern) if not re.search(pattern, str( xml_after_attach.xmltreefile)): test.fail('Missing memory alias: %s' % pattern) # Test on non-ppc64le hosts else: set_hpt(vmxml, sync=False, **hpt_attrs) result = virsh.define(vmxml.xml) libvirt.check_exit_status(result, status_error) libvirt.check_result(result, error_msg) finally: bk_xml.sync() if hugepage: utils_misc.umount('hugetlbfs', '/dev/hugepages', 'hugetlbfs') utils_memory.set_num_huge_pages(0)
def modify_iface_xml(sync=True): """ Modify interface xml options :param sync: whether or not sync vmxml after the iface xml modified, default to be True """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) if pxe_boot: # Config boot console for pxe boot osxml = vm_xml.VMOSXML() osxml.type = vmxml.os.type osxml.arch = vmxml.os.arch osxml.machine = vmxml.os.machine osxml.loader = "/usr/share/seabios/bios.bin" osxml.bios_useserial = "yes" if utils_misc.compare_qemu_version(4, 0, 0, False): osxml.bios_reboot_timeout = "-1" osxml.boots = ['network'] del vmxml.os vmxml.os = osxml xml_devices = vmxml.devices iface_index = xml_devices.index( xml_devices.by_device_tag("interface")[0]) iface = xml_devices[iface_index] if not sync: params.setdefault('original_iface', vmxml.devices[iface_index]) iface_bandwidth = {} iface_inbound = ast.literal_eval(iface_bandwidth_inbound) iface_outbound = ast.literal_eval(iface_bandwidth_outbound) if iface_inbound: iface_bandwidth["inbound"] = iface_inbound if iface_outbound: iface_bandwidth["outbound"] = iface_outbound if iface_bandwidth: bandwidth = iface.new_bandwidth(**iface_bandwidth) iface.bandwidth = bandwidth iface_type = params.get("iface_type", "network") iface.type_name = iface_type source = ast.literal_eval(iface_source) if not source: source = {"network": "default"} net_ifs = utils_net.get_net_if(state="UP") # Check source device is valid or not, # if it's not in host interface list, try to set # source device to first active interface of host if (iface.type_name == "direct" and 'dev' in source and source['dev'] not in net_ifs): logging.warning("Source device %s is not a interface of host, reset to %s", source['dev'], net_ifs[0]) source['dev'] = net_ifs[0] del iface.source iface.source = source if iface_model: iface.model = get_iface_model(iface_model, host_arch) if iface_rom: iface.rom = eval(iface_rom) if iface_boot: vmxml.remove_all_boots() iface.boot = iface_boot logging.debug("New interface xml file: %s", iface) if sync: vmxml.devices = xml_devices vmxml.xmltreefile.write() vmxml.sync() else: return iface
def trigger_events(dom, events_list=[]): """ Trigger various events in events_list :param dom: the vm objects corresponding to the domain :return: the expected output that virsh event command prints out """ expected_events_list = [] save_path = os.path.join(tmpdir, "%s_event.save" % dom.name) print(dom.name) xmlfile = dom.backup_xml() new_disk = os.path.join(tmpdir, "%s_new_disk.img" % dom.name) dest_path = os.path.join(data_dir.get_data_dir(), "copy") try: for event in events_list: logging.debug("Current event is: %s", event) if event in ['start', 'restore', 'create', 'edit', 'define', 'undefine', 'crash', 'device-removal-failed', 'watchdog', 'io-error']: if dom.is_alive(): dom.destroy() if event in ['create', 'define']: dom.undefine() else: if not dom.is_alive(): dom.start() dom.wait_for_login().close() if event == "resume": dom.pause() if event == "undefine": virsh.undefine(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Undefined Removed") elif event == "create": virsh.create(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") elif event == "destroy": virsh.destroy(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Stopped Destroyed") elif event == "define": virsh.define(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Defined Added") elif event == "start": virsh.start(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") dom.wait_for_login().close() elif event == "suspend": virsh.suspend(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") if not libvirt_version.version_compare(5, 3, 0): expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") elif event == "resume": virsh.resume(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "save": virsh.save(dom.name, save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") expected_events_list.append("'lifecycle' for %s:" " Stopped Saved") elif event == "restore": if not os.path.exists(save_path): logging.error("%s not exist", save_path) else: virsh.restore(save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Started Restored") expected_events_list.append("'lifecycle' for %s:" " Resumed Snapshot") elif event == "edit": #Check whether 'description' element exists. domxml = virsh.dumpxml(dom.name).stdout.strip() find_desc = parseString(domxml).getElementsByTagName("description") if find_desc == []: #If not exists, add one for it. logging.info("Adding <description> to guest") virsh.desc(dom.name, "--config", "Added desc for testvm", **virsh_dargs) #The edit operation is to delete 'description' element. edit_cmd = [r":g/<description.*<\/description>/d"] utlv.exec_virsh_edit(dom.name, edit_cmd) expected_events_list.append("'lifecycle' for %s:" " Defined Updated") elif event == "shutdown": if signal_name is None: virsh.shutdown(dom.name, **virsh_dargs) # Wait a few seconds for shutdown finish time.sleep(3) if utils_misc.compare_qemu_version(2, 9, 0): #Shutdown reason distinguished from qemu_2.9.0-9 expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished after guest request") else: os.kill(dom.get_pid(), getattr(signal, signal_name)) if utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished after host request") if not utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished") wait_for_shutoff(dom) expected_events_list.append("'lifecycle' for %s:" " Stopped Shutdown") elif event == "crash": if not vmxml.xmltreefile.find('devices').findall('panic'): # Set panic device panic_dev = Panic() panic_dev.model = panic_model panic_dev.addr_type = addr_type panic_dev.addr_iobase = addr_iobase vmxml.add_device(panic_dev) vmxml.on_crash = "coredump-restart" vmxml.sync() logging.info("Guest xml now is: %s", vmxml) dom.start() session = dom.wait_for_login() # Stop kdump in the guest session.cmd("systemctl stop kdump", ignore_all_errors=True) # Enable sysRq session.cmd("echo 1 > /proc/sys/kernel/sysrq") try: # Crash the guest session.cmd("echo c > /proc/sysrq-trigger", timeout=90) except (ShellTimeoutError, ShellProcessTerminatedError) as details: logging.info(details) session.close() expected_events_list.append("'lifecycle' for %s:" " Crashed Panicked") expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "reset": virsh.reset(dom.name, **virsh_dargs) expected_events_list.append("'reboot' for %s") elif event == "vcpupin": virsh.vcpupin(dom.name, '0', '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.vcpupin0: 0") elif event == "emulatorpin": virsh.emulatorpin(dom.name, '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.emulatorpin: 0") elif event == "setmem": mem_size = int(params.get("mem_size", 512000)) virsh.setmem(dom.name, mem_size, **virsh_dargs) expected_events_list.append("'balloon-change' for %s:") elif event == "device-added-removed": add_disk(dom.name, new_disk, 'vdb', '') expected_events_list.append("'device-added' for %s:" " virtio-disk1") virsh.detach_disk(dom.name, 'vdb', **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " virtio-disk1") iface_xml_obj = create_iface_xml() iface_xml_obj.xmltreefile.write() virsh.detach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " net0") time.sleep(2) virsh.attach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-added' for %s:" " net0") elif event == "block-threshold": add_disk(dom.name, new_disk, 'vdb', '', format=disk_format) logging.debug(process.run('qemu-img info %s -U' % new_disk)) virsh.domblkthreshold(vm_name, 'vdb', '100M') session = dom.wait_for_login() session.cmd("mkfs.ext4 /dev/vdb && mount /dev/vdb /mnt && ls /mnt && " "dd if=/dev/urandom of=/mnt/bigfile bs=1M count=300 && sync") time.sleep(5) session.close() expected_events_list.append("'block-threshold' for %s:" " dev: vdb(%s) 104857600 29368320") virsh.detach_disk(dom.name, 'vdb', **virsh_dargs) elif event == "change-media": target_device = "hdc" device_target_bus = params.get("device_target_bus", "ide") disk_blk = vm_xml.VMXML.get_disk_blk(dom.name) logging.info("disk_blk %s", disk_blk) if target_device not in disk_blk: logging.info("Adding cdrom to guest") if dom.is_alive(): dom.destroy() add_disk(dom.name, new_disk, target_device, ("--type cdrom --sourcetype file --driver qemu " + "--config --targetbus %s" % device_target_bus)) dom.start() all_options = new_disk + " --insert" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " closed") all_options = new_disk + " --eject" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") elif event == "hwclock": session = dom.wait_for_login() try: session.cmd("hwclock --systohc", timeout=60) except (ShellTimeoutError, ShellProcessTerminatedError) as details: logging.info(details) session.close() expected_events_list.append("'rtc-change' for %s:") elif event == "metadata_set": metadata_uri = params.get("metadata_uri") metadata_key = params.get("metadata_key") metadata_value = params.get("metadata_value") virsh.metadata(dom.name, metadata_uri, options="", key=metadata_key, new_metadata=metadata_value, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") elif event == "metadata_edit": metadata_uri = "http://herp.derp/" metadata_key = "herp" metadata_value = "<derp xmlns:foobar='http://foo.bar/'>foo<bar></bar></derp>" virsh_cmd = r"virsh metadata %s --uri %s --key %s %s" virsh_cmd = virsh_cmd % (dom.name, metadata_uri, metadata_key, "--edit") session = aexpect.ShellSession("sudo -s") logging.info("Running command: %s", virsh_cmd) try: session.sendline(virsh_cmd) session.sendline(r":insert") session.sendline(metadata_value) session.sendline(".") session.send('ZZ') remote.handle_prompts(session, None, None, r"[\#\$]\s*$", debug=True, timeout=60) except Exception as e: test.error("Error occured: %s" % e) session.close() # Check metadata after edit virsh.metadata(dom.name, metadata_uri, options="", key=metadata_key, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") elif event == "metadata_remove": virsh.metadata(dom.name, metadata_uri, options="--remove", key=metadata_key, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") elif event == "blockcommit": disk_path = dom.get_blk_devices()['vda']['source'] virsh.snapshot_create_as(dom.name, "s1 --disk-only --no-metadata", **virsh_dargs) snapshot_path = dom.get_blk_devices()['vda']['source'] virsh.blockcommit(dom.name, "vda", "--active --pivot", **virsh_dargs) expected_events_list.append("'block-job' for %s: " "Active Block Commit for " + "%s" % snapshot_path + " ready") expected_events_list.append("'block-job-2' for %s: " "Active Block Commit for vda ready") expected_events_list.append("'block-job' for %s: " "Active Block Commit for " + "%s" % disk_path + " completed") expected_events_list.append("'block-job-2' for %s: " "Active Block Commit for vda completed") os.unlink(snapshot_path) elif event == "blockcopy": disk_path = dom.get_blk_devices()['vda']['source'] dom.undefine() virsh.blockcopy(dom.name, "vda", dest_path, "--pivot", **virsh_dargs) expected_events_list.append("'block-job' for %s: " "Block Copy for " + "%s" % disk_path + " ready") expected_events_list.append("'block-job-2' for %s: " "Block Copy for vda ready") expected_events_list.append("'block-job' for %s: " "Block Copy for " + "%s" % dest_path + " completed") expected_events_list.append("'block-job-2' for %s: " "Block Copy for vda completed") elif event == "detach-dimm": prepare_vmxml_mem(vmxml) tg_size = params.get("dimm_size") tg_sizeunit = params.get("dimm_unit") dimm_xml = utils_hotplug.create_mem_xml(tg_size, None, None, tg_sizeunit) virsh.attach_device(dom.name, dimm_xml.xml, flagstr="--config", **virsh_dargs) vmxml_dimm = vm_xml.VMXML.new_from_dumpxml(dom.name) logging.debug("Current vmxml with plugged dimm dev is %s\n" % vmxml_dimm) virsh.start(dom.name, **virsh_dargs) dom.wait_for_login().close() result = virsh.detach_device(dom.name, dimm_xml.xml, debug=True, ignore_status=True) expected_fails = params.get("expected_fails") utlv.check_result(result, expected_fails) vmxml_live = vm_xml.VMXML.new_from_dumpxml(dom.name) logging.debug("Current vmxml after hot-unplug dimm is %s\n" % vmxml_live) expected_events_list.append("'device-removal-failed' for %s: dimm0") elif event == "watchdog": vmxml.remove_all_device_by_type('watchdog') watchdog_dev = Watchdog() watchdog_dev.model_type = params.get("watchdog_model") action = params.get("action") watchdog_dev.action = action vmxml.add_device(watchdog_dev) vmxml.sync() logging.debug("Current vmxml with watchdog dev is %s\n" % vmxml) virsh.start(dom.name, **virsh_dargs) session = dom.wait_for_login() try: session.cmd("echo 0 > /dev/watchdog") except (ShellTimeoutError, ShellProcessTerminatedError) as details: test.fail("Failed to trigger watchdog: %s" % details) session.close() # watchdog acts slowly, waiting for it. time.sleep(30) expected_events_list.append("'watchdog' for %s: " + "%s" % action) if action == 'pause': expected_events_list.append("'lifecycle' for %s: Suspended Watchdog") virsh.resume(dom.name, **virsh_dargs) else: # action == 'reset' expected_events_list.append("'reboot' for %s") elif event == "io-error": part_size = params.get("part_size") resume_event = params.get("resume_event") suspend_event = params.get("suspend_event") process.run("truncate -s %s %s" % (part_size, small_part), shell=True) utlv.mkfs(small_part, part_format) utils_misc.mount(small_part, mount_point, None) add_disk(dom.name, new_disk, 'vdb', '--subdriver qcow2 --config', 'qcow2') dom.start() session = dom.wait_for_login() session.cmd("mkfs.ext4 /dev/vdb && mount /dev/vdb /mnt && ls /mnt && " "dd if=/dev/zero of=/mnt/test.img bs=1M count=50", ignore_all_errors=True) time.sleep(5) session.close() expected_events_list.append("'io-error' for %s: " + "%s" % new_disk + r" \(virtio-disk1\) pause") expected_events_list.append("'io-error-reason' for %s: " + "%s" % new_disk + r" \(virtio-disk1\) pause due to enospc") expected_events_list.append(suspend_event) process.run("df -hT") virsh.resume(dom.name, **virsh_dargs) time.sleep(5) expected_events_list.append(resume_event) expected_events_list.append("'io-error' for %s: " + "%s" % new_disk + r" \(virtio-disk1\) pause") expected_events_list.append("'io-error-reason' for %s: " + "%s" % new_disk + r" \(virtio-disk1\) pause due to enospc") expected_events_list.append(suspend_event) ret = virsh.domstate(dom.name, "--reason", **virsh_dargs) if ret.stdout.strip() != "paused (I/O error)": test.fail("Domain state should still be paused due to I/O error!") else: test.error("Unsupported event: %s" % event) # Event may not received immediately time.sleep(3) finally: if os.path.exists(save_path): os.unlink(save_path) if os.path.exists(new_disk): os.unlink(new_disk) if os.path.exists(dest_path): os.unlink(dest_path) return [(dom.name, event) for event in expected_events_list]
def trigger_events(dom, events_list=[]): """ Trigger various events in events_list :param dom: the vm objects corresponding to the domain :return: the expected output that virsh event command prints out """ expected_events_list = [] save_path = os.path.join(tmpdir, "%s_event.save" % dom.name) print(dom.name) xmlfile = dom.backup_xml() try: for event in events_list: logging.debug("Current event is: %s", event) if event in ['start', 'restore', 'create', 'edit', 'define', 'undefine', 'crash']: if dom.is_alive(): dom.destroy() if event in ['create', 'define']: dom.undefine() else: if not dom.is_alive(): dom.start() dom.wait_for_login().close() if event == "resume": dom.pause() if event == "undefine": virsh.undefine(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Undefined Removed") elif event == "create": virsh.create(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") elif event == "destroy": virsh.destroy(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Stopped Destroyed") elif event == "define": virsh.define(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Defined Added") elif event == "start": virsh.start(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") dom.wait_for_login().close() elif event == "suspend": virsh.suspend(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") if not libvirt_version.version_compare(5, 3, 0): expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") elif event == "resume": virsh.resume(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "save": virsh.save(dom.name, save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") expected_events_list.append("'lifecycle' for %s:" " Stopped Saved") elif event == "restore": if not os.path.exists(save_path): logging.error("%s not exist", save_path) else: virsh.restore(save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Started Restored") expected_events_list.append("'lifecycle' for %s:" " Resumed Snapshot") elif event == "edit": #Check whether 'description' element exists. domxml = virsh.dumpxml(dom.name).stdout.strip() find_desc = parseString(domxml).getElementsByTagName("description") if find_desc == []: #If not exists, add one for it. logging.info("Adding <description> to guest") virsh.desc(dom.name, "--config", "Added desc for testvm", **virsh_dargs) #The edit operation is to delete 'description' element. edit_cmd = [r":g/<description.*<\/description>/d"] utlv.exec_virsh_edit(dom.name, edit_cmd) expected_events_list.append("'lifecycle' for %s:" " Defined Updated") elif event == "shutdown": if signal_name is None: virsh.shutdown(dom.name, **virsh_dargs) # Wait a few seconds for shutdown finish time.sleep(3) if utils_misc.compare_qemu_version(2, 9, 0): #Shutdown reason distinguished from qemu_2.9.0-9 expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished after guest request") else: os.kill(dom.get_pid(), getattr(signal, signal_name)) if utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished after host request") if not utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished") wait_for_shutoff(dom) expected_events_list.append("'lifecycle' for %s:" " Stopped Shutdown") elif event == "crash": if not vmxml.xmltreefile.find('devices').findall('panic'): # Set panic device panic_dev = Panic() panic_dev.model = panic_model panic_dev.addr_type = addr_type panic_dev.addr_iobase = addr_iobase vmxml.add_device(panic_dev) vmxml.on_crash = "coredump-restart" vmxml.sync() logging.info("Guest xml now is: %s", vmxml) dom.start() session = dom.wait_for_login() # Stop kdump in the guest session.cmd("systemctl stop kdump", ignore_all_errors=True) # Enable sysRq session.cmd("echo 1 > /proc/sys/kernel/sysrq") try: # Crash the guest session.cmd("echo c > /proc/sysrq-trigger", timeout=60) except (ShellTimeoutError, ShellProcessTerminatedError) as details: logging.info(details) session.close() expected_events_list.append("'lifecycle' for %s:" " Crashed Panicked") expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "reset": virsh.reset(dom.name, **virsh_dargs) expected_events_list.append("'reboot' for %s") elif event == "vcpupin": virsh.vcpupin(dom.name, '0', '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.vcpupin0: 0") elif event == "emulatorpin": virsh.emulatorpin(dom.name, '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.emulatorpin: 0") elif event == "setmem": mem_size = int(params.get("mem_size", 512000)) virsh.setmem(dom.name, mem_size, **virsh_dargs) expected_events_list.append("'balloon-change' for %s:") elif event == "device-added-removed": add_disk(dom.name, new_disk, 'vdb', '') expected_events_list.append("'device-added' for %s:" " virtio-disk1") virsh.detach_disk(dom.name, 'vdb', **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " virtio-disk1") iface_xml_obj = create_iface_xml() iface_xml_obj.xmltreefile.write() virsh.detach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " net0") time.sleep(2) virsh.attach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-added' for %s:" " net0") elif event == "block-threshold": add_disk(dom.name, new_disk, 'vdb', '', format=disk_format) logging.debug(process.run('qemu-img info %s -U' % new_disk)) virsh.domblkthreshold(vm_name, 'vdb', '100M') session = dom.wait_for_login() session.cmd("mkfs.ext4 /dev/vdb && mount /dev/vdb /mnt && ls /mnt && " "dd if=/dev/urandom of=/mnt/bigfile bs=1M count=300 && sync") time.sleep(5) session.close() expected_events_list.append("'block-threshold' for %s:" " dev: vdb(%s) 104857600 29368320") virsh.detach_disk(dom.name, 'vdb', **virsh_dargs) elif event == "change-media": target_device = "hdc" device_target_bus = params.get("device_target_bus", "ide") disk_blk = vm_xml.VMXML.get_disk_blk(dom.name) logging.info("disk_blk %s", disk_blk) if target_device not in disk_blk: logging.info("Adding cdrom to guest") if dom.is_alive(): dom.destroy() add_disk(dom.name, "''", target_device, ("--type cdrom --sourcetype file --driver qemu " + "--config --targetbus %s" % device_target_bus)) dom.start() all_options = new_disk + " --insert" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " closed") all_options = new_disk + " --eject" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") elif event == "hwclock": session = dom.wait_for_login() try: session.cmd("hwclock --systohc", timeout=60) except (ShellTimeoutError, ShellProcessTerminatedError) as details: logging.info(details) session.close() expected_events_list.append("'rtc-change' for %s:") elif event == "metadata_set": metadata_uri = params.get("metadata_uri") metadata_key = params.get("metadata_key") metadata_value = params.get("metadata_value") virsh.metadata(dom.name, metadata_uri, options="", key=metadata_key, new_metadata=metadata_value, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") elif event == "metadata_edit": metadata_uri = "http://herp.derp/" metadata_key = "herp" metadata_value = "<derp xmlns:foobar='http://foo.bar/'>foo<bar></bar></derp>" virsh_cmd = r"virsh metadata %s --uri %s --key %s %s" virsh_cmd = virsh_cmd % (dom.name, metadata_uri, metadata_key, "--edit") session = aexpect.ShellSession("sudo -s") logging.info("Running command: %s", virsh_cmd) try: session.sendline(virsh_cmd) session.sendline(r":insert") session.sendline(metadata_value) session.sendline(".") session.send('ZZ') remote.handle_prompts(session, None, None, r"[\#\$]\s*$", debug=True, timeout=60) except Exception as e: test.error("Error occured: %s" % e) session.close() # Check metadata after edit virsh.metadata(dom.name, metadata_uri, options="", key=metadata_key, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") elif event == "metadata_remove": virsh.metadata(dom.name, metadata_uri, options="--remove", key=metadata_key, **virsh_dargs) expected_events_list.append("'metadata-change' for %s: " "element http://app.org/") else: test.error("Unsupported event: %s" % event) # Event may not received immediately time.sleep(3) finally: if os.path.exists(save_path): os.unlink(save_path) if os.path.exists(new_disk): os.unlink(new_disk) return [(dom.name, event) for event in expected_events_list]
def run(test, params, env): """ Test virsh blockresize command for block device of domain. 1) Init the variables from params. 2) Create an image with specified format. 3) Attach a disk image to vm. 4) Test blockresize for the disk 5) Detach the disk """ # MAIN TEST CODE ### # Process cartesian parameters vm_name = params.get("main_vm", "avocado-vt-vm1") image_format = params.get("disk_image_format", "qcow2") initial_disk_size = params.get("initial_disk_size", "500K") status_error = "yes" == params.get("status_error", "yes") resize_value = params.get("resize_value") virsh_dargs = {'debug': True} # Skip 'qed' cases for libvirt version greater than 1.1.0 if libvirt_version.version_compare(1, 1, 0): if image_format == "qed": test.cancel("QED support changed, check bug: " "https://bugzilla.redhat.com/show_bug.cgi" "?id=731570") # Create an image. tmp_dir = data_dir.get_tmp_dir() image_path = os.path.join(tmp_dir, "blockresize_test") logging.info("Create image: %s, " "size %s, " "format %s", image_path, initial_disk_size, image_format) cmd = "qemu-img create -f %s %s %s" % (image_format, image_path, initial_disk_size) ret = process.run(cmd, allow_output_check='combined', shell=True) status, output = (ret.exit_status, ret.stdout_text.strip()) if status: test.error("Creating image file %s failed: %s" % (image_path, output)) # Hotplug the image as disk device result = virsh.attach_disk(vm_name, source=image_path, target="vdd", extra=" --subdriver %s" % image_format, **virsh_dargs) if result.exit_status: test.error("Failed to attach disk %s to VM: %s." % (image_path, result.stderr.strip())) if resize_value == "over_size": # Use byte unit for over_size test resize_value = "%s" % OVER_SIZE + "b" # Run the test try: result = virsh.blockresize(vm_name, image_path, resize_value, **virsh_dargs) status = result.exit_status err = result.stderr.strip() # Check status_error if status_error: #if Qemu version > 2.11, zero_size shrink can be supported. qemu_version = utils_misc.get_qemu_version() is_rhev_installed = qemu_version['is_rhev'] zero_size_hit = (resize_value == "0" and utils_misc.compare_qemu_version( 2, 11, 0, is_rhev=is_rhev_installed)) if (status == 0 or err == "") and (not zero_size_hit): test.fail("Expect failure, but run successfully!") # No need to do more test return else: if status != 0 or err != "": # bz 1002813 will result in an error on this err_str = "unable to execute QEMU command 'block_resize': Could not resize: Invalid argument" if resize_value[-2] in "kb" and re.search(err_str, err): test.cancel("BZ 1002813 not yet applied") else: test.fail("Run failed with right " "virsh blockresize command") # Although kb should not be used, libvirt/virsh will accept it and # consider it as a 1000 bytes, which caused issues for qed & qcow2 # since they expect a value evenly divisible by 512 (hence bz 1002813). if "kb" in resize_value: value = int(resize_value[:-2]) if image_format in ["qed", "qcow2"]: # qcow2 and qed want a VIR_ROUND_UP value based on 512 byte # sectors - hence this less than visually appealing formula expected_size = (((value * 1000) + 512 - 1) // 512) * 512 else: # Raw images... # Ugh - there's some rather ugly looking math when kb # (or mb, gb, tb, etc.) are used as the scale for the # value to create an image. The blockresize for the # running VM uses a qemu json call which differs from # qemu-img would do - resulting in (to say the least) # awkward sizes. We'll just have to make sure we don't # deviates more than a sector. expected_size = value * 1000 elif "kib" in resize_value: value = int(resize_value[:-3]) expected_size = value * 1024 elif resize_value[-1] in "b": expected_size = int(resize_value[:-1]) elif resize_value[-1] in "k": value = int(resize_value[:-1]) expected_size = value * 1024 elif resize_value[-1] == "m": value = int(resize_value[:-1]) expected_size = value * 1024 * 1024 elif resize_value[-1] == "g": value = int(resize_value[:-1]) expected_size = value * 1024 * 1024 * 1024 cmd = "qemu-img info %s" % image_path if libvirt_storage.check_qemu_image_lock_support(): cmd += " -U" ret = process.run(cmd, allow_output_check='combined', shell=True) status, output = (ret.exit_status, ret.stdout_text.strip()) value_return_by_qemu_img = re.search( r'virtual size:\s+(\d+(\.\d+)?)+\s?G', output).group(1) if value != int(float(value_return_by_qemu_img)): test.fail( "initial image size in config is not equals to value returned by qemu-img info" ) else: test.error("Unknown scale value") image_info = utils_misc.get_image_info(image_path) actual_size = int(image_info['vsize']) logging.info( "The expected block size is %s bytes, " "the actual block size is %s bytes", expected_size, actual_size) # See comment above regarding Raw images if image_format == "raw" and resize_value[-2] in "kb": if abs(int(actual_size) - int(expected_size)) > 512: test.fail("New raw blocksize set by blockresize do " "not match the expected value") else: if int(actual_size) != int(expected_size): test.fail("New blocksize set by blockresize is " "different from actual size from " "'qemu-img info'") finally: virsh.detach_disk(vm_name, target="vdd", **virsh_dargs)
def run(test, params, env): """ Test command: virsh setmem. 1) Prepare vm environment. 2) Handle params 3) Prepare libvirtd status. 4) Run test command and wait for current memory's stable. 5) Recover environment. 4) Check result. """ def get_vm_usable_mem(session): """ Get total usable RAM from /proc/meminfo """ cmd = "cat /proc/meminfo" proc_mem = session.cmd_output(cmd) total_usable_mem = re.search(r'MemTotal:\s+(\d+)\s+[kK]B', proc_mem).group(1) return int(total_usable_mem) def vm_unusable_mem(session): """ Get the unusable RAM of the VM. """ # Get total physical memory from dmidecode cmd = "dmidecode -t 17" dmi_mem = session.cmd_output(cmd) dmi_mem_size = re.findall(r'Size:\s(\d+\s+[K|M|G]B)', dmi_mem) if not dmi_mem_size: test.fail("Cannot get memory size info inside VM.") total_physical_mem = 0 for size_info in dmi_mem_size: mem_size = int(size_info.split()[0].strip()) mem_unit = size_info.split()[1].strip() if mem_unit.lower() == 'kb': total_physical_mem += mem_size elif mem_unit.lower() == 'mb': total_physical_mem += mem_size * 1024 elif mem_unit.lower() == 'gb': total_physical_mem += mem_size * 1048576 return total_physical_mem - get_vm_usable_mem(session) def make_domref(domarg, vm_ref, domid, vm_name, domuuid): """ Create domain options of command """ # Specify domain as argument or parameter if domarg == "yes": dom_darg_key = "domainarg" else: dom_darg_key = "domain" # How to reference domain if vm_ref == "domid": dom_darg_value = domid elif vm_ref == "domname": dom_darg_value = vm_name elif vm_ref == "domuuid": dom_darg_value = domuuid elif vm_ref == "none": dom_darg_value = None elif vm_ref == "emptystring": dom_darg_value = '""' else: # stick in value directly dom_darg_value = vm_ref return {dom_darg_key: dom_darg_value} def make_sizeref(sizearg, mem_ref, original_mem): """ Create size options of command """ if sizearg == "yes": size_darg_key = "sizearg" else: size_darg_key = "size" if mem_ref == "halfless": size_darg_value = "%d" % (original_mem // 2) elif mem_ref == "halfmore": size_darg_value = "%d" % int(original_mem * 1.5) # no fraction elif mem_ref == "same": size_darg_value = "%d" % original_mem elif mem_ref == "emptystring": size_darg_value = '""' elif mem_ref == "zero": size_darg_value = "0" elif mem_ref == "toosmall": size_darg_value = "1024" elif mem_ref == "toobig": size_darg_value = "1099511627776" # (KiB) One Petabyte elif mem_ref == "none": size_darg_value = None else: # stick in value directly size_darg_value = mem_ref return {size_darg_key: size_darg_value} def cal_deviation(actual, expected): """ Calculate deviation of actual result and expected result """ numerator = float(actual) denominator = float(expected) if numerator > denominator: numerator = denominator denominator = float(actual) return 100 - (100 * (numerator / denominator)) def is_old_libvirt(): """ Check if libvirt is old version """ regex = r'\s+\[--size\]\s+' return bool(not virsh.has_command_help_match('setmem', regex)) def print_debug_stats(original_inside_mem, original_outside_mem, test_inside_mem, test_outside_mem, expected_outside_mem, expected_inside_mem, delta_percentage, unusable_mem): """ Print debug message for test """ # Calculate deviation inside_deviation = cal_deviation(test_inside_mem, expected_inside_mem) outside_deviation = cal_deviation(test_outside_mem, expected_outside_mem) dbgmsg = ("Unusable memory of VM : %d KiB\n" "Original inside memory : %d KiB\n" "Expected inside memory : %d KiB\n" "Actual inside memory : %d KiB\n" "Inside memory deviation : %0.2f%%\n" "Original outside memory : %d KiB\n" "Expected outside memory : %d KiB\n" "Actual outside memory : %d KiB\n" "Outside memory deviation: %0.2f%%\n" "Acceptable deviation : %0.2f%%" % (unusable_mem, original_inside_mem, expected_inside_mem, test_inside_mem, inside_deviation, original_outside_mem, expected_outside_mem, test_outside_mem, outside_deviation, delta_percentage)) for dbgline in dbgmsg.splitlines(): logging.debug(dbgline) # MAIN TEST CODE ### # Process cartesian parameters vm_ref = params.get("setmem_vm_ref", "") mem_ref = params.get("setmem_mem_ref", "") flags = params.get("setmem_flags", "") status_error = "yes" == params.get("status_error", "no") old_libvirt_fail = "yes" == params.get("setmem_old_libvirt_fail", "no") quiesce_delay = int(params.get("setmem_quiesce_delay", "1")) domarg = params.get("setmem_domarg", "no") sizearg = params.get("setmem_sizearg", "no") libvirt_status = params.get("libvirt", "on") delta_percentage = float(params.get("setmem_delta_per", "10")) start_vm = "yes" == params.get("start_vm", "yes") vm_name = params.get("main_vm", "avocado-vt-vm1") paused_after_start_vm = "yes" == params.get("paused_after_start_vm", "no") manipulate_dom_before_setmem = "yes" == params.get( "manipulate_dom_before_setmem", "no") manipulate_dom_after_setmem = "yes" == params.get( "manipulate_dom_after_setmem", "no") manipulate_action = params.get("manipulate_action", "") readonly = "yes" == params.get("setmem_readonly", "no") expect_msg = params.get("setmem_err_msg") driver_packed = params.get("driver_packed", "on") with_packed = "yes" == params.get("with_packed", "no") expect_xml_line = params.get("expect_xml_line") expect_qemu_line = params.get("expect_qemu_line") vm = env.get_vm(vm_name) # Back up domain XML vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) backup_xml = vmxml.copy() if with_packed and not libvirt_version.version_compare(6, 3, 0): test.cancel("The virtio packed attribute is not supported in" " current libvirt version.") vmosxml = vmxml.os need_mkswap = False if manipulate_action in ['s3', 's4']: vm.destroy() BIOS_BIN = "/usr/share/seabios/bios.bin" if os.path.isfile(BIOS_BIN): vmosxml.loader = BIOS_BIN vmxml.os = vmosxml vmxml.sync() else: logging.error("Not find %s on host", BIOS_BIN) vmxml.set_pm_suspend(vm_name, "yes", "yes") vm.prepare_guest_agent() if manipulate_action == "s4": need_mkswap = not vm.has_swap() if need_mkswap: logging.debug("Creating swap partition") vm.create_swap_partition() memballoon_model = params.get("memballoon_model", "") if memballoon_model: vm.destroy() vmxml.del_device('memballoon', by_tag=True) memballoon_xml = vmxml.get_device_class('memballoon')() memballoon_xml.model = memballoon_model if with_packed: memballoon_xml.driver_packed = driver_packed vmxml.add_device(memballoon_xml) logging.info(memballoon_xml) vmxml.sync() vm.start() # Check guest dumpxml and qemu command in with_packed attribute if with_packed: expect_xml_line = expect_xml_line % driver_packed if utils_misc.compare_qemu_version(6, 2, 0, is_rhev=False): expect_qemu_line = expect_qemu_line % "true" else: expect_qemu_line = expect_qemu_line % driver_packed libvirt.check_dumpxml(vm, expect_xml_line) libvirt.check_qemu_cmd_line(expect_qemu_line) remove_balloon_driver = "yes" == params.get("remove_balloon_driver", "no") if remove_balloon_driver: if not vm.is_alive(): logging.error("Can't remove module as guest not running") else: session = vm.wait_for_login() cmd = "rmmod virtio_balloon" s_rmmod, o_rmmod = session.cmd_status_output(cmd) if s_rmmod != 0: logging.error( "Fail to remove module virtio_balloon in guest:\n%s", o_rmmod) session.close() # Get original data domid = vm.get_id() domuuid = vm.get_uuid() uri = vm.connect_uri if not vm.is_alive(): vm.start() session = vm.wait_for_login() if session.cmd_status('dmidecode'): # The physical memory size is in vm xml, use it when dmidecode not # supported unusable_mem = int(vmxml.max_mem) - get_vm_usable_mem(session) else: unusable_mem = vm_unusable_mem(session) original_outside_mem = vm.get_used_mem() original_inside_mem = get_vm_usable_mem(session) session.close() # Prepare VM state if not start_vm: vm.destroy() else: if paused_after_start_vm: vm.pause() old_libvirt = is_old_libvirt() if old_libvirt: logging.info("Running test on older libvirt") use_kilobytes = True else: logging.info("Running test on newer libvirt") use_kilobytes = False # Argument pattern is complex, build with dargs dargs = { 'flagstr': flags, 'use_kilobytes': use_kilobytes, 'uri': uri, 'ignore_status': True, "debug": True, 'readonly': readonly } dargs.update(make_domref(domarg, vm_ref, domid, vm_name, domuuid)) dargs.update(make_sizeref(sizearg, mem_ref, original_outside_mem)) # Prepare libvirtd status libvirtd = utils_libvirtd.Libvirtd() if libvirt_status == "off": libvirtd.stop() else: if not libvirtd.is_running(): libvirtd.start() if status_error or (old_libvirt_fail & old_libvirt): logging.info("Error Test: Expecting an error to occur!") try: memory_change = True if manipulate_dom_before_setmem: manipulate_domain(test, vm_name, manipulate_action) if manipulate_action in ['save', 'managedsave', 's4']: memory_change = False result = virsh.setmem(**dargs) status = result.exit_status if status is 0: logging.info("Waiting %d seconds for VM memory to settle", quiesce_delay) # It takes time for kernel to settle on new memory # and current clean pages is not predictable. Therefore, # extremely difficult to determine quiescence, so # sleep one second per error percent is reasonable option. time.sleep(quiesce_delay) if manipulate_dom_before_setmem: manipulate_domain(test, vm_name, manipulate_action, True) if manipulate_dom_after_setmem: manipulate_domain(test, vm_name, manipulate_action) manipulate_domain(test, vm_name, manipulate_action, True) # Recover libvirtd status if libvirt_status == "off": libvirtd.start() # Gather stats if not running error test if not status_error and not old_libvirt_fail: # Expected results for both inside and outside if remove_balloon_driver: expected_mem = original_outside_mem else: if not memory_change: expected_mem = original_inside_mem elif sizearg == "yes": expected_mem = int(dargs["sizearg"]) else: expected_mem = int(dargs["size"]) if memory_change: # Should minus unusable memory for inside memory check expected_inside_mem = expected_mem - unusable_mem expected_outside_mem = expected_mem else: expected_inside_mem = expected_mem expected_outside_mem = original_outside_mem def get_vm_mem(): """ Test results for both inside and outside :return: Get vm memory for both inside and outside """ if not memory_change: test_inside_mem = original_inside_mem test_outside_mem = original_outside_mem else: if vm.state() == "shut off": vm.start() elif vm.state() == "paused": # Make sure it's never paused vm.resume() session = vm.wait_for_login() # Actual results test_inside_mem = get_vm_usable_mem(session) session.close() test_outside_mem = vm.get_used_mem() return (test_inside_mem, test_outside_mem) # Don't care about memory comparison on error test def verify_outside_result(): _, test_outside_mem = get_vm_mem() return (cal_deviation(test_outside_mem, expected_outside_mem) <= delta_percentage) def verify_inside_result(): test_inside_mem, _ = get_vm_mem() return (cal_deviation(test_inside_mem, expected_inside_mem) <= delta_percentage) msg = "test conditions not met: " error_flag = 0 if status is not 0: error_flag = 1 msg += "Non-zero virsh setmem exit code. " if not utils_misc.wait_for(verify_outside_result, timeout=240): error_flag = 1 msg += "Outside memory deviated. " if not utils_misc.wait_for(verify_inside_result, timeout=240): error_flag = 1 msg += "Inside memory deviated. " test_inside_mem, test_outside_mem = get_vm_mem() print_debug_stats(original_inside_mem, original_outside_mem, test_inside_mem, test_outside_mem, expected_outside_mem, expected_inside_mem, delta_percentage, unusable_mem) if error_flag: test.fail(msg) elif not status_error and old_libvirt_fail: if status is 0: if old_libvirt: test.fail("Error test did not result in an error") else: if not old_libvirt: test.fail("Newer libvirt failed when it should not") else: # Verify an error test resulted in error if status is 0: test.fail("Error test did not result in an error") if expect_msg: libvirt.check_result(result, expect_msg.split(';')) finally: if need_mkswap: vm.cleanup_swap() vm.destroy() backup_xml.sync()
def trigger_events(dom, events_list=[]): """ Trigger various events in events_list :param dom: the vm objects corresponding to the domain :return: the expected output that virsh event command prints out """ expected_events_list = [] save_path = os.path.join(tmpdir, "%s_event.save" % dom.name) print(dom.name) xmlfile = dom.backup_xml() try: for event in events_list: if event in [ 'start', 'restore', 'create', 'define', 'undefine', 'crash' ]: if dom.is_alive(): dom.destroy() if event in ['create', 'define']: dom.undefine() else: if not dom.is_alive(): dom.start() dom.wait_for_login().close() if event == "resume": dom.pause() if event == "undefine": virsh.undefine(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Undefined Removed") elif event == "create": virsh.create(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") elif event == "destroy": virsh.destroy(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Stopped Destroyed") elif event == "define": virsh.define(xmlfile, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Defined Added") elif event == "start": virsh.start(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") expected_events_list.append("'lifecycle' for %s:" " Started Booted") dom.wait_for_login().close() elif event == "suspend": virsh.suspend(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") if not libvirt_version.version_compare(5, 3, 0): expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") elif event == "resume": virsh.resume(dom.name, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "save": virsh.save(dom.name, save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Suspended Paused") expected_events_list.append("'lifecycle' for %s:" " Stopped Saved") elif event == "restore": if not os.path.exists(save_path): logging.error("%s not exist", save_path) else: virsh.restore(save_path, **virsh_dargs) expected_events_list.append("'lifecycle' for %s:" " Started Restored") expected_events_list.append("'lifecycle' for %s:" " Resumed Snapshot") elif event == "edit": #Check whether 'description' element exists. domxml = virsh.dumpxml(dom.name).stdout.strip() find_desc = parseString(domxml).getElementsByTagName( "description") if find_desc == []: #If not exists, add one for it. logging.info("Adding <description> to guest") virsh.desc(dom.name, "--config", "Added desc for testvm", **virsh_dargs) #The edit operation is to delete 'description' element. edit_cmd = [r":g/<description.*<\/description>/d"] utlv.exec_virsh_edit(dom.name, edit_cmd) expected_events_list.append("'lifecycle' for %s:" " Defined Updated") elif event == "shutdown": if signal_name is None: virsh.shutdown(dom.name, **virsh_dargs) # Wait a few seconds for shutdown finish time.sleep(3) if utils_misc.compare_qemu_version(2, 9, 0): #Shutdown reason distinguished from qemu_2.9.0-9 expected_events_list.append( "'lifecycle' for %s:" " Shutdown Finished after guest request") else: os.kill(dom.get_pid(), getattr(signal, signal_name)) if utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append( "'lifecycle' for %s:" " Shutdown Finished after host request") if not utils_misc.compare_qemu_version(2, 9, 0): expected_events_list.append("'lifecycle' for %s:" " Shutdown Finished") expected_events_list.append("'lifecycle' for %s:" " Stopped Shutdown") elif event == "crash": if not vmxml.xmltreefile.find('devices').findall('panic'): # Set panic device panic_dev = Panic() panic_dev.model = panic_model panic_dev.addr_type = addr_type panic_dev.addr_iobase = addr_iobase vmxml.add_device(panic_dev) vmxml.on_crash = "coredump-restart" vmxml.sync() logging.info("Guest xml now is: %s", vmxml) dom.start() session = dom.wait_for_login() # Stop kdump in the guest session.cmd("systemctl stop kdump", ignore_all_errors=True) # Enable sysRq session.cmd("echo 1 > /proc/sys/kernel/sysrq") try: # Crash the guest session.cmd("echo c > /proc/sysrq-trigger", timeout=60) except (ShellTimeoutError, ShellProcessTerminatedError) as details: logging.info(details) session.close() expected_events_list.append("'lifecycle' for %s:" " Crashed Panicked") expected_events_list.append("'lifecycle' for %s:" " Resumed Unpaused") elif event == "reset": virsh.reset(dom.name, **virsh_dargs) expected_events_list.append("'reboot' for %s") elif event == "vcpupin": virsh.vcpupin(dom.name, '0', '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.vcpupin0: 0") elif event == "emulatorpin": virsh.emulatorpin(dom.name, '0', **virsh_dargs) expected_events_list.append("'tunable' for %s:" "\n\tcputune.emulatorpin: 0") elif event == "setmem": mem_size = int(params.get("mem_size", 512000)) virsh.setmem(dom.name, mem_size, **virsh_dargs) expected_events_list.append("'balloon-change' for %s:") elif event == "device-added-removed": add_disk(dom.name, new_disk, 'vdb', '') expected_events_list.append("'device-added' for %s:" " virtio-disk1") virsh.detach_disk(dom.name, 'vdb', **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " virtio-disk1") iface_xml_obj = create_iface_xml() iface_xml_obj.xmltreefile.write() virsh.detach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-removed' for %s:" " net0") time.sleep(2) virsh.attach_device(dom.name, iface_xml_obj.xml, **virsh_dargs) expected_events_list.append("'device-added' for %s:" " net0") elif event == "change-media": target_device = "hdc" device_target_bus = params.get("device_target_bus", "ide") disk_blk = vm_xml.VMXML.get_disk_blk(dom.name) logging.info("disk_blk %s", disk_blk) if target_device not in disk_blk: logging.info("Adding cdrom to guest") if dom.is_alive(): dom.destroy() add_disk( dom.name, "''", target_device, ("--type cdrom --sourcetype file --driver qemu " + "--config --targetbus %s" % device_target_bus)) dom.start() all_options = new_disk + " --insert" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " closed") all_options = new_disk + " --eject" virsh.change_media(dom.name, target_device, all_options, **virsh_dargs) expected_events_list.append("'tray-change' for %s disk" + " .*%s.*:" % device_target_bus + " opened") else: test.error("Unsupported event: %s" % event) # Event may not received immediately time.sleep(3) finally: if os.path.exists(save_path): os.unlink(save_path) if os.path.exists(new_disk): os.unlink(new_disk) return [(dom.name, event) for event in expected_events_list]
def run(test, params, env): """ Test the tpm virtual devices 1. prepare a guest with different tpm devices 2. check whether the guest can be started 3. check the xml and qemu cmd line 4. check tpm usage in guest os """ # Tpm passthrough supported since libvirt 1.0.5. if not libvirt_version.version_compare(1, 0, 5): test.cancel("Tpm device is not supported " "on current libvirt version.") # Tpm passthrough supported since qemu 2.12.0-49. if not utils_misc.compare_qemu_version(2, 9, 0, is_rhev=False): test.cancel("Tpm device is not supported " "on current qemu version.") status_error = ("yes" == params.get("status_error", "no")) tpm_model = params.get("tpm_model") backend_type = params.get("backend_type") backend_version = params.get("backend_version") device_path = params.get("device_path") tpm_num = int(params.get("tpm_num", 1)) multi_vms = ("yes" == params.get("multi_vms", "no")) # Check tpm chip on host for passthrough testing if backend_type == "passthrough": dmesg_info = process.getoutput("dmesg|grep tpm -wi", shell=True) logging.debug("dmesg info about tpm:\n %s", dmesg_info) dmesg_error = re.search("No TPM chip found|TPM is disabled", dmesg_info) if dmesg_error: test.cancel(dmesg_error.group()) else: # Try to check host tpm chip version tpm_v = None if re.search("2.0 TPM", dmesg_info): tpm_v = "2.0" if not utils_package.package_install("tpm2-tools"): # package_install() return 'True' if succeed test.error("Failed to install tpm2-tools on host") else: if re.search("1.2 TPM", dmesg_info): tpm_v = "1.2" # If "1.2 TPM" or no version info in dmesg, try to test a tpm1.2 at first if not utils_package.package_install("tpm-tools"): test.error("Failed to install tpm-tools on host") vm_names = params.get("vms").split() vm_name = vm_names[0] vm = env.get_vm(vm_name) vm_xml = VMXML.new_from_inactive_dumpxml(vm_name) vm_xml_backup = vm_xml.copy() if vm.is_alive(): vm.destroy() vm2 = None if multi_vms: if len(vm_names) > 1: vm2_name = vm_names[1] vm2 = env.get_vm(vm2_name) vm2_xml = VMXML.new_from_inactive_dumpxml(vm2_name) vm2_xml_backup = vm2_xml.copy() else: # Clone additional vms if needed try: utils_path.find_command("virt-clone") except utils_path.CmdNotFoundError: if not utils_package.package_install(["virt-install"]): test.cancel("Failed to install virt-install on host") vm2_name = "vm2_" + utils_misc.generate_random_string(5) ret_clone = utils_libguestfs.virt_clone_cmd(vm_name, vm2_name, True, timeout=360, debug=True) if ret_clone.exit_status: test.error("Need more than one domains, but error occured when virt-clone.") vm2 = vm.clone(vm2_name) vm2_xml = VMXML.new_from_inactive_dumpxml(vm2_name) if vm2.is_alive(): vm2.destroy() service_mgr = service.ServiceManager() def check_dumpxml(): """ Check whether the added devices are shown in the guest xml """ logging.info("------Checking guest dumpxml------") if tpm_model: pattern = '<tpm model="%s">' % tpm_model else: # The default tpm model is "tpm-tis" pattern = '<tpm model="tpm-tis">' # Check tpm model xml_after_adding_device = VMXML.new_from_dumpxml(vm_name) logging.debug("xml after add tpm dev is %s", xml_after_adding_device) if pattern not in str(xml_after_adding_device): test.fail("Can not find the %s tpm device xml " "in the guest xml file." % tpm_model) # Check backend type pattern = '<backend type="%s"' % backend_type if pattern not in str(xml_after_adding_device): test.fail("Can not find the %s backend type xml for tpm dev " "in the guest xml file." % backend_type) # Check backend version if backend_version: pattern = "\'emulator\' version=\"%s\"" % backend_version if pattern not in str(xml_after_adding_device): test.fail("Can not find the %s backend version xml for tpm dev " "in the guest xml file." % backend_version) # Check device path if backend_type == "passthrough": pattern = '<device path="/dev/tpm0"' if pattern not in str(xml_after_adding_device): test.fail("Can not find the %s device path xml for tpm dev " "in the guest xml file." % device_path) logging.info('------PASS on guest dumpxml check------') def check_qemu_cmd_line(): """ Check whether the added devices are shown in the qemu cmd line """ logging.info("------Checking qemu cmd line------") if not vm.get_pid(): test.fail('VM pid file missing.') with open('/proc/%s/cmdline' % vm.get_pid()) as cmdline_file: cmdline = cmdline_file.read() logging.debug("Qemu cmd line info:\n %s", cmdline) # Check tpm model pattern = "-device.%s" % tpm_model if not re.search(pattern, cmdline): test.fail("Can not find the %s tpm device " "in qemu cmd line." % tpm_model) # Check backend type if backend_type == "passthrough": pattern_list = ["-tpmdev.passthrough"] dev_num = re.search(r"\d+", device_path).group() pattern_list.append("id=tpm-tpm%s" % dev_num) for pattern in pattern_list: if not re.search(pattern, cmdline): test.fail("Can not find the %s tpm device " "in qemu cmd line." % pattern) logging.info("------PASS on qemu cmd line check------") def get_host_tpm_bef(tpm_v): """ Test host tpm function and identify its real version before passthrough Since sometimes dmesg info doesn't include tpm msg, need use tpm-tool or tpm2-tools to try the function. :param tpm_v: host tpm version get from dmesg info :return: host tpm version """ logging.info("------Checking host tpm device before passthrough------") # Try tcsd tool for suspected tpm1.2 chip on host if tpm_v != "2.0": if not service_mgr.start('tcsd'): # service_mgr.start() return 'True' if succeed if tpm_v == "1.2": test.fail("Host tcsd.serivce start failed") else: # Means tpm_v got nothing from dmesg, log failure here and # go to 'elif' to try tpm2.0 tools. logging.info("Host tcsd.serivce start failed") else: tpm_real_v = "1.2" logging.info("Host tpm version info:") result = process.run("tpm_version", ignore_status=False) logging.debug("[host]# tpm_version\n %s", result.stdout) time.sleep(2) service_mgr.stop('tcsd') elif tpm_v != "1.2": # Try tpm2.0 tools if not utils_package.package_install("tpm2-tools"): test.error("Failed to install tpm2-tools on host") if process.run("tpm2_getrandom 5", ignore_status=True).exit_status: test.cancel("Both tcsd and tpm2-tools can not work, " "pls check your host tpm version and test env.") else: tpm_real_v = "2.0" logging.info("------PASS on host tpm device check------") return tpm_real_v def test_host_tpm_aft(tpm_real_v): """ Test host tpm function after passthrough :param tpm_real_v: host tpm real version indentified from testing """ logging.info("------Checking host tpm device after passthrough------") if tpm_real_v == "1.2": if service_mgr.start('tcsd'): time.sleep(2) service_mgr.stop('tcsd') test.fail("Host tpm should not work after passthrough to guest.") else: logging.info("Expected failure: Tpm is being used by guest.") elif tpm_real_v == "2.0": if not process.run("tpm2_getrandom 7", ignore_status=True).exit_status: test.fail("Host tpm should not work after passthrough to guest.") else: logging.info("Expected failure: Tpm is being used by guest.") logging.info("------PASS on host tpm device check------") def test_guest_tpm(expect_version, session, expect_fail): """ Test tpm function in guest :param expect_version: guest tpm version, as host version, or emulator specified :param session: Guest session to be tested :param expect_fail: guest tpm is expectedly fail to work """ logging.info("------Checking guest tpm device work------") if expect_version == "1.2": # Install tpm-tools and test by tcsd method if not utils_package.package_install(["tpm-tools"], session, 360): test.error("Failed to install tpm-tools package in guest") else: status, output = session.cmd_status_output("systemctl start tcsd") logging.debug(output) if status: if expect_fail: test.cancel("tpm-crb passthrough only works with host tpm2.0, " "but your host tpm version is 1.2") else: test.fail("Failed to start tcsd.service in guest") else: status, output = session.cmd_status_output("tpm_version") logging.debug(output) if status: test.fail("Guest tpm can not work") else: # If expect_version is tpm2.0, install and test by tpm2-tools if not utils_package.package_install(["tpm2-tools"], session, 360): test.error("Failed to install tpm2-tools package in guest") else: status, output = session.cmd_status_output("tpm2_getrandom 11") logging.debug(output) if status: test.fail("Guest tpm can not work") logging.info("------PASS on guest tpm device work check------") def reuse_by_vm2(tpm_dev): """ Try to passthrough tpm to a second guest, when it's being used by one guest. :param tpm_dev: tpm device to be added into guest xml """ logging.info("------Trying to passthrough tpm to a second domain------") vm2_xml.remove_all_device_by_type('tpm') vm2_xml.add_device(tpm_dev) vm2_xml.sync() ret = virsh.start(vm2_name, ignore_status=True, debug=True) if ret: logging.info("Expected failure when try to passthrough a tpm" " that being used by another guest") return else: test.fail("Reuse a passthroughed tpm should not succeed.") try: tpm_real_v = None if backend_type == "passthrough": tpm_real_v = get_host_tpm_bef(tpm_v) logging.debug("The host tpm real version is %s", tpm_real_v) vm_xml.remove_all_device_by_type('tpm') tpm_dev = Tpm() if tpm_model: tpm_dev.tpm_model = tpm_model backend = tpm_dev.Backend() backend.backend_type = backend_type if device_path: backend.device_path = device_path tpm_dev.backend = backend logging.debug("tpm dev xml to add is %s", tpm_dev) for num in range(tpm_num): vm_xml.add_device(tpm_dev, True) try: vm_xml.sync() except LibvirtXMLError as e: if tpm_num > 1 and backend_type == "passthrough": logging.info("Expected failure when define a guest with multi tpm passthrough" " configured in xml.") # Stop test when get expected failure return else: test.fail("Test failed in vmxml.sync(), detail:%s." % e) if tpm_num > 1 and backend_type == "passthrough": test.fail("Passthrough multi tpm should not succeed.") check_dumpxml() # For default model, no need start guest to test if tpm_model: expect_fail = False virsh.start(vm_name, ignore_status=False, debug=True) check_qemu_cmd_line() session = vm.wait_for_login() if backend_type == "passthrough": if tpm_real_v == "1.2" and tpm_model == "tpm-crb": expect_fail = True test_guest_tpm(tpm_real_v, session, expect_fail) if multi_vms: reuse_by_vm2(tpm_dev) # Stop test when get expected failure return test_host_tpm_aft(tpm_real_v) else: # emulator backend test_guest_tpm(backend_version, session, expect_fail) session.close() finally: vm_xml_backup.sync() if vm2: if len(vm_names) > 1: vm2_xml_backup.sync() else: virsh.remove_domain(vm2_name, "--remove-all-storage", debug=True)
def run_test_iface_acpi(case): """ Test acpi setting of interface :param case: test case """ if not utils_misc.compare_qemu_version(6, 1, 0, is_rhev=False) and \ machine_type == 'q35': test.cancel('This test is not supported on q35 ' 'by qemu until v6.1.0') acpi_index = params.get('acpi_index') iface_in_vm = params.get('iface_in_vm') iface_attrs = eval(params.get('iface_attrs')) # Create interface with acpi setting iface_acpi = interface.Interface('network') iface_acpi.setup_attrs(**iface_attrs) logging.debug('New interface with acpi: %s', iface_acpi) # Setup vm features with acpi if there is not features = vmxml.features feature_acpi = 'acpi' if not features.has_feature(feature_acpi): features.add_feature(feature_acpi) vmxml.features = features # Prepare vmxml for further test vmxml.remove_all_device_by_type('interface') vmxml.sync() # Test vm with iface with acpi if case == 'inplace': libvirt.add_vm_device(vmxml, iface_acpi) vm.start() # Test hotplug iface with acpi elif case == 'hotplug': vm.start() virsh.attach_device(vm_name, iface_acpi.xml, **VIRSH_ARGS) # Test boundary/negative values of acpi setting elif case == 'value_test': vm.start() vm.wait_for_serial_login().close() at_result = virsh.attach_device(vm_name, iface_acpi.xml, debug=True) libvirt.check_exit_status(at_result, status_error) if status_error: libvirt.check_result(at_result, expect_str) return # Check acpi setting in iface after adding/attaching to vm new_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) new_iface = new_vmxml.get_devices('interface')[0] logging.debug('Interface with acpi in vm: %s', new_iface) if new_iface.xmltreefile.find('acpi') is None: test.fail('acpi not found in Interface') if new_iface.acpi.get('index') != acpi_index: test.fail('Index of acpi check failed, should be %s' % acpi_index) # Check acpi inside vm session = vm.wait_for_serial_login() ip_output = session.cmd_output('ip l') logging.debug(ip_output) vm_ifaces = utils_net.get_linux_ifname(session) if iface_in_vm in vm_ifaces: logging.info('Found Interface %s in vm', iface_in_vm) else: test.fail('Interface %s not found in vm' % iface_in_vm) # Hotunplug iface virsh.detach_device(vm_name, new_iface.xml, **VIRSH_ARGS) vm_ifaces = utils_net.get_linux_ifname(session) if iface_in_vm in vm_ifaces: test.fail('Interface %s should be removed from vmxml' % iface_in_vm) new_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) if new_vmxml.get_devices('interface'): test.fail('Interface should be removed.')
def run(test, params, env): """ Test Hotplug/unplug interface device(s) """ def setup_default(): """ Default setup """ logging.debug("Remove VM's interface devices.") libvirt_vmxml.remove_vm_devices_by_type(vm, 'interface') vm_attrs = eval(params.get('vm_attrs', '{}')) if vm_attrs: vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) vmxml.setup_attrs(**vm_attrs) vmxml.sync() def teardown_default(): """ Default cleanup """ pass def setup_vdpa(): """ Setup vDPA environment """ setup_default() test_env_obj = None if test_target == "simulator": test_env_obj = utils_vdpa.VDPASimulatorTest() test_env_obj.setup() else: vdpa_mgmt_tool_extra = params.get("vdpa_mgmt_tool_extra", "") pf_pci = utils_vdpa.get_vdpa_pci() test_env_obj = utils_vdpa.VDPAOvsTest( pf_pci, mgmt_tool_extra=vdpa_mgmt_tool_extra) test_env_obj.setup() params['mac_addr'] = test_env_obj.vdpa_mac.get( params.get("vdpa_dev", "vdpa0")) return test_env_obj def teardown_vdpa(): """ Cleanup vDPA environment """ if test_target != "simulator": service.Factory.create_service("NetworkManager").restart() if test_obj: test_obj.cleanup() def test_vdpa(): """ Hotplug/unplug vDPA type interface 1) Start the vm, hotplug the interface 2) Login to the vm and check the network function 3) Hot-unplug the interface """ vm.start() vm_session = vm.wait_for_serial_login(timeout=240) br_name = None if test_target == "mellanox": br_name = test_obj.br_name for _i in range(eval(params.get('repeat_times', '1'))): interface_base.attach_iface_device(vm_name, dev_type, params) vdpa_base.check_vdpa_conn(vm_session, test_target, br_name) check_points.check_vm_iface_queues(vm_session, params) interface_base.detach_iface_device(vm_name, dev_type) libvirt_version.is_libvirt_feature_supported(params) supported_qemu_ver = eval( params.get('func_supported_since_qemu_kvm_ver', '()')) if supported_qemu_ver: if not utils_misc.compare_qemu_version(*supported_qemu_ver, False): test.cancel("Current qemu version doesn't support this test!") # Variable assignment test_target = params.get('test_target', '') dev_type = params.get('dev_type', '') vm_name = params.get('main_vm') vm = env.get_vm(vm_name) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) backup_vmxml = vmxml.copy() run_test = eval("test_%s" % dev_type) setup_test = eval("setup_%s" % dev_type) if "setup_%s" % dev_type in \ locals() else setup_default teardown_test = eval("teardown_%s" % dev_type) if "teardown_%s" % \ dev_type in locals() else teardown_default test_obj = None try: # Execute test test_obj = setup_test() run_test() finally: backup_vmxml.sync() teardown_test()
def run(test, params, env): """ Test for basic controller device function. 1) Define the VM w/o specified controller device and check result meets expectation. 2) Start the guest and check if start result meets expectation 3) Test the function of started controller device 4) Shutdown the VM and clean up environment """ def setup_os_xml(): """ Prepare os part of VM XML. """ osxml = vm_xml.os orig_machine = osxml.machine # avocado-vt only use virt machine type on aarch64 if platform.machine() == 'aarch64': osxml.machine = 'virt' return if os_machine: osxml.machine = os_machine vm_xml.os = osxml else: cur_machine = orig_machine def setup_controller_xml(index, addr_target=None): """ Prepare controller devices of VM XML. :param index: The index of controller :param addr_target: The controller address """ ctrl = Controller(type_name=cntlr_type) if model: ctrl.model = model if pcihole: ctrl.pcihole64 = pcihole if vectors: ctrl.vectors = vectors if index: ctrl.index = index if chassisNr: ctrl.target = {'chassisNr': chassisNr} if model_name: ctrl.model_name = {'name': model_name} if addr_target: match = re.match(r"(?P<bus>[0-9]*):(?P<slot>[0-9a-f]*).(?P<function>[0-9])", addr_target) if match: addr_dict = match.groupdict() addr_dict['bus'] = hex(int(addr_dict['bus'], 16)) addr_dict['slot'] = hex(int(addr_dict['slot'], 16)) addr_dict['function'] = hex(int(addr_dict['function'], 16)) addr_dict['domain'] = '0x0000' ctrl.address = ctrl.new_controller_address(attrs=addr_dict) logging.debug("Controller XML is:%s", ctrl) vm_xml.add_device(ctrl) if cmpnn_cntlr_model is not None: for num in range(int(cmpnn_cntlr_num)): ctrl = Controller(type_name=cntlr_type) ctrl.model = cmpnn_cntlr_model + str(num+1) ctrl.index = index logging.debug("Controller XML is:%s", ctrl) vm_xml.add_device(ctrl) def define_and_check(guest_xml): """ Define the guest and check the result. :param guest_xml: The guest VMXML instance """ fail_patts = [] if expect_err_msg: fail_patts.append(expect_err_msg) guest_xml.undefine() res = vm_xml.virsh.define(guest_xml.xml) logging.debug("Expect failures: %s", fail_patts) libvirt.check_result(res, expected_fails=fail_patts) return not res.exit_status def start_and_check(): """ Predict the error message when starting and try to start the guest. """ fail_patts = [] if expect_err_msg: fail_patts.append(expect_err_msg) res = virsh.start(vm_name) logging.debug("Expect failures: %s", fail_patts) libvirt.check_result(res, expected_fails=fail_patts) return not res.exit_status def prepare_qemu_pattern(elem): """ Collect the patterns to be searched in qemu command line. :param elem: a Controller object :return: A list including search patterns """ search_qemu_cmd = [] bus = int(elem.address.attrs.get('bus'), 0) slot = int(elem.address.attrs.get('slot'), 0) func = int(elem.address.attrs.get('function'), 0) addr_str = '%02d:%02d.%1d' % (bus, slot, func) name = elem.alias.get('name') if elem.model != 'dmi-to-pci-bridge': chassisNR = elem.target.get('chassisNr') value = "pci-bridge,chassis_nr=%s" % chassisNR value = "%s,id=%s,bus=pci.%d,addr=%#x" % (value, name, bus, slot) else: value = "%s" % elem.model_name['name'] value = "%s,id=%s,bus=pcie.%d,addr=%#x" % (value, name, bus, slot) tup = ('-device', value) search_qemu_cmd.append(tup) return search_qemu_cmd def get_patt_inx_ctl(cur_vm_xml, qemu_list, inx): """ Get search pattern in qemu line for some kind of cases :param cur_vm_xml: Guest xml :param qemu_list: List for storing qemu search patterns :param inx: Controller index used :return: a tuple for (search_result, qemu_list) """ (search_result, qemu_search) = check_cntrl(cur_vm_xml, cntlr_type, model, inx, None, True) if qemu_search: qemu_list.extend(qemu_search) return (search_result, qemu_list) def get_patt_non_zero_bus(cur_vm_xml): """ Get search pattern for multiple controllers with non-zero bus. :param cur_vm_xml: The guest VMXML instance :return: List, The search pattern list """ actual_set = set() for elem in cur_vm_xml.devices.by_device_tag('controller'): if (elem.type == cntlr_type and elem.model == model): actual_set.add(int(elem.index)) qemu_list = prepare_qemu_pattern(elem) expect_set = set() for num in range(1, int(pci_bus_number) + 1): expect_set.add(num) logging.debug("expect: %s, actual: %s", expect_set, actual_set) if (not actual_set.issubset(expect_set) or not expect_set.issubset(actual_set)): test.fail("The actual index set (%s)does " "not match the expect index set " "(%s)." % (actual_set, expect_set)) return qemu_list def get_search_patt_qemu_line(): """ Check if the guest XML has the expected content. :return: -device pci-bridge,chassis_nr=1,id=pci.1,bus=pci.0,addr=0x3 """ cur_vm_xml = VMXML.new_from_dumpxml(vm_name) qemu_list = [] # Check the pci-root controller has index = 0 if no_pci_controller == "yes": (_, qemu_list) = get_patt_inx_ctl(cur_vm_xml, qemu_list, '0') return qemu_list # Check index numbers of pci-bridge controllers should be equal # to the pci_bus_number if int(pci_bus_number) > 0: return get_patt_non_zero_bus(cur_vm_xml) # All controllers should exist if there is a gap between two PCI # controller indexes if index and index_second and int(index) > 0 and int(index_second) > 0: for idx in range(int(index_second), int(index) + 1): (_, qemu_list) = get_patt_inx_ctl(cur_vm_xml, qemu_list, str(idx)) return qemu_list # All controllers should exist with index among [1..index] if index and int(index) > 0 and not index_second: for idx in range(1, int(index) + 1): (search_result, qemu_list) = get_patt_inx_ctl(cur_vm_xml, qemu_list, str(idx)) if not search_result: test.fail("Can not find %s controller " "with index %s." % (model, str(idx))) return qemu_list def get_controller_addr(cntlr_type=None, model=None, index=None, cntlr_bus=None): """ Get the address of testing controller from VM XML as a string with format a. "bus:slot.function" for pci address type b. "cssid:ssid.devno" for ccw address type :param cntlr_type: controller type, e.g. pci :param model: controller model, e.g. pcie-root-port :param index: controller index, e.g. '0' :param cntlr_bus: controller bus type, e.g. pci, ccw :return: a tuple including an address string, bus, slot, function, multifunction """ if model in ['pci-root', 'pcie-root']: return (None, None, None, None, None) addr_str = None cur_vm_xml = VMXML.new_from_dumpxml(vm_name) for elem in cur_vm_xml.devices.by_device_tag('controller'): if ( (cntlr_type is None or elem.type == cntlr_type) and (model is None or elem.model == model) and (index is None or elem.index == index)): addr_elem = elem.address if addr_elem is None: test.error("Can not find 'Address' " "element for the controller") p4 = None if 'ccw' == cntlr_bus: p1 = int(addr_elem.attrs.get('cssid'), 0) p2 = int(addr_elem.attrs.get('ssid'), 0) p3 = int(addr_elem.attrs.get('devno'), 0) else: p1 = int(addr_elem.attrs.get('bus'), 0) p2 = int(addr_elem.attrs.get('slot'), 0) p3 = int(addr_elem.attrs.get('function'), 0) p4 = addr_elem.attrs.get('multifunction') addr_str = '%02d:%02x.%1d' % (p1, p2, p3) logging.debug("Controller address is %s", addr_str) return (addr_str, p1, p2, p3, p4) return (None, None, None, None, None) def check_controller_addr(cntlr_bus=None): """ Check test controller address against expectation. :param cntlr_bus: controller bus type, e.g. pci, ccw """ (addr_str, _, _, _, _) = get_controller_addr(cntlr_type, model, index, cntlr_bus) if model in ['pci-root', 'pcie-root']: if addr_str is None: return else: test.fail('Expect controller do not have address, ' 'but got "%s"' % addr_str) if index != 0: if '00:00' in addr_str: test.fail("Invalid PCI address 0000:00:00, " "at least one of domain, bus, " "or slot must be > 0") exp_addr_patt = r'00:[0-9]{2}.[0-9]' if model in ['ehci']: exp_addr_patt = r'0[1-9]:[0-9]{2}.[0-9]' if addr_str: exp_addr_patt = addr_str if 'ccw' == cntlr_bus: exp_addr_patt = r'254:\d+\.\d+' if not re.match(exp_addr_patt, addr_str): test.fail('Expect get controller address "%s", ' 'but got "%s"' % (exp_addr_patt, addr_str)) def check_qemu_cmdline(search_pattern=None): """ Check domain qemu command line against expectation. :param search_pattern: search list with tuple objects """ with open('/proc/%s/cmdline' % vm.get_pid()) as proc_file: cmdline = proc_file.read() options = cmdline.split('\x00') logging.debug(options) # Search the command line options for the given patterns if search_pattern and isinstance(search_pattern, list): for pattern in search_pattern: key = pattern[0] value = pattern[1] logging.debug("key=%s, value=%s", key, value) found = False check_value = False for opt in options: if check_value: if re.findall(value, opt): logging.debug("Found the expected (%s %s) in qemu " "command line" % (key, value)) found = True break check_value = False if key == opt: check_value = True if not found: test.fail("Can not find '%s %s' in qemu " "command line" % (key, value)) # Get pcihole options from qemu command line pcihole_opt = '' for idx, opt in enumerate(options): if 'pci-hole64-size' in opt: pcihole_opt = opt # Get expected pcihole options from params exp_pcihole_opt = '' if (cntlr_type == 'pci' and model in ['pci-root', 'pcie-root'] and pcihole): if 'pc' in cur_machine: exp_pcihole_opt = 'i440FX-pcihost' elif 'q35' in cur_machine: exp_pcihole_opt = 'q35-pcihost' exp_pcihole_opt += '.pci-hole64-size=%sK' % pcihole # Check options against expectation if pcihole_opt != exp_pcihole_opt: test.fail('Expect get qemu command serial option "%s", ' 'but got "%s"' % (exp_pcihole_opt, pcihole_opt)) # Check usb options against expectation if cntlr_type == "usb": pattern = "" if cmpnn_cntlr_num is not None: for num in range(int(cmpnn_cntlr_num)): name = (cmpnn_cntlr_model+str(num+1)).split('-') pattern = pattern + r"-device.%s-usb-%s.*" % (name[0], name[1]) elif model == "ehci": pattern = r"-device.usb-ehci" elif model == "qemu-xhci": pattern = r"-device.qemu-xhci" logging.debug("pattern is %s", pattern) if pattern and not re.search(pattern, cmdline): test.fail("Expect get usb model info in qemu cmdline, but failed!") def check_guest(cntlr_type, cntlr_model, cntlr_index=None, cntlr_bus=""): """ Check status within the guest against expectation. :param cntlr_type: //controller@type, e.g. ide :param cntlr_model: //controller@model, e.g. virtio-scsi :param cntlr_index: //controller@index, e.g. '0' :param cntlr_bus: //controller/address@type, e.g. pci :raise avocado.core.exceptions.TestFail: Fails the test if checks fail :raise avocado.core.exceptions.TestError: Fails if test couldn't be fully executed :return: None """ if model == 'pci-root' or model == 'pcie-root': return (addr_str, _, _, _, _) = get_controller_addr(cntlr_type=cntlr_type, model=cntlr_model, index=cntlr_index, cntlr_bus=cntlr_bus) if 'ccw' == cntlr_bus: check_ccw_bus_type(addr_str) else: check_pci_bus_type(addr_str, cntlr_index, cntlr_model, cntlr_type) def check_ccw_bus_type(addr_str): """ Uses lszdev to check for device info in guest. :param addr_str: Device address from libvirt :raise avocado.core.exceptions.TestFail: Fails the test if unexpected test values :raise avocado.core.exceptions.TestError: Fails if can't query dev info in guest :return: None """ session = vm.wait_for_login(serial=True) cmd = 'lszdev generic-ccw --columns ID' status, output = session.cmd_status_output(cmd) logging.debug("lszdev output is: %s", output) if status: test.error("Failed to get guest device info, check logs.") devno = int(addr_str.split('.')[-1]) devno_str = hex(devno).replace('0x', '').zfill(4) if devno_str not in output: test.fail("Can't find device with number %s in guest. Searched for %s in %s" % (devno, devno_str, output)) def check_pci_bus_type(addr_str, cntlr_index, cntlr_model, cntlr_type): """ Uses lspci to check for device info in guest. :param addr_str: Device address from libvirt :param cntlr_index: controller index :param cntlr_model: controller model :param cntlr_type: controller type :raise avocado.core.exceptions.TestError: Fails if device info not found :raise avocado.core.exceptions.TestFail: Fails if unexcepted test values :return: None """ pci_name = 'PCI bridge:' verbose_option = "" if cntlr_type == 'virtio-serial': verbose_option = '-vvv' if (addr_str is None and model != 'pci-root' and model != 'pcie-root'): test.error("Can't find target controller in XML") if cntlr_index: logging.debug("%s, %s, %s", cntlr_type, cntlr_model, cntlr_index) if (addr_str is None and cntlr_model != 'pci-root' and cntlr_model != 'pcie-root'): test.error("Can't find target controller in XML") session = vm.wait_for_login(serial=True) status, output = session.cmd_status_output('lspci %s -s %s' % (verbose_option, addr_str)) logging.debug("lspci output is: %s", output) if (cntlr_type == 'virtio-serial' and (vectors and int(vectors) == 0)): if 'MSI' in output: test.fail("Expect MSI disable with zero vectors, " "but got %s" % output) if (cntlr_type == 'virtio-serial' and (vectors is None or int(vectors) != 0)): if 'MSI' not in output: test.fail("Expect MSI enable with non-zero vectors, " "but got %s" % output) if (cntlr_type == 'pci'): if pci_name not in output: test.fail("Can't find target pci device" " '%s' on guest " % addr_str) def check_guest_by_pattern(patterns): """ Search the command output with specified patterns :param patterns: patterns to search in guest. Type: str or list """ logging.debug("Search pattern:{}".format(patterns)) session = vm.wait_for_login(serial=True) libvirt.check_cmd_output('lspci', eval(patterns), session=session) session.close() def check_cntrl(vm_xml, cntlr_type, cntlr_model, cntlr_index, check_dict, qemu_pattern): """ Check the controller or get the controller's search patterns. Currently check_dict and qemu_pattern are not supported to be used at same time. :param vm_xml, the guest VMXML instance :param cntlr_type, the controller type :param cntlr_model, the controller's model :param cntlr_index, the controller's index :param check_dict, the dict for checking in the controller :param qemu_pattern: True if it needs to be checked with qemu command line. False if not. :return Tuple (Controller, List) if qemu_pattern Controller: the controller found. List: a list including qemu search patterns :return None if check_dict :raise test.fail if the model name is not expected :raise test.error if the controller is not found """ qemu_list = None for elem in vm_xml.devices.by_device_tag('controller'): if (cntlr_type == elem.type and cntlr_model == elem.model): if cntlr_index and cntlr_index != elem.index: continue if qemu_pattern: if cntlr_model not in ['pci-root', 'pcie-root']: qemu_list = prepare_qemu_pattern(elem) return (elem, qemu_list) if check_dict: logging.debug("Checking list {}".format(check_dict)) if ('modelname' in check_dict and elem.model_name['name'] != check_dict['modelname']): test.fail("Can't find the expected model name {} " "with (type:{}, model:{}, index:{}), " "found {}".format(check_dict['modelname'], cntlr_type, cntlr_model, cntlr_index, elem.model_name['name'])) if ('busNr' in check_dict and elem.target['busNr'] != check_dict['busNr']): test.fail("Can't find the expected busNr {} " "with (type:{}, model:{}, index:{}), " "found {}".format(check_dict['busNr'], cntlr_type, cntlr_model, cntlr_index, elem.target['busNr'])) else: logging.debug("Check controller successfully") return test.error("Can't find the specified controller with " "(type:{}, model:{}, index:{})".format(cntlr_type, cntlr_model, cntlr_index)) def detach_device(vm_name): """ Detach a device from the given guest :param vm_name: The guest name :return: None """ attach_dev_type = params.get("attach_dev_type", 'disk') detach_option = params.get("detach_option") if attach_dev_type == 'interface': ret = virsh.detach_interface(vm_name, detach_option, **virsh_dargs) else: logging.debug("No need to detach any device.") def attach_device(vm_name): """ Attach devices to the guest for some times :param vm_name: The guest name :return: None """ attach_count = params.get("attach_count", '1') attach_dev_type = params.get("attach_dev_type", 'disk') attach_option = params.get("attach_option") if attach_option.count('--address '): index_str = "%02x" % int(auto_indexes_dict['pcie-root-port'][0]) attach_option = attach_option % index_str for count in range(0, int(attach_count)): if attach_dev_type == 'disk': file_path = tempfile.mktemp(dir=data_dir.get_tmp_dir()) libvirt.create_local_disk('file', file_path, size='1') ret = virsh.attach_disk(vm_name, file_path, params.get('dev_target', 'vdb'), extra=attach_option, **virsh_dargs) elif attach_dev_type == 'interface': ret = virsh.attach_interface(vm_name, attach_option, **virsh_dargs) else: logging.debug("No need to attach any device.") break def check_detach_attach_result(vm_name, cmd, pattern, expect_output, option='--hmp'): """ Check the attach/detach result by qemu_monitor_command. :param vm_name: guest name :param cmd: the command for qemu_monitor_command :param pattern: regular expr used to search the output of qemu_monitor_command :param expect_output: the expected output for qemu_monitor_command :param option: option for qemu_monitor_command :raise test.fail if the pattern is not matched :return: the qemu_monitor_command output """ ret = virsh.qemu_monitor_command(vm_name, cmd, option) libvirt.check_result(ret) if pattern and expect_output: if not re.findall(pattern, ret.stdout.strip()): test.fail("Can't find the pattern '{}' in " "qemu monitor command " "output'{}'".format(pattern, ret.stdout.strip())) else: return expect_output == ret.stdout.strip() def check_guest_by_cmd(cmds, expect_error=False): """ Execute the command within guest and check status :param cmds: Str or List, The command executed in guest :param expect_error: True if the command is expected to fail :return: None :raise test.fail if command status is not as expected """ def _check_cmd_result(cmd): logging.debug("Command in guest gets result: %s", output) if status and not expect_error: test.fail("Command '{}' fails in guest with status " "'{}'".format(cmd, status)) elif status and expect_error: logging.debug("Command '{}' fails in guest as " "expected".format(cmd)) elif not status and not expect_error: logging.debug("Check guest by command successfully") else: test.fail("Check guest by command successfully, " "but expect failure") logging.debug("Execute command '{}' in guest".format(cmds)) session = vm.wait_for_login(serial=True) (status, output) = (None, None) if isinstance(cmds, str): status, output = session.cmd_status_output(cmds) _check_cmd_result(cmds) elif isinstance(cmds, list): for cmd in cmds: if isinstance(cmd, str): status, output = session.cmd_status_output(cmd) _check_cmd_result(cmd) elif isinstance(cmd, dict): for cmd_key in cmd.keys(): status, output = session.cmd_status_output(cmd_key) if output.strip() != cmd[cmd_key]: test.fail("Command '{}' does not get " "expect result {}, but found " "{}".format(cmd_key, cmd[cmd_key], output.strip())) def get_device_bus(vm_xml, device_type): """ Get the bus that the devices are attached to. :param vm_xml: Guest xml :param device_type: The type of device, like disk, interface :return a list includes buses the devices attached to """ devices = vm_xml.get_devices(device_type=device_type) bus_list = [] for device in devices: logging.debug("device:{}".format(device)) bus = device.address.attrs['bus'] logging.debug("This device's bus:{}".format(bus)) bus_list.append(bus) return bus_list def add_device_xml(vm_xml, device_type, device_cfg_dict): """ Add a device xml to the existing vm xml :param vm_xml: the existing vm xml object :param device_type: type of device to be added :param device_cfg_dict: the configuration of the device :return: None """ vm_xml.remove_all_device_by_type(device_type) dev_obj = vm_xml.get_device_class(device_type)() dev_cfg = eval(device_cfg_dict) if device_type == 'sound': dev_obj.model_type = dev_cfg.get("model") elif device_type == 'rng': dev_obj.rng_model = dev_cfg.get("model") rng_backend = dev_obj.Backend() rng_backend.backend_model = "random" dev_obj.backend = rng_backend elif device_type == 'memballoon': dev_obj.model = dev_cfg.get("model") if 'bus' in dev_cfg: addr_dict = {'bus': dev_cfg.get("bus"), 'type': dev_cfg.get("type", "pci"), 'slot': dev_cfg.get("slot", "0x00")} if device_type == 'rng': dev_obj.address = dev_obj\ .new_rng_address(**{"attrs": addr_dict}) else: dev_obj.address = addr_dict vm_xml.add_device(dev_obj) def check_multifunction(): """ Check if multifunction is found in vm xml for specified controller :raise: test.fail if multifunction is not as expected """ (_, _, _, _, multi_func) = get_controller_addr(cntlr_type, model, '0') if not multi_func or multi_func != 'on': test.fail("Can't find multifunction=on in certain " "controller(type:{}, model:{}, " "index:{})".format(cntlr_type, model, 0)) os_machine = params.get('machine_type', None) libvirt.check_machine_type_arch(os_machine) cntlr_type = params.get('controller_type', None) model = params.get('controller_model', None) index = params.get('controller_index', None) vectors = params.get('controller_vectors', None) pcihole = params.get('controller_pcihole64', None) chassisNr = params.get('chassisNr', None) addr_str = params.get('controller_address', None) cmpnn_cntlr_model = params.get('companion_controller_model', None) cmpnn_cntlr_num = params.get('companion_controller_num', None) vm_name = params.get("main_vm", "avocado-vt-vm1") no_pci_controller = params.get("no_pci_controller", "no") pci_bus_number = params.get("pci_bus_number", "0") remove_address = "yes" == params.get("remove_address", "yes") remove_contr = "yes" == params.get("remove_contr", "yes") setup_controller = params.get("setup_controller", "yes") index_second = params.get("controller_index_second", None) cntlr_bus = params.get('controller_bus') cur_machine = os_machine check_qemu = "yes" == params.get("check_qemu", "no") check_within_guest = "yes" == params.get("check_within_guest", "no") run_vm = "yes" == params.get("run_vm", "no") second_level_controller_num = params.get("second_level_controller_num", "0") check_contr_addr = "yes" == params.get("check_contr_addr", "yes") qemu_patterns = params.get("qemu_patterns") status_error = "yes" == params.get("status_error", "no") model_name = params.get("model_name", None) expect_err_msg = params.get("err_msg", None) new_pcie_root_port_model = params.get("new_model") old_pcie_root_port_model = params.get("old_model") add_contrl_list = params.get("add_contrl_list") auto_bus = "yes" == params.get("auto_bus", "no") check_cntrls_list = params.get("check_cntrls_list") sound_dict = params.get("sound_dict") balloon_dict = params.get("balloon_dict") rng_dict = params.get("rng_dict") guest_patterns = params.get("guest_patterns") attach_option = params.get("attach_option") detach_option = params.get("detach_option") attach_dev_type = params.get("attach_dev_type", 'disk') remove_nic = "yes" == params.get("remove_nic", 'no') qemu_monitor_cmd = params.get("qemu_monitor_cmd") cmd_in_guest = params.get("cmd_in_guest") check_dev_bus = "yes" == params.get("check_dev_bus", "no") cpu_numa_cells = params.get("cpu_numa_cells") virsh_dargs = {'ignore_status': False, 'debug': True} auto_indexes_dict = {} auto_index = params.get('auto_index', 'no') == 'yes' auto_slot = params.get('auto_slot', 'no') == 'yes' libvirt_version.is_libvirt_feature_supported(params) if index and index_second: if int(index) > int(index_second): test.error("Invalid parameters") vm = env.get_vm(vm_name) vm_xml = VMXML.new_from_inactive_dumpxml(vm_name) vm_xml_backup = vm_xml.copy() try: if remove_contr: vm_xml.remove_all_device_by_type('controller') if remove_address: remove_devices(vm_xml, 'address') remove_devices(vm_xml, 'usb') if remove_nic: remove_devices(vm_xml, 'interface') # Get the max controller index in current vm xml the_model = 'pci-root' if 'ppc' in platform.machine() else 'pcie-root-port' if add_contrl_list: ret_indexes = libvirt_pcicontr.get_max_contr_indexes(vm_xml, 'pci', the_model) if ret_indexes and len(ret_indexes) > 0: if auto_bus: new_index = "0x%02x" % (int(ret_indexes[0]) + 1) add_contrl_list = re.sub(r"'bus': '%s'", "'bus': '%s'" % new_index, add_contrl_list, count=5) logging.debug("bus is set automatically with %s", new_index) if auto_slot: available_slot = libvirt_pcicontr.get_free_pci_slot(vm_xml) if not available_slot: test.error("No pci slot is available any more. Please check your vm xml.") add_contrl_list = re.sub(r"'slot': '%s'", "'slot': '%s'" % available_slot, add_contrl_list, count=5) logging.debug("slot is set automatically with %s", available_slot) if auto_index: new_index = int(ret_indexes[0]) + 1 add_contrl_list = re.sub(r"'index': '%s'", "'index': '%s'" % new_index, add_contrl_list, count=5) logging.debug("index is set automatically with %s", new_index) logging.debug("Now add_contrl_list=%s", add_contrl_list) if setup_controller == "yes": if add_contrl_list: contrls = eval(add_contrl_list) for one_contrl in contrls: contr_dict = {} cntl_target = '' if 'model' in one_contrl: contr_dict.update({'controller_model': one_contrl['model']}) if 'busNr' in one_contrl: cntl_target = "{'busNr': %s}" % one_contrl['busNr'] if 'chassisNr' in one_contrl: cntl_target += "{'chassisNr': '%s'}" % one_contrl['chassisNr'] if 'alias' in one_contrl: contr_dict.update({'contr_alias': one_contrl['alias']}) if 'type' in one_contrl: contr_dict.update({'controller_type': one_contrl['type']}) else: contr_dict.update({'controller_type': 'pci'}) if 'node' in one_contrl: contr_dict.update({'controller_node': one_contrl['node']}) if 'index' in one_contrl: contr_dict.update({'controller_index': one_contrl['index']}) contr_dict.update({'controller_target': cntl_target}) addr = None if 'bus' in one_contrl: addr = {'bus': one_contrl['bus']} if 'slot' in one_contrl: addr.update({'slot': one_contrl['slot']}) if 'func' in one_contrl: addr.update({'function': one_contrl['func']}) if addr: contr_dict.update({'controller_addr': str(addr)}) logging.debug(contr_dict) controller_add = libvirt.create_controller_xml(contr_dict) vm_xml.add_device(controller_add) logging.debug("Add a controller: %s" % controller_add) else: if index_second: setup_controller_xml(index_second) setup_controller_xml(index, addr_str) if second_level_controller_num: for indx in range(2, int(second_level_controller_num) + 2): addr_second = "0%s:0%s.0" % (index, str(indx)) setup_controller_xml(str(indx), addr_second) setup_os_xml() if int(pci_bus_number) > 0: address_params = {'bus': "%0#4x" % int(pci_bus_number), 'slot': "%0#4x" % int(pci_bus_number)} libvirt.set_disk_attr(vm_xml, 'vda', 'address', address_params) if cpu_numa_cells: if not vm_xml.cpu: vmxml_cpu = VMCPUXML() vmxml_cpu.xml = "<cpu mode='host-model'><numa/></cpu>" else: vmxml_cpu = vm_xml.cpu logging.debug("Existing cpu configuration in guest xml:\n%s", vmxml_cpu) vmxml_cpu.mode = 'host-model' if platform.machine() == 'aarch64': vmxml_cpu.mode = 'host-passthrough' vmxml_cpu.remove_elem_by_xpath('/model') vmxml_cpu.remove_elem_by_xpath('/numa') vmxml_cpu.numa_cell = VMCPUXML.dicts_to_cells(eval(cpu_numa_cells)) vm_xml.cpu = vmxml_cpu vm_xml.vcpu = int(params.get('vcpu_count', 4)) if sound_dict: add_device_xml(vm_xml, 'sound', sound_dict) if rng_dict: add_device_xml(vm_xml, 'rng', rng_dict) if balloon_dict: add_device_xml(vm_xml, 'memballoon', balloon_dict) logging.debug("Test VM XML before define is %s" % vm_xml) if not define_and_check(vm_xml): logging.debug("Can't define the VM, exiting.") return vm_xml = VMXML.new_from_dumpxml(vm_name) logging.debug("Test VM XML after define is %s" % vm_xml) if auto_index: contrls = eval(add_contrl_list) for one_contrl in contrls: ret_indexes = libvirt_pcicontr.get_max_contr_indexes(vm_xml, one_contrl.get('type', 'pci'), one_contrl.get('model')) auto_indexes_dict.update({one_contrl['model']: ret_indexes}) if check_contr_addr: check_controller_addr(cntlr_bus) if new_pcie_root_port_model and old_pcie_root_port_model: if utils_misc.compare_qemu_version(2, 9, 0, False): expect_model = new_pcie_root_port_model else: expect_model = old_pcie_root_port_model logging.debug("Expect the model for 'pcie-root-port': " "%s" % expect_model) check_dict = {'modelname': expect_model} check_cntrl(vm_xml, 'pci', 'pcie-root-port', '2', check_dict, False) if check_cntrls_list: for check_one in eval(check_cntrls_list): logging.debug("The controller to be checked: {}".format(check_one)) check_cntrl(vm_xml, check_one.get('type', 'pci'), check_one.get('model'), check_one.get('index'), check_one, False) if run_vm: try: if not start_and_check(): logging.debug("Can't start the VM, exiting.") return except virt_vm.VMStartError as detail: test.fail(detail) # Need coldplug/hotplug if attach_option: attach_device(vm_name) vm_xml = VMXML.new_from_dumpxml(vm_name) logging.debug("Guest xml after attaching device:{}".format(vm_xml)) # Check device's bus if needed if check_dev_bus: buses = get_device_bus(vm_xml, attach_dev_type) if len(buses) == 0: test.fail("No bus was found") if buses[0] != params.get("expect_bus"): test.fail("The expected bus for device is {}, " "but found {}".format(params.get("expect_bus"), buses[0])) if qemu_monitor_cmd: check_detach_attach_result(vm_name, qemu_monitor_cmd, params.get("qemu_monitor_pattern"), None) # Check guest xml if attach_dev_type == 'interface' and 'e1000e' in attach_option: cntls = vm_xml.get_controllers(controller_type='pci', model='pcie-root-port') cntl_index_list = [] for cntl in cntls: cntl_index_list.append(cntl.get('index')) logging.debug("All pcie-root-port controllers' " "index: {}".format(cntl_index_list)) bus_list = get_device_bus(vm_xml, "interface") for bus in bus_list: if str(int(bus, 16)) not in cntl_index_list: test.fail("The attached NIC with bus '{}' is not attached " "to any pcie-root-port by default".format(bus)) if check_qemu: if qemu_patterns: if auto_index: index_str = "%x" % int(auto_indexes_dict['pcie-root-port'][0]) qemu_patterns = qemu_patterns % index_str logging.debug("qemu_patterns=%s", qemu_patterns) if qemu_patterns.count('multifunction=on'): check_multifunction() search_qemu_cmd = eval(qemu_patterns) logging.debug(search_qemu_cmd) else: search_qemu_cmd = get_search_patt_qemu_line() check_qemu_cmdline(search_pattern=search_qemu_cmd) vm.wait_for_login().close() if check_within_guest: try: if int(pci_bus_number) > 0: for contr_idx in range(1, int(pci_bus_number) + 1): check_guest(cntlr_type, model, str(contr_idx)) return if index: check_max_index = int(index) + int(second_level_controller_num) for contr_idx in range(1, int(check_max_index) + 1): check_guest(cntlr_type, model, str(contr_idx)) elif guest_patterns: check_guest_by_pattern(guest_patterns) elif cmd_in_guest: check_guest_by_cmd(eval(cmd_in_guest)) else: check_guest(cntlr_type, model, cntlr_bus=cntlr_bus) if model == 'pcie-root': # Need check other auto added controller check_guest(cntlr_type, 'dmi-to-pci-bridge', '1') check_guest(cntlr_type, 'pci-bridge', '2') except remote.LoginTimeoutError as e: logging.debug(e) if not status_error: raise # Need hotunplug if detach_option: detach_device(vm_name) if qemu_monitor_cmd: check_detach_attach_result(vm_name, qemu_monitor_cmd, params.get("qemu_monitor_pattern"), "") if cmd_in_guest: check_guest_by_cmd(eval(cmd_in_guest), expect_error=True) finally: vm_xml_backup.sync()
def run(test, params, env): """ Test virsh {at|de}tach-disk command. The command can attach new disk/detach disk. 1.Prepare test environment,destroy or suspend a VM. 2.Perform virsh attach/detach-disk operation. 3.Recover test environment. 4.Confirm the test result. """ def check_vm_partition(vm, device, os_type, target_name, old_parts): """ Check VM disk's partition. :param vm. VM guest. :param os_type. VM's operation system type. :param target_name. Device target type. :return: True if check successfully. """ logging.info("Checking VM partittion...") if vm.is_dead(): vm.start() try: attached = False if os_type == "linux": session = vm.wait_for_login() new_parts = utils_disk.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.debug("Added parts: %s" % added_parts) for i in range(len(added_parts)): if device == "disk": if target_name.startswith("vd"): if added_parts[i].startswith("vd"): attached = True elif target_name.startswith( "hd") or target_name.startswith("sd"): if added_parts[i].startswith("sd"): attached = True elif device == "cdrom": if added_parts[i].startswith("sr"): attached = True session.close() return attached except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def acpiphp_module_modprobe(vm, os_type): """ Add acpiphp module if VM's os type is rhle5.* :param vm. VM guest. :param os_type. VM's operation system type. :return: True if operate successfully. """ if vm.is_dead(): vm.start() try: if os_type == "linux": session = vm.wait_for_login() s_rpm, _ = session.cmd_status_output("rpm --version") # If status is different from 0, this # guest OS doesn't support the rpm package # manager if s_rpm: session.close() return True _, o_vd = session.cmd_status_output( "rpm -qa | grep redhat-release") if o_vd.find("5Server") != -1: s_mod, o_mod = session.cmd_status_output( "modprobe acpiphp") del o_mod if s_mod != 0: session.close() return False session.close() return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_shareable(at_with_shareable, test_twice): """ check if current libvirt version support shareable option at_with_shareable: True or False. Whether attach disk with shareable option test_twice: True or False. Whether perform operations twice return: True or cancel the test """ if at_with_shareable or test_twice: if libvirt_version.version_compare(3, 9, 0): return True else: test.cancel( "Current libvirt version doesn't support shareable feature" ) # Get test command. test_cmd = params.get("at_dt_disk_test_cmd", "attach-disk") vm_ref = params.get("at_dt_disk_vm_ref", "name") at_options = params.get("at_dt_disk_at_options", "") dt_options = params.get("at_dt_disk_dt_options", "") at_with_shareable = "yes" == params.get("at_with_shareable", 'no') pre_vm_state = params.get("at_dt_disk_pre_vm_state", "running") status_error = "yes" == params.get("status_error", 'no') no_attach = params.get("at_dt_disk_no_attach", 'no') os_type = params.get("os_type", "linux") qemu_file_lock = params.get("qemu_file_lock", "") if qemu_file_lock: if utils_misc.compare_qemu_version(2, 9, 0): logging.info('From qemu-kvm-rhev 2.9.0:' 'QEMU image locking, which should prevent multiple ' 'runs of QEMU or qemu-img when a VM is running.') if test_cmd == "detach-disk" or pre_vm_state == "shut off": test.cancel('This case is not supported.') else: logging.info( 'The expect result is failure as opposed with succeed') status_error = True # Disk specific attributes. device = params.get("at_dt_disk_device", "disk") device_source_name = params.get("at_dt_disk_device_source", "attach.img") device_source_format = params.get("at_dt_disk_device_source_format", "raw") device_target = params.get("at_dt_disk_device_target", "vdd") device_disk_bus = params.get("at_dt_disk_bus_type", "virtio") source_path = "yes" == params.get("at_dt_disk_device_source_path", "yes") create_img = "yes" == params.get("at_dt_disk_create_image", "yes") test_twice = "yes" == params.get("at_dt_disk_test_twice", "no") test_type = "yes" == params.get("at_dt_disk_check_type", "no") test_audit = "yes" == params.get("at_dt_disk_check_audit", "no") test_block_dev = "yes" == params.get("at_dt_disk_iscsi_device", "no") test_logcial_dev = "yes" == params.get("at_dt_disk_logical_device", "no") restart_libvirtd = "yes" == params.get("at_dt_disk_restart_libvirtd", "no") detach_disk_with_print_xml = "yes" == params.get( "detach_disk_with_print_xml", "no") vg_name = params.get("at_dt_disk_vg", "vg_test_0") lv_name = params.get("at_dt_disk_lv", "lv_test_0") serial = params.get("at_dt_disk_serial", "") address = params.get("at_dt_disk_address", "") address2 = params.get("at_dt_disk_address2", "") cache_options = params.get("cache_options", "") time_sleep = params.get("time_sleep", 3) if check_shareable(at_with_shareable, test_twice): at_options += " --mode shareable" if serial: at_options += (" --serial %s" % serial) if address2: at_options_twice = at_options + (" --address %s" % address2) if address: at_options += (" --address %s" % address) if cache_options: if cache_options.count("directsync"): if not libvirt_version.version_compare(1, 0, 0): test.cancel("'directsync' cache option doesn't " "support in current libvirt version.") at_options += (" --cache %s" % cache_options) vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Start vm and get all partions in vm. if vm.is_dead(): vm.start() session = vm.wait_for_login() old_parts = utils_disk.get_parts_list(session) session.close() vm.destroy(gracefully=False) # Back up xml file. backup_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Create virtual device file. device_source_path = os.path.join(data_dir.get_tmp_dir(), device_source_name) if test_block_dev: device_source = libvirt.setup_or_cleanup_iscsi(True) if not device_source: # We should skip this case test.cancel("Can not get iscsi device name in host") if test_logcial_dev: lv_utils.vg_create(vg_name, device_source) device_source = libvirt.create_local_disk("lvm", size="10M", vgname=vg_name, lvname=lv_name) logging.debug("New created volume: %s", lv_name) else: if source_path and create_img: device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1G", disk_format=device_source_format) else: device_source = device_source_name # if we are testing audit, we need to start audit servcie first. if test_audit: auditd_service = Factory.create_service("auditd") if not auditd_service.status(): auditd_service.start() logging.info("Auditd service status: %s" % auditd_service.status()) # If we are testing cdrom device, we need to detach hdc in VM first. if device == "cdrom": if vm.is_alive(): vm.destroy(gracefully=False) s_detach = virsh.detach_disk(vm_name, device_target, "--config") if not s_detach: logging.error("Detach hdc failed before test.") # If we are testing detach-disk, we need to attach certain device first. if test_cmd == "detach-disk" and no_attach != "yes": s_at_options = "--driver qemu --config" #Since lock feature is introduced in libvirt 3.9.0 afterwards, disk shareable options #need be set if disk needs be attached multitimes if check_shareable(at_with_shareable, test_twice): s_at_options += " --mode shareable" s_attach = virsh.attach_disk(vm_name, device_source, device_target, s_at_options, debug=True).exit_status if s_attach != 0: logging.error("Attaching device failed before testing detach-disk") else: logging.debug( "Attaching device succeeded before testing detach-disk") if test_twice: device_target2 = params.get("at_dt_disk_device_target2", device_target) device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1", disk_format=device_source_format) s_attach = virsh.attach_disk(vm_name, device_source, device_target2, s_at_options).exit_status if s_attach != 0: logging.error("Attaching device failed before testing " "detach-disk test_twice") vm.start() vm.wait_for_login() # Add acpiphp module before testing if VM's os type is rhle5.* if not acpiphp_module_modprobe(vm, os_type): test.error("Add acpiphp module failed before test.") # Turn VM into certain state. if pre_vm_state == "paused": logging.info("Suspending %s..." % vm_name) if vm.is_alive(): vm.pause() elif pre_vm_state == "shut off": logging.info("Shuting down %s..." % vm_name) if vm.is_alive(): vm.destroy(gracefully=False) # Get disk count before test. disk_count_before_cmd = vm_xml.VMXML.get_disk_count(vm_name) # Test. domid = vm.get_id() domuuid = vm.get_uuid() # Confirm how to reference a VM. if vm_ref == "name": vm_ref = vm_name elif vm_ref.find("invalid") != -1: vm_ref = params.get(vm_ref) elif vm_ref == "id": vm_ref = domid elif vm_ref == "hex_id": vm_ref = hex(int(domid)) elif vm_ref == "uuid": vm_ref = domuuid else: vm_ref = "" if test_cmd == "attach-disk": status = virsh.attach_disk(vm_ref, device_source, device_target, at_options, debug=True).exit_status elif test_cmd == "detach-disk": # For detach disk with print-xml option, it only print information,and not actual disk detachment. if detach_disk_with_print_xml and libvirt_version.version_compare( 4, 5, 0): ret = virsh.detach_disk(vm_ref, device_target, at_options) libvirt.check_exit_status(ret) cmd = ("echo \"%s\" | grep -A 16 %s" % (ret.stdout.strip(), device_source_name)) if process.system(cmd, ignore_status=True, shell=True): test.error("Check disk with source image name failed") status = virsh.detach_disk(vm_ref, device_target, dt_options, debug=True).exit_status if restart_libvirtd: libvirtd_serv = utils_libvirtd.Libvirtd() libvirtd_serv.restart() if test_twice: device_target2 = params.get("at_dt_disk_device_target2", device_target) device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1G", disk_format=device_source_format) if test_cmd == "attach-disk": if address2: at_options = at_options_twice status = virsh.attach_disk(vm_ref, device_source, device_target2, at_options, debug=True).exit_status elif test_cmd == "detach-disk": status = virsh.detach_disk(vm_ref, device_target2, dt_options, debug=True).exit_status # Resume guest after command. On newer libvirt this is fixed as it has # been a bug. The change in xml file is done after the guest is resumed. if pre_vm_state == "paused": vm.resume() time.sleep(5) # Check audit log check_audit_after_cmd = True if test_audit: grep_audit = ('grep "%s" /var/log/audit/audit.log' % test_cmd.split("-")[0]) cmd = (grep_audit + ' | ' + 'grep "%s" | tail -n1 | grep "res=success"' % device_source) if process.run(cmd, shell=True).exit_status: logging.error("Audit check failed") check_audit_after_cmd = False # Need wait a while for xml to sync time.sleep(float(time_sleep)) # Check disk count after command. check_count_after_cmd = True disk_count_after_cmd = vm_xml.VMXML.get_disk_count(vm_name) if test_cmd == "attach-disk": if disk_count_after_cmd == disk_count_before_cmd: check_count_after_cmd = False elif test_cmd == "detach-disk": if disk_count_after_cmd < disk_count_before_cmd: check_count_after_cmd = False # Recover VM state. if pre_vm_state == "shut off": vm.start() # Check in VM after command. check_vm_after_cmd = True check_vm_after_cmd = check_vm_partition(vm, device, os_type, device_target, old_parts) # Check disk type after attach. check_disk_type = True if test_type: if test_block_dev: check_disk_type = vm_xml.VMXML.check_disk_type( vm_name, device_source, "block") else: check_disk_type = vm_xml.VMXML.check_disk_type( vm_name, device_source, "file") # Check disk serial after attach. check_disk_serial = True if serial: disk_serial = vm_xml.VMXML.get_disk_serial(vm_name, device_target) if serial != disk_serial: check_disk_serial = False # Check disk address after attach. check_disk_address = True if address: disk_address = vm_xml.VMXML.get_disk_address(vm_name, device_target) if address != disk_address: check_disk_address = False # Check multifunction address after attach. check_disk_address2 = True if address2: disk_address2 = vm_xml.VMXML.get_disk_address(vm_name, device_target2) if address2 != disk_address2: check_disk_address2 = False # Check disk cache option after attach. check_cache_after_cmd = True if cache_options: disk_cache = vm_xml.VMXML.get_disk_attr(vm_name, device_target, "driver", "cache") if cache_options == "default": if disk_cache is not None: check_cache_after_cmd = False elif disk_cache != cache_options: check_cache_after_cmd = False # Eject cdrom test eject_cdrom = "yes" == params.get("at_dt_disk_eject_cdrom", "no") save_vm = "yes" == params.get("at_dt_disk_save_vm", "no") save_file = os.path.join(data_dir.get_tmp_dir(), "vm.save") try: if eject_cdrom: eject_params = { 'type_name': "file", 'device_type': "cdrom", 'target_dev': device_target, 'target_bus': device_disk_bus } eject_xml = libvirt.create_disk_xml(eject_params) with open(eject_xml) as eject_file: logging.debug("Eject CDROM by XML: %s", eject_file.read()) # Run command tiwce to make sure cdrom tray open first #BZ892289 # Open tray virsh.attach_device(domainarg=vm_name, filearg=eject_xml, debug=True) # Add time sleep between two attach commands. if time_sleep: time.sleep(float(time_sleep)) # Eject cdrom result = virsh.attach_device(domainarg=vm_name, filearg=eject_xml, debug=True) if result.exit_status != 0: test.fail("Eject CDROM failed") if vm_xml.VMXML.check_disk_exist(vm_name, device_source): test.fail("Find %s after do eject" % device_source) # Save and restore VM if save_vm: result = virsh.save(vm_name, save_file, debug=True) libvirt.check_exit_status(result) result = virsh.restore(save_file, debug=True) libvirt.check_exit_status(result) if vm_xml.VMXML.check_disk_exist(vm_name, device_source): test.fail("Find %s after do restore" % device_source) # Destroy VM. vm.destroy(gracefully=False) # Check disk count after VM shutdown (with --config). check_count_after_shutdown = True inactive_vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) disk_count_after_shutdown = len(inactive_vmxml.get_disk_all()) if test_cmd == "attach-disk": if disk_count_after_shutdown == disk_count_before_cmd: check_count_after_shutdown = False elif test_cmd == "detach-disk": if disk_count_after_shutdown < disk_count_before_cmd: check_count_after_shutdown = False finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) logging.debug("Restore the VM XML") backup_xml.sync() if os.path.exists(save_file): os.remove(save_file) if test_block_dev: if test_logcial_dev: libvirt.delete_local_disk("lvm", vgname=vg_name, lvname=lv_name) lv_utils.vg_remove(vg_name) process.run("pvremove %s" % device_source, shell=True, ignore_status=True) libvirt.setup_or_cleanup_iscsi(False) else: libvirt.delete_local_disk("file", device_source) # Check results. if status_error: if not status: test.fail("virsh %s exit with unexpected value." % test_cmd) else: if status: test.fail("virsh %s failed." % test_cmd) if test_cmd == "attach-disk": if at_options.count("config"): if not check_count_after_shutdown: test.fail("Cannot see config attached device " "in xml file after VM shutdown.") if not check_disk_serial: test.fail("Serial set failed after attach") if not check_disk_address: test.fail("Address set failed after attach") if not check_disk_address2: test.fail("Address(multifunction) set failed" " after attach") else: if not check_count_after_cmd: test.fail("Cannot see device in xml file" " after attach.") if not check_vm_after_cmd: test.fail("Cannot see device in VM after" " attach.") if not check_disk_type: test.fail("Check disk type failed after" " attach.") if not check_audit_after_cmd: test.fail("Audit hotplug failure after attach") if not check_cache_after_cmd: test.fail("Check cache failure after attach") if at_options.count("persistent"): if not check_count_after_shutdown: test.fail("Cannot see device attached " "with persistent after " "VM shutdown.") else: if check_count_after_shutdown: test.fail("See non-config attached device " "in xml file after VM shutdown.") elif test_cmd == "detach-disk": if dt_options.count("config"): if check_count_after_shutdown: test.fail("See config detached device in " "xml file after VM shutdown.") else: if check_count_after_cmd: test.fail("See device in xml file " "after detach.") if check_vm_after_cmd: test.fail("See device in VM after detach.") if not check_audit_after_cmd: test.fail("Audit hotunplug failure " "after detach") if dt_options.count("persistent"): if check_count_after_shutdown: test.fail("See device deattached " "with persistent after " "VM shutdown.") else: if not check_count_after_shutdown: test.fail("See non-config detached " "device in xml file after " "VM shutdown.") else: test.error("Unknown command %s." % test_cmd)
def run(test, params, env): """ Test virsh {at|de}tach-disk command. The command can attach new disk/detach disk. 1.Prepare test environment,destroy or suspend a VM. 2.Perform virsh attach/detach-disk operation. 3.Recover test environment. 4.Confirm the test result. """ def check_vm_partition(vm, device, os_type, target_name, old_parts): """ Check VM disk's partition. :param vm. VM guest. :param os_type. VM's operation system type. :param target_name. Device target type. :return: True if check successfully. """ logging.info("Checking VM partittion...") if vm.is_dead(): vm.start() try: attached = False if os_type == "linux": session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.debug("Added parts: %s" % added_parts) for i in range(len(added_parts)): if device == "disk": if target_name.startswith("vd"): if added_parts[i].startswith("vd"): attached = True elif target_name.startswith("hd") or target_name.startswith("sd"): if added_parts[i].startswith("sd"): attached = True elif device == "cdrom": if added_parts[i].startswith("sr"): attached = True session.close() return attached except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def acpiphp_module_modprobe(vm, os_type): """ Add acpiphp module if VM's os type is rhle5.* :param vm. VM guest. :param os_type. VM's operation system type. :return: True if operate successfully. """ if vm.is_dead(): vm.start() try: if os_type == "linux": session = vm.wait_for_login() s_rpm, _ = session.cmd_status_output( "rpm --version") # If status is different from 0, this # guest OS doesn't support the rpm package # manager if s_rpm: session.close() return True _, o_vd = session.cmd_status_output( "rpm -qa | grep redhat-release") if o_vd.find("5Server") != -1: s_mod, o_mod = session.cmd_status_output( "modprobe acpiphp") del o_mod if s_mod != 0: session.close() return False session.close() return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_shareable(at_with_shareable, test_twice): """ check if current libvirt version support shareable option at_with_shareable: True or False. Whether attach disk with shareable option test_twice: True or False. Whether perform operations twice return: True or cancel the test """ if at_with_shareable or test_twice: if libvirt_version.version_compare(3, 9, 0): return True else: test.cancel("Current libvirt version doesn't support shareable feature") # Get test command. test_cmd = params.get("at_dt_disk_test_cmd", "attach-disk") vm_ref = params.get("at_dt_disk_vm_ref", "name") at_options = params.get("at_dt_disk_at_options", "") dt_options = params.get("at_dt_disk_dt_options", "") at_with_shareable = "yes" == params.get("at_with_shareable", 'no') pre_vm_state = params.get("at_dt_disk_pre_vm_state", "running") status_error = "yes" == params.get("status_error", 'no') no_attach = params.get("at_dt_disk_no_attach", 'no') os_type = params.get("os_type", "linux") qemu_file_lock = params.get("qemu_file_lock", "") if qemu_file_lock: if utils_misc.compare_qemu_version(2, 9, 0): logging.info('From qemu-kvm-rhev 2.9.0:' 'QEMU image locking, which should prevent multiple ' 'runs of QEMU or qemu-img when a VM is running.') if test_cmd == "detach-disk" or pre_vm_state == "shut off": test.cancel('This case is not supported.') else: logging.info('The expect result is failure as opposed with succeed') status_error = True # Disk specific attributes. device = params.get("at_dt_disk_device", "disk") device_source_name = params.get("at_dt_disk_device_source", "attach.img") device_source_format = params.get("at_dt_disk_device_source_format", "raw") device_target = params.get("at_dt_disk_device_target", "vdd") device_disk_bus = params.get("at_dt_disk_bus_type", "virtio") source_path = "yes" == params.get("at_dt_disk_device_source_path", "yes") create_img = "yes" == params.get("at_dt_disk_create_image", "yes") test_twice = "yes" == params.get("at_dt_disk_test_twice", "no") test_type = "yes" == params.get("at_dt_disk_check_type", "no") test_audit = "yes" == params.get("at_dt_disk_check_audit", "no") test_block_dev = "yes" == params.get("at_dt_disk_iscsi_device", "no") test_logcial_dev = "yes" == params.get("at_dt_disk_logical_device", "no") restart_libvirtd = "yes" == params.get("at_dt_disk_restart_libvirtd", "no") detach_disk_with_print_xml = "yes" == params.get("detach_disk_with_print_xml", "no") vg_name = params.get("at_dt_disk_vg", "vg_test_0") lv_name = params.get("at_dt_disk_lv", "lv_test_0") serial = params.get("at_dt_disk_serial", "") address = params.get("at_dt_disk_address", "") address2 = params.get("at_dt_disk_address2", "") cache_options = params.get("cache_options", "") time_sleep = params.get("time_sleep", 3) if check_shareable(at_with_shareable, test_twice): at_options += " --mode shareable" if serial: at_options += (" --serial %s" % serial) if address2: at_options_twice = at_options + (" --address %s" % address2) if address: at_options += (" --address %s" % address) if cache_options: if cache_options.count("directsync"): if not libvirt_version.version_compare(1, 0, 0): test.cancel("'directsync' cache option doesn't " "support in current libvirt version.") at_options += (" --cache %s" % cache_options) vm_name = params.get("main_vm") vm = env.get_vm(vm_name) # Start vm and get all partions in vm. if vm.is_dead(): vm.start() session = vm.wait_for_login() old_parts = libvirt.get_parts_list(session) session.close() vm.destroy(gracefully=False) # Back up xml file. backup_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Create virtual device file. device_source_path = os.path.join(data_dir.get_tmp_dir(), device_source_name) if test_block_dev: device_source = libvirt.setup_or_cleanup_iscsi(True) if not device_source: # We should skip this case test.cancel("Can not get iscsi device name in host") if test_logcial_dev: lv_utils.vg_create(vg_name, device_source) device_source = libvirt.create_local_disk("lvm", size="10M", vgname=vg_name, lvname=lv_name) logging.debug("New created volume: %s", lv_name) else: if source_path and create_img: device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1G", disk_format=device_source_format) else: device_source = device_source_name # if we are testing audit, we need to start audit servcie first. if test_audit: auditd_service = Factory.create_service("auditd") if not auditd_service.status(): auditd_service.start() logging.info("Auditd service status: %s" % auditd_service.status()) # If we are testing cdrom device, we need to detach hdc in VM first. if device == "cdrom": if vm.is_alive(): vm.destroy(gracefully=False) s_detach = virsh.detach_disk(vm_name, device_target, "--config") if not s_detach: logging.error("Detach hdc failed before test.") # If we are testing detach-disk, we need to attach certain device first. if test_cmd == "detach-disk" and no_attach != "yes": s_at_options = "--driver qemu --config" #Since lock feature is introduced in libvirt 3.9.0 afterwards, disk shareable options #need be set if disk needs be attached multitimes if check_shareable(at_with_shareable, test_twice): s_at_options += " --mode shareable" s_attach = virsh.attach_disk(vm_name, device_source, device_target, s_at_options, debug=True).exit_status if s_attach != 0: logging.error("Attaching device failed before testing detach-disk") else: logging.debug("Attaching device succeeded before testing detach-disk") if test_twice: device_target2 = params.get("at_dt_disk_device_target2", device_target) device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1", disk_format=device_source_format) s_attach = virsh.attach_disk(vm_name, device_source, device_target2, s_at_options).exit_status if s_attach != 0: logging.error("Attaching device failed before testing " "detach-disk test_twice") vm.start() vm.wait_for_login() # Add acpiphp module before testing if VM's os type is rhle5.* if not acpiphp_module_modprobe(vm, os_type): test.error("Add acpiphp module failed before test.") # Turn VM into certain state. if pre_vm_state == "paused": logging.info("Suspending %s..." % vm_name) if vm.is_alive(): vm.pause() elif pre_vm_state == "shut off": logging.info("Shuting down %s..." % vm_name) if vm.is_alive(): vm.destroy(gracefully=False) # Get disk count before test. disk_count_before_cmd = vm_xml.VMXML.get_disk_count(vm_name) # Test. domid = vm.get_id() domuuid = vm.get_uuid() # Confirm how to reference a VM. if vm_ref == "name": vm_ref = vm_name elif vm_ref.find("invalid") != -1: vm_ref = params.get(vm_ref) elif vm_ref == "id": vm_ref = domid elif vm_ref == "hex_id": vm_ref = hex(int(domid)) elif vm_ref == "uuid": vm_ref = domuuid else: vm_ref = "" if test_cmd == "attach-disk": status = virsh.attach_disk(vm_ref, device_source, device_target, at_options, debug=True).exit_status elif test_cmd == "detach-disk": # For detach disk with print-xml option, it only print information,and not actual disk detachment. if detach_disk_with_print_xml and libvirt_version.version_compare(4, 5, 0): ret = virsh.detach_disk(vm_ref, device_target, at_options) libvirt.check_exit_status(ret) cmd = ("echo \"%s\" | grep -A 16 %s" % (ret.stdout.strip(), device_source_name)) if process.system(cmd, ignore_status=True, shell=True): test.error("Check disk with source image name failed") status = virsh.detach_disk(vm_ref, device_target, dt_options, debug=True).exit_status if restart_libvirtd: libvirtd_serv = utils_libvirtd.Libvirtd() libvirtd_serv.restart() if test_twice: device_target2 = params.get("at_dt_disk_device_target2", device_target) device_source = libvirt.create_local_disk( "file", path=device_source_path, size="1G", disk_format=device_source_format) if test_cmd == "attach-disk": if address2: at_options = at_options_twice status = virsh.attach_disk(vm_ref, device_source, device_target2, at_options, debug=True).exit_status elif test_cmd == "detach-disk": status = virsh.detach_disk(vm_ref, device_target2, dt_options, debug=True).exit_status # Resume guest after command. On newer libvirt this is fixed as it has # been a bug. The change in xml file is done after the guest is resumed. if pre_vm_state == "paused": vm.resume() time.sleep(5) # Check audit log check_audit_after_cmd = True if test_audit: grep_audit = ('grep "%s" /var/log/audit/audit.log' % test_cmd.split("-")[0]) cmd = (grep_audit + ' | ' + 'grep "%s" | tail -n1 | grep "res=success"' % device_source) if process.run(cmd, shell=True).exit_status: logging.error("Audit check failed") check_audit_after_cmd = False # Need wait a while for xml to sync time.sleep(float(time_sleep)) # Check disk count after command. check_count_after_cmd = True disk_count_after_cmd = vm_xml.VMXML.get_disk_count(vm_name) if test_cmd == "attach-disk": if disk_count_after_cmd == disk_count_before_cmd: check_count_after_cmd = False elif test_cmd == "detach-disk": if disk_count_after_cmd < disk_count_before_cmd: check_count_after_cmd = False # Recover VM state. if pre_vm_state == "shut off": vm.start() # Check in VM after command. check_vm_after_cmd = True check_vm_after_cmd = check_vm_partition(vm, device, os_type, device_target, old_parts) # Check disk type after attach. check_disk_type = True if test_type: if test_block_dev: check_disk_type = vm_xml.VMXML.check_disk_type(vm_name, device_source, "block") else: check_disk_type = vm_xml.VMXML.check_disk_type(vm_name, device_source, "file") # Check disk serial after attach. check_disk_serial = True if serial: disk_serial = vm_xml.VMXML.get_disk_serial(vm_name, device_target) if serial != disk_serial: check_disk_serial = False # Check disk address after attach. check_disk_address = True if address: disk_address = vm_xml.VMXML.get_disk_address(vm_name, device_target) if address != disk_address: check_disk_address = False # Check multifunction address after attach. check_disk_address2 = True if address2: disk_address2 = vm_xml.VMXML.get_disk_address(vm_name, device_target2) if address2 != disk_address2: check_disk_address2 = False # Check disk cache option after attach. check_cache_after_cmd = True if cache_options: disk_cache = vm_xml.VMXML.get_disk_attr(vm_name, device_target, "driver", "cache") if cache_options == "default": if disk_cache is not None: check_cache_after_cmd = False elif disk_cache != cache_options: check_cache_after_cmd = False # Eject cdrom test eject_cdrom = "yes" == params.get("at_dt_disk_eject_cdrom", "no") save_vm = "yes" == params.get("at_dt_disk_save_vm", "no") save_file = os.path.join(data_dir.get_tmp_dir(), "vm.save") try: if eject_cdrom: eject_params = {'type_name': "file", 'device_type': "cdrom", 'target_dev': device_target, 'target_bus': device_disk_bus} eject_xml = libvirt.create_disk_xml(eject_params) with open(eject_xml) as eject_file: logging.debug("Eject CDROM by XML: %s", eject_file.read()) # Run command tiwce to make sure cdrom tray open first #BZ892289 # Open tray virsh.attach_device(domainarg=vm_name, filearg=eject_xml, debug=True) # Add time sleep between two attach commands. if time_sleep: time.sleep(float(time_sleep)) # Eject cdrom result = virsh.attach_device(domainarg=vm_name, filearg=eject_xml, debug=True) if result.exit_status != 0: test.fail("Eject CDROM failed") if vm_xml.VMXML.check_disk_exist(vm_name, device_source): test.fail("Find %s after do eject" % device_source) # Save and restore VM if save_vm: result = virsh.save(vm_name, save_file, debug=True) libvirt.check_exit_status(result) result = virsh.restore(save_file, debug=True) libvirt.check_exit_status(result) if vm_xml.VMXML.check_disk_exist(vm_name, device_source): test.fail("Find %s after do restore" % device_source) # Destroy VM. vm.destroy(gracefully=False) # Check disk count after VM shutdown (with --config). check_count_after_shutdown = True inactive_vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) disk_count_after_shutdown = len(inactive_vmxml.get_disk_all()) if test_cmd == "attach-disk": if disk_count_after_shutdown == disk_count_before_cmd: check_count_after_shutdown = False elif test_cmd == "detach-disk": if disk_count_after_shutdown < disk_count_before_cmd: check_count_after_shutdown = False finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) logging.debug("Restore the VM XML") backup_xml.sync() if os.path.exists(save_file): os.remove(save_file) if test_block_dev: if test_logcial_dev: libvirt.delete_local_disk("lvm", vgname=vg_name, lvname=lv_name) lv_utils.vg_remove(vg_name) process.run("pvremove %s" % device_source, shell=True, ignore_status=True) libvirt.setup_or_cleanup_iscsi(False) else: libvirt.delete_local_disk("file", device_source) # Check results. if status_error: if not status: test.fail("virsh %s exit with unexpected value." % test_cmd) else: if status: test.fail("virsh %s failed." % test_cmd) if test_cmd == "attach-disk": if at_options.count("config"): if not check_count_after_shutdown: test.fail("Cannot see config attached device " "in xml file after VM shutdown.") if not check_disk_serial: test.fail("Serial set failed after attach") if not check_disk_address: test.fail("Address set failed after attach") if not check_disk_address2: test.fail("Address(multifunction) set failed" " after attach") else: if not check_count_after_cmd: test.fail("Cannot see device in xml file" " after attach.") if not check_vm_after_cmd: test.fail("Cannot see device in VM after" " attach.") if not check_disk_type: test.fail("Check disk type failed after" " attach.") if not check_audit_after_cmd: test.fail("Audit hotplug failure after attach") if not check_cache_after_cmd: test.fail("Check cache failure after attach") if at_options.count("persistent"): if not check_count_after_shutdown: test.fail("Cannot see device attached " "with persistent after " "VM shutdown.") else: if check_count_after_shutdown: test.fail("See non-config attached device " "in xml file after VM shutdown.") elif test_cmd == "detach-disk": if dt_options.count("config"): if check_count_after_shutdown: test.fail("See config detached device in " "xml file after VM shutdown.") else: if check_count_after_cmd: test.fail("See device in xml file " "after detach.") if check_vm_after_cmd: test.fail("See device in VM after detach.") if not check_audit_after_cmd: test.fail("Audit hotunplug failure " "after detach") if dt_options.count("persistent"): if check_count_after_shutdown: test.fail("See device deattached " "with persistent after " "VM shutdown.") else: if not check_count_after_shutdown: test.fail("See non-config detached " "device in xml file after " "VM shutdown.") else: test.error("Unknown command %s." % test_cmd)