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), e: logging.error(str(e)) return False
def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param target: Disk dev in VM. :param old_parts: Original disk partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False
def check_in_vm(vm, old_parts): """ Check mount/read/write disk in VM. :param vm: Virtual machine to be checked. :param old_parts: Original disk partitions in VM. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: test.fail("The number of new partitions is invalid in VM") else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) session.close() if status: test.fail("Disk operation in VM failed:%s" % output) except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when check disk in vm: %s" % err)
def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param target: Disk dev in VM. :param old_parts: Original disk partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test".format( added_part)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False
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"): 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), e: logging.error(str(e)) return False
def check_in_vm(old_parts): """ Check mount/read/write disk in VM. :param old_parts: pre-operated partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) logging.debug("new parted:%s", new_parts) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False if 'sr' not in added_part and 'fd' not in added_part: cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", output) if status != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False
def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() rpm_stat = session.cmd_status( "rpm -q parted || " "yum install -y parted", 300) if rpm_stat != 0: raise exceptions.TestFail( "Failed to query/install parted, make sure" " that you have usable repo in guest") new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Cann't see added partition in VM") return False libvirt.mk_part("/dev/%s" % added_part, size="10M", session=session) # Run partprobe to make the change take effect. process.run("partprobe", ignore_status=True, shell=True) libvirt.mkfs("/dev/%s1" % added_part, "ext3", session=session) cmd = ("mount /dev/%s1 /mnt && echo '123' > /mnt/testfile" " && cat /mnt/testfile && umount /mnt" % added_part) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) session.close() if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError), e: logging.error(str(e)) return False
def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() rpm_stat = session.cmd_status("rpm -q parted || " "yum install -y parted", 300) if rpm_stat != 0: test.fail("Failed to query/install parted, make sure" " that you have usable repo in guest") new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False device_source = os.path.join(os.sep, 'dev', added_part) libvirt.mk_label(device_source, session=session) libvirt.mk_part(device_source, size="10M", session=session) # Run partprobe to make the change take effect. process.run("partprobe", ignore_status=True, shell=True) libvirt.mkfs("/dev/%s1" % added_part, "ext3", session=session) cmd = ("mount /dev/%s1 /mnt && echo '123' > /mnt/testfile" " && cat /mnt/testfile && umount /mnt" % added_part) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) session.close() if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False
def check_in_vm(vm_obj, target, old_parts, read_only=False): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm_obj.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False cmd = ("mount /dev/{0} /mnt && ls /mnt && (sleep 15;" " touch /mnt/testfile; umount /mnt)" .format(added_part)) s, o = session.cmd_status_output(cmd, timeout=60) session.close() logging.info("Check disk operation in VM:\n, %s, %s", s, o) # Readonly fs, check the error messages. # The command may return True, read-only # messges can be found from the command output if read_only: if "Read-only file system" not in o: return False else: return True # Other errors if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False
def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm: VM guest. :param target: Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) try: cmd_status, cmd_output = session.cmd_status_output(cmd) except Exception as detail: test.error("Error occurred when run cmd: fdisk, %s" % detail) logging.info("Check disk operation in VM:\n%s", cmd_output) session.close() if cmd_status != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as detail: logging.error(str(detail)) return False
def get_new_disks(old_partitions): """ Get new virtual disks in vm after disk plug. :param old_partitions: Already existing partitions in vm. :return: New disks/partitions in vm. """ session = None try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_partitions = libvirt.get_parts_list(session) added_partitions = list(set(new_partitions).difference(set(old_partitions))) if not added_partitions: logging.debug("No new partitions found in vm.") else: logging.debug("Newly added partition(s) is: %s", added_partitions) return added_partitions except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when get new disk: %s", str(err)) finally: if session: session.close()
def run(test, params, env): """ Test disk encryption option. 1.Prepare backend storage (blkdev/iscsi/gluster/ceph) 2.Use luks format to encrypt the backend storage 3.Prepare a disk xml indicating to the backend storage with valid/invalid luks password 4.Start VM with disk hot/cold plugged 5.Check some disk operations in VM 6.Check backend storage is still in luks format 7.Recover test environment """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def encrypt_dev(device, params): """ Encrypt device with luks format :param device: Storage deivce to be encrypted. :param params: From the dict to get encryption password. """ password = params.get("luks_encrypt_passwd", "password") size = params.get("luks_size", "500M") cmd = ( "qemu-img create -f luks " "--object secret,id=sec0,data=`printf '%s' | base64`,format=base64 " "-o key-secret=sec0 %s %s" % (password, device, size)) if process.system(cmd, shell=True): test.fail("Can't create a luks encrypted img by qemu-img") def check_dev_format(device, fmt="luks"): """ Check if device is in luks format :param device: Storage deivce to be checked. :param fmt: Expected disk format. :return: If device's format equals to fmt, return True, else return False. """ cmd_result = process.run("qemu-img" + ' -h', ignore_status=True, shell=True, verbose=False) if b'-U' in cmd_result.stdout: cmd = ("qemu-img info -U %s| grep -i 'file format' " "| grep -i %s" % (device, fmt)) else: cmd = ("qemu-img info %s| grep -i 'file format' " "| grep -i %s" % (device, fmt)) cmd_result = process.run(cmd, ignore_status=True, shell=True) if cmd_result.exit_status: test.fail("device %s is not in %s format. err is: %s" % (device, fmt, cmd_result.stderr)) def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param target: Disk dev in VM. :param old_parts: Original disk partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test".format( added_part)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_format = params.get("virt_disk_device_format", "raw") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") backend_storage_type = params.get("backend_storage_type", "iscsi") # Backend storage options. storage_size = params.get("storage_size", "1G") enable_auth = "yes" == params.get("enable_auth") # Luks encryption info, luks_encrypt_passwd is the password used to encrypt # luks image, and luks_secret_passwd is the password set to luks secret, you # can set a wrong password to luks_secret_passwd for negative tests luks_encrypt_passwd = params.get("luks_encrypt_passwd", "password") luks_secret_passwd = params.get("luks_secret_passwd", "password") # Backend storage auth info use_auth_usage = "yes" == params.get("use_auth_usage") if use_auth_usage: use_auth_uuid = False else: use_auth_uuid = "yes" == params.get("use_auth_uuid", "yes") auth_sec_usage_type = params.get("auth_sec_usage_type", "iscsi") auth_sec_usage_target = params.get("auth_sec_usage_target", "libvirtiscsi") status_error = "yes" == params.get("status_error") check_partitions = "yes" == params.get("virt_disk_check_partitions", "yes") hotplug_disk = "yes" == params.get("hotplug_disk", "no") encryption_in_source = "yes" == params.get("encryption_in_source", "no") auth_in_source = "yes" == params.get("auth_in_source", "no") auth_sec_uuid = "" luks_sec_uuid = "" disk_auth_dict = {} disk_encryption_dict = {} pvt = None if ((encryption_in_source or auth_in_source) and not libvirt_version.version_compare(3, 9, 0)): test.cancel("Cannot put <encryption> or <auth> inside disk <source> " "in this libvirt version.") # 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: # Setup backend storage if backend_storage_type == "iscsi": iscsi_host = params.get("iscsi_host") iscsi_port = params.get("iscsi_port") if device_type == "block": device_source = libvirt.setup_or_cleanup_iscsi(is_setup=True) disk_src_dict = {'attrs': {'dev': device_source}} elif device_type == "network": if enable_auth: chap_user = params.get("chap_user", "redhat") chap_passwd = params.get("chap_passwd", "password") auth_sec_usage = params.get("auth_sec_usage", "libvirtiscsi") auth_sec_dict = { "sec_usage": "iscsi", "sec_target": auth_sec_usage } auth_sec_uuid = libvirt.create_secret(auth_sec_dict) # Set password of auth secret (not luks encryption secret) virsh.secret_set_value(auth_sec_uuid, chap_passwd, encode=True, debug=True) iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi( is_setup=True, is_login=False, image_size=storage_size, chap_user=chap_user, chap_passwd=chap_passwd, portal_ip=iscsi_host) # ISCSI auth attributes for disk xml if use_auth_uuid: disk_auth_dict = { "auth_user": chap_user, "secret_type": auth_sec_usage_type, "secret_uuid": auth_sec_uuid } elif use_auth_usage: disk_auth_dict = { "auth_user": chap_user, "secret_type": auth_sec_usage_type, "secret_usage": auth_sec_usage_target } else: iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi( is_setup=True, is_login=False, image_size=storage_size, portal_ip=iscsi_host) device_source = "iscsi://%s:%s/%s/%s" % ( iscsi_host, iscsi_port, iscsi_target, lun_num) disk_src_dict = { "attrs": { "protocol": "iscsi", "name": "%s/%s" % (iscsi_target, lun_num) }, "hosts": [{ "name": iscsi_host, "port": iscsi_port }] } elif backend_storage_type == "gluster": gluster_vol_name = params.get("gluster_vol_name", "gluster_vol1") gluster_pool_name = params.get("gluster_pool_name", "gluster_pool1") gluster_img_name = params.get("gluster_img_name", "gluster1.img") gluster_host_ip = libvirt.setup_or_cleanup_gluster( is_setup=True, vol_name=gluster_vol_name, pool_name=gluster_pool_name) device_source = "gluster://%s/%s/%s" % ( gluster_host_ip, gluster_vol_name, gluster_img_name) disk_src_dict = { "attrs": { "protocol": "gluster", "name": "%s/%s" % (gluster_vol_name, gluster_img_name) }, "hosts": [{ "name": gluster_host_ip, "port": "24007" }] } elif backend_storage_type == "ceph": ceph_host_ip = params.get("ceph_host_ip", "EXAMPLE_HOSTS") ceph_mon_ip = params.get("ceph_mon_ip", "EXAMPLE_MON_HOST") ceph_host_port = params.get("ceph_host_port", "EXAMPLE_PORTS") ceph_disk_name = params.get("ceph_disk_name", "EXAMPLE_SOURCE_NAME") ceph_client_name = params.get("ceph_client_name") ceph_client_key = params.get("ceph_client_key") ceph_auth_user = params.get("ceph_auth_user") ceph_auth_key = params.get("ceph_auth_key") enable_auth = "yes" == params.get("enable_auth") key_file = os.path.join(data_dir.get_tmp_dir(), "ceph.key") key_opt = "" # Prepare a blank params to confirm if delete the configure at the end of the test ceph_cfg = "" if not utils_package.package_install(["ceph-common"]): test.error("Failed to install ceph-common") # Create config file if it doesn't exist ceph_cfg = ceph.create_config_file(ceph_mon_ip) if enable_auth: # If enable auth, prepare a local file to save key if ceph_client_name and ceph_client_key: with open(key_file, 'w') as f: f.write("[%s]\n\tkey = %s\n" % (ceph_client_name, ceph_client_key)) key_opt = "--keyring %s" % key_file auth_sec_dict = { "sec_usage": auth_sec_usage_type, "sec_name": "ceph_auth_secret" } auth_sec_uuid = libvirt.create_secret(auth_sec_dict) virsh.secret_set_value(auth_sec_uuid, ceph_auth_key, debug=True) disk_auth_dict = { "auth_user": ceph_auth_user, "secret_type": auth_sec_usage_type, "secret_uuid": auth_sec_uuid } cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(ceph_mon_ip, key_opt, ceph_disk_name)) else: test.error("No ceph client name/key provided.") device_source = "rbd:%s:mon_host=%s:keyring=%s" % ( ceph_disk_name, ceph_mon_ip, key_file) else: device_source = "rbd:%s:mon_host=%s" % (ceph_disk_name, ceph_mon_ip) disk_src_dict = { "attrs": { "protocol": "rbd", "name": ceph_disk_name }, "hosts": [{ "name": ceph_host_ip, "port": ceph_host_port }] } elif backend_storage_type == "nfs": pool_name = params.get("pool_name", "nfs_pool") pool_target = params.get("pool_target", "nfs_mount") pool_type = params.get("pool_type", "netfs") nfs_server_dir = params.get("nfs_server_dir", "nfs_server") emulated_image = params.get("emulated_image") image_name = params.get("nfs_image_name", "nfs.img") tmp_dir = data_dir.get_tmp_dir() pvt = libvirt.PoolVolumeTest(test, params) pvt.pre_pool(pool_name, pool_type, pool_target, emulated_image) nfs_mount_dir = os.path.join(tmp_dir, pool_target) device_source = nfs_mount_dir + image_name disk_src_dict = { 'attrs': { 'file': device_source, 'type_name': 'file' } } else: test.cancel("Only iscsi/gluster/rbd/nfs can be tested for now.") logging.debug("device source is: %s", device_source) luks_sec_uuid = libvirt.create_secret(params) logging.debug("A secret created with uuid = '%s'", luks_sec_uuid) ret = virsh.secret_set_value(luks_sec_uuid, luks_secret_passwd, encode=True, debug=True) encrypt_dev(device_source, params) libvirt.check_exit_status(ret) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device disk_xml.target = {"dev": device_target, "bus": device_bus} driver_dict = {"name": "qemu", "type": device_format} disk_xml.driver = driver_dict disk_source = disk_xml.new_disk_source(**disk_src_dict) if disk_auth_dict: logging.debug("disk auth dict is: %s" % disk_auth_dict) if auth_in_source: disk_source.auth = disk_xml.new_auth(**disk_auth_dict) else: disk_xml.auth = disk_xml.new_auth(**disk_auth_dict) disk_encryption_dict = { "encryption": "luks", "secret": { "type": "passphrase", "uuid": luks_sec_uuid } } disk_encryption = disk_xml.new_encryption(**disk_encryption_dict) if encryption_in_source: disk_source.encryption = disk_encryption else: disk_xml.encryption = disk_encryption disk_xml.source = disk_source logging.debug("new disk xml is: %s", disk_xml) # Sync VM xml if not hotplug_disk: vmxml.add_device(disk_xml) vmxml.sync() try: vm.start() vm.wait_for_login() except virt_vm.VMStartError as details: # When use wrong password in disk xml for cold plug cases, # VM cannot be started if status_error and not hotplug_disk: logging.info("VM failed to start as expected: %s" % str(details)) else: test.fail("VM should start but failed: %s" % str(details)) if hotplug_disk: result = virsh.attach_device(vm_name, disk_xml.xml, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) if check_partitions and not status_error: if not check_in_vm(device_target, old_parts): test.fail("Check disk partitions in VM failed") check_dev_format(device_source) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync("--snapshots-metadata") # Clean up backend storage if backend_storage_type == "iscsi": libvirt.setup_or_cleanup_iscsi(is_setup=False) elif backend_storage_type == "gluster": libvirt.setup_or_cleanup_gluster(is_setup=False, vol_name=gluster_vol_name, pool_name=gluster_pool_name) elif backend_storage_type == "ceph": # Remove ceph configure file if created. if ceph_cfg: os.remove(ceph_cfg) cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(ceph_mon_ip, key_opt, ceph_disk_name)) cmd_result = process.run(cmd, ignore_status=True, shell=True) logging.debug("result of rbd removal: %s", cmd_result) if os.path.exists(key_file): os.remove(key_file) # Clean up secrets if auth_sec_uuid: virsh.secret_undefine(auth_sec_uuid) if luks_sec_uuid: virsh.secret_undefine(luks_sec_uuid) # Clean up pools if pvt: pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image)
def run(test, params, env): """ Test command: virsh qemu-agent-command. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) status_cmd = params.get("status_cmd", "") freeze_cmd = params.get("freeze_cmd", "") thaw_cmd = params.get("thaw_cmd", "") xml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: def get_dirty(session, frozen=False): """ Get dirty data of guest """ try: data_cmd = "cat /proc/meminfo | grep Dirty" if not frozen: result = utils_misc.wait_for(lambda: int(session. cmd_output(data_cmd). strip().split()[1]) != 0, 60) if result: return int(session.cmd_output(data_cmd).strip(). split()[1]) else: return 0 dirty_info = session.cmd_output(data_cmd).strip() return int(dirty_info.split()[1]) else: result = utils_misc.wait_for(lambda: int(session. cmd_output(data_cmd). strip().split()[1]) == 0, 60) if result: return 0 else: return int(session.cmd_output(data_cmd).strip(). split()[1]) except (IndexError, ValueError) as details: test.fail("Get dirty info failed: %s" % details) device_source_path = os.path.join(data_dir.get_tmp_dir(), "disk.img") device_source = libvirt.create_local_disk("file", path=device_source_path, disk_format="qcow2") vm.prepare_guest_agent() # Do operation before freeze guest filesystem session = vm.wait_for_login() tmp_file = "/mnt/test.file" try: # Create extra image and attach to guest, then mount old_parts = libvirt.get_parts_list(session) ret = virsh.attach_disk(vm_name, device_source, "vdd") if ret.exit_status: test.fail("Attaching device failed before testing agent:%s" % ret.stdout.strip()) time.sleep(1) new_parts = libvirt.get_parts_list(session) added_part = list(set(new_parts).difference(set(old_parts))) session.cmd("mkfs.ext3 -F /dev/{0} && mount /dev/{0} /mnt".format(added_part[0])) # Generate dirty memory session.cmd("rm -f %s" % tmp_file) session.cmd_output("cp /dev/zero %s 2>/dev/null &" % tmp_file) # Get original dirty data org_dirty_info = get_dirty(session) fz_cmd_result = virsh.qemu_agent_command(vm_name, freeze_cmd, ignore_status=True, debug=True) libvirt.check_exit_status(fz_cmd_result) # Get frozen dirty data fz_dirty_info = get_dirty(session, True) st_cmd_result = virsh.qemu_agent_command(vm_name, status_cmd, ignore_status=True, debug=True) libvirt.check_exit_status(st_cmd_result) if not st_cmd_result.stdout.strip().count("frozen"): test.fail("Guest filesystem status is not frozen: %s" % st_cmd_result.stdout.strip()) tw_cmd_result = virsh.qemu_agent_command(vm_name, thaw_cmd, ignore_status=True, debug=True) libvirt.check_exit_status(tw_cmd_result) # Get thawed dirty data tw_dirty_info = get_dirty(session) st_cmd_result = virsh.qemu_agent_command(vm_name, status_cmd, ignore_status=True, debug=True) libvirt.check_exit_status(st_cmd_result) if not st_cmd_result.stdout.strip().count("thawed"): test.fail("Guest filesystem status is not thawed: %s" % st_cmd_result.stdout.strip()) logging.info("Original dirty data: %s" % org_dirty_info) logging.info("Frozen dirty data: %s" % fz_dirty_info) logging.info("Thawed dirty data: %s" % tw_dirty_info) if not tw_dirty_info or not org_dirty_info: test.fail("The thawed dirty data should not be 0!") if fz_dirty_info: test.fail("The frozen dirty data should be 0!") finally: # Thaw the file system that remove action can be done virsh.qemu_agent_command(vm_name, thaw_cmd, ignore_status=True) session.cmd("rm -f %s" % tmp_file) session.close() finally: xml_backup.sync() os.remove(device_source_path)
def run(test, params, env): """ Test command: domfsinfo [--domain] The command gets information of domain's mounted filesystems. """ start_vm = ("yes" == params.get("start_vm", "yes")) start_ga = ("yes" == params.get("start_ga", "yes")) prepare_channel = ("yes" == params.get("prepare_channel", "yes")) status_error = ("yes" == params.get("status_error", "no")) vm_name = params.get("main_vm") vm = env.get_vm(vm_name) mount_dir = params.get("mount_dir", None) quiet_mode = ("yes" == params.get("quiet_mode", False)) readonly_mode = ("yes" == params.get("readonly_mode", False)) nfs_mount = ("yes" == params.get("nfs_mount", False)) domfsfreeze = ("yes" == params.get("domfsfreeze", False)) # Hotplug and Unplug options hotplug_unplug = ("yes" == params.get("hotplug_unplug", False)) disk_name = params.get("disk_name", "test") disk_path = os.path.join(test.tmpdir, disk_name) disk_target = params.get("disk_target", "vdb") fs_type = params.get("fs_type", "ext3") new_part = "" fail_pat = [] check_point_msg = params.get("check_point_msg", "") if check_point_msg: for msg in check_point_msg.split(";"): fail_pat.append(msg) def hotplug_domain_disk(domain, target, source=None, hotplug=True): """ Hot-plug/Hot-unplug disk for domain :param domain: Guest name :param source: Source of disk device, can leave None if hotplug=False :param target: Target of disk device :param hotplug: True means hotplug, False means hot-unplug :return: Virsh command object """ if hotplug: result = virsh.attach_disk(domain, source, target, "--live", ignore_status=False, debug=True) else: result = virsh.detach_disk(domain, target, "--live", ignore_status=False, debug=True) # It need more time for attachment to take effect time.sleep(5) vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) cleanup_nfs = False try: reset_kwargs = { "start_vm": start_vm, "start_ga": start_ga, "prepare_channel": prepare_channel } reset_domain(vm, **reset_kwargs) if domfsfreeze: result = virsh.domfsfreeze(vm_name, debug=True) if result.exit_status: test.fail("Failed to execute virsh.domfsfreeze:\n%s" % result.stderr) if nfs_mount: nfs_device = libvirt.setup_or_cleanup_nfs(True, mount_dir=mount_dir, is_mount=True) if nfs_device: cleanup_nfs = True if hotplug_unplug: session = vm.wait_for_login() new_device = libvirt.create_local_disk("file", path=disk_path, size="1") parts_list_before_attach = libvirt.get_parts_list(session) hotplug_domain_disk(vm_name, disk_target, new_device) parts_list_after_attach = libvirt.get_parts_list(session) new_part = list( set(parts_list_after_attach).difference( set(parts_list_before_attach)))[0] logging.debug("The new partition is %s", new_part) libvirt.mkfs("/dev/%s" % new_part, fs_type, session=session) session.cmd_status( "mkdir -p {0} ; mount /dev/{1} {0}; ls {0}".format( mount_dir, new_part)) session.close() # Run test case command_dargs = { "readonly": readonly_mode, "quiet": quiet_mode, "debug": True } result = virsh.domfsinfo(vm_name, **command_dargs) if not result.exit_status: if fail_pat: test.fail("Expected fail with %s, but run succeed:\n%s" % (fail_pat, result)) else: if not fail_pat: test.fail("Expected success, but run failed:\n%s" % result) else: # If not any pattern matches(fail_pat, result.stderr) if not any(p in result.stderr for p in fail_pat): test.fail( "Expected fail with one of %s, but failed with:\n%s" % (fail_pat, result)) # Check virsh.domfsinfo output cmd_output = result.stdout if quiet_mode: head_pat = "Mountpoint\s+Name\s+Type\s+Target" check_output(cmd_output, head_pat, test, expected=False) elif nfs_mount: check_output(cmd_output, mount_dir, test, expected=False) elif hotplug_unplug: blk_target = re.findall(r'[a-z]+', new_part)[0] disk_pat = "%s\s+%s\s+%s\s+%s\s+" % (mount_dir, new_part, fs_type, blk_target) check_output(cmd_output, disk_pat, test, expected=True) # Unplug domain disk hotplug_domain_disk(vm_name, target=new_part, hotplug=False) result = virsh.domfsinfo(vm_name, **command_dargs) if result.exit_status: test.fail( "Failed to run virsh.domfsinfo after disk unplug:\n%s" % result.stderr) check_output(result.stdout, disk_pat, test, expected=False) else: # Verify virsh.domfsinfo consistency if not status_error: session = vm.wait_for_login(timeout=120) domfsinfo = vm.domfsinfo() expected_result = get_mount_fs(session) if domfsinfo and expected_result: check_domfsinfo(domfsinfo, expected_result, test) else: logging.debug("Virsh.domfsinfo output:\n%s", domfsinfo) logging.debug("Expected_result is:\n%s", expected_result) test.error("Command output inconsistent with expected") session.close() finally: if cleanup_nfs: libvirt.setup_or_cleanup_nfs(False, mount_dir=mount_dir) if vm.is_alive(): vm.destroy() if hotplug_unplug: if disk_path: cmd = "rm -rf %s" % disk_path process.run(cmd) vmxml_backup.sync()
def run(test, params, env): """ Test startupPolicy for CD-ROM/floppy/Volume disks. Steps: 1. Prepare disk media image. 2. Setup startupPolicy for a disk. 3. Start the domain. 4. Save the domain. 5. Remove the disk source file and restore the domain. 6. Update startupPolicy for a disk. 7. Destroy the domain. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) startup_policy = params.get("policy") def create_iscsi_pool(): """ Setup iSCSI target,and create one iSCSI pool. """ libvirt.setup_or_cleanup_iscsi(is_setup=False) iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi(is_setup=True, is_login=False, image_size='1G', chap_user="", chap_passwd="", portal_ip=disk_src_host) # Define an iSCSI pool xml to create it pool_src_xml = pool_xml.SourceXML() pool_src_xml.host_name = pool_src_host pool_src_xml.device_path = iscsi_target poolxml = pool_xml.PoolXML(pool_type=pool_type) poolxml.name = pool_name poolxml.set_source(pool_src_xml) poolxml.target_path = "/dev/disk/by-path" # Create iSCSI pool. virsh.pool_destroy(pool_name, **virsh_dargs) cmd_result = virsh.pool_create(poolxml.xml, **virsh_dargs) libvirt.check_exit_status(cmd_result) def create_volume(pvt, created_vol_name=None): """ Create iSCSI volume. :param pvt: PoolVolumeTest object :param created_vol_name: Created volume name """ try: if pool_type == "iscsi": create_iscsi_pool() else: pvt.pre_pool(pool_name, pool_type, pool_target, emulated_image) pvt.pre_vol(vol_name=created_vol_name, vol_format=vol_format, capacity=capacity, allocation=None, pool_name=pool_name) except Exception as pool_exception: pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image, **virsh_dargs) test.error("Error occurred when prepare" + "pool xml with message %s:\n" % str(pool_exception)) def get_vol(): """Get the volume info""" # Refresh the pool cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) # Get volume name cmd_result = virsh.vol_list(pool_name, **virsh_dargs) libvirt.check_exit_status(cmd_result) vol_list = [] vol_list = re.findall(r"(\S+)\ +(\S+)[\ +\n]", str(cmd_result.stdout)) try: return vol_list[1] except IndexError: return None # Wait for a while so that we can get the volume info vol_info = utils_misc.wait_for(get_vol, 10) if vol_info: tmp_vol_name, tmp_vol_path = vol_info else: test.error("Failed to get volume info") process.run('qemu-img create -f qcow2 %s %s' % (tmp_vol_path, '100M'), shell=True) return vol_info def check_disk_source(vm_name, target_dev, expect_value): """ Check the disk source: file and startupPolicy. :param vm_name: Domain name :param target_dev: Disk's target device :param expect_value: Expect value of source file and source startupPolicy """ logging.debug("Expect source file is '%s'", expect_value[0]) logging.debug("Expect source startupPolicy is '%s'", expect_value[1]) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disks = vmxml.get_disk_all() source_value = [] try: disk_source = disks[target_dev].find('source') source_value.append(disk_source.get('file')) source_value.append(disk_source.get('startupPolicy')) except KeyError: test.error("No %s in domain %s" % (target_dev, vm_name)) logging.debug("Actual source file is '%s'", source_value[0]) logging.debug("Actual source startupPolicy is '%s'", source_value[1]) if source_value == expect_value: logging.debug("Domain disk XML check pass") else: test.error("Domain disk XML check fail") def create_disk_xml(): """ Create a disk xml file for attaching to a domain. """ if disk_type == "file": process.run("qemu-img create %s %s" % (media_file, image_size), shell=True) disk_params = {'device_type': device_type, 'type_name': disk_type, 'target_dev': target_dev, 'target_bus': target_bus} if disk_type == "file": disk_params_src = {'source_protocol': "file", 'source_file': media_file, 'source_startupPolicy': startup_policy} elif disk_type == "volume": disk_params_src = {'source_pool': pool_name, 'source_volume': vol_name, 'driver_type': 'qcow2', 'source_startupPolicy': startup_policy} if pool_type == "iscsi": disk_params_src.update({'source_mode': "host"}) disk_params.update(disk_params_src) disk_xml = libvirt.create_disk_xml(disk_params) shutil.copyfile(disk_xml, disk_xml_file) return disk_xml def check_in_vm(old_parts): """ Check mount/read/write disk in VM. :param old_parts: pre-operated partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) logging.debug("new parted:%s", new_parts) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False if 'sr' not in added_part and 'fd' not in added_part: cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", output) if status != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_policy_update(origin_policy, policy_list, xml_policy_file, device_type, flag_str): """ Check updated policy after executing virsh update-device. :param origin_policy: the inherit startup policy value. :param policy_list: updated policy list. :param xml_policy_file: xml file for startupPolicy. :param device_type: device type,cdrom or disk.,etc :param flag_str: it can be --config,--live and --persistent. """ for policy in policy_list: xmltreefile = XMLTreeFile(xml_policy_file) try: policy_item = xmltreefile.find('/source') policy_item.set('startupPolicy', policy) except AttributeError as elem_attr: test.error("Fail to find startupPolicy attribute.%s", str(elem_attr)) xmltreefile.write(xml_policy_file, encoding="UTF-8") ret = virsh.update_device(vm_name, xml_policy_file, flagstr=flag_str, debug=True) if all([device_type == "disk", policy == "requisite"]): libvirt.check_exit_status(ret, True) return else: libvirt.check_exit_status(ret) def check_policy_value(active_policy, inactive_policy): """ Check policy value in dumpxml with active or inactive option :param active_policy: active policy attribute value :param inactive_policy: inactive policy attribute value """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_list = vmxml.devices.by_device_tag("disk") disk = disk_list[len(disk_list)-1] if not active_policy == disk.source.attrs["startupPolicy"]: test.error("Actual policy:%s in active state is not equal to expected:%s" % (active_policy, disk.source.attrs["startupPolicy"])) vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) disk_list = vmxml.devices.by_device_tag("disk") disk = disk_list[len(disk_list)-1] if not inactive_policy == disk.source.attrs["startupPolicy"]: test.error("Actual policy:%s in inactive state is not equal to expected: %s" % (inactive_policy, disk.source.attrs["startupPolicy"])) if flag_str == "--live": check_policy_value(policy, origin_policy) elif flag_str == "--config": check_policy_value(origin_policy, policy) elif flag_str == "--persistent": check_policy_value(policy, policy) def check_source_update(xml_policy_file): """ Update source and policy at the same time,then check those changes. :param xml_policy_file: VM xml policy file """ xmltreefile = XMLTreeFile(xml_policy_file) policy_item = xmltreefile.find('/source') def configure_startup_policy(update=False, policy='optional'): """ Configure startupPolicy attribute value. :param update: update value or not :param policy: policy value :return: flag_option and boolean value """ if update: del policy_item.attrib["startupPolicy"] else: policy_item.set("startupPolicy", policy) flag_option = "--live" xmltreefile.write(xml_policy_file, encoding="UTF-8") return flag_option, False # Update source and startUpPolicy attribute value. def update_source_policy(update=True, policy='optional'): """ Update startupPolicy source value. :param update: update value or not :param policy: policy value :return: flag_option and boolean value """ source_file = policy_item.get('file') if update: new_source_file = source_file+".empty" else: new_source_file = source_file+".new" shutil.copyfile(source_file, new_source_file) policy_item.set("file", new_source_file) policy_item.set("startupPolicy", policy) flag_option = "--persistent" xmltreefile.write(xml_policy_file, encoding="UTF-8") return flag_option, False function_list = [configure_startup_policy, update_source_policy, configure_startup_policy, update_source_policy] function_parameter = [False, False, True, True] # Loop all above scenarios to update device. for index in list(range(len(function_list))): try: func = function_list[index] para = function_parameter[index] flag_option, update_error = func(para) ret = virsh.update_device(vm_name, xml_policy_file, flagstr=flag_option, debug=True) libvirt.check_exit_status(ret, expect_error=update_error) except AttributeError as elem_attr: test.error("Fail to remove startupPolicy attribute:%s" % str(elem_attr)) except Exception as update_device_exception: test.error("Fail to update device:%s" % str(update_device_exception)) finally: source_file = policy_item.get('file') new_source_file = source_file+".new" if os.path.exists(new_source_file): os.remove(new_source_file) def rename_file(source_file, target_file, revert=False): """ Rename a file or revert it. :param source_file: The source file name. :param target_file: The target file name. :param revert: It can be True or False. """ try: if not revert: os.rename(source_file, target_file) logging.debug("Rename %s to %s", source_file, target_file) else: os.rename(target_file, source_file) logging.debug("Rename %s to %s", target_file, source_file) except OSError as err: test.fail("Rename image failed: %s" % str(err)) # Back VM XML vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Start VM and get all partitions 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) # Get start,restore configuration parameters. start_error = "yes" == params.get("start_error", "no") restore_error = "yes" == params.get("restore_error", "no") virsh_dargs = {'debug': True, 'ignore_status': True} attach_option = params.get("attach_option") # Create disk xml and attach it. device_type = params.get("device_type") disk_type = params.get("disk_type", "network") disk_src_host = params.get("disk_source_host", "127.0.0.1") target_dev = params.get("target_dev") target_bus = params.get("disk_target_bus", "virtio") image_size = params.get("image_size", "1.44M") emulated_image = "emulated-iscsi" # Storage pool and volume related paramters. pool_name = params.get("pool_name", "iscsi_pool") pool_type = params.get("pool_type") pool_target = params.get("pool_target", "/dev/disk/by-path") pool_src_host = params.get("pool_source_host", "127.0.0.1") vol_name = params.get("volume_name") capacity = params.get("volume_size", "1048576") vol_format = params.get("volume_format") # Source file parameters. media_name = params.get("media_name") media_file = os.path.join(test.tmpdir, media_name) media_file_new = media_file + ".new" save_file = os.path.join(test.tmpdir, "vm.save") snapshot_name = "s1" # Policy related paramters. disk_xml_file = os.path.join(test.tmpdir, "attach_disk.xml") disk_xml_policy_file = os.path.join(test.tmpdir, "attach_policy_disk.xml") update_policy = "yes" == params.get("update_policy", "no") policy_only = "yes" == params.get("policy_only", "no") update_policy_list = params.get("update_policy_list").split() expect_value = [None, startup_policy] try: if disk_type == "volume": pvt = libvirt.PoolVolumeTest(test, params) vol_name, vol_path = create_volume(pvt, vol_name) vol_path_new = vol_path + ".new" # Create disk xml. create_disk_xml() if vm.is_alive(): vm.destroy() try: # Backup disk xml file for policy update if update_policy=True. if update_policy: shutil.copyfile(disk_xml_file, disk_xml_policy_file) result = virsh.attach_device(domainarg=vm_name, filearg=disk_xml_file, flagstr="--config", **virsh_dargs) # For iSCSI pool volume,startupPolicy attribute is not valid for it. # Moreover,setting disk 'requisite' is allowed only for cdrom or floppy. if pool_type == "iscsi" or all([device_type == "disk", startup_policy == "requisite"]): libvirt.check_exit_status(result, expect_error=True) return else: libvirt.check_exit_status(result, expect_error=False) except Exception as attach_device_exception: logging.debug("Attach device throws exception:%s", str(attach_device_exception)) os.remove(media_file) test.error("Attach %s fail" % device_type) # Check update policy operations. if disk_type == "file" and update_policy: vm.start() if policy_only: check_policy_update(startup_policy, update_policy_list, disk_xml_policy_file, device_type, attach_option) else: check_source_update(disk_xml_policy_file) elif disk_type == "file": # Step 1. Start domain and destroy it normally vm.start() vm.destroy() # Step 2. Remove the source_file then start the domain rename_file(media_file, media_file_new) result = virsh.start(vm_name, **virsh_dargs) libvirt.check_exit_status(result, expect_error=start_error) # For libvirt version >=2.0.0, feature is updated and startup policy attribute # can not exist alone without source protocol. if not start_error and not libvirt_version.version_compare(2, 0, 0): check_disk_source(vm_name, target_dev, expect_value) # Step 3. Move back the source file and start the domain(if needed). rename_file(media_file, media_file_new, revert=True) if not vm.is_alive(): vm.start() # Step 4. Save the domain normally, then remove the source file # and restore it back vm.save_to_file(save_file) rename_file(media_file, media_file_new) result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=restore_error) if not restore_error and not libvirt_version.version_compare(2, 0, 0): check_disk_source(vm_name, target_dev, expect_value) # Step 5. Move back the source file and restore the domain(if needed) rename_file(media_file, media_file_new, revert=True) if not vm.is_alive(): result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=False) elif disk_type == "volume": # Step 1. Start domain and destroy it normally. vm.start() # Step 1 Start VM successfully. if not check_in_vm(old_parts): test.fail("Check disk partitions in VM failed") # Step 2 Move the volume to other place, refresh the pool, then reboot the guest. rename_file(vol_path, vol_path_new) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) vm.destroy() result = virsh.start(vm_name, **virsh_dargs) libvirt.check_exit_status(result, expect_error=start_error) # Step 3 Move back the source file and start. rename_file(vol_path, vol_path_new, revert=True) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) if not vm.is_alive(): vm.start() # Step 4 Save the domain normally, then remove the source file,then restore domain. vm.save_to_file(save_file) rename_file(vol_path, vol_path_new) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=restore_error) # Step 5, Create snapshot,move the source to other place,then revert snapshot. if device_type == "disk": rename_file(vol_path, vol_path_new, revert=True) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) if restore_error: result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result) ret = virsh.snapshot_create_as(vm_name, snapshot_name, **virsh_dargs) libvirt.check_exit_status(ret) rename_file(vol_path, vol_path_new) ret = virsh.snapshot_revert(vm_name, snapshot_name, **virsh_dargs) # Clean up snapshot. libvirt.clean_up_snapshots(vm_name, domxml=vmxml_backup) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() if disk_type == "volume": pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image, **virsh_dargs) if os.path.exists(save_file): os.remove(save_file) if os.path.exists(disk_xml_file): os.remove(disk_xml_file) if os.path.exists(media_file): os.remove(media_file) if os.path.exists(disk_xml_policy_file): os.remove(disk_xml_policy_file)
def run(test, params, env): """ Test startupPolicy for CD-ROM/floppy/Volume disks. Steps: 1. Prepare disk media image. 2. Setup startupPolicy for a disk. 3. Start the domain. 4. Save the domain. 5. Remove the disk source file and restore the domain. 6. Update startupPolicy for a disk. 7. Destroy the domain. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) startup_policy = params.get("policy") def create_iscsi_pool(): """ Setup iSCSI target,and create one iSCSI pool. """ libvirt.setup_or_cleanup_iscsi(is_setup=False) iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi(is_setup=True, is_login=False, image_size='1G', chap_user="", chap_passwd="", portal_ip=disk_src_host) # Define an iSCSI pool xml to create it pool_src_xml = pool_xml.SourceXML() pool_src_xml.host_name = pool_src_host pool_src_xml.device_path = iscsi_target poolxml = pool_xml.PoolXML(pool_type=pool_type) poolxml.name = pool_name poolxml.set_source(pool_src_xml) poolxml.target_path = "/dev/disk/by-path" # Create iSCSI pool. virsh.pool_destroy(pool_name, **virsh_dargs) cmd_result = virsh.pool_create(poolxml.xml, **virsh_dargs) libvirt.check_exit_status(cmd_result) def create_volume(pvt, created_vol_name=None): """ Create iSCSI volume. :param pvt: PoolVolumeTest object :param created_vol_name: Created volume name """ try: if pool_type == "iscsi": create_iscsi_pool() else: pvt.pre_pool(pool_name, pool_type, pool_target, emulated_image) pvt.pre_vol(vol_name=created_vol_name, vol_format=vol_format, capacity=capacity, allocation=None, pool_name=pool_name) except Exception as pool_exception: pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image, **virsh_dargs) test.error("Error occurred when prepare" + "pool xml with message %s:\n" % str(pool_exception)) def get_vol(): """Get the volume info""" # Refresh the pool cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) # Get volume name cmd_result = virsh.vol_list(pool_name, **virsh_dargs) libvirt.check_exit_status(cmd_result) vol_list = [] vol_list = re.findall(r"(\S+)\ +(\S+)", str(cmd_result.stdout.strip())) try: return vol_list[1] except IndexError: return None # Wait for a while so that we can get the volume info vol_info = utils_misc.wait_for(get_vol, 10) if vol_info: tmp_vol_name, tmp_vol_path = vol_info else: test.error("Failed to get volume info") process.run('qemu-img create -f qcow2 %s %s' % (tmp_vol_path, '100M'), shell=True) return vol_info def check_disk_source(vm_name, target_dev, expect_value): """ Check the disk source: file and startupPolicy. :param vm_name: Domain name :param target_dev: Disk's target device :param expect_value: Expect value of source file and source startupPolicy """ logging.debug("Expect source file is '%s'", expect_value[0]) logging.debug("Expect source startupPolicy is '%s'", expect_value[1]) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disks = vmxml.get_disk_all() source_value = [] try: disk_source = disks[target_dev].find('source') source_value.append(disk_source.get('file')) source_value.append(disk_source.get('startupPolicy')) except KeyError: test.error("No %s in domain %s" % (target_dev, vm_name)) logging.debug("Actual source file is '%s'", source_value[0]) logging.debug("Actual source startupPolicy is '%s'", source_value[1]) if source_value == expect_value: logging.debug("Domain disk XML check pass") else: test.error("Domain disk XML check fail") def create_disk_xml(): """ Create a disk xml file for attaching to a domain. """ if disk_type == "file": process.run("qemu-img create %s %s" % (media_file, image_size), shell=True) disk_params = {'device_type': device_type, 'type_name': disk_type, 'target_dev': target_dev, 'target_bus': target_bus} if disk_type == "file": disk_params_src = {'source_protocol': "file", 'source_file': media_file, 'source_startupPolicy': startup_policy} elif disk_type == "volume": disk_params_src = {'source_pool': pool_name, 'source_volume': vol_name, 'driver_type': 'qcow2', 'source_startupPolicy': startup_policy} if pool_type == "iscsi": disk_params_src.update({'source_mode': "host"}) disk_params.update(disk_params_src) disk_xml = libvirt.create_disk_xml(disk_params) shutil.copyfile(disk_xml, disk_xml_file) return disk_xml def check_in_vm(old_parts): """ Check mount/read/write disk in VM. :param old_parts: pre-operated partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) logging.debug("new parted:%s", new_parts) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False if 'sr' not in added_part and 'fd' not in added_part: cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", output) if status != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_policy_update(origin_policy, policy_list, xml_policy_file, device_type, flag_str): """ Check updated policy after executing virsh update-device. :param origin_policy: the inherit startup policy value. :param policy_list: updated policy list. :param xml_policy_file: xml file for startupPolicy. :param device_type: device type,cdrom or disk.,etc :param flag_str: it can be --config,--live and --persistent. """ for policy in policy_list: xmltreefile = XMLTreeFile(xml_policy_file) try: policy_item = xmltreefile.find('/source') policy_item.set('startupPolicy', policy) except AttributeError as elem_attr: test.error("Fail to find startupPolicy attribute.%s", str(elem_attr)) xmltreefile.write(xml_policy_file, encoding="UTF-8") ret = virsh.update_device(vm_name, xml_policy_file, flagstr=flag_str, debug=True) if all([device_type == "disk", policy == "requisite"]): libvirt.check_exit_status(ret, True) return else: libvirt.check_exit_status(ret) def check_policy_value(active_policy, inactive_policy): """ Check policy value in dumpxml with active or inactive option :param active_policy: active policy attribute value :param inactive_policy: inactive policy attribute value """ vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_list = vmxml.devices.by_device_tag("disk") disk = disk_list[len(disk_list)-1] if not active_policy == disk.source.attrs["startupPolicy"]: test.error("Actual policy:%s in active state is not equal to expected:%s" % (active_policy, disk.source.attrs["startupPolicy"])) vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) disk_list = vmxml.devices.by_device_tag("disk") disk = disk_list[len(disk_list)-1] if not inactive_policy == disk.source.attrs["startupPolicy"]: test.error("Actual policy:%s in inactive state is not equal to expected: %s" % (inactive_policy, disk.source.attrs["startupPolicy"])) if flag_str == "--live": check_policy_value(policy, origin_policy) elif flag_str == "--config": check_policy_value(origin_policy, policy) elif flag_str == "--persistent": check_policy_value(policy, policy) def check_source_update(xml_policy_file): """ Update source and policy at the same time,then check those changes. :param xml_policy_file: VM xml policy file """ xmltreefile = XMLTreeFile(xml_policy_file) policy_item = xmltreefile.find('/source') def configure_startup_policy(update=False, policy='optional'): """ Configure startupPolicy attribute value. :param update: update value or not :param policy: policy value :return: flag_option and boolean value """ if update: del policy_item.attrib["startupPolicy"] else: policy_item.set("startupPolicy", policy) flag_option = "--live" xmltreefile.write(xml_policy_file, encoding="UTF-8") return flag_option, False # Update source and startUpPolicy attribute value. def update_source_policy(update=True, policy='optional'): """ Update startupPolicy source value. :param update: update value or not :param policy: policy value :return: flag_option and boolean value """ source_file = policy_item.get('file') if update: new_source_file = source_file+".empty" else: new_source_file = source_file+".new" shutil.copyfile(source_file, new_source_file) policy_item.set("file", new_source_file) policy_item.set("startupPolicy", policy) flag_option = "--persistent" xmltreefile.write(xml_policy_file, encoding="UTF-8") return flag_option, False function_list = [configure_startup_policy, update_source_policy, configure_startup_policy, update_source_policy] function_parameter = [False, False, True, True] # Loop all above scenarios to update device. for index in list(range(len(function_list))): try: func = function_list[index] para = function_parameter[index] flag_option, update_error = func(para) ret = virsh.update_device(vm_name, xml_policy_file, flagstr=flag_option, debug=True) libvirt.check_exit_status(ret, expect_error=update_error) except AttributeError as elem_attr: test.error("Fail to remove startupPolicy attribute:%s" % str(elem_attr)) except Exception as update_device_exception: test.error("Fail to update device:%s" % str(update_device_exception)) finally: source_file = policy_item.get('file') new_source_file = source_file+".new" if os.path.exists(new_source_file): os.remove(new_source_file) def rename_file(source_file, target_file, revert=False): """ Rename a file or revert it. :param source_file: The source file name. :param target_file: The target file name. :param revert: It can be True or False. """ try: if not revert: os.rename(source_file, target_file) logging.debug("Rename %s to %s", source_file, target_file) else: os.rename(target_file, source_file) logging.debug("Rename %s to %s", target_file, source_file) except OSError as err: test.fail("Rename image failed: %s" % str(err)) # Back VM XML vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) # Start VM and get all partitions 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) # Get start,restore configuration parameters. start_error = "yes" == params.get("start_error", "no") restore_error = "yes" == params.get("restore_error", "no") virsh_dargs = {'debug': True, 'ignore_status': True} attach_option = params.get("attach_option") # Create disk xml and attach it. device_type = params.get("device_type") disk_type = params.get("disk_type", "network") disk_src_host = params.get("disk_source_host", "127.0.0.1") target_dev = params.get("target_dev") target_bus = params.get("disk_target_bus", "virtio") image_size = params.get("image_size", "1.44M") emulated_image = "emulated-iscsi" # Storage pool and volume related paramters. pool_name = params.get("pool_name", "iscsi_pool") pool_type = params.get("pool_type") pool_target = params.get("pool_target", "/dev/disk/by-path") pool_src_host = params.get("pool_source_host", "127.0.0.1") vol_name = params.get("volume_name") capacity = params.get("volume_size", "1048576") vol_format = params.get("volume_format") # Source file parameters. media_name = params.get("media_name") media_file = os.path.join(data_dir.get_tmp_dir(), media_name) media_file_new = media_file + ".new" save_file = os.path.join(data_dir.get_tmp_dir(), "vm.save") snapshot_name = "s1" # Policy related paramters. disk_xml_file = os.path.join(data_dir.get_tmp_dir(), "attach_disk.xml") disk_xml_policy_file = os.path.join(data_dir.get_tmp_dir(), "attach_policy_disk.xml") update_policy = "yes" == params.get("update_policy", "no") policy_only = "yes" == params.get("policy_only", "no") update_policy_list = params.get("update_policy_list").split() expect_value = [None, startup_policy] try: if disk_type == "volume": pvt = libvirt.PoolVolumeTest(test, params) vol_name, vol_path = create_volume(pvt, vol_name) vol_path_new = vol_path + ".new" # Create disk xml. create_disk_xml() if vm.is_alive(): vm.destroy() try: # Backup disk xml file for policy update if update_policy=True. if update_policy: shutil.copyfile(disk_xml_file, disk_xml_policy_file) result = virsh.attach_device(domainarg=vm_name, filearg=disk_xml_file, flagstr="--config", **virsh_dargs) # For iSCSI pool volume,startupPolicy attribute is not valid for it. # Moreover,setting disk 'requisite' is allowed only for cdrom or floppy. if pool_type == "iscsi" or all([device_type == "disk", startup_policy == "requisite"]): libvirt.check_exit_status(result, expect_error=True) return else: libvirt.check_exit_status(result, expect_error=False) except Exception as attach_device_exception: logging.debug("Attach device throws exception:%s", str(attach_device_exception)) os.remove(media_file) test.error("Attach %s fail" % device_type) # Check update policy operations. if disk_type == "file" and update_policy: vm.start() if policy_only: check_policy_update(startup_policy, update_policy_list, disk_xml_policy_file, device_type, attach_option) else: check_source_update(disk_xml_policy_file) elif disk_type == "file": # Step 1. Start domain and destroy it normally vm.start() vm.destroy() # Step 2. Remove the source_file then start the domain rename_file(media_file, media_file_new) result = virsh.start(vm_name, **virsh_dargs) libvirt.check_exit_status(result, expect_error=start_error) # For libvirt version >=2.0.0, feature is updated and startup policy attribute # can not exist alone without source protocol. if not start_error and not libvirt_version.version_compare(2, 0, 0): check_disk_source(vm_name, target_dev, expect_value) # Step 3. Move back the source file and start the domain(if needed). rename_file(media_file, media_file_new, revert=True) if not vm.is_alive(): vm.start() # Step 4. Save the domain normally, then remove the source file # and restore it back vm.save_to_file(save_file) rename_file(media_file, media_file_new) result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=restore_error) if not restore_error and not libvirt_version.version_compare(2, 0, 0): check_disk_source(vm_name, target_dev, expect_value) # Step 5. Move back the source file and restore the domain(if needed) rename_file(media_file, media_file_new, revert=True) if not vm.is_alive(): result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=False) elif disk_type == "volume": # Step 1. Start domain and destroy it normally. vm.start() # Step 1 Start VM successfully. if not check_in_vm(old_parts): test.fail("Check disk partitions in VM failed") # Step 2 Move the volume to other place, refresh the pool, then reboot the guest. rename_file(vol_path, vol_path_new) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) vm.destroy() result = virsh.start(vm_name, **virsh_dargs) libvirt.check_exit_status(result, expect_error=start_error) # Step 3 Move back the source file and start. rename_file(vol_path, vol_path_new, revert=True) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) if not vm.is_alive(): vm.start() # Step 4 Save the domain normally, then remove the source file,then restore domain. vm.save_to_file(save_file) rename_file(vol_path, vol_path_new) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result, expect_error=restore_error) # Step 5, Create snapshot,move the source to other place,then revert snapshot. if device_type == "disk": rename_file(vol_path, vol_path_new, revert=True) cmd_result = virsh.pool_refresh(pool_name) libvirt.check_exit_status(cmd_result) if restore_error: result = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(result) ret = virsh.snapshot_create_as(vm_name, snapshot_name, **virsh_dargs) libvirt.check_exit_status(ret) rename_file(vol_path, vol_path_new) ret = virsh.snapshot_revert(vm_name, snapshot_name, **virsh_dargs) # Clean up snapshot. libvirt.clean_up_snapshots(vm_name, domxml=vmxml_backup) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() if disk_type == "volume": pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image, **virsh_dargs) if os.path.exists(save_file): os.remove(save_file) if os.path.exists(disk_xml_file): os.remove(disk_xml_file) if os.path.exists(media_file): os.remove(media_file) if os.path.exists(disk_xml_policy_file): os.remove(disk_xml_policy_file)
def run(test, params, env): """ Test steps: 1. Prepare a multipath device. 2. Prepare virtual disk xml using this multipath device. 3. Hot/Cold-plug the disk xml to virtual machine. 4. Check the attached disk in the virtual machine. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) mpath_conf_path = params.get('mpath_conf_path', '/etc/multipath.conf') mpath_conf_bkup_path = params.get('mpath_conf_bkup_path', '/etc/multipath.conf.bkup') mpath_conf_exist = False def prepare_multipath_conf(): """ Prepare the multipath.conf to make sure iscsi lun can be seen as a multipath device. :return: True means the multipath.conf exists at first, False means not. """ multipath_conf_exist = False mpath_conf_content = ("defaults {\n" " user_friendly_names yes\n" " path_grouping_policy multibus\n" " failback immediate\n" " no_path_retry fail\n" "}\n") if os.path.exists(mpath_conf_bkup_path): os.remove(mpath_conf_bkup_path) if os.path.exists(mpath_conf_path): multipath_conf_exist = True shutil.move(mpath_conf_path, mpath_conf_bkup_path) with open(mpath_conf_path, 'wt') as mpath_conf_file: mpath_conf_file.write(mpath_conf_content) return multipath_conf_exist def recover_multipath_conf(remove_mpath_conf=False): """ Recover the multipath.conf. :param remove_mpath_conf: True to remove multipath.conf. """ if os.path.exists(mpath_conf_bkup_path): if os.path.exists(mpath_conf_path): os.remove(mpath_conf_path) shutil.move(mpath_conf_bkup_path, mpath_conf_path) if os.path.exists(mpath_conf_path) and remove_mpath_conf: os.remove(mpath_conf_path) def check_in_vm(vm, old_parts): """ Check mount/read/write disk in VM. :param vm: Virtual machine to be checked. :param old_parts: Original disk partitions in VM. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: test.fail("The number of new partitions is invalid in VM") else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) session.close() if status: test.fail("Disk operation in VM failed:%s" % output) except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when check disk in vm: %s" % err) storage_size = params.get("storage_size", "1G") hotplug_disk = "yes" == params.get("hotplug_disk", "no") status_error = "yes" == params.get("status_error") # 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() vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) try: # Setup backend storage mpath_conf_exist = prepare_multipath_conf() mpath.restart_multipathd() old_mpath_devs = mpath.find_mpath_devs() libvirt.setup_or_cleanup_iscsi(is_setup=True) mpath.restart_multipathd() cur_mpath_devs = mpath.find_mpath_devs() new_mpath_devs = list(set(cur_mpath_devs).difference( set(old_mpath_devs))) logging.debug("newly added mpath devs are: %s", new_mpath_devs) # Prepare disk xml disk_params = {} disk_params['type_name'] = params.get("virt_disk_type", "block") disk_params['source_file'] = '/dev/mapper/' + new_mpath_devs[0] disk_params['device_type'] = params.get("virt_disk_device", "lun") disk_params['sgio'] = params.get("sgio", "filtered") disk_params['rawio'] = params.get("rawio", "no") disk_params['target_dev'] = params.get("virt_disk_device_target", "sdb") disk_params['target_bus'] = params.get("virt_disk_device_bus", "scsi") disk_params['driver_name'] = params.get("virt_disk_drive_name", "qemu") disk_params['driver_type'] = params.get("virt_disk_device_format", "raw") disk_xml = libvirt.create_disk_xml(disk_params) # Test disk operation with newly added disk xml attach_option = "" if not hotplug_disk: attach_option = "--config" result = virsh.attach_device(vm_name, disk_xml, flagstr=attach_option, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) if not hotplug_disk: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() check_in_vm(vm, old_parts) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() # Clean up backend storage libvirt.setup_or_cleanup_iscsi(is_setup=False) recover_multipath_conf(not mpath_conf_exist) mpath.restart_multipathd()
def run(test, params, env): """ Test for vhba hostdev passthrough. 1. create a vhba 2. prepare hostdev xml for lun device of the newly created vhba 3.1 If hot attach, attach-device the hostdev xml to vm 3.2 If cold attach, add the hostdev to vm and start it 4. login the vm and check the attached disk 5. detach-device the hostdev xml 6. login the vm to check the partitions """ def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm: VM guest. :param target: Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) try: cmd_status, cmd_output = session.cmd_status_output(cmd) except Exception as detail: test.error("Error occurred when run cmd: fdisk, %s" % detail) logging.info("Check disk operation in VM:\n%s", cmd_output) session.close() if cmd_status != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as detail: logging.error(str(detail)) return False try: status_error = "yes" == params.get("status_error", "no") vm_name = params.get("main_vm", "avocado-vt-vm1") device_target = params.get("hostdev_disk_target", "hdb") scsi_wwnn = params.get("scsi_wwnn", "ENTER.YOUR.WWNN") scsi_wwpn = params.get("scsi_wwpn", "ENTER.YOUR.WWPN") attach_method = params.get('attach_method', 'hot') vm = env.get_vm(vm_name) vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} new_vhbas = [] if scsi_wwnn.count("ENTER.YOUR.WWNN") or \ scsi_wwpn.count("ENTER.YOUR.WWPN"): test.cancel("You didn't provide proper wwpn/wwnn") if vm.is_dead(): vm.start() session = vm.wait_for_login() old_parts = libvirt.get_parts_list(session) # find first online hba online_hbas = [] online_hbas = utils_npiv.find_hbas("hba") if not online_hbas: test.cancel("NO ONLINE HBAs!") first_online_hba = online_hbas[0] # create vhba based on the first online hba old_vhbas = utils_npiv.find_hbas("vhba") logging.debug("Original online vHBAs: %s", old_vhbas) new_vhba = utils_npiv.nodedev_create_from_xml( {"nodedev_parent": first_online_hba, "scsi_wwnn": scsi_wwnn, "scsi_wwpn": scsi_wwpn}) if not utils_misc.wait_for(lambda: utils_npiv.is_vhbas_added(old_vhbas), timeout=_TIMEOUT): test.fail("vhba not successfully created") new_vhbas.append(new_vhba) # find first available lun of the newly created vhba lun_dicts = [] first_lun = {} if not utils_misc.wait_for(lambda: utils_npiv.find_scsi_luns(new_vhba), timeout=_TIMEOUT): test.fail("There is no available lun storage for " "wwpn: %s, please check your wwns or " "contact IT admins" % scsi_wwpn) lun_dicts = utils_npiv.find_scsi_luns(new_vhba) logging.debug("The luns discovered are: %s", lun_dicts) first_lun = lun_dicts[0] # prepare hostdev xml for the first lun kwargs = {'addr_bus': first_lun['bus'], 'addr_target': first_lun['target'], 'addr_unit': first_lun['unit']} new_hostdev_xml = utils_npiv.create_hostdev_xml( adapter_name="scsi_host"+first_lun['scsi'], **kwargs) logging.info("New hostdev xml as follow:") logging.info(new_hostdev_xml) new_hostdev_xml.xmltreefile.write() if attach_method == "hot": # attach-device the lun's hostdev xml to guest vm result = virsh.attach_device(vm_name, new_hostdev_xml.xml) libvirt.check_exit_status(result, status_error) elif attach_method == "cold": if vm.is_alive(): vm.destroy(gracefully=False) vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml.devices = vmxml.devices.append(new_hostdev_xml) vmxml.sync() vm.start() session = vm.wait_for_login() logging.debug("The new vm's xml is: \n%s", vmxml) # login vm and check the disk check_result = check_in_vm(vm, device_target, old_parts) if not check_result: test.fail("check disk in vm failed") result = virsh.detach_device(vm_name, new_hostdev_xml.xml) libvirt.check_exit_status(result, status_error) # login vm and check disk actually removed parts_after_detach = libvirt.get_parts_list(session) old_parts.sort() parts_after_detach.sort() if parts_after_detach == old_parts: logging.info("hostdev successfully detached.") else: test.fail("Device not successfully detached. " "Still existing in vm's /proc/partitions") finally: utils_npiv.vhbas_cleanup(new_vhbas) # recover vm if vm.is_alive(): vm.destroy(gracefully=False) logging.info("Restoring vm...") vmxml_backup.sync() process.system('service multipathd restart', verbose=True)
def run(test, params, env): """ Test usb virtual disk plug/unplug. 1.Prepare a vm with usb controllers 2.Prepare a local image 3.Prepare a virtual disk xml 4.Attach the virtual disk to the vm 5.Check the disk in vm 6.Unplug the disk from vm """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) def get_new_disks(old_partitions): """ Get new virtual disks in vm after disk plug. :param old_partitions: Already existing partitions in vm. :return: New disks/partitions in vm. """ session = None try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_partitions = libvirt.get_parts_list(session) added_partitions = list(set(new_partitions).difference(set(old_partitions))) if not added_partitions: logging.debug("No new partitions found in vm.") else: logging.debug("Newly added partition(s) is: %s", added_partitions) return added_partitions except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when get new disk: %s", str(err)) finally: if session: session.close() def check_disk_type(partition): """ Check if a disk partition is a usb disk in vm. :param partition: The disk partition in vm to be checked. :return: If the disk is a usb device, return True. """ session = None try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) cmd = "ls -l /dev/disk/by-id/ | grep %s | grep -i usb" % partition status = session.cmd_status(cmd) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: logging.debug("Error happens when check if new disk is usb device: %s", str(err)) return False finally: if session: session.close() def check_disk_io(partition): """ Check if the disk partition in vm can be normally used. :param partition: The disk partition in vm to be checked. :return: If the disk can be used, return True. """ session = None try: session = vm.wait_for_login() cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(partition)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: logging.debug("Error happens when check disk io in vm: %s", str(err)) return False finally: if session: session.close() def remove_usbs(vmxml): """ Remove all USB devices and controllers from a vm's xml. :param vmxml: The vm's xml. """ try: for xml in vmxml.xmltreefile.findall('/devices/*'): if (xml.get('bus') == 'usb') or (xml.get('type') == 'usb'): vmxml.xmltreefile.remove(xml) except (AttributeError, TypeError): pass # Element doesn't exist at first vmxml.xmltreefile.write() def prepare_usb_controller(vmxml, usb_models): """ Add usb controllers into vm's xml. :param vmxml: The vm's xml. :param usb_models: The usb models will be used in usb controller(s). """ if not usb_models: test.error("No usb model provided.") # Add disk usb controller(s) for usb_model in usb_models: usb_controller = Controller("controller") usb_controller.type = "usb" usb_controller.index = "0" usb_controller.model = usb_model vmxml.add_device(usb_controller) # Redefine domain vmxml.sync() def prepare_local_image(): """ Prepare a local image. :return: The path to the image file. """ image_filename = params.get("image_filename", "raw.img") image_format = params.get("image_format", "raw") image_size = params.get("image_size", "1G") image_path = os.path.join(test.tmpdir, image_filename) try: if image_format in ["raw", "qcow2"]: image_path = libvirt.create_local_disk("file", image_path, image_size, disk_format=image_format) else: test.cancel("We only test raw & qcow2 format for now.") except Exception as err: test.error("Error happens when prepare local image: %s", err) return image_path def prepare_virt_disk_xml(image_path): """ Prepare the virtual disk xml to be attached/detached. :param image_path: The path to the local image. :return: The virtual disk xml. """ virt_disk_device = params.get("virt_disk_device", "disk") virt_disk_device_type = params.get("virt_disk_device_type", "file") virt_disk_device_format = params.get("virt_disk_device_format", "raw") virt_disk_device_target = params.get("virt_disk_device_target", "sdb") virt_disk_device_bus = params.get("virt_disk_device_bus", "usb") disk_xml = Disk(type_name=virt_disk_device_type) disk_xml.device = virt_disk_device disk_src_dict = {'attrs': {'file': image_path, 'type_name': 'file'}} disk_xml.source = disk_xml.new_disk_source(**disk_src_dict) driver_dict = {"name": "qemu", "type": virt_disk_device_format} disk_xml.driver = driver_dict disk_xml.target = {"dev": virt_disk_device_target, "bus": virt_disk_device_bus} return disk_xml usb_models = params.get("usb_model").split() coldplug = "yes" == params.get("coldplug") status_error = "yes" == params.get("status_error") new_disks = [] new_disk = "" attach_options = "" # Get disk partitions info before hot/cold plug virtual disk if vm.is_dead(): vm.start() session = vm.wait_for_login() old_partitions = libvirt.get_parts_list(session) session.close() vm.destroy(gracefully=False) # Backup vm xml vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml = vmxml_backup.copy() try: remove_usbs(vmxml) prepare_usb_controller(vmxml, usb_models) vm.start() session = vm.wait_for_login() disk_xml = prepare_virt_disk_xml(prepare_local_image()) session.close() if coldplug: attach_options = "--config" # Attach virtual disk to vm result = virsh.attach_device(vm_name, disk_xml.xml, flagstr=attach_options, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) # Check the attached disk in vm if coldplug: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() utils_misc.wait_for(lambda: get_new_disks(old_partitions), 20) new_disks = get_new_disks(old_partitions) if len(new_disks) != 1: test.fail("Attached 1 virtual disk but got %s." % len(new_disk)) new_disk = new_disks[0] if not check_disk_type(new_disk): test.fail("The newly attached disk is not a usb one.") if not check_disk_io(new_disk): test.fail("Cannot operate the newly added disk in vm.") # Detach the disk from vm result = virsh.detach_device(vm_name, disk_xml.xml, flagstr=attach_options, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) # Check the detached disk in vm if coldplug: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() utils_misc.wait_for(lambda: not get_new_disks(old_partitions), 20) new_disks = get_new_disks(old_partitions) if len(new_disks) != 0: test.fail("Unplug virtual disk failed.") finally: if vm.is_alive(): vm.destroy(gracefully=False) # Restoring vm vmxml_backup.sync()
def run(test, params, env): """ Test disk encryption option. 1.Prepare test environment,destroy or suspend a VM. 2.Prepare pool, volume. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. 6.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def create_pool(p_name, p_type, p_target): """ Define and start a pool. :param p_name. Pool name. :param p_type. Pool type. :param p_target. Pool target path. """ p_xml = pool_xml.PoolXML(pool_type=p_type) p_xml.name = p_name p_xml.target_path = p_target if not os.path.exists(p_target): os.mkdir(p_target) p_xml.xmltreefile.write() ret = virsh.pool_define(p_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_build(p_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_start(p_name, **virsh_dargs) libvirt.check_exit_status(ret) def create_vol(p_name, target_encrypt_params, vol_params): """ Create volume. :param p_name. Pool name. :param target_encrypt_params encrypt parameters in dict. :param vol_params. Volume parameters dict. :return: True if create successfully. """ volxml = vol_xml.VolXML() v_xml = volxml.new_vol(**vol_params) v_xml.encryption = volxml.new_encryption(**target_encrypt_params) v_xml.xmltreefile.write() ret = virsh.vol_create(p_name, v_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) def create_secret(vol_path): """ Create secret. :param vol_path. volume path. :return: secret id if create successfully. """ sec_xml = secret_xml.SecretXML("no", "yes") sec_xml.description = "volume secret" sec_xml.usage = 'volume' sec_xml.volume = vol_path sec_xml.xmltreefile.write() ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) # Get secret uuid. try: encryption_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() except IndexError as e: test.error("Fail to get newly created secret uuid") logging.debug("Secret uuid %s", encryption_uuid) # Set secret value. encoding = locale.getpreferredencoding() secret_string = base64.b64encode( secret_password_no_encoded.encode(encoding)).decode(encoding) ret = virsh.secret_set_value(encryption_uuid, secret_string, **virsh_dargs) libvirt.check_exit_status(ret) return encryption_uuid def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() rpm_stat = session.cmd_status( "rpm -q parted || " "yum install -y parted", 300) if rpm_stat != 0: test.fail("Failed to query/install parted, make sure" " that you have usable repo in guest") new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False device_source = os.path.join(os.sep, 'dev', added_part) libvirt.mk_label(device_source, session=session) libvirt.mk_part(device_source, size="10M", session=session) # Run partprobe to make the change take effect. process.run("partprobe", ignore_status=True, shell=True) libvirt.mkfs("/dev/%s1" % added_part, "ext3", session=session) cmd = ("mount /dev/%s1 /mnt && echo '123' > /mnt/testfile" " && cat /mnt/testfile && umount /mnt" % added_part) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) session.close() if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") encryption_in_source = "yes" == params.get("encryption_in_source") encryption_out_source = "yes" == params.get("encryption_out_source") if encryption_in_source and not libvirt_version.version_compare(3, 9, 0): test.cancel("Cannot put <encryption> inside disk <source> in " "this libvirt version.") # Pool/Volume options. pool_name = params.get("pool_name") pool_type = params.get("pool_type") pool_target = params.get("pool_target") volume_name = params.get("vol_name") volume_alloc = params.get("vol_alloc") volume_cap_unit = params.get("vol_cap_unit") volume_cap = params.get("vol_cap") volume_target_path = params.get("target_path") volume_target_format = params.get("target_format") volume_target_encypt = params.get("target_encypt", "") volume_target_label = params.get("target_label") hotplug = "yes" == params.get("virt_disk_device_hotplug") status_error = "yes" == params.get("status_error") secret_type = params.get("secret_type", "passphrase") secret_password_no_encoded = params.get("secret_password_no_encoded", "redhat") virt_disk_qcow2_format = "yes" == params.get("virt_disk_qcow2_format") 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) sec_encryption_uuid = None try: # Prepare the disk. sec_uuids = [] create_pool(pool_name, pool_type, pool_target) vol_params = { "name": volume_name, "capacity": int(volume_cap), "allocation": int(volume_alloc), "format": volume_target_format, "path": volume_target_path, "label": volume_target_label, "capacity_unit": volume_cap_unit } vol_encryption_params = {} vol_encryption_params.update({"format": volume_target_encypt}) # For any disk format other than qcow2, it need create secret firstly. if not virt_disk_qcow2_format: # create secret. sec_encryption_uuid = create_secret(volume_target_path) sec_uuids.append(sec_encryption_uuid) vol_encryption_params.update( {"secret": { "type": secret_type, "uuid": sec_encryption_uuid }}) try: # If Libvirt version is lower than 2.5.0 # Creating luks encryption volume is not supported,so skip it. create_vol(pool_name, vol_encryption_params, vol_params) except AssertionError as info: err_msgs = ("create: invalid option") if str(info).count(err_msgs): test.error("Creating luks encryption volume " "is not supported on this libvirt version") else: test.error("Failed to create volume." "Error: %s" % str(info)) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device if device_type == "file": dev_attrs = "file" elif device_type == "dir": dev_attrs = "dir" else: dev_attrs = "dev" disk_source = disk_xml.new_disk_source( **{"attrs": { dev_attrs: volume_target_path }}) disk_xml.driver = { "name": "qemu", "type": volume_target_format, "cache": "none" } disk_xml.target = {"dev": device_target, "bus": device_bus} v_xml = vol_xml.VolXML.new_from_vol_dumpxml(volume_name, pool_name) sec_uuids.append(v_xml.encryption.secret["uuid"]) if not status_error: logging.debug("vol info -- format: %s, type: %s, uuid: %s", v_xml.encryption.format, v_xml.encryption.secret["type"], v_xml.encryption.secret["uuid"]) encryption_dict = { "encryption": v_xml.encryption.format, "secret": { "type": v_xml.encryption.secret["type"], "uuid": v_xml.encryption.secret["uuid"] } } if encryption_in_source: disk_source.encryption = disk_xml.new_encryption( **encryption_dict) if encryption_out_source: disk_xml.encryption = disk_xml.new_encryption( **encryption_dict) disk_xml.source = disk_source logging.debug("disk xml is:\n%s" % disk_xml) if not hotplug: # Sync VM xml. vmxml.add_device(disk_xml) vmxml.sync() try: # Start the VM and do disk hotplug if required, # then check disk status in vm. # Note that LUKS encrypted virtual disk without <encryption> # can be normally started or attached since qemu will just treat # it as RAW, so we don't test LUKS with status_error=TRUE. vm.start() vm.wait_for_login() if status_error: if hotplug: logging.debug("attaching disk, expecting error...") result = virsh.attach_device(vm_name, disk_xml.xml) libvirt.check_exit_status(result, status_error) else: test.fail("VM started unexpectedly.") else: if hotplug: result = virsh.attach_device(vm_name, disk_xml.xml, debug=True) libvirt.check_exit_status(result) if not check_in_vm(vm, device_target, old_parts): test.fail("Check encryption disk in VM failed") result = virsh.detach_device(vm_name, disk_xml.xml, debug=True) libvirt.check_exit_status(result) else: if not check_in_vm(vm, device_target, old_parts): test.fail("Check encryption disk in VM failed") except virt_vm.VMStartError as e: if status_error: if hotplug: test.fail( "In hotplug scenario, VM should " "start successfully but not." "Error: %s", str(e)) else: logging.debug( "VM failed to start as expected." "Error: %s", str(e)) else: # Libvirt2.5.0 onward,AES-CBC encrypted qcow2 images is no # longer supported. err_msgs = ("AES-CBC encrypted qcow2 images is" " no longer supported in system emulators") if str(e).count(err_msgs): test.cancel(err_msgs) else: test.fail("VM failed to start." "Error: %s" % str(e)) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) logging.info("Restoring vm...") vmxml_backup.sync() # Clean up pool, vol for sec_uuid in set(sec_uuids): virsh.secret_undefine(sec_uuid, **virsh_dargs) virsh.vol_delete(volume_name, pool_name, **virsh_dargs) if pool_name in virsh.pool_state_dict(): virsh.pool_destroy(pool_name, **virsh_dargs) virsh.pool_undefine(pool_name, **virsh_dargs)
def run(test, params, env): """ Test disk encryption option. 1.Prepare test environment,destroy or suspend a VM. 2.Prepare pool, volume. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. 6.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def create_pool(p_name, p_type, p_target): """ Define and start a pool. :param p_name. Pool name. :param p_type. Pool type. :param p_target. Pool target path. """ p_xml = pool_xml.PoolXML(pool_type=p_type) p_xml.name = p_name p_xml.target_path = p_target if not os.path.exists(p_target): os.mkdir(p_target) p_xml.xmltreefile.write() ret = virsh.pool_define(p_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_build(p_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_start(p_name, **virsh_dargs) libvirt.check_exit_status(ret) def create_vol(p_name, target_encrypt_params, vol_params): """ Create volume. :param p_name. Pool name. :param target_encrypt_params encrypt parameters in dict. :param vol_params. Volume parameters dict. :return: True if create successfully. """ volxml = vol_xml.VolXML() v_xml = volxml.new_vol(**vol_params) v_xml.encryption = volxml.new_encryption(**target_encrypt_params) v_xml.xmltreefile.write() ret = virsh.vol_create(p_name, v_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) def create_secret(vol_path): """ Create secret. :param vol_path. volume path. :return: secret id if create successfully. """ sec_xml = secret_xml.SecretXML("no", "yes") sec_xml.description = "volume secret" sec_xml.usage = 'volume' sec_xml.volume = vol_path sec_xml.xmltreefile.write() ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) # Get secret uuid. try: encryption_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() except IndexError as e: test.error("Fail to get newly created secret uuid") logging.debug("Secret uuid %s", encryption_uuid) # Set secret value. encoding = locale.getpreferredencoding() secret_string = base64.b64encode(secret_password_no_encoded.encode(encoding)).decode(encoding) ret = virsh.secret_set_value(encryption_uuid, secret_string, **virsh_dargs) libvirt.check_exit_status(ret) return encryption_uuid def check_in_vm(vm, target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() rpm_stat = session.cmd_status("rpm -q parted || " "yum install -y parted", 300) if rpm_stat != 0: test.fail("Failed to query/install parted, make sure" " that you have usable repo in guest") new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False device_source = os.path.join(os.sep, 'dev', added_part) libvirt.mk_label(device_source, session=session) libvirt.mk_part(device_source, size="10M", session=session) # Run partprobe to make the change take effect. process.run("partprobe", ignore_status=True, shell=True) libvirt.mkfs("/dev/%s1" % added_part, "ext3", session=session) cmd = ("mount /dev/%s1 /mnt && echo '123' > /mnt/testfile" " && cat /mnt/testfile && umount /mnt" % added_part) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) session.close() if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") encryption_in_source = "yes" == params.get("encryption_in_source") encryption_out_source = "yes" == params.get("encryption_out_source") if encryption_in_source and not libvirt_version.version_compare(3, 9, 0): test.cancel("Cannot put <encryption> inside disk <source> in " "this libvirt version.") # Pool/Volume options. pool_name = params.get("pool_name") pool_type = params.get("pool_type") pool_target = params.get("pool_target") volume_name = params.get("vol_name") volume_alloc = params.get("vol_alloc") volume_cap_unit = params.get("vol_cap_unit") volume_cap = params.get("vol_cap") volume_target_path = params.get("target_path") volume_target_format = params.get("target_format") volume_target_encypt = params.get("target_encypt", "") volume_target_label = params.get("target_label") hotplug = "yes" == params.get("virt_disk_device_hotplug") status_error = "yes" == params.get("status_error") secret_type = params.get("secret_type", "passphrase") secret_password_no_encoded = params.get("secret_password_no_encoded", "redhat") virt_disk_qcow2_format = "yes" == params.get("virt_disk_qcow2_format") 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) sec_encryption_uuid = None try: # Prepare the disk. sec_uuids = [] create_pool(pool_name, pool_type, pool_target) vol_params = {"name": volume_name, "capacity": int(volume_cap), "allocation": int(volume_alloc), "format": volume_target_format, "path": volume_target_path, "label": volume_target_label, "capacity_unit": volume_cap_unit} vol_encryption_params = {} vol_encryption_params.update({"format": volume_target_encypt}) # For any disk format other than qcow2, it need create secret firstly. if not virt_disk_qcow2_format: # create secret. sec_encryption_uuid = create_secret(volume_target_path) sec_uuids.append(sec_encryption_uuid) vol_encryption_params.update({"secret": {"type": secret_type, "uuid": sec_encryption_uuid}}) try: # If Libvirt version is lower than 2.5.0 # Creating luks encryption volume is not supported,so skip it. create_vol(pool_name, vol_encryption_params, vol_params) except AssertionError as info: err_msgs = ("create: invalid option") if str(info).count(err_msgs): test.error("Creating luks encryption volume " "is not supported on this libvirt version") else: test.error("Failed to create volume." "Error: %s" % str(info)) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device if device_type == "file": dev_attrs = "file" elif device_type == "dir": dev_attrs = "dir" else: dev_attrs = "dev" disk_source = disk_xml.new_disk_source( **{"attrs": {dev_attrs: volume_target_path}}) disk_xml.driver = {"name": "qemu", "type": volume_target_format, "cache": "none"} disk_xml.target = {"dev": device_target, "bus": device_bus} v_xml = vol_xml.VolXML.new_from_vol_dumpxml(volume_name, pool_name) sec_uuids.append(v_xml.encryption.secret["uuid"]) if not status_error: logging.debug("vol info -- format: %s, type: %s, uuid: %s", v_xml.encryption.format, v_xml.encryption.secret["type"], v_xml.encryption.secret["uuid"]) encryption_dict = {"encryption": v_xml.encryption.format, "secret": {"type": v_xml.encryption.secret["type"], "uuid": v_xml.encryption.secret["uuid"]}} if encryption_in_source: disk_source.encryption = disk_xml.new_encryption( **encryption_dict) if encryption_out_source: disk_xml.encryption = disk_xml.new_encryption( **encryption_dict) disk_xml.source = disk_source logging.debug("disk xml is:\n%s" % disk_xml) if not hotplug: # Sync VM xml. vmxml.add_device(disk_xml) vmxml.sync() try: # Start the VM and do disk hotplug if required, # then check disk status in vm. # Note that LUKS encrypted virtual disk without <encryption> # can be normally started or attached since qemu will just treat # it as RAW, so we don't test LUKS with status_error=TRUE. vm.start() vm.wait_for_login() if status_error: if hotplug: logging.debug("attaching disk, expecting error...") result = virsh.attach_device(vm_name, disk_xml.xml) libvirt.check_exit_status(result, status_error) else: test.fail("VM started unexpectedly.") else: if hotplug: result = virsh.attach_device(vm_name, disk_xml.xml, debug=True) libvirt.check_exit_status(result) if not check_in_vm(vm, device_target, old_parts): test.fail("Check encryption disk in VM failed") result = virsh.detach_device(vm_name, disk_xml.xml, debug=True) libvirt.check_exit_status(result) else: if not check_in_vm(vm, device_target, old_parts): test.fail("Check encryption disk in VM failed") except virt_vm.VMStartError as e: if status_error: if hotplug: test.fail("In hotplug scenario, VM should " "start successfully but not." "Error: %s", str(e)) else: logging.debug("VM failed to start as expected." "Error: %s", str(e)) else: # Libvirt2.5.0 onward,AES-CBC encrypted qcow2 images is no # longer supported. err_msgs = ("AES-CBC encrypted qcow2 images is" " no longer supported in system emulators") if str(e).count(err_msgs): test.cancel(err_msgs) else: test.fail("VM failed to start." "Error: %s" % str(e)) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) logging.info("Restoring vm...") vmxml_backup.sync() # Clean up pool, vol for sec_uuid in set(sec_uuids): virsh.secret_undefine(sec_uuid, **virsh_dargs) virsh.vol_delete(volume_name, pool_name, **virsh_dargs) if pool_name in virsh.pool_state_dict(): virsh.pool_destroy(pool_name, **virsh_dargs) virsh.pool_undefine(pool_name, **virsh_dargs)
def run(test, params, env): """ Test disk encryption option. 1.Prepare test environment, destroy or suspend a VM. 2.Prepare tgtd and secret config. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. 6.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def check_save_restore(save_file): """ Test domain save and restore. """ # Save the domain. ret = virsh.save(vm_name, save_file, **virsh_dargs) libvirt.check_exit_status(ret) # Restore the domain. ret = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(ret) def check_snapshot(): """ Test domain snapshot operation. """ snapshot1 = "s1" snapshot2 = "s2" ret = virsh.snapshot_create_as(vm_name, snapshot1) libvirt.check_exit_status(ret) ret = virsh.snapshot_create_as( vm_name, "%s --disk-only --diskspec vda," "file=/tmp/testvm-snap1" % snapshot2) libvirt.check_exit_status(ret, True) ret = virsh.snapshot_create_as( vm_name, "%s --memspec file=%s,snapshot=external" " --diskspec vda,file=/tmp/testvm-snap2" % (snapshot2, snapshot2)) libvirt.check_exit_status(ret, True) def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] elif target.startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Cann't see added partition in VM") return False cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test".format( added_part)) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_qemu_cmd(): """ Check qemu-kvm command line options """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) if driver_iothread: cmd += " | grep iothread=iothread%s" % driver_iothread if process.system(cmd, ignore_status=True, shell=True): test.fail("Can't see disk option '%s' " "in command line" % cmd) # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_format = params.get("virt_disk_device_format", "raw") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") # Controller specific attributes. cntlr_type = params.get('controller_type', None) cntlr_model = params.get('controller_model', None) cntlr_index = params.get('controller_index', None) controller_addr_options = params.get('controller_addr_options', None) driver_iothread = params.get("driver_iothread") # iscsi options. iscsi_target = params.get("iscsi_target") iscsi_host = params.get("iscsi_host") iscsi_port = params.get("iscsi_port") emulated_size = params.get("iscsi_image_size", "1") uuid = params.get("uuid", "") auth_uuid = "yes" == params.get("auth_uuid", "") auth_usage = "yes" == params.get("auth_usage", "") status_error = "yes" == params.get("status_error") define_error = "yes" == params.get("define_error", "no") test_save_snapshot = "yes" == params.get("test_save_snapshot", "no") test_qemu_cmd = "yes" == params.get("test_qemu_cmd", "no") check_partitions = "yes" == params.get("virt_disk_check_partitions", "yes") secret_uuid = "" # 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: chap_user = "" chap_passwd = "" if auth_uuid or auth_usage: auth_place_in_location = params.get("auth_place_in_location") if 'source' in auth_place_in_location and not libvirt_version.version_compare( 3, 9, 0): test.cancel( "place auth in source is not supported in current libvirt version" ) auth_type = params.get("auth_type") secret_usage_target = params.get("secret_usage_target") secret_usage_type = params.get("secret_usage_type") chap_user = params.get("iscsi_user") chap_passwd = params.get("iscsi_password") sec_xml = secret_xml.SecretXML("no", "yes") sec_xml.description = "iSCSI secret" sec_xml.auth_type = auth_type sec_xml.auth_username = chap_user sec_xml.usage = secret_usage_type sec_xml.target = secret_usage_target sec_xml.xmltreefile.write() ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) secret_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() logging.debug("Secret uuid %s", secret_uuid) if secret_uuid == "": test.error("Failed to get secret uuid") # Set secret value encoding = locale.getpreferredencoding() secret_string = base64.b64encode( chap_passwd.encode(encoding)).decode(encoding) ret = virsh.secret_set_value(secret_uuid, secret_string, **virsh_dargs) libvirt.check_exit_status(ret) # Setup iscsi target iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi( is_setup=True, is_login=False, image_size=emulated_size, chap_user=chap_user, chap_passwd=chap_passwd, portal_ip=iscsi_host) # If we use qcow2 disk format, should format iscsi disk first. if device_format == "qcow2": cmd = ( "qemu-img create -f qcow2 iscsi://%s:%s/%s/%s %s" % (iscsi_host, iscsi_port, iscsi_target, lun_num, emulated_size)) process.run(cmd, shell=True) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device disk_xml.target = {"dev": device_target, "bus": device_bus} driver_dict = {"name": "qemu", "type": device_format} # For lun type device, iothread attribute need to be set in controller. if driver_iothread and device != "lun": driver_dict.update({"iothread": driver_iothread}) vmxml.iothreads = int(driver_iothread) elif driver_iothread: vmxml.iothreads = int(driver_iothread) disk_xml.driver = driver_dict # Check if we want to use a faked uuid. if not uuid: uuid = secret_uuid auth_dict = {} if auth_uuid: auth_dict = { "auth_user": chap_user, "secret_type": secret_usage_type, "secret_uuid": uuid } elif auth_usage: auth_dict = { "auth_user": chap_user, "secret_type": secret_usage_type, "secret_usage": secret_usage_target } disk_source = disk_xml.new_disk_source( **{ "attrs": { "protocol": "iscsi", "name": "%s/%s" % (iscsi_target, lun_num) }, "hosts": [{ "name": iscsi_host, "port": iscsi_port }] }) if auth_dict: disk_auth = disk_xml.new_auth(**auth_dict) if 'source' in auth_place_in_location: disk_source.auth = disk_auth if 'disk' in auth_place_in_location: disk_xml.auth = disk_auth disk_xml.source = disk_source # Sync VM xml. vmxml.add_device(disk_xml) # After virtio 1.0 is enabled, lun type device need use virtio-scsi # instead of virtio, so additional controller is needed. # Add controller. if device == "lun": ctrl = Controller(type_name=cntlr_type) if cntlr_model is not None: ctrl.model = cntlr_model if cntlr_index is not None: ctrl.index = cntlr_index ctrl_addr_dict = {} for addr_option in controller_addr_options.split(','): if addr_option != "": addr_part = addr_option.split('=') ctrl_addr_dict.update( {addr_part[0].strip(): addr_part[1].strip()}) ctrl.address = ctrl.new_controller_address(attrs=ctrl_addr_dict) # If driver_iothread is true, need add iothread attribute in controller. if driver_iothread: ctrl_driver_dict = {} ctrl_driver_dict.update({"iothread": driver_iothread}) ctrl.driver = ctrl_driver_dict logging.debug("Controller XML is:%s", ctrl) if cntlr_type: vmxml.del_controller(cntlr_type) else: vmxml.del_controller("scsi") vmxml.add_device(ctrl) try: # Start the VM and check status. vmxml.sync() vm.start() if status_error: test.fail("VM started unexpectedly.") # Check Qemu command line if test_qemu_cmd: check_qemu_cmd() except virt_vm.VMStartError as e: if status_error: if re.search(uuid, str(e)): pass else: test.fail("VM failed to start." "Error: %s" % str(e)) except xcepts.LibvirtXMLError as xml_error: if not define_error: test.fail("Failed to define VM:\n%s" % xml_error) else: # Check partitions in VM. if check_partitions: if not check_in_vm(device_target, old_parts): test.fail("Check disk partitions in VM failed") # Test domain save/restore/snapshot. if test_save_snapshot: save_file = os.path.join(data_dir.get_tmp_dir(), "%.save" % vm_name) check_save_restore(save_file) check_snapshot() if os.path.exists(save_file): os.remove(save_file) finally: # Delete snapshots. libvirt.clean_up_snapshots(vm_name, domxml=vmxml_backup) # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync("--snapshots-metadata") # Delete the tmp files. libvirt.setup_or_cleanup_iscsi(is_setup=False) # Clean up secret if secret_uuid: virsh.secret_undefine(secret_uuid)
def run(test, params, env): """ Test disk encryption option. 1.Prepare test environment, destroy or suspend a VM. 2.Prepare tgtd and secret config. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. 6.Confirm the test result. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def check_save_restore(save_file): """ Test domain save and restore. """ # Save the domain. ret = virsh.save(vm_name, save_file, **virsh_dargs) libvirt.check_exit_status(ret) # Restore the domain. ret = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(ret) def check_snapshot(): """ Test domain snapshot operation. """ snapshot1 = "s1" snapshot2 = "s2" ret = virsh.snapshot_create_as(vm_name, snapshot1) libvirt.check_exit_status(ret) ret = virsh.snapshot_create_as(vm_name, "%s --disk-only --diskspec vda," "file=/tmp/testvm-snap1" % snapshot2) libvirt.check_exit_status(ret, True) ret = virsh.snapshot_create_as(vm_name, "%s --memspec file=%s,snapshot=external" " --diskspec vda,file=/tmp/testvm-snap2" % (snapshot2, snapshot2)) libvirt.check_exit_status(ret, True) def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] elif target.startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Cann't see added partition in VM") return False cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) s, o = session.cmd_status_output(cmd) logging.info("Check disk operation in VM:\n%s", o) if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def check_qemu_cmd(): """ Check qemu-kvm command line options """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) if driver_iothread: cmd += " | grep iothread=iothread%s" % driver_iothread if process.system(cmd, ignore_status=True, shell=True): test.fail("Can't see disk option '%s' " "in command line" % cmd) def check_auth_plaintext(vm_name, password): """ Check if libvirt passed the plaintext of the chap authentication password to qemu. :param vm_name: The name of vm to be checked. :param password: The plaintext of password used for chap authentication. :return: True if using plaintext, False if not. """ cmd = ("ps -ef | grep -v grep | grep qemu-kvm | grep %s | grep %s" % (vm_name, password)) return process.system(cmd, ignore_status=True, shell=True) == 0 # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_format = params.get("virt_disk_device_format", "raw") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") # Controller specific attributes. cntlr_type = params.get('controller_type', None) cntlr_model = params.get('controller_model', None) cntlr_index = params.get('controller_index', None) controller_addr_options = params.get('controller_addr_options', None) driver_iothread = params.get("driver_iothread") # iscsi options. iscsi_target = params.get("iscsi_target") iscsi_host = params.get("iscsi_host") iscsi_port = params.get("iscsi_port") emulated_size = params.get("iscsi_image_size", "1") uuid = params.get("uuid", "") auth_uuid = "yes" == params.get("auth_uuid", "") auth_usage = "yes" == params.get("auth_usage", "") status_error = "yes" == params.get("status_error") define_error = "yes" == params.get("define_error", "no") test_save_snapshot = "yes" == params.get("test_save_snapshot", "no") test_qemu_cmd = "yes" == params.get("test_qemu_cmd", "no") check_partitions = "yes" == params.get("virt_disk_check_partitions", "yes") secret_uuid = "" # 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: chap_user = "" chap_passwd = "" if auth_uuid or auth_usage: auth_place_in_location = params.get("auth_place_in_location") if 'source' in auth_place_in_location and not libvirt_version.version_compare(3, 9, 0): test.cancel("place auth in source is not supported in current libvirt version") auth_type = params.get("auth_type") secret_usage_target = params.get("secret_usage_target") secret_usage_type = params.get("secret_usage_type") chap_user = params.get("iscsi_user") chap_passwd = params.get("iscsi_password") sec_xml = secret_xml.SecretXML("no", "yes") sec_xml.description = "iSCSI secret" sec_xml.auth_type = auth_type sec_xml.auth_username = chap_user sec_xml.usage = secret_usage_type sec_xml.target = secret_usage_target sec_xml.xmltreefile.write() ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) secret_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() logging.debug("Secret uuid %s", secret_uuid) if secret_uuid == "": test.error("Failed to get secret uuid") # Set secret value encoding = locale.getpreferredencoding() secret_string = base64.b64encode(chap_passwd.encode(encoding)).decode(encoding) ret = virsh.secret_set_value(secret_uuid, secret_string, **virsh_dargs) libvirt.check_exit_status(ret) # Setup iscsi target iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi(is_setup=True, is_login=False, image_size=emulated_size, chap_user=chap_user, chap_passwd=chap_passwd, portal_ip=iscsi_host) # If we use qcow2 disk format, should format iscsi disk first. if device_format == "qcow2": cmd = ("qemu-img create -f qcow2 iscsi://%s:%s/%s/%s %s" % (iscsi_host, iscsi_port, iscsi_target, lun_num, emulated_size)) process.run(cmd, shell=True) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device disk_xml.target = {"dev": device_target, "bus": device_bus} driver_dict = {"name": "qemu", "type": device_format} # For lun type device, iothread attribute need to be set in controller. if driver_iothread and device != "lun": driver_dict.update({"iothread": driver_iothread}) vmxml.iothreads = int(driver_iothread) elif driver_iothread: vmxml.iothreads = int(driver_iothread) disk_xml.driver = driver_dict # Check if we want to use a faked uuid. if not uuid: uuid = secret_uuid auth_dict = {} if auth_uuid: auth_dict = {"auth_user": chap_user, "secret_type": secret_usage_type, "secret_uuid": uuid} elif auth_usage: auth_dict = {"auth_user": chap_user, "secret_type": secret_usage_type, "secret_usage": secret_usage_target} disk_source = disk_xml.new_disk_source( **{"attrs": {"protocol": "iscsi", "name": "%s/%s" % (iscsi_target, lun_num)}, "hosts": [{"name": iscsi_host, "port": iscsi_port}]}) if auth_dict: disk_auth = disk_xml.new_auth(**auth_dict) if 'source' in auth_place_in_location: disk_source.auth = disk_auth if 'disk' in auth_place_in_location: disk_xml.auth = disk_auth disk_xml.source = disk_source # Sync VM xml. vmxml.add_device(disk_xml) # After virtio 1.0 is enabled, lun type device need use virtio-scsi # instead of virtio, so additional controller is needed. # Add controller. if device == "lun": ctrl = Controller(type_name=cntlr_type) if cntlr_model is not None: ctrl.model = cntlr_model if cntlr_index is not None: ctrl.index = cntlr_index ctrl_addr_dict = {} for addr_option in controller_addr_options.split(','): if addr_option != "": addr_part = addr_option.split('=') ctrl_addr_dict.update({addr_part[0].strip(): addr_part[1].strip()}) ctrl.address = ctrl.new_controller_address(attrs=ctrl_addr_dict) # If driver_iothread is true, need add iothread attribute in controller. if driver_iothread: ctrl_driver_dict = {} ctrl_driver_dict.update({"iothread": driver_iothread}) ctrl.driver = ctrl_driver_dict logging.debug("Controller XML is:%s", ctrl) if cntlr_type: vmxml.del_controller(cntlr_type) else: vmxml.del_controller("scsi") vmxml.add_device(ctrl) try: # Start the VM and check status. vmxml.sync() vm.start() if status_error: test.fail("VM started unexpectedly.") # Check Qemu command line if test_qemu_cmd: check_qemu_cmd() except virt_vm.VMStartError as e: if status_error: if re.search(uuid, str(e)): pass else: test.fail("VM failed to start." "Error: %s" % str(e)) except xcepts.LibvirtXMLError as xml_error: if not define_error: test.fail("Failed to define VM:\n%s" % xml_error) else: # Check partitions in VM. if check_partitions: if not check_in_vm(device_target, old_parts): test.fail("Check disk partitions in VM failed") # Test domain save/restore/snapshot. if test_save_snapshot: save_file = os.path.join(data_dir.get_tmp_dir(), "%.save" % vm_name) check_save_restore(save_file) check_snapshot() if os.path.exists(save_file): os.remove(save_file) # Test libvirt doesn't pass the plaintext of chap password to qemu, # this function is implemented in libvirt 4.3.0-1. if (libvirt_version.version_compare(4, 3, 0) and (auth_uuid or auth_usage) and chap_passwd): if(check_auth_plaintext(vm_name, chap_passwd)): test.fail("Libvirt should not pass plaintext of chap " "password to qemu-kvm.") finally: # Delete snapshots. libvirt.clean_up_snapshots(vm_name, domxml=vmxml_backup) # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync("--snapshots-metadata") # Delete the tmp files. libvirt.setup_or_cleanup_iscsi(is_setup=False) # Clean up secret if secret_uuid: virsh.secret_undefine(secret_uuid)
def run(test, params, env): """ Test rbd disk device. 1.Prepare test environment,destroy or suspend a VM. 2.Prepare disk image. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} additional_xml_file = os.path.join(data_dir.get_tmp_dir(), "additional_disk.xml") def config_ceph(): """ Write the configs to the file. """ src_host = disk_src_host.split() src_port = disk_src_port.split() conf_str = "mon_host = " hosts = [] for host, port in zip(src_host, src_port): hosts.append("%s:%s" % (host, port)) with open(disk_src_config, 'w') as f: f.write(conf_str + ','.join(hosts) + '\n') def create_pool(): """ Define and start a pool. """ sp = libvirt_storage.StoragePool() if create_by_xml: p_xml = pool_xml.PoolXML(pool_type=pool_type) p_xml.name = pool_name s_xml = pool_xml.SourceXML() s_xml.vg_name = disk_src_pool source_host = [] for (host_name, host_port) in zip(disk_src_host.split(), disk_src_port.split()): source_host.append({'name': host_name, 'port': host_port}) s_xml.hosts = source_host if auth_type: s_xml.auth_type = auth_type if auth_user: s_xml.auth_username = auth_user if auth_usage: s_xml.secret_usage = auth_usage p_xml.source = s_xml logging.debug("Pool xml: %s", p_xml) p_xml.xmltreefile.write() ret = virsh.pool_define(p_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_build(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_start(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) else: auth_opt = "" if client_name and client_key: auth_opt = ( "--auth-type %s --auth-username %s --secret-usage '%s'" % (auth_type, auth_user, auth_usage)) if not sp.define_rbd_pool( pool_name, mon_host, disk_src_pool, extra=auth_opt): test.fail("Failed to define storage pool") if not sp.build_pool(pool_name): test.fail("Failed to build storage pool") if not sp.start_pool(pool_name): test.fail("Failed to start storage pool") # Check pool operation ret = virsh.pool_refresh(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_uuid(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) # pool-info pool_info = sp.pool_info(pool_name) if pool_info["Autostart"] != 'no': test.fail("Failed to check pool information") # pool-autostart if not sp.set_pool_autostart(pool_name): test.fail("Failed to set pool autostart") pool_info = sp.pool_info(pool_name) if pool_info["Autostart"] != 'yes': test.fail("Failed to check pool information") # pool-autostart --disable if not sp.set_pool_autostart(pool_name, "--disable"): test.fail("Failed to set pool autostart") # If port is not pre-configured, port value should not be hardcoded in pool information. if "yes" == params.get("rbd_port", "no"): if 'port' in virsh.pool_dumpxml(pool_name): test.fail("port attribute should not be in pool information") # find-storage-pool-sources-as if "yes" == params.get("find_storage_pool_sources_as", "no"): ret = virsh.find_storage_pool_sources_as("rbd", mon_host) libvirt.check_result(ret, skip_if=unsupported_err) def create_vol(vol_params): """ Create volume. :param p_name. Pool name. :param vol_params. Volume parameters dict. :return: True if create successfully. """ pvt = libvirt.PoolVolumeTest(test, params) if create_by_xml: pvt.pre_vol_by_xml(pool_name, **vol_params) else: pvt.pre_vol(vol_name, None, '2G', None, pool_name) def check_vol(vol_params): """ Check volume information. """ pv = libvirt_storage.PoolVolume(pool_name) # Supported operation if vol_name not in pv.list_volumes(): test.fail("Volume %s doesn't exist" % vol_name) ret = virsh.vol_dumpxml(vol_name, pool_name) libvirt.check_exit_status(ret) # vol-info if not pv.volume_info(vol_name): test.fail("Can't see volume info") # vol-key ret = virsh.vol_key(vol_name, pool_name) libvirt.check_exit_status(ret) if "%s/%s" % (disk_src_pool, vol_name) not in ret.stdout.strip(): test.fail("Volume key isn't correct") # vol-path ret = virsh.vol_path(vol_name, pool_name) libvirt.check_exit_status(ret) if "%s/%s" % (disk_src_pool, vol_name) not in ret.stdout.strip(): test.fail("Volume path isn't correct") # vol-pool ret = virsh.vol_pool("%s/%s" % (disk_src_pool, vol_name)) libvirt.check_exit_status(ret) if pool_name not in ret.stdout.strip(): test.fail("Volume pool isn't correct") # vol-name ret = virsh.vol_name("%s/%s" % (disk_src_pool, vol_name)) libvirt.check_exit_status(ret) if vol_name not in ret.stdout.strip(): test.fail("Volume name isn't correct") # vol-resize ret = virsh.vol_resize(vol_name, "2G", pool_name) libvirt.check_exit_status(ret) # Not supported operation # vol-clone ret = virsh.vol_clone(vol_name, cloned_vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-create-from volxml = vol_xml.VolXML() vol_params.update({"name": "%s" % create_from_cloned_volume}) v_xml = volxml.new_vol(**vol_params) v_xml.xmltreefile.write() ret = virsh.vol_create_from(pool_name, v_xml.xml, vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-wipe ret = virsh.vol_wipe(vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-upload ret = virsh.vol_upload(vol_name, vm.get_first_disk_devices()['source'], "--pool %s" % pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-download ret = virsh.vol_download(vol_name, cloned_vol_name, "--pool %s" % pool_name) libvirt.check_result(ret, skip_if=unsupported_err) def check_qemu_cmd(): """ Check qemu command line options. """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) process.run(cmd, shell=True) if disk_src_name: cmd += " | grep file=rbd:%s:" % disk_src_name if auth_user and auth_key: cmd += ('id=%s:auth_supported=cephx' % auth_user) if disk_src_config: cmd += " | grep 'conf=%s'" % disk_src_config elif mon_host: hosts = '\:6789\;'.join(mon_host.split()) cmd += " | grep 'mon_host=%s'" % hosts if driver_iothread: cmd += " | grep iothread%s" % driver_iothread # Run the command process.run(cmd, shell=True) def check_save_restore(): """ Test save and restore operation """ save_file = os.path.join(data_dir.get_tmp_dir(), "%s.save" % vm_name) ret = virsh.save(vm_name, save_file, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(ret) if os.path.exists(save_file): os.remove(save_file) # Login to check vm status vm.wait_for_login().close() def check_snapshot(snap_option, target_dev='vda'): """ Test snapshot operation. """ snap_name = "s1" snap_mem = os.path.join(data_dir.get_tmp_dir(), "rbd.mem") snap_disk = os.path.join(data_dir.get_tmp_dir(), "rbd.disk") xml_snap_exp = [ "disk name='%s' snapshot='external' type='file'" % target_dev ] xml_dom_exp = [ "source file='%s'" % snap_disk, "backingStore type='network' index='1'", "source protocol='rbd' name='%s'" % disk_src_name ] if snap_option.count("disk-only"): options = ("%s --diskspec %s,file=%s --disk-only" % (snap_name, target_dev, snap_disk)) elif snap_option.count("disk-mem"): options = ("%s --memspec file=%s --diskspec %s,file=" "%s" % (snap_name, snap_mem, target_dev, snap_disk)) xml_snap_exp.append("memory snapshot='external' file='%s'" % snap_mem) else: options = snap_name ret = virsh.snapshot_create_as(vm_name, options) if test_disk_internal_snapshot or test_disk_readonly: libvirt.check_result(ret, expected_fails=unsupported_err) else: libvirt.check_result(ret, skip_if=unsupported_err) # check xml file. if not ret.exit_status: snap_xml = virsh.snapshot_dumpxml(vm_name, snap_name, debug=True).stdout.strip() dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() # Delete snapshots. libvirt.clean_up_snapshots(vm_name) if os.path.exists(snap_mem): os.remove(snap_mem) if os.path.exists(snap_disk): os.remove(snap_disk) if not all([x in snap_xml for x in xml_snap_exp]): test.fail("Failed to check snapshot xml") if not all([x in dom_xml for x in xml_dom_exp]): test.fail("Failed to check domain xml") def check_blockcopy(target): """ Block copy operation test. """ blk_file = os.path.join(data_dir.get_tmp_dir(), "blk.rbd") if os.path.exists(blk_file): os.remove(blk_file) blk_mirror = ("mirror type='file' file='%s' " "format='raw' job='copy'" % blk_file) # Do blockcopy ret = virsh.blockcopy(vm_name, target, blk_file) libvirt.check_result(ret, skip_if=unsupported_err) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if not dom_xml.count(blk_mirror): test.fail("Can't see block job in domain xml") # Abort ret = virsh.blockjob(vm_name, target, "--abort") libvirt.check_exit_status(ret) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if dom_xml.count(blk_mirror): test.fail("Failed to abort block job") if os.path.exists(blk_file): os.remove(blk_file) # Sleep for a while after abort operation. time.sleep(5) # Do blockcopy again ret = virsh.blockcopy(vm_name, target, blk_file) libvirt.check_exit_status(ret) # Wait for complete def wait_func(): ret = virsh.blockjob(vm_name, target, "--info") return ret.stderr.count("Block Copy: [100 %]") timeout = params.get("blockjob_timeout", 600) utils_misc.wait_for(wait_func, int(timeout)) # Pivot ret = virsh.blockjob(vm_name, target, "--pivot") libvirt.check_exit_status(ret) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if not dom_xml.count("source file='%s'" % blk_file): test.fail("Failed to pivot block job") # Remove the disk file. if os.path.exists(blk_file): os.remove(blk_file) def check_in_vm(vm_obj, target, old_parts, read_only=False): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm_obj.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False cmd = ("mount /dev/{0} /mnt && ls /mnt && (sleep 15;" " touch /mnt/testfile; umount /mnt)".format(added_part)) s, o = session.cmd_status_output(cmd, timeout=60) session.close() logging.info("Check disk operation in VM:\n, %s, %s", s, o) # Readonly fs, check the error messages. # The command may return True, read-only # messges can be found from the command output if read_only: if "Read-only file system" not in o: return False else: return True # Other errors if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def clean_up_volume_snapshots(): """ Get all snapshots for rbd_vol.img volume,unprotect and then clean up them. """ cmd = ("rbd -m {0} {1} info {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) if process.run(cmd, ignore_status=True, shell=True).exit_status: return # Get snapshot list. cmd = ("rbd -m {0} {1} snap" " list {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) snaps_out = process.run(cmd, ignore_status=True, shell=True).stdout_text snap_names = [] if snaps_out: for line in snaps_out.rsplit("\n"): if line.startswith("SNAPID") or line == "": continue snap_line = line.rsplit() if len(snap_line) == 4: snap_names.append(snap_line[1]) logging.debug("Find snapshots: %s", snap_names) # Unprotect snapshot first,otherwise it will fail to purge volume for snap_name in snap_names: cmd = ("rbd -m {0} {1} snap" " unprotect {2}@{3}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name), snap_name)) process.run(cmd, ignore_status=True, shell=True) # Purge volume,and then delete volume. cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} snap" " purge {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) process.run(cmd, ignore_status=True, shell=True) def make_snapshot(): """ make external snapshots. :return external snapshot path list """ logging.info("Making snapshot...") first_disk_source = vm.get_first_disk_devices()['source'] snapshot_path_list = [] snapshot2_file = os.path.join(data_dir.get_tmp_dir(), "mem.s2") snapshot3_file = os.path.join(data_dir.get_tmp_dir(), "mem.s3") snapshot4_file = os.path.join(data_dir.get_tmp_dir(), "mem.s4") snapshot4_disk_file = os.path.join(data_dir.get_tmp_dir(), "disk.s4") snapshot5_file = os.path.join(data_dir.get_tmp_dir(), "mem.s5") snapshot5_disk_file = os.path.join(data_dir.get_tmp_dir(), "disk.s5") # Attempt to take different types of snapshots. snapshots_param_dict = { "s1": "s1 --disk-only --no-metadata", "s2": "s2 --memspec %s --no-metadata" % snapshot2_file, "s3": "s3 --memspec %s --no-metadata --live" % snapshot3_file, "s4": "s4 --memspec %s --diskspec vda,file=%s --no-metadata" % (snapshot4_file, snapshot4_disk_file), "s5": "s5 --memspec %s --diskspec vda,file=%s --live --no-metadata" % (snapshot5_file, snapshot5_disk_file) } for snapshot_name in sorted(snapshots_param_dict.keys()): ret = virsh.snapshot_create_as(vm_name, snapshots_param_dict[snapshot_name], **virsh_dargs) libvirt.check_exit_status(ret) if snapshot_name != 's4' and snapshot_name != 's5': snapshot_path_list.append( first_disk_source.replace('qcow2', snapshot_name)) return snapshot_path_list def get_secret_list(): """ Get secret list. :return secret list """ logging.info("Get secret list ...") secret_list_result = virsh.secret_list() secret_list = results_stdout_52lts( secret_list_result).strip().splitlines() # First two lines contain table header followed by entries # for each secret, such as: # # UUID Usage # -------------------------------------------------------------------------------- # b4e8f6d3-100c-4e71-9f91-069f89742273 ceph client.libvirt secret secret_list = secret_list[2:] result = [] # If secret list is empty. if secret_list: for line in secret_list: # Split on whitespace, assume 1 column linesplit = line.split(None, 1) result.append(linesplit[0]) return result mon_host = params.get("mon_host") disk_src_name = params.get("disk_source_name") disk_src_config = params.get("disk_source_config") disk_src_host = params.get("disk_source_host") disk_src_port = params.get("disk_source_port") disk_src_pool = params.get("disk_source_pool") disk_format = params.get("disk_format", "raw") driver_iothread = params.get("driver_iothread") snap_name = params.get("disk_snap_name") attach_device = "yes" == params.get("attach_device", "no") attach_disk = "yes" == params.get("attach_disk", "no") test_save_restore = "yes" == params.get("test_save_restore", "no") test_snapshot = "yes" == params.get("test_snapshot", "no") test_blockcopy = "yes" == params.get("test_blockcopy", "no") test_qemu_cmd = "yes" == params.get("test_qemu_cmd", "no") test_vm_parts = "yes" == params.get("test_vm_parts", "no") additional_guest = "yes" == params.get("additional_guest", "no") create_snapshot = "yes" == params.get("create_snapshot", "no") convert_image = "yes" == params.get("convert_image", "no") create_volume = "yes" == params.get("create_volume", "no") create_by_xml = "yes" == params.get("create_by_xml", "no") client_key = params.get("client_key") client_name = params.get("client_name") auth_key = params.get("auth_key") auth_user = params.get("auth_user") auth_type = params.get("auth_type") auth_usage = params.get("secret_usage") pool_name = params.get("pool_name") pool_type = params.get("pool_type") vol_name = params.get("vol_name") cloned_vol_name = params.get("cloned_volume", "cloned_test_volume") create_from_cloned_volume = params.get("create_from_cloned_volume", "create_from_cloned_test_volume") vol_cap = params.get("vol_cap") vol_cap_unit = params.get("vol_cap_unit") start_vm = "yes" == params.get("start_vm", "no") test_disk_readonly = "yes" == params.get("test_disk_readonly", "no") test_disk_internal_snapshot = "yes" == params.get( "test_disk_internal_snapshot", "no") test_json_pseudo_protocol = "yes" == params.get("json_pseudo_protocol", "no") disk_snapshot_with_sanlock = "yes" == params.get( "disk_internal_with_sanlock", "no") # Prepare a blank params to confirm if delete the configure at the end of the test ceph_cfg = "" # Create config file if it doesn't exist ceph_cfg = ceph.create_config_file(mon_host) # 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) if additional_guest: guest_name = "%s_%s" % (vm_name, '1') timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout, ignore_status=False) additional_vm = vm.clone(guest_name) if start_vm: virsh.start(guest_name) # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) key_opt = "" secret_uuid = None snapshot_path = None key_file = os.path.join(data_dir.get_tmp_dir(), "ceph.key") img_file = os.path.join(data_dir.get_tmp_dir(), "%s_test.img" % vm_name) front_end_img_file = os.path.join(data_dir.get_tmp_dir(), "%s_frontend_test.img" % vm_name) # Construct a unsupported error message list to skip these kind of tests unsupported_err = [] if driver_iothread: unsupported_err.append('IOThreads not supported') if test_snapshot: unsupported_err.append('live disk snapshot not supported') if test_disk_readonly: if not libvirt_version.version_compare(5, 0, 0): unsupported_err.append('Could not create file: Permission denied') unsupported_err.append('Permission denied') else: unsupported_err.append( 'unsupported configuration: external snapshot ' + 'for readonly disk vdb is not supported') if test_disk_internal_snapshot: unsupported_err.append( 'unsupported configuration: internal snapshot for disk ' + 'vdb unsupported for storage type raw') if test_blockcopy: unsupported_err.append('block copy is not supported') if attach_disk: unsupported_err.append('No such file or directory') if create_volume: unsupported_err.append("backing 'volume' disks isn't yet supported") unsupported_err.append('this function is not supported') try: # Clean up dirty secrets in test environments if there have. dirty_secret_list = get_secret_list() if dirty_secret_list: for dirty_secret_uuid in dirty_secret_list: virsh.secret_undefine(dirty_secret_uuid) # Prepare test environment. qemu_config = LibvirtQemuConfig() if disk_snapshot_with_sanlock: # Install necessary package:sanlock,libvirt-lock-sanlock if not utils_package.package_install(["sanlock"]): test.error("fail to install sanlock") if not utils_package.package_install(["libvirt-lock-sanlock"]): test.error("fail to install libvirt-lock-sanlock") # Set virt_use_sanlock result = process.run("setsebool -P virt_use_sanlock 1", shell=True) if result.exit_status: test.error("Failed to set virt_use_sanlock value") # Update lock_manager in qemu.conf qemu_config.lock_manager = 'sanlock' # Update qemu-sanlock.conf. san_lock_config = LibvirtSanLockConfig() san_lock_config.user = '******' san_lock_config.group = 'sanlock' san_lock_config.host_id = 1 san_lock_config.auto_disk_leases = True process.run("mkdir -p /var/lib/libvirt/sanlock", shell=True) san_lock_config.disk_lease_dir = "/var/lib/libvirt/sanlock" san_lock_config.require_lease_for_disks = False # Start sanlock service and restart libvirtd to enforce changes. result = process.run("systemctl start wdmd", shell=True) if result.exit_status: test.error("Failed to start wdmd service") result = process.run("systemctl start sanlock", shell=True) if result.exit_status: test.error("Failed to start sanlock service") utils_libvirtd.Libvirtd().restart() # Prepare lockspace and lease file for sanlock in order. sanlock_cmd_dict = OrderedDict() sanlock_cmd_dict[ "truncate -s 1M /var/lib/libvirt/sanlock/TEST_LS"] = "Failed to truncate TEST_LS" sanlock_cmd_dict[ "sanlock direct init -s TEST_LS:0:/var/lib/libvirt/sanlock/TEST_LS:0"] = "Failed to sanlock direct init TEST_LS:0" sanlock_cmd_dict[ "chown sanlock:sanlock /var/lib/libvirt/sanlock/TEST_LS"] = "Failed to chown sanlock TEST_LS" sanlock_cmd_dict[ "restorecon -R -v /var/lib/libvirt/sanlock"] = "Failed to restorecon sanlock" sanlock_cmd_dict[ "truncate -s 1M /var/lib/libvirt/sanlock/test-disk-resource-lock"] = "Failed to truncate test-disk-resource-lock" sanlock_cmd_dict[ "sanlock direct init -r TEST_LS:test-disk-resource-lock:" + "/var/lib/libvirt/sanlock/test-disk-resource-lock:0"] = "Failed to sanlock direct init test-disk-resource-lock" sanlock_cmd_dict[ "chown sanlock:sanlock " + "/var/lib/libvirt/sanlock/test-disk-resource-lock"] = "Failed to chown test-disk-resource-loc" sanlock_cmd_dict[ "sanlock client add_lockspace -s TEST_LS:1:" + "/var/lib/libvirt/sanlock/TEST_LS:0"] = "Failed to client add_lockspace -s TEST_LS:0" for sanlock_cmd in sanlock_cmd_dict.keys(): result = process.run(sanlock_cmd, shell=True) if result.exit_status: test.error(sanlock_cmd_dict[sanlock_cmd]) # Create one lease device and add it to VM. san_lock_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) lease_device = Lease() lease_device.lockspace = 'TEST_LS' lease_device.key = 'test-disk-resource-lock' lease_device.target = { 'path': '/var/lib/libvirt/sanlock/test-disk-resource-lock' } san_lock_vmxml.add_device(lease_device) san_lock_vmxml.sync() # Install ceph-common package which include rbd command if utils_package.package_install(["ceph-common"]): if client_name and client_key: with open(key_file, 'w') as f: f.write("[%s]\n\tkey = %s\n" % (client_name, client_key)) key_opt = "--keyring %s" % key_file # Create secret xml sec_xml = secret_xml.SecretXML("no", "no") sec_xml.usage = auth_type sec_xml.usage_name = auth_usage sec_xml.xmltreefile.write() logging.debug("Secret xml: %s", sec_xml) ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) secret_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() logging.debug("Secret uuid %s", secret_uuid) if secret_uuid is None: test.error("Failed to get secret uuid") # Set secret value auth_key = params.get("auth_key") ret = virsh.secret_set_value(secret_uuid, auth_key, **virsh_dargs) libvirt.check_exit_status(ret) # Delete the disk if it exists cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) else: test.error("Failed to install ceph-common") if disk_src_config: config_ceph() disk_path = ("rbd:%s:mon_host=%s" % (disk_src_name, mon_host)) if auth_user and auth_key: disk_path += (":id=%s:key=%s" % (auth_user, auth_key)) targetdev = params.get("disk_target", "vdb") # To be compatible with create_disk_xml function, # some parameters need to be updated. params.update({ "type_name": params.get("disk_type", "network"), "target_bus": params.get("disk_target_bus"), "target_dev": targetdev, "secret_uuid": secret_uuid, "source_protocol": params.get("disk_source_protocol"), "source_name": disk_src_name, "source_host_name": disk_src_host, "source_host_port": disk_src_port }) # Prepare disk image if convert_image: first_disk = vm.get_first_disk_devices() blk_source = first_disk['source'] # Convert the image to remote storage disk_cmd = ("rbd -m %s %s info %s 2> /dev/null|| qemu-img convert" " -O %s %s %s" % (mon_host, key_opt, disk_src_name, disk_format, blk_source, disk_path)) process.run(disk_cmd, ignore_status=False, shell=True) elif create_volume: vol_params = { "name": vol_name, "capacity": int(vol_cap), "capacity_unit": vol_cap_unit, "format": disk_format } create_pool() create_vol(vol_params) check_vol(vol_params) else: # Create an local image and make FS on it. disk_cmd = ("qemu-img create -f %s %s 10M && mkfs.ext4 -F %s" % (disk_format, img_file, img_file)) process.run(disk_cmd, ignore_status=False, shell=True) # Convert the image to remote storage disk_cmd = ( "rbd -m %s %s info %s 2> /dev/null|| qemu-img convert -O" " %s %s %s" % (mon_host, key_opt, disk_src_name, disk_format, img_file, disk_path)) process.run(disk_cmd, ignore_status=False, shell=True) # Create disk snapshot if needed. if create_snapshot: snap_cmd = ("rbd -m %s %s snap create %s@%s" % (mon_host, key_opt, disk_src_name, snap_name)) process.run(snap_cmd, ignore_status=False, shell=True) if test_json_pseudo_protocol: # Create one frontend image with the rbd backing file. json_str = ('json:{"file.driver":"rbd",' '"file.filename":"rbd:%s:mon_host=%s"}' % (disk_src_name, mon_host)) # pass different json string according to the auth config if auth_user and auth_key: json_str = ('%s:id=%s:key=%s"}' % (json_str[:-2], auth_user, auth_key)) disk_cmd = ("qemu-img create -f qcow2 -b '%s' %s" % (json_str, front_end_img_file)) disk_path = front_end_img_file process.run(disk_cmd, ignore_status=False, shell=True) # If hot plug, start VM first, and then wait the OS boot. # Otherwise stop VM if running. if start_vm: if vm.is_dead(): vm.start() vm.wait_for_login().close() else: if not vm.is_dead(): vm.destroy() if attach_device: if create_volume: params.update({"source_pool": pool_name}) params.update({"type_name": "volume"}) # No need auth options for volume if "auth_user" in params: params.pop("auth_user") if "auth_type" in params: params.pop("auth_type") if "secret_type" in params: params.pop("secret_type") if "secret_uuid" in params: params.pop("secret_uuid") if "secret_usage" in params: params.pop("secret_usage") xml_file = libvirt.create_disk_xml(params) if additional_guest: # Copy xml_file for additional guest VM. shutil.copyfile(xml_file, additional_xml_file) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) if additional_guest: # Make sure the additional VM is running if additional_vm.is_dead(): additional_vm.start() additional_vm.wait_for_login().close() ret = virsh.attach_device(guest_name, additional_xml_file, "", debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif attach_disk: opts = params.get("attach_option", "") ret = virsh.attach_disk(vm_name, disk_path, targetdev, opts) libvirt.check_result(ret, skip_if=unsupported_err) elif test_disk_readonly: params.update({'readonly': "yes"}) xml_file = libvirt.create_disk_xml(params) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif test_disk_internal_snapshot: xml_file = libvirt.create_disk_xml(params) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif disk_snapshot_with_sanlock: if vm.is_dead(): vm.start() snapshot_path = make_snapshot() if vm.is_alive(): vm.destroy() elif not create_volume: libvirt.set_vm_disk(vm, params) if test_blockcopy: logging.info("Creating %s...", vm_name) vmxml_for_test = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) if vm.is_alive(): vm.destroy(gracefully=False) vm.undefine() if virsh.create(vmxml_for_test.xml, **virsh_dargs).exit_status: vmxml_backup.define() test.fail("Can't create the domain") elif vm.is_dead(): vm.start() # Wait for vm is running vm.wait_for_login(timeout=600).close() if additional_guest: if additional_vm.is_dead(): additional_vm.start() # Check qemu command line if test_qemu_cmd: check_qemu_cmd() # Check partitions in vm if test_vm_parts: if not check_in_vm( vm, targetdev, old_parts, read_only=create_snapshot): test.fail("Failed to check vm partitions") if additional_guest: if not check_in_vm(additional_vm, targetdev, old_parts): test.fail("Failed to check vm partitions") # Save and restore operation if test_save_restore: check_save_restore() if test_snapshot: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option) if test_blockcopy: check_blockcopy(targetdev) if test_disk_readonly: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option, 'vdb') if test_disk_internal_snapshot: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option, targetdev) # Detach the device. if attach_device: xml_file = libvirt.create_disk_xml(params) ret = virsh.detach_device(vm_name, xml_file) libvirt.check_exit_status(ret) if additional_guest: ret = virsh.detach_device(guest_name, xml_file) libvirt.check_exit_status(ret) elif attach_disk: ret = virsh.detach_disk(vm_name, targetdev) libvirt.check_exit_status(ret) # Check disk in vm after detachment. if attach_device or attach_disk: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) if len(new_parts) != len(old_parts): test.fail("Disk still exists in vm" " after detachment") session.close() except virt_vm.VMStartError as details: for msg in unsupported_err: if msg in str(details): test.cancel(str(details)) else: test.fail("VM failed to start." "Error: %s" % str(details)) finally: # Remove ceph configure file if created. if ceph_cfg: os.remove(ceph_cfg) # Delete snapshots. 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") # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) if additional_guest: virsh.remove_domain(guest_name, "--remove-all-storage", ignore_stauts=True) # Remove the snapshot. if create_snapshot: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} snap" " purge {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) elif create_volume: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, cloned_vol_name))) process.run(cmd, ignore_status=True, shell=True) cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format( mon_host, key_opt, os.path.join(disk_src_pool, create_from_cloned_volume))) process.run(cmd, ignore_status=True, shell=True) clean_up_volume_snapshots() else: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) # Delete tmp files. if os.path.exists(key_file): os.remove(key_file) if os.path.exists(img_file): os.remove(img_file) # Clean up volume, pool if vol_name and vol_name in str(virsh.vol_list(pool_name).stdout): virsh.vol_delete(vol_name, pool_name) if pool_name and pool_name in virsh.pool_state_dict(): virsh.pool_destroy(pool_name, **virsh_dargs) virsh.pool_undefine(pool_name, **virsh_dargs) # Clean up secret secret_list = get_secret_list() if secret_list: for secret_uuid in secret_list: virsh.secret_undefine(secret_uuid) logging.info("Restoring vm...") vmxml_backup.sync() if disk_snapshot_with_sanlock: # Restore virt_use_sanlock setting. process.run("setsebool -P virt_use_sanlock 0", shell=True) # Restore qemu config qemu_config.restore() utils_libvirtd.Libvirtd().restart() # Force shutdown sanlock service. process.run("sanlock client shutdown -f 1", shell=True) # Clean up lockspace folder process.run("rm -rf /var/lib/libvirt/sanlock/*", shell=True) if snapshot_path is not None: for snapshot in snapshot_path: if os.path.exists(snapshot): os.remove(snapshot)
at_options += (" --address %s" % address) if cache_options: if cache_options.count("directsync"): if not libvirt_version.version_compare(1, 0, 0): raise error.TestNAError("'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(test.tmpdir, device_source_name) if test_block_dev: device_source = libvirt.setup_or_cleanup_iscsi(True) if not device_source: # We should skip this case raise error.TestNAError("Can not get iscsi device name in host") if test_logcial_dev: lv_utils.vg_create(vg_name, device_source)
def run(test, params, env): """ Test rbd disk device. 1.Prepare test environment,destroy or suspend a VM. 2.Prepare disk image. 3.Edit disks xml and start the domain. 4.Perform test operation. 5.Recover test environment. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} additional_xml_file = os.path.join(data_dir.get_tmp_dir(), "additional_disk.xml") def config_ceph(): """ Write the configs to the file. """ src_host = disk_src_host.split() src_port = disk_src_port.split() conf_str = "mon_host = " hosts = [] for host, port in zip(src_host, src_port): hosts.append("%s:%s" % (host, port)) with open(disk_src_config, 'w') as f: f.write(conf_str + ','.join(hosts) + '\n') def create_pool(): """ Define and start a pool. """ sp = libvirt_storage.StoragePool() if create_by_xml: p_xml = pool_xml.PoolXML(pool_type=pool_type) p_xml.name = pool_name s_xml = pool_xml.SourceXML() s_xml.vg_name = disk_src_pool source_host = [] for (host_name, host_port) in zip( disk_src_host.split(), disk_src_port.split()): source_host.append({'name': host_name, 'port': host_port}) s_xml.hosts = source_host if auth_type: s_xml.auth_type = auth_type if auth_user: s_xml.auth_username = auth_user if auth_usage: s_xml.secret_usage = auth_usage p_xml.source = s_xml logging.debug("Pool xml: %s", p_xml) p_xml.xmltreefile.write() ret = virsh.pool_define(p_xml.xml, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_build(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_start(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) else: auth_opt = "" if client_name and client_key: auth_opt = ("--auth-type %s --auth-username %s --secret-usage '%s'" % (auth_type, auth_user, auth_usage)) if not sp.define_rbd_pool(pool_name, mon_host, disk_src_pool, extra=auth_opt): test.fail("Failed to define storage pool") if not sp.build_pool(pool_name): test.fail("Failed to build storage pool") if not sp.start_pool(pool_name): test.fail("Failed to start storage pool") # Check pool operation ret = virsh.pool_refresh(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.pool_uuid(pool_name, **virsh_dargs) libvirt.check_exit_status(ret) # pool-info pool_info = sp.pool_info(pool_name) if pool_info["Autostart"] != 'no': test.fail("Failed to check pool information") # pool-autostart if not sp.set_pool_autostart(pool_name): test.fail("Failed to set pool autostart") pool_info = sp.pool_info(pool_name) if pool_info["Autostart"] != 'yes': test.fail("Failed to check pool information") # pool-autostart --disable if not sp.set_pool_autostart(pool_name, "--disable"): test.fail("Failed to set pool autostart") # If port is not pre-configured, port value should not be hardcoded in pool information. if "yes" == params.get("rbd_port", "no"): if 'port' in virsh.pool_dumpxml(pool_name): test.fail("port attribute should not be in pool information") # find-storage-pool-sources-as if "yes" == params.get("find_storage_pool_sources_as", "no"): ret = virsh.find_storage_pool_sources_as("rbd", mon_host) libvirt.check_result(ret, skip_if=unsupported_err) def create_vol(vol_params): """ Create volume. :param p_name. Pool name. :param vol_params. Volume parameters dict. :return: True if create successfully. """ pvt = libvirt.PoolVolumeTest(test, params) if create_by_xml: pvt.pre_vol_by_xml(pool_name, **vol_params) else: pvt.pre_vol(vol_name, None, '2G', None, pool_name) def check_vol(vol_params): """ Check volume information. """ pv = libvirt_storage.PoolVolume(pool_name) # Supported operation if vol_name not in pv.list_volumes(): test.fail("Volume %s doesn't exist" % vol_name) ret = virsh.vol_dumpxml(vol_name, pool_name) libvirt.check_exit_status(ret) # vol-info if not pv.volume_info(vol_name): test.fail("Can't see volume info") # vol-key ret = virsh.vol_key(vol_name, pool_name) libvirt.check_exit_status(ret) if "%s/%s" % (disk_src_pool, vol_name) not in ret.stdout.strip(): test.fail("Volume key isn't correct") # vol-path ret = virsh.vol_path(vol_name, pool_name) libvirt.check_exit_status(ret) if "%s/%s" % (disk_src_pool, vol_name) not in ret.stdout.strip(): test.fail("Volume path isn't correct") # vol-pool ret = virsh.vol_pool("%s/%s" % (disk_src_pool, vol_name)) libvirt.check_exit_status(ret) if pool_name not in ret.stdout.strip(): test.fail("Volume pool isn't correct") # vol-name ret = virsh.vol_name("%s/%s" % (disk_src_pool, vol_name)) libvirt.check_exit_status(ret) if vol_name not in ret.stdout.strip(): test.fail("Volume name isn't correct") # vol-resize ret = virsh.vol_resize(vol_name, "2G", pool_name) libvirt.check_exit_status(ret) # Not supported operation # vol-clone ret = virsh.vol_clone(vol_name, cloned_vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-create-from volxml = vol_xml.VolXML() vol_params.update({"name": "%s" % create_from_cloned_volume}) v_xml = volxml.new_vol(**vol_params) v_xml.xmltreefile.write() ret = virsh.vol_create_from(pool_name, v_xml.xml, vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-wipe ret = virsh.vol_wipe(vol_name, pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-upload ret = virsh.vol_upload(vol_name, vm.get_first_disk_devices()['source'], "--pool %s" % pool_name) libvirt.check_result(ret, skip_if=unsupported_err) # vol-download ret = virsh.vol_download(vol_name, cloned_vol_name, "--pool %s" % pool_name) libvirt.check_result(ret, skip_if=unsupported_err) def check_qemu_cmd(): """ Check qemu command line options. """ cmd = ("ps -ef | grep %s | grep -v grep " % vm_name) process.run(cmd, shell=True) if disk_src_name: cmd += " | grep file=rbd:%s:" % disk_src_name if auth_user and auth_key: cmd += ('id=%s:auth_supported=cephx' % auth_user) if disk_src_config: cmd += " | grep 'conf=%s'" % disk_src_config elif mon_host: hosts = '\:6789\;'.join(mon_host.split()) cmd += " | grep 'mon_host=%s'" % hosts if driver_iothread: cmd += " | grep iothread%s" % driver_iothread # Run the command process.run(cmd, shell=True) def check_save_restore(): """ Test save and restore operation """ save_file = os.path.join(data_dir.get_tmp_dir(), "%s.save" % vm_name) ret = virsh.save(vm_name, save_file, **virsh_dargs) libvirt.check_exit_status(ret) ret = virsh.restore(save_file, **virsh_dargs) libvirt.check_exit_status(ret) if os.path.exists(save_file): os.remove(save_file) # Login to check vm status vm.wait_for_login().close() def check_snapshot(snap_option, target_dev='vda'): """ Test snapshot operation. """ snap_name = "s1" snap_mem = os.path.join(data_dir.get_tmp_dir(), "rbd.mem") snap_disk = os.path.join(data_dir.get_tmp_dir(), "rbd.disk") xml_snap_exp = ["disk name='%s' snapshot='external' type='file'" % target_dev] xml_dom_exp = ["source file='%s'" % snap_disk, "backingStore type='network' index='1'", "source protocol='rbd' name='%s'" % disk_src_name] if snap_option.count("disk-only"): options = ("%s --diskspec %s,file=%s --disk-only" % (snap_name, target_dev, snap_disk)) elif snap_option.count("disk-mem"): options = ("%s --memspec file=%s --diskspec %s,file=" "%s" % (snap_name, snap_mem, target_dev, snap_disk)) xml_snap_exp.append("memory snapshot='external' file='%s'" % snap_mem) else: options = snap_name ret = virsh.snapshot_create_as(vm_name, options) if test_disk_internal_snapshot or test_disk_readonly: libvirt.check_result(ret, expected_fails=unsupported_err) else: libvirt.check_result(ret, skip_if=unsupported_err) # check xml file. if not ret.exit_status: snap_xml = virsh.snapshot_dumpxml(vm_name, snap_name, debug=True).stdout.strip() dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() # Delete snapshots. libvirt.clean_up_snapshots(vm_name) if os.path.exists(snap_mem): os.remove(snap_mem) if os.path.exists(snap_disk): os.remove(snap_disk) if not all([x in snap_xml for x in xml_snap_exp]): test.fail("Failed to check snapshot xml") if not all([x in dom_xml for x in xml_dom_exp]): test.fail("Failed to check domain xml") def check_blockcopy(target): """ Block copy operation test. """ blk_file = os.path.join(data_dir.get_tmp_dir(), "blk.rbd") if os.path.exists(blk_file): os.remove(blk_file) blk_mirror = ("mirror type='file' file='%s' " "format='raw' job='copy'" % blk_file) # Do blockcopy ret = virsh.blockcopy(vm_name, target, blk_file) libvirt.check_result(ret, skip_if=unsupported_err) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if not dom_xml.count(blk_mirror): test.fail("Can't see block job in domain xml") # Abort ret = virsh.blockjob(vm_name, target, "--abort") libvirt.check_exit_status(ret) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if dom_xml.count(blk_mirror): test.fail("Failed to abort block job") if os.path.exists(blk_file): os.remove(blk_file) # Sleep for a while after abort operation. time.sleep(5) # Do blockcopy again ret = virsh.blockcopy(vm_name, target, blk_file) libvirt.check_exit_status(ret) # Wait for complete def wait_func(): ret = virsh.blockjob(vm_name, target, "--info") return ret.stderr.count("Block Copy: [100 %]") timeout = params.get("blockjob_timeout", 600) utils_misc.wait_for(wait_func, int(timeout)) # Pivot ret = virsh.blockjob(vm_name, target, "--pivot") libvirt.check_exit_status(ret) dom_xml = virsh.dumpxml(vm_name, debug=True).stdout.strip() if not dom_xml.count("source file='%s'" % blk_file): test.fail("Failed to pivot block job") # Remove the disk file. if os.path.exists(blk_file): os.remove(blk_file) def check_in_vm(vm_obj, target, old_parts, read_only=False): """ Check mount/read/write disk in VM. :param vm. VM guest. :param target. Disk dev in VM. :return: True if check successfully. """ try: session = vm_obj.wait_for_login() new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False added_part = None if target.startswith("vd"): if added_parts[0].startswith("vd"): added_part = added_parts[0] elif target.startswith("hd"): if added_parts[0].startswith("sd"): added_part = added_parts[0] if not added_part: logging.error("Can't see added partition in VM") return False cmd = ("mount /dev/{0} /mnt && ls /mnt && (sleep 15;" " touch /mnt/testfile; umount /mnt)" .format(added_part)) s, o = session.cmd_status_output(cmd, timeout=60) session.close() logging.info("Check disk operation in VM:\n, %s, %s", s, o) # Readonly fs, check the error messages. # The command may return True, read-only # messges can be found from the command output if read_only: if "Read-only file system" not in o: return False else: return True # Other errors if s != 0: return False return True except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False def clean_up_volume_snapshots(): """ Get all snapshots for rbd_vol.img volume,unprotect and then clean up them. """ cmd = ("rbd -m {0} {1} info {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) if process.run(cmd, ignore_status=True, shell=True).exit_status: return # Get snapshot list. cmd = ("rbd -m {0} {1} snap" " list {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) snaps_out = process.run(cmd, ignore_status=True, shell=True).stdout_text snap_names = [] if snaps_out: for line in snaps_out.rsplit("\n"): if line.startswith("SNAPID") or line == "": continue snap_line = line.rsplit() if len(snap_line) == 4: snap_names.append(snap_line[1]) logging.debug("Find snapshots: %s", snap_names) # Unprotect snapshot first,otherwise it will fail to purge volume for snap_name in snap_names: cmd = ("rbd -m {0} {1} snap" " unprotect {2}@{3}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name), snap_name)) process.run(cmd, ignore_status=True, shell=True) # Purge volume,and then delete volume. cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} snap" " purge {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, vol_name))) process.run(cmd, ignore_status=True, shell=True) def make_snapshot(): """ make external snapshots. :return external snapshot path list """ logging.info("Making snapshot...") first_disk_source = vm.get_first_disk_devices()['source'] snapshot_path_list = [] snapshot2_file = os.path.join(data_dir.get_tmp_dir(), "mem.s2") snapshot3_file = os.path.join(data_dir.get_tmp_dir(), "mem.s3") snapshot4_file = os.path.join(data_dir.get_tmp_dir(), "mem.s4") snapshot4_disk_file = os.path.join(data_dir.get_tmp_dir(), "disk.s4") snapshot5_file = os.path.join(data_dir.get_tmp_dir(), "mem.s5") snapshot5_disk_file = os.path.join(data_dir.get_tmp_dir(), "disk.s5") # Attempt to take different types of snapshots. snapshots_param_dict = {"s1": "s1 --disk-only --no-metadata", "s2": "s2 --memspec %s --no-metadata" % snapshot2_file, "s3": "s3 --memspec %s --no-metadata --live" % snapshot3_file, "s4": "s4 --memspec %s --diskspec vda,file=%s --no-metadata" % (snapshot4_file, snapshot4_disk_file), "s5": "s5 --memspec %s --diskspec vda,file=%s --live --no-metadata" % (snapshot5_file, snapshot5_disk_file)} for snapshot_name in sorted(snapshots_param_dict.keys()): ret = virsh.snapshot_create_as(vm_name, snapshots_param_dict[snapshot_name], **virsh_dargs) libvirt.check_exit_status(ret) if snapshot_name != 's4' and snapshot_name != 's5': snapshot_path_list.append(first_disk_source.replace('qcow2', snapshot_name)) return snapshot_path_list def get_secret_list(): """ Get secret list. :return secret list """ logging.info("Get secret list ...") secret_list_result = virsh.secret_list() secret_list = results_stdout_52lts(secret_list_result).strip().splitlines() # First two lines contain table header followed by entries # for each secret, such as: # # UUID Usage # -------------------------------------------------------------------------------- # b4e8f6d3-100c-4e71-9f91-069f89742273 ceph client.libvirt secret secret_list = secret_list[2:] result = [] # If secret list is empty. if secret_list: for line in secret_list: # Split on whitespace, assume 1 column linesplit = line.split(None, 1) result.append(linesplit[0]) return result mon_host = params.get("mon_host") disk_src_name = params.get("disk_source_name") disk_src_config = params.get("disk_source_config") disk_src_host = params.get("disk_source_host") disk_src_port = params.get("disk_source_port") disk_src_pool = params.get("disk_source_pool") disk_format = params.get("disk_format", "raw") driver_iothread = params.get("driver_iothread") snap_name = params.get("disk_snap_name") attach_device = "yes" == params.get("attach_device", "no") attach_disk = "yes" == params.get("attach_disk", "no") test_save_restore = "yes" == params.get("test_save_restore", "no") test_snapshot = "yes" == params.get("test_snapshot", "no") test_blockcopy = "yes" == params.get("test_blockcopy", "no") test_qemu_cmd = "yes" == params.get("test_qemu_cmd", "no") test_vm_parts = "yes" == params.get("test_vm_parts", "no") additional_guest = "yes" == params.get("additional_guest", "no") create_snapshot = "yes" == params.get("create_snapshot", "no") convert_image = "yes" == params.get("convert_image", "no") create_volume = "yes" == params.get("create_volume", "no") create_by_xml = "yes" == params.get("create_by_xml", "no") client_key = params.get("client_key") client_name = params.get("client_name") auth_key = params.get("auth_key") auth_user = params.get("auth_user") auth_type = params.get("auth_type") auth_usage = params.get("secret_usage") pool_name = params.get("pool_name") pool_type = params.get("pool_type") vol_name = params.get("vol_name") cloned_vol_name = params.get("cloned_volume", "cloned_test_volume") create_from_cloned_volume = params.get("create_from_cloned_volume", "create_from_cloned_test_volume") vol_cap = params.get("vol_cap") vol_cap_unit = params.get("vol_cap_unit") start_vm = "yes" == params.get("start_vm", "no") test_disk_readonly = "yes" == params.get("test_disk_readonly", "no") test_disk_internal_snapshot = "yes" == params.get("test_disk_internal_snapshot", "no") test_json_pseudo_protocol = "yes" == params.get("json_pseudo_protocol", "no") disk_snapshot_with_sanlock = "yes" == params.get("disk_internal_with_sanlock", "no") # Create /etc/ceph/ceph.conf file to suppress false warning error message. process.run("mkdir -p /etc/ceph", ignore_status=True, shell=True) cmd = ("echo 'mon_host = {0}' >/etc/ceph/ceph.conf" .format(mon_host)) process.run(cmd, ignore_status=True, shell=True) # 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) if additional_guest: guest_name = "%s_%s" % (vm_name, '1') timeout = params.get("clone_timeout", 360) utils_libguestfs.virt_clone_cmd(vm_name, guest_name, True, timeout=timeout, ignore_status=False) additional_vm = vm.clone(guest_name) if start_vm: virsh.start(guest_name) # Back up xml file. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) key_opt = "" secret_uuid = None snapshot_path = None key_file = os.path.join(data_dir.get_tmp_dir(), "ceph.key") img_file = os.path.join(data_dir.get_tmp_dir(), "%s_test.img" % vm_name) front_end_img_file = os.path.join(data_dir.get_tmp_dir(), "%s_frontend_test.img" % vm_name) # Construct a unsupported error message list to skip these kind of tests unsupported_err = [] if driver_iothread: unsupported_err.append('IOThreads not supported') if test_snapshot: unsupported_err.append('live disk snapshot not supported') if test_disk_readonly: if not libvirt_version.version_compare(5, 0, 0): unsupported_err.append('Could not create file: Permission denied') unsupported_err.append('Permission denied') else: unsupported_err.append('unsupported configuration: external snapshot ' + 'for readonly disk vdb is not supported') if test_disk_internal_snapshot: unsupported_err.append('unsupported configuration: internal snapshot for disk ' + 'vdb unsupported for storage type raw') if test_blockcopy: unsupported_err.append('block copy is not supported') if attach_disk: unsupported_err.append('No such file or directory') if create_volume: unsupported_err.append("backing 'volume' disks isn't yet supported") unsupported_err.append('this function is not supported') try: # Clean up dirty secrets in test environments if there have. dirty_secret_list = get_secret_list() if dirty_secret_list: for dirty_secret_uuid in dirty_secret_list: virsh.secret_undefine(dirty_secret_uuid) # Prepare test environment. qemu_config = LibvirtQemuConfig() if disk_snapshot_with_sanlock: # Install necessary package:sanlock,libvirt-lock-sanlock if not utils_package.package_install(["sanlock"]): test.error("fail to install sanlock") if not utils_package.package_install(["libvirt-lock-sanlock"]): test.error("fail to install libvirt-lock-sanlock") # Set virt_use_sanlock result = process.run("setsebool -P virt_use_sanlock 1", shell=True) if result.exit_status: test.error("Failed to set virt_use_sanlock value") # Update lock_manager in qemu.conf qemu_config.lock_manager = 'sanlock' # Update qemu-sanlock.conf. san_lock_config = LibvirtSanLockConfig() san_lock_config.user = '******' san_lock_config.group = 'sanlock' san_lock_config.host_id = 1 san_lock_config.auto_disk_leases = True process.run("mkdir -p /var/lib/libvirt/sanlock", shell=True) san_lock_config.disk_lease_dir = "/var/lib/libvirt/sanlock" san_lock_config.require_lease_for_disks = False # Start sanlock service and restart libvirtd to enforce changes. result = process.run("systemctl start wdmd", shell=True) if result.exit_status: test.error("Failed to start wdmd service") result = process.run("systemctl start sanlock", shell=True) if result.exit_status: test.error("Failed to start sanlock service") utils_libvirtd.Libvirtd().restart() # Prepare lockspace and lease file for sanlock in order. sanlock_cmd_dict = OrderedDict() sanlock_cmd_dict["truncate -s 1M /var/lib/libvirt/sanlock/TEST_LS"] = "Failed to truncate TEST_LS" sanlock_cmd_dict["sanlock direct init -s TEST_LS:0:/var/lib/libvirt/sanlock/TEST_LS:0"] = "Failed to sanlock direct init TEST_LS:0" sanlock_cmd_dict["chown sanlock:sanlock /var/lib/libvirt/sanlock/TEST_LS"] = "Failed to chown sanlock TEST_LS" sanlock_cmd_dict["restorecon -R -v /var/lib/libvirt/sanlock"] = "Failed to restorecon sanlock" sanlock_cmd_dict["truncate -s 1M /var/lib/libvirt/sanlock/test-disk-resource-lock"] = "Failed to truncate test-disk-resource-lock" sanlock_cmd_dict["sanlock direct init -r TEST_LS:test-disk-resource-lock:" + "/var/lib/libvirt/sanlock/test-disk-resource-lock:0"] = "Failed to sanlock direct init test-disk-resource-lock" sanlock_cmd_dict["chown sanlock:sanlock " + "/var/lib/libvirt/sanlock/test-disk-resource-lock"] = "Failed to chown test-disk-resource-loc" sanlock_cmd_dict["sanlock client add_lockspace -s TEST_LS:1:" + "/var/lib/libvirt/sanlock/TEST_LS:0"] = "Failed to client add_lockspace -s TEST_LS:0" for sanlock_cmd in sanlock_cmd_dict.keys(): result = process.run(sanlock_cmd, shell=True) if result.exit_status: test.error(sanlock_cmd_dict[sanlock_cmd]) # Create one lease device and add it to VM. san_lock_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) lease_device = Lease() lease_device.lockspace = 'TEST_LS' lease_device.key = 'test-disk-resource-lock' lease_device.target = {'path': '/var/lib/libvirt/sanlock/test-disk-resource-lock'} san_lock_vmxml.add_device(lease_device) san_lock_vmxml.sync() # Install ceph-common package which include rbd command if utils_package.package_install(["ceph-common"]): if client_name and client_key: with open(key_file, 'w') as f: f.write("[%s]\n\tkey = %s\n" % (client_name, client_key)) key_opt = "--keyring %s" % key_file # Create secret xml sec_xml = secret_xml.SecretXML("no", "no") sec_xml.usage = auth_type sec_xml.usage_name = auth_usage sec_xml.xmltreefile.write() logging.debug("Secret xml: %s", sec_xml) ret = virsh.secret_define(sec_xml.xml) libvirt.check_exit_status(ret) secret_uuid = re.findall(r".+\S+(\ +\S+)\ +.+\S+", ret.stdout.strip())[0].lstrip() logging.debug("Secret uuid %s", secret_uuid) if secret_uuid is None: test.error("Failed to get secret uuid") # Set secret value auth_key = params.get("auth_key") ret = virsh.secret_set_value(secret_uuid, auth_key, **virsh_dargs) libvirt.check_exit_status(ret) # Delete the disk if it exists cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) else: test.error("Failed to install ceph-common") if disk_src_config: config_ceph() disk_path = ("rbd:%s:mon_host=%s" % (disk_src_name, mon_host)) if auth_user and auth_key: disk_path += (":id=%s:key=%s" % (auth_user, auth_key)) targetdev = params.get("disk_target", "vdb") # To be compatible with create_disk_xml function, # some parameters need to be updated. params.update({ "type_name": params.get("disk_type", "network"), "target_bus": params.get("disk_target_bus"), "target_dev": targetdev, "secret_uuid": secret_uuid, "source_protocol": params.get("disk_source_protocol"), "source_name": disk_src_name, "source_host_name": disk_src_host, "source_host_port": disk_src_port}) # Prepare disk image if convert_image: first_disk = vm.get_first_disk_devices() blk_source = first_disk['source'] # Convert the image to remote storage disk_cmd = ("rbd -m %s %s info %s 2> /dev/null|| qemu-img convert" " -O %s %s %s" % (mon_host, key_opt, disk_src_name, disk_format, blk_source, disk_path)) process.run(disk_cmd, ignore_status=False, shell=True) elif create_volume: vol_params = {"name": vol_name, "capacity": int(vol_cap), "capacity_unit": vol_cap_unit, "format": disk_format} create_pool() create_vol(vol_params) check_vol(vol_params) else: # Create an local image and make FS on it. disk_cmd = ("qemu-img create -f %s %s 10M && mkfs.ext4 -F %s" % (disk_format, img_file, img_file)) process.run(disk_cmd, ignore_status=False, shell=True) # Convert the image to remote storage disk_cmd = ("rbd -m %s %s info %s 2> /dev/null|| qemu-img convert -O" " %s %s %s" % (mon_host, key_opt, disk_src_name, disk_format, img_file, disk_path)) process.run(disk_cmd, ignore_status=False, shell=True) # Create disk snapshot if needed. if create_snapshot: snap_cmd = ("rbd -m %s %s snap create %s@%s" % (mon_host, key_opt, disk_src_name, snap_name)) process.run(snap_cmd, ignore_status=False, shell=True) if test_json_pseudo_protocol: # Create one frontend image with the rbd backing file. json_str = ('json:{"file.driver":"rbd",' '"file.filename":"rbd:%s:mon_host=%s"}' % (disk_src_name, mon_host)) # pass different json string according to the auth config if auth_user and auth_key: json_str = ('%s:id=%s:key=%s"}' % (json_str[:-2], auth_user, auth_key)) disk_cmd = ("qemu-img create -f qcow2 -b '%s' %s" % (json_str, front_end_img_file)) disk_path = front_end_img_file process.run(disk_cmd, ignore_status=False, shell=True) # If hot plug, start VM first, and then wait the OS boot. # Otherwise stop VM if running. if start_vm: if vm.is_dead(): vm.start() vm.wait_for_login().close() else: if not vm.is_dead(): vm.destroy() if attach_device: if create_volume: params.update({"source_pool": pool_name}) params.update({"type_name": "volume"}) # No need auth options for volume if "auth_user" in params: params.pop("auth_user") if "auth_type" in params: params.pop("auth_type") if "secret_type" in params: params.pop("secret_type") if "secret_uuid" in params: params.pop("secret_uuid") if "secret_usage" in params: params.pop("secret_usage") xml_file = libvirt.create_disk_xml(params) if additional_guest: # Copy xml_file for additional guest VM. shutil.copyfile(xml_file, additional_xml_file) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) if additional_guest: # Make sure the additional VM is running if additional_vm.is_dead(): additional_vm.start() additional_vm.wait_for_login().close() ret = virsh.attach_device(guest_name, additional_xml_file, "", debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif attach_disk: opts = params.get("attach_option", "") ret = virsh.attach_disk(vm_name, disk_path, targetdev, opts) libvirt.check_result(ret, skip_if=unsupported_err) elif test_disk_readonly: params.update({'readonly': "yes"}) xml_file = libvirt.create_disk_xml(params) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif test_disk_internal_snapshot: xml_file = libvirt.create_disk_xml(params) opts = params.get("attach_option", "") ret = virsh.attach_device(vm_name, xml_file, flagstr=opts, debug=True) libvirt.check_result(ret, skip_if=unsupported_err) elif disk_snapshot_with_sanlock: if vm.is_dead(): vm.start() snapshot_path = make_snapshot() if vm.is_alive(): vm.destroy() elif not create_volume: libvirt.set_vm_disk(vm, params) if test_blockcopy: logging.info("Creating %s...", vm_name) vmxml_for_test = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) if vm.is_alive(): vm.destroy(gracefully=False) vm.undefine() if virsh.create(vmxml_for_test.xml, **virsh_dargs).exit_status: vmxml_backup.define() test.fail("Can't create the domain") elif vm.is_dead(): vm.start() # Wait for vm is running vm.wait_for_login(timeout=600).close() if additional_guest: if additional_vm.is_dead(): additional_vm.start() # Check qemu command line if test_qemu_cmd: check_qemu_cmd() # Check partitions in vm if test_vm_parts: if not check_in_vm(vm, targetdev, old_parts, read_only=create_snapshot): test.fail("Failed to check vm partitions") if additional_guest: if not check_in_vm(additional_vm, targetdev, old_parts): test.fail("Failed to check vm partitions") # Save and restore operation if test_save_restore: check_save_restore() if test_snapshot: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option) if test_blockcopy: check_blockcopy(targetdev) if test_disk_readonly: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option, 'vdb') if test_disk_internal_snapshot: snap_option = params.get("snapshot_option", "") check_snapshot(snap_option, targetdev) # Detach the device. if attach_device: xml_file = libvirt.create_disk_xml(params) ret = virsh.detach_device(vm_name, xml_file) libvirt.check_exit_status(ret) if additional_guest: ret = virsh.detach_device(guest_name, xml_file) libvirt.check_exit_status(ret) elif attach_disk: ret = virsh.detach_disk(vm_name, targetdev) libvirt.check_exit_status(ret) # Check disk in vm after detachment. if attach_device or attach_disk: session = vm.wait_for_login() new_parts = libvirt.get_parts_list(session) if len(new_parts) != len(old_parts): test.fail("Disk still exists in vm" " after detachment") session.close() except virt_vm.VMStartError as details: for msg in unsupported_err: if msg in str(details): test.cancel(str(details)) else: test.fail("VM failed to start." "Error: %s" % str(details)) finally: # Remove /etc/ceph/ceph.conf file if exists. if os.path.exists('/etc/ceph/ceph.conf'): os.remove('/etc/ceph/ceph.conf') # Delete snapshots. 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") # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) if additional_guest: virsh.remove_domain(guest_name, "--remove-all-storage", ignore_stauts=True) # Remove the snapshot. if create_snapshot: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} snap" " purge {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) elif create_volume: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, cloned_vol_name))) process.run(cmd, ignore_status=True, shell=True) cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, os.path.join(disk_src_pool, create_from_cloned_volume))) process.run(cmd, ignore_status=True, shell=True) clean_up_volume_snapshots() else: cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm {2}" "".format(mon_host, key_opt, disk_src_name)) process.run(cmd, ignore_status=True, shell=True) # Delete tmp files. if os.path.exists(key_file): os.remove(key_file) if os.path.exists(img_file): os.remove(img_file) # Clean up volume, pool if vol_name and vol_name in str(virsh.vol_list(pool_name).stdout): virsh.vol_delete(vol_name, pool_name) if pool_name and pool_name in virsh.pool_state_dict(): virsh.pool_destroy(pool_name, **virsh_dargs) virsh.pool_undefine(pool_name, **virsh_dargs) # Clean up secret secret_list = get_secret_list() if secret_list: for secret_uuid in secret_list: virsh.secret_undefine(secret_uuid) logging.info("Restoring vm...") vmxml_backup.sync() if disk_snapshot_with_sanlock: # Restore virt_use_sanlock setting. process.run("setsebool -P virt_use_sanlock 0", shell=True) # Restore qemu config qemu_config.restore() utils_libvirtd.Libvirtd().restart() # Force shutdown sanlock service. process.run("sanlock client shutdown -f 1", shell=True) # Clean up lockspace folder process.run("rm -rf /var/lib/libvirt/sanlock/*", shell=True) if snapshot_path is not None: for snapshot in snapshot_path: if os.path.exists(snapshot): os.remove(snapshot)
if cache_options: if cache_options.count("directsync"): if not libvirt_version.version_compare(1, 0, 0): raise error.TestNAError( "'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(test.tmpdir, device_source_name) if test_block_dev: device_source = libvirt.setup_or_cleanup_iscsi(True) if not device_source: # We should skip this case raise error.TestNAError("Can not get iscsi device name in host") if test_logcial_dev: lv_utils.vg_create(vg_name, device_source)
def run(test, params, env): """ Test usb virtual disk plug/unplug. 1.Prepare a vm with usb controllers 2.Prepare a local image 3.Prepare a virtual disk xml 4.Attach the virtual disk to the vm 5.Check the disk in vm 6.Unplug the disk from vm """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) def get_new_disks(old_partitions): """ Get new virtual disks in vm after disk plug. :param old_partitions: Already existing partitions in vm. :return: New disks/partitions in vm. """ session = None try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_partitions = libvirt.get_parts_list(session) added_partitions = list(set(new_partitions).difference(set(old_partitions))) if not added_partitions: logging.debug("No new partitions found in vm.") else: logging.debug("Newly added partition(s) is: %s", added_partitions) return added_partitions except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when get new disk: %s", str(err)) finally: if session: session.close() def check_disk_type(partition): """ Check if a disk partition is a usb disk in vm. :param partition: The disk partition in vm to be checked. :return: If the disk is a usb device, return True. """ session = None try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) cmd = "ls -l /dev/disk/by-id/ | grep %s | grep -i usb" % partition status = session.cmd_status(cmd) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: logging.debug("Error happens when check if new disk is usb device: %s", str(err)) return False finally: if session: session.close() def check_disk_io(partition): """ Check if the disk partition in vm can be normally used. :param partition: The disk partition in vm to be checked. :return: If the disk can be used, return True. """ session = None try: session = vm.wait_for_login() cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(partition)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: logging.debug("Error happens when check disk io in vm: %s", str(err)) return False finally: if session: session.close() def remove_usbs(vmxml): """ Remove all USB devices and controllers from a vm's xml. :param vmxml: The vm's xml. """ try: for xml in vmxml.xmltreefile.findall('/devices/*'): if (xml.get('bus') == 'usb') or (xml.get('type') == 'usb'): vmxml.xmltreefile.remove(xml) except (AttributeError, TypeError): pass # Element doesn't exist at first vmxml.xmltreefile.write() def prepare_usb_controller(vmxml, usb_models): """ Add usb controllers into vm's xml. :param vmxml: The vm's xml. :param usb_models: The usb models will be used in usb controller(s). """ if not usb_models: test.error("No usb model provided.") # Add disk usb controller(s) for usb_model in usb_models: usb_controller = Controller("controller") usb_controller.type = "usb" usb_controller.index = "0" usb_controller.model = usb_model vmxml.add_device(usb_controller) # Redefine domain vmxml.sync() def prepare_local_image(): """ Prepare a local image. :return: The path to the image file. """ image_filename = params.get("image_filename", "raw.img") image_format = params.get("image_format", "raw") image_size = params.get("image_size", "1G") image_path = os.path.join(data_dir.get_tmp_dir(), image_filename) try: if image_format in ["raw", "qcow2"]: image_path = libvirt.create_local_disk("file", image_path, image_size, disk_format=image_format) else: test.cancel("We only test raw & qcow2 format for now.") except Exception as err: test.error("Error happens when prepare local image: %s", err) return image_path def prepare_virt_disk_xml(image_path): """ Prepare the virtual disk xml to be attached/detached. :param image_path: The path to the local image. :return: The virtual disk xml. """ virt_disk_device = params.get("virt_disk_device", "disk") virt_disk_device_type = params.get("virt_disk_device_type", "file") virt_disk_device_format = params.get("virt_disk_device_format", "raw") virt_disk_device_target = params.get("virt_disk_device_target", "sdb") virt_disk_device_bus = params.get("virt_disk_device_bus", "usb") disk_xml = Disk(type_name=virt_disk_device_type) disk_xml.device = virt_disk_device disk_src_dict = {'attrs': {'file': image_path, 'type_name': 'file'}} disk_xml.source = disk_xml.new_disk_source(**disk_src_dict) driver_dict = {"name": "qemu", "type": virt_disk_device_format} disk_xml.driver = driver_dict disk_xml.target = {"dev": virt_disk_device_target, "bus": virt_disk_device_bus} return disk_xml usb_models = params.get("usb_model").split() coldplug = "yes" == params.get("coldplug") status_error = "yes" == params.get("status_error") new_disks = [] new_disk = "" attach_options = "" # Get disk partitions info before hot/cold plug virtual disk if vm.is_dead(): vm.start() session = vm.wait_for_login() old_partitions = libvirt.get_parts_list(session) session.close() vm.destroy(gracefully=False) # Backup vm xml vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml = vmxml_backup.copy() try: remove_usbs(vmxml) prepare_usb_controller(vmxml, usb_models) vm.start() session = vm.wait_for_login() disk_xml = prepare_virt_disk_xml(prepare_local_image()) session.close() if coldplug: attach_options = "--config" # Attach virtual disk to vm result = virsh.attach_device(vm_name, disk_xml.xml, flagstr=attach_options, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) # Check the attached disk in vm if coldplug: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() utils_misc.wait_for(lambda: get_new_disks(old_partitions), 20) new_disks = get_new_disks(old_partitions) if len(new_disks) != 1: test.fail("Attached 1 virtual disk but got %s." % len(new_disk)) new_disk = new_disks[0] if not check_disk_type(new_disk): test.fail("The newly attached disk is not a usb one.") if not check_disk_io(new_disk): test.fail("Cannot operate the newly added disk in vm.") # Detach the disk from vm result = virsh.detach_device(vm_name, disk_xml.xml, flagstr=attach_options, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) # Check the detached disk in vm if coldplug: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() utils_misc.wait_for(lambda: not get_new_disks(old_partitions), 20) new_disks = get_new_disks(old_partitions) if len(new_disks) != 0: test.fail("Unplug virtual disk failed.") finally: if vm.is_alive(): vm.destroy(gracefully=False) # Restoring vm vmxml_backup.sync()
def run(test, params, env): """ Test steps: 1. Prepare a multipath device. 2. Prepare virtual disk xml using this multipath device. 3. Hot/Cold-plug the disk xml to virtual machine. 4. Check the attached disk in the virtual machine. """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) mpath_conf_path = params.get('mpath_conf_path', '/etc/multipath.conf') mpath_conf_bkup_path = params.get('mpath_conf_bkup_path', '/etc/multipath.conf.bkup') def prepare_multipath_conf(): """ Prepare the multipath.conf to make sure iscsi lun can be seen as a multipath device. """ mpath_conf_content = ("defaults {\n" " user_friendly_names yes\n" " path_grouping_policy multibus\n" " failback immediate\n" " no_path_retry fail\n" "}\n") if os.path.exists(mpath_conf_bkup_path): os.remove(mpath_conf_bkup_path) if os.path.exists(mpath_conf_path): shutil.move(mpath_conf_path, mpath_conf_bkup_path) with open(mpath_conf_path, 'wt') as mpath_conf_file: mpath_conf_file.write(mpath_conf_content) def recover_multipath_conf(): """ Recover the multipath.conf. """ if os.path.exists(mpath_conf_bkup_path): if os.path.exists(mpath_conf_path): os.remove(mpath_conf_path) shutil.move(mpath_conf_bkup_path, mpath_conf_path) def check_in_vm(vm, old_parts): """ Check mount/read/write disk in VM. :param vm: Virtual machine to be checked. :param old_parts: Original disk partitions in VM. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: test.fail("The number of new partitions is invalid in VM") else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) session.close() if status: test.fail("Disk operation in VM failed:%s" % output) except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as err: test.fail("Error happens when check disk in vm: %s" % err) storage_size = params.get("storage_size", "1G") hotplug_disk = "yes" == params.get("hotplug_disk", "no") status_error = "yes" == params.get("status_error") # 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() vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) try: # Setup backend storage prepare_multipath_conf() mpath.restart_multipathd() old_mpath_devs = mpath.find_mpath_devs() libvirt.setup_or_cleanup_iscsi(is_setup=True) mpath.restart_multipathd() cur_mpath_devs = mpath.find_mpath_devs() new_mpath_devs = list(set(cur_mpath_devs).difference( set(old_mpath_devs))) logging.debug("newly added mpath devs are: %s", new_mpath_devs) # Prepare disk xml disk_params = {} disk_params['type_name'] = params.get("virt_disk_type", "block") disk_params['source_file'] = '/dev/mapper/' + new_mpath_devs[0] disk_params['device_type'] = params.get("virt_disk_device", "lun") disk_params['sgio'] = params.get("sgio", "filtered") disk_params['rawio'] = params.get("rawio", "no") disk_params['target_dev'] = params.get("virt_disk_device_target", "sdb") disk_params['target_bus'] = params.get("virt_disk_device_bus", "scsi") disk_params['driver_name'] = params.get("virt_disk_drive_name", "qemu") disk_params['driver_type'] = params.get("virt_disk_device_format", "raw") disk_xml = libvirt.create_disk_xml(disk_params) # Test disk operation with newly added disk xml attach_option = "" if not hotplug_disk: attach_option = "--config" result = virsh.attach_device(vm_name, disk_xml, flagstr=attach_option, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) if not hotplug_disk: vm.destroy(gracefully=False) vm.start() vm.wait_for_login().close() check_in_vm(vm, old_parts) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync() # Clean up backend storage libvirt.setup_or_cleanup_iscsi(is_setup=False) recover_multipath_conf()
def run(test, params, env): """ Test disk encryption option. 1.Prepare backend storage (blkdev/iscsi/gluster/ceph) 2.Use luks format to encrypt the backend storage 3.Prepare a disk xml indicating to the backend storage with valid/invalid luks password 4.Start VM with disk hot/cold plugged 5.Check some disk operations in VM 6.Check backend storage is still in luks format 7.Recover test environment """ vm_name = params.get("main_vm") vm = env.get_vm(vm_name) virsh_dargs = {'debug': True, 'ignore_status': True} def encrypt_dev(device, params): """ Encrypt device with luks format :param device: Storage deivce to be encrypted. :param params: From the dict to get encryption password. """ password = params.get("luks_encrypt_passwd", "password") size = params.get("luks_size", "500M") cmd = ("qemu-img create -f luks " "--object secret,id=sec0,data=`printf '%s' | base64`,format=base64 " "-o key-secret=sec0 %s %s" % (password, device, size)) if process.system(cmd, shell=True): test.fail("Can't create a luks encrypted img by qemu-img") def check_dev_format(device, fmt="luks"): """ Check if device is in luks format :param device: Storage deivce to be checked. :param fmt: Expected disk format. :return: If device's format equals to fmt, return True, else return False. """ cmd_result = process.run("qemu-img" + ' -h', ignore_status=True, shell=True, verbose=False) if b'-U' in cmd_result.stdout: cmd = ("qemu-img info -U %s| grep -i 'file format' " "| grep -i %s" % (device, fmt)) else: cmd = ("qemu-img info %s| grep -i 'file format' " "| grep -i %s" % (device, fmt)) cmd_result = process.run(cmd, ignore_status=True, shell=True) if cmd_result.exit_status: test.fail("device %s is not in %s format. err is: %s" % (device, fmt, cmd_result.stderr)) def check_in_vm(target, old_parts): """ Check mount/read/write disk in VM. :param target: Disk dev in VM. :param old_parts: Original disk partitions in VM. :return: True if check successfully. """ try: session = vm.wait_for_login() if platform.platform().count('ppc64'): time.sleep(10) new_parts = libvirt.get_parts_list(session) added_parts = list(set(new_parts).difference(set(old_parts))) logging.info("Added parts:%s", added_parts) if len(added_parts) != 1: logging.error("The number of new partitions is invalid in VM") return False else: added_part = added_parts[0] cmd = ("fdisk -l /dev/{0} && mkfs.ext4 -F /dev/{0} && " "mkdir -p test && mount /dev/{0} test && echo" " teststring > test/testfile && umount test" .format(added_part)) status, output = session.cmd_status_output(cmd) logging.debug("Disk operation in VM:\nexit code:\n%s\noutput:\n%s", status, output) return status == 0 except (remote.LoginError, virt_vm.VMError, aexpect.ShellError) as e: logging.error(str(e)) return False # Disk specific attributes. device = params.get("virt_disk_device", "disk") device_target = params.get("virt_disk_device_target", "vdd") device_format = params.get("virt_disk_device_format", "raw") device_type = params.get("virt_disk_device_type", "file") device_bus = params.get("virt_disk_device_bus", "virtio") backend_storage_type = params.get("backend_storage_type", "iscsi") # Backend storage options. storage_size = params.get("storage_size", "1G") enable_auth = "yes" == params.get("enable_auth") # Luks encryption info, luks_encrypt_passwd is the password used to encrypt # luks image, and luks_secret_passwd is the password set to luks secret, you # can set a wrong password to luks_secret_passwd for negative tests luks_encrypt_passwd = params.get("luks_encrypt_passwd", "password") luks_secret_passwd = params.get("luks_secret_passwd", "password") # Backend storage auth info use_auth_usage = "yes" == params.get("use_auth_usage") if use_auth_usage: use_auth_uuid = False else: use_auth_uuid = "yes" == params.get("use_auth_uuid", "yes") auth_sec_usage_type = params.get("auth_sec_usage_type", "iscsi") auth_sec_usage_target = params.get("auth_sec_usage_target", "libvirtiscsi") status_error = "yes" == params.get("status_error") check_partitions = "yes" == params.get("virt_disk_check_partitions", "yes") hotplug_disk = "yes" == params.get("hotplug_disk", "no") encryption_in_source = "yes" == params.get("encryption_in_source", "no") auth_in_source = "yes" == params.get("auth_in_source", "no") auth_sec_uuid = "" luks_sec_uuid = "" disk_auth_dict = {} disk_encryption_dict = {} pvt = None if ((encryption_in_source or auth_in_source) and not libvirt_version.version_compare(3, 9, 0)): test.cancel("Cannot put <encryption> or <auth> inside disk <source> " "in this libvirt version.") # 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. vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) try: # Setup backend storage if backend_storage_type == "iscsi": iscsi_host = params.get("iscsi_host") iscsi_port = params.get("iscsi_port") if device_type == "block": device_source = libvirt.setup_or_cleanup_iscsi(is_setup=True) disk_src_dict = {'attrs': {'dev': device_source}} elif device_type == "network": if enable_auth: chap_user = params.get("chap_user", "redhat") chap_passwd = params.get("chap_passwd", "password") auth_sec_usage = params.get("auth_sec_usage", "libvirtiscsi") auth_sec_dict = {"sec_usage": "iscsi", "sec_target": auth_sec_usage} auth_sec_uuid = libvirt.create_secret(auth_sec_dict) # Set password of auth secret (not luks encryption secret) virsh.secret_set_value(auth_sec_uuid, chap_passwd, encode=True, debug=True) iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi( is_setup=True, is_login=False, image_size=storage_size, chap_user=chap_user, chap_passwd=chap_passwd, portal_ip=iscsi_host) # ISCSI auth attributes for disk xml if use_auth_uuid: disk_auth_dict = {"auth_user": chap_user, "secret_type": auth_sec_usage_type, "secret_uuid": auth_sec_uuid} elif use_auth_usage: disk_auth_dict = {"auth_user": chap_user, "secret_type": auth_sec_usage_type, "secret_usage": auth_sec_usage_target} else: iscsi_target, lun_num = libvirt.setup_or_cleanup_iscsi( is_setup=True, is_login=False, image_size=storage_size, portal_ip=iscsi_host) device_source = "iscsi://%s:%s/%s/%s" % (iscsi_host, iscsi_port, iscsi_target, lun_num) disk_src_dict = {"attrs": {"protocol": "iscsi", "name": "%s/%s" % (iscsi_target, lun_num)}, "hosts": [{"name": iscsi_host, "port": iscsi_port}]} elif backend_storage_type == "gluster": gluster_vol_name = params.get("gluster_vol_name", "gluster_vol1") gluster_pool_name = params.get("gluster_pool_name", "gluster_pool1") gluster_img_name = params.get("gluster_img_name", "gluster1.img") gluster_host_ip = libvirt.setup_or_cleanup_gluster( is_setup=True, vol_name=gluster_vol_name, pool_name=gluster_pool_name) device_source = "gluster://%s/%s/%s" % (gluster_host_ip, gluster_vol_name, gluster_img_name) disk_src_dict = {"attrs": {"protocol": "gluster", "name": "%s/%s" % (gluster_vol_name, gluster_img_name)}, "hosts": [{"name": gluster_host_ip, "port": "24007"}]} elif backend_storage_type == "ceph": ceph_host_ip = params.get("ceph_host_ip", "EXAMPLE_HOSTS") ceph_mon_ip = params.get("ceph_mon_ip", "EXAMPLE_MON_HOST") ceph_host_port = params.get("ceph_host_port", "EXAMPLE_PORTS") ceph_disk_name = params.get("ceph_disk_name", "EXAMPLE_SOURCE_NAME") ceph_client_name = params.get("ceph_client_name") ceph_client_key = params.get("ceph_client_key") ceph_auth_user = params.get("ceph_auth_user") ceph_auth_key = params.get("ceph_auth_key") enable_auth = "yes" == params.get("enable_auth") key_file = os.path.join(data_dir.get_tmp_dir(), "ceph.key") key_opt = "" # Prepare a blank params to confirm if delete the configure at the end of the test ceph_cfg = "" if not utils_package.package_install(["ceph-common"]): test.error("Failed to install ceph-common") # Create config file if it doesn't exist ceph_cfg = ceph.create_config_file(ceph_mon_ip) if enable_auth: # If enable auth, prepare a local file to save key if ceph_client_name and ceph_client_key: with open(key_file, 'w') as f: f.write("[%s]\n\tkey = %s\n" % (ceph_client_name, ceph_client_key)) key_opt = "--keyring %s" % key_file auth_sec_dict = {"sec_usage": auth_sec_usage_type, "sec_name": "ceph_auth_secret"} auth_sec_uuid = libvirt.create_secret(auth_sec_dict) virsh.secret_set_value(auth_sec_uuid, ceph_auth_key, debug=True) disk_auth_dict = {"auth_user": ceph_auth_user, "secret_type": auth_sec_usage_type, "secret_uuid": auth_sec_uuid} cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(ceph_mon_ip, key_opt, ceph_disk_name)) else: test.error("No ceph client name/key provided.") device_source = "rbd:%s:mon_host=%s:keyring=%s" % (ceph_disk_name, ceph_mon_ip, key_file) else: device_source = "rbd:%s:mon_host=%s" % (ceph_disk_name, ceph_mon_ip) disk_src_dict = {"attrs": {"protocol": "rbd", "name": ceph_disk_name}, "hosts": [{"name": ceph_host_ip, "port": ceph_host_port}]} elif backend_storage_type == "nfs": pool_name = params.get("pool_name", "nfs_pool") pool_target = params.get("pool_target", "nfs_mount") pool_type = params.get("pool_type", "netfs") nfs_server_dir = params.get("nfs_server_dir", "nfs_server") emulated_image = params.get("emulated_image") image_name = params.get("nfs_image_name", "nfs.img") tmp_dir = data_dir.get_tmp_dir() pvt = libvirt.PoolVolumeTest(test, params) pvt.pre_pool(pool_name, pool_type, pool_target, emulated_image) nfs_mount_dir = os.path.join(tmp_dir, pool_target) device_source = nfs_mount_dir + image_name disk_src_dict = {'attrs': {'file': device_source, 'type_name': 'file'}} else: test.cancel("Only iscsi/gluster/rbd/nfs can be tested for now.") logging.debug("device source is: %s", device_source) luks_sec_uuid = libvirt.create_secret(params) logging.debug("A secret created with uuid = '%s'", luks_sec_uuid) ret = virsh.secret_set_value(luks_sec_uuid, luks_secret_passwd, encode=True, debug=True) encrypt_dev(device_source, params) libvirt.check_exit_status(ret) # Add disk xml. vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name) disk_xml = Disk(type_name=device_type) disk_xml.device = device disk_xml.target = {"dev": device_target, "bus": device_bus} driver_dict = {"name": "qemu", "type": device_format} disk_xml.driver = driver_dict disk_source = disk_xml.new_disk_source(**disk_src_dict) if disk_auth_dict: logging.debug("disk auth dict is: %s" % disk_auth_dict) if auth_in_source: disk_source.auth = disk_xml.new_auth(**disk_auth_dict) else: disk_xml.auth = disk_xml.new_auth(**disk_auth_dict) disk_encryption_dict = {"encryption": "luks", "secret": {"type": "passphrase", "uuid": luks_sec_uuid}} disk_encryption = disk_xml.new_encryption(**disk_encryption_dict) if encryption_in_source: disk_source.encryption = disk_encryption else: disk_xml.encryption = disk_encryption disk_xml.source = disk_source logging.debug("new disk xml is: %s", disk_xml) # Sync VM xml if not hotplug_disk: vmxml.add_device(disk_xml) vmxml.sync() try: vm.start() vm.wait_for_login() except virt_vm.VMStartError as details: # When use wrong password in disk xml for cold plug cases, # VM cannot be started if status_error and not hotplug_disk: logging.info("VM failed to start as expected: %s" % str(details)) else: test.fail("VM should start but failed: %s" % str(details)) if hotplug_disk: result = virsh.attach_device(vm_name, disk_xml.xml, ignore_status=True, debug=True) libvirt.check_exit_status(result, status_error) if check_partitions and not status_error: if not check_in_vm(device_target, old_parts): test.fail("Check disk partitions in VM failed") check_dev_format(device_source) finally: # Recover VM. if vm.is_alive(): vm.destroy(gracefully=False) vmxml_backup.sync("--snapshots-metadata") # Clean up backend storage if backend_storage_type == "iscsi": libvirt.setup_or_cleanup_iscsi(is_setup=False) elif backend_storage_type == "gluster": libvirt.setup_or_cleanup_gluster(is_setup=False, vol_name=gluster_vol_name, pool_name=gluster_pool_name) elif backend_storage_type == "ceph": # Remove ceph configure file if created. if ceph_cfg: os.remove(ceph_cfg) cmd = ("rbd -m {0} {1} info {2} && rbd -m {0} {1} rm " "{2}".format(ceph_mon_ip, key_opt, ceph_disk_name)) cmd_result = process.run(cmd, ignore_status=True, shell=True) logging.debug("result of rbd removal: %s", cmd_result) if os.path.exists(key_file): os.remove(key_file) # Clean up secrets if auth_sec_uuid: virsh.secret_undefine(auth_sec_uuid) if luks_sec_uuid: virsh.secret_undefine(luks_sec_uuid) # Clean up pools if pvt: pvt.cleanup_pool(pool_name, pool_type, pool_target, emulated_image)
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).exit_status if s_attach != 0: logging.error("Attaching device failed 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)