This case will test getDomainCapabilities API, and connection_getDomainCapabilities test case was added to test_connection.conf --- cases/test_connection.conf | 10 + repos/virconn/connection_getDomainCapabilities.py | 438 ++++++++++++++++++++++ 2 files changed, 448 insertions(+) create mode 100644 repos/virconn/connection_getDomainCapabilities.py diff --git a/cases/test_connection.conf b/cases/test_connection.conf index 7552024..5719937 100644 --- a/cases/test_connection.conf +++ b/cases/test_connection.conf @@ -47,3 +47,13 @@ virconn:free_pages 0 pagesize 4k,2M + +virconn:connection_getDomainCapabilities + emulatorbin + /usr/libexec/qemu-kvm + arch + x86_64 + machine + pc-i440fx-rhel7.0.0 + virttype + kvm diff --git a/repos/virconn/connection_getDomainCapabilities.py b/repos/virconn/connection_getDomainCapabilities.py new file mode 100644 index 0000000..f0cfa1f --- /dev/null +++ b/repos/virconn/connection_getDomainCapabilities.py @@ -0,0 +1,438 @@ +#!/usr/bin/env python +# test getDomainCapabilities() API for libvirtd + +import os +import libvirt +import hashlib +import fcntl + +from xml.dom import minidom +from libvirt import libvirtError +from src import sharedmod +from utils import utils + +required_params = ('emulatorbin','arch','machine','virttype',) +optional_params = {} + +QEMU_CAPS = "" +API_FILE = "/tmp/caps_from_api.xml" +CMD = "rm -rf %s" +OVMF = "/usr/share/OVMF/OVMF_CODE.fd" +IOMMU = "/sys/kernel/iommu_groups/" +VFIO = "/dev/vfio/vfio" +KVM = "/dev/kvm" +KVM_CHECK_EXTENSION = 44547 +KVM_CAP_IOMMU = 18 +maxcpu = 0 + +ovmf_f = False +drive = False +drive_forma = False +drive_readonly = False +blk_sg_io = False +usb_storage = False +device = False +scsi_generic = False +vfio_pci = False + +def clean_env(logger): + """ + clean testing environment + """ + status, output = utils.exec_cmd(CMD % API_FILE, shell=True) + if status != 0: + logger.error("Can not delete %s" % API_FILE) + else: + logger.debug("Deleted %s successfully" % API_FILE) + +def get_hypervisor_ver(emulatorbin,logger): + """ + Obtain qemu-kvm's version, and return a number value of version + """ + RPM = "rpm -qf %s" + status, package = utils.exec_cmd(RPM % emulatorbin, shell=True) + if not status: + logger.debug("The package is %s" % package) + else: + logger.debug("The package is %s" % package) + return 0 + package = package[0].split('-') + version = "" + for item in package: + if not item.isalnum(): + for v in item.split("."): + version = version + v.rjust(3,"0") + break + return int(version) + +def validate_caps_from_hv(emulatorbin,logger): + """ + Validate the relative caps between libvirt and qemu-kvm + """ + F1 = "%s -h| grep \"\-drive\"" + F2 = "%s -h| grep \"format=\"" + F3 = "%s -h| grep \"readonly=\"" + F4 = "%s -h| grep \"^\\-device\"" + l = [F1,F2,F3,F4] + flags = [] + for item in l: + status, temp = utils.exec_cmd(item % emulatorbin, shell=True) + if not status: + flags.append(True) + logger.debug("Got: %s from vh" % temp) + else: + flags.append(False) + logger.debug("Got: %s from vh" % temp) + if get_hypervisor_ver(emulatorbin,logger) >= 11000: + flags.append(True) + else: + flags.append(False) + libvirt_f = [drive,drive_forma,drive_readonly,device,blk_sg_io] + if flags == libvirt_f: + return True + else: + return False + +def generate_hash(emulatorbin,logger): + """ + generate file name using sha256 + """ + global QEMU_CAPS + QEMU_CAPS = "/var/cache/libvirt/qemu/capabilities/" + file_name = hashlib.sha256(emulatorbin).hexdigest() + QEMU_CAPS = QEMU_CAPS + file_name + ".xml" + logger.debug("Cache file is %s" % QEMU_CAPS) + +def get_maxcpu(machine,logger): + """ + return maxcpu for given machine type from QEMU_CAPS xml + """ + global maxcpu + xml = minidom.parse(QEMU_CAPS) + qemu = xml.getElementsByTagName('qemuCaps')[0] + for item in qemu.getElementsByTagName('machine'): + if item.getAttribute('name') == machine: + maxcpu = int(item.getAttribute('maxCpus')) + return True + +def get_os_flags(logger): + """ + Read results from QEMU_CAPS file and set three flags + """ + global drive, drive_forma, drive_readonly + xml = minidom.parse(QEMU_CAPS) + qemu = xml.getElementsByTagName('qemuCaps')[0] + for item in qemu.getElementsByTagName('flag'): + if item.getAttribute('name') == "drive": + drive = True + if item.getAttribute('name') == "drive-format": + drive_forma = True + if item.getAttribute('name') == "drive-readonly": + drive_readonly = True + logger.debug("drive = %s; drive_format = %s; drive_readonly = %s"\ + % (drive,drive_forma,drive_readonly)) + return True + +def get_disk_flags(logger): + """ + Read results from QEMU_CAPS file and set two flags + """ + global blk_sg_io,usb_storage + xml = minidom.parse(QEMU_CAPS) + qemu = xml.getElementsByTagName('qemuCaps')[0] + for item in qemu.getElementsByTagName('flag'): + if item.getAttribute('name') == "blk-sg-io": + blk_sg_io = True + if item.getAttribute('name') == "usb-storage": + usb_storage = True + logger.debug("blk_sg_io = %s; usb_storage = %s" % (blk_sg_io,usb_storage)) + return True + +def get_hostdev_flags(logger): + """ + Read results from QEMU_CAPS file and set three flags + """ + global device, scsi_generic,vfio_pci + xml = minidom.parse(QEMU_CAPS) + for item in xml.getElementsByTagName('flag'): + if item.getAttribute('name') == "device": + device = True + if item.getAttribute('name') == "scsi-generic": + scsi_generic = True + if item.getAttribute('name') == "vfio-pci": + vfio_pci = True + logger.debug("device = %s; scsi_generic = %s; vfio_pci = %s"\ + % (device,scsi_generic,vfio_pci)) + return True + +def supportsPassthroughVFIO(logger): + """ + check the vfio mode + """ + if not os.path.exists(IOMMU): + logger.error("File %s is not exist" % IOMMU) + return False + if not os.path.exists(VFIO): + logger.error("Module %s is not exist" % VFIO) + return False + if len(os.listdir(IOMMU)) > 0: + return True + return False + +def supportsPassthroughKVM(logger): + """ + check the legacy kvm mode + """ + if not os.path.exists(KVM): + logger.error("File %s is not exist" % KVM) + return False + with open(KVM, "r") as kvmfd: + if fcntl.ioctl(kvmfd,KVM_CHECK_EXTENSION,KVM_CAP_IOMMU) == 1: + return True + return False + +def check_common_values(given_list,logger): + """ + Check path/machine/arch/vcpu parameters + """ + xml = minidom.parse(API_FILE) + dom = xml.getElementsByTagName('domainCapabilities')[0] + #get path/machine/arch/vcpu from xml generated by api + path = dom.getElementsByTagName('path')[0].childNodes[0].data + domain = dom.getElementsByTagName('domain')[0].childNodes[0].data + machine = dom.getElementsByTagName('machine')[0].childNodes[0].data + arch = dom.getElementsByTagName('arch')[0].childNodes[0].data + vcpu = dom.getElementsByTagName('vcpu')[0].getAttribute('max') + #put all of them to a list + list1 = [str(path),str(machine),str(arch),int(vcpu)] + logger.debug("Got 4 common parameters: %s" % list1) + if given_list == list1: + logger.debug("Checking common value: Pass") + else: + logger.debug("Checking common value: Fail") + return False + return True + +def check_os(arch, logger): + """ + check the os part + """ + alltype = ["rom","pflash"] + allreadonly = ["yes","no"] + type_api = [] + readonly_api = [] + xml = minidom.parse(API_FILE) + dom = xml.getElementsByTagName('domainCapabilities')[0] + os = dom.getElementsByTagName('os')[0] + loader = os.getElementsByTagName('loader')[0] + ovmf1 = os.getElementsByTagName('value')[0] + ovmf1 = ovmf1.childNodes[0].data + logger.debug("Got OVMF path is %s" % ovmf1) + if ovmf_f and ovmf1 == OVMF: + pass + else: + return False + enum = loader.getElementsByTagName('enum') + for item in enum: + if item.getAttribute('name') == "type": + value = item.getElementsByTagName("value") + for temp in value: + type_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "readonly": + value = item.getElementsByTagName("value") + for temp in value: + readonly_api.append(str(temp.childNodes[0].data)) + if not (drive & drive_forma): + alltype.remove("pflash") + if not drive_readonly: + allreadonly = [] + logger.debug("Got type list: %s" % type_api) + logger.debug("Got readonly list: %s" % readonly_api) + if not type_api == alltype: + return False + if not readonly_api == allreadonly: + return False + return True + +def check_disk(logger): + """ + check the disk part in <devices> + """ + alldevice = ["disk","cdrom","floppy","lun"] + allbus = ["ide","fdc","scsi","virtio","usb"] + device_api = [] + bus_api = [] + xml = minidom.parse(API_FILE) + dom = xml.getElementsByTagName('domainCapabilities')[0] + devices = dom.getElementsByTagName('devices')[0] + disk = devices.getElementsByTagName('disk')[0] + enum = disk.getElementsByTagName('enum') + for item in enum: + if item.getAttribute('name') == "diskDevice": + value = item.getElementsByTagName("value") + for temp in value: + device_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "bus": + value = item.getElementsByTagName("value") + for temp in value: + bus_api.append(str(temp.childNodes[0].data)) + if not blk_sg_io: + alldevice.remove("lun") + if not usb_storage: + allbus.remove("usb") + logger.debug("Got diskDevice list: %s" % device_api) + logger.debug("Got bus list: %s" % bus_api) + if not device_api == alldevice: + return False + if not bus_api == allbus: + return False + return True + +def check_hostdev(logger): + """ + check the hostdev part in <devices> + """ + allmode = ["subsystem"] + allpolicy = ["default","mandatory","requisite","optional"] + allsubsys = ["usb","pci","scsi"] + allbackend = ["default","default","vfio","kvm"] + mode_api = [] + policy_api = [] + subsys_api = [] + backend_api = [] + caps_api = [] + xml = minidom.parse(API_FILE) + dom = xml.getElementsByTagName('domainCapabilities')[0] + devices = dom.getElementsByTagName('devices')[0] + hostdev = devices.getElementsByTagName('hostdev')[0] + enum = hostdev.getElementsByTagName('enum') + for item in enum: + if item.getAttribute('name') == "mode": + value = item.getElementsByTagName("value") + for temp in value: + mode_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "startupPolicy": + value = item.getElementsByTagName("value") + for temp in value: + policy_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "subsysType": + value = item.getElementsByTagName("value") + for temp in value: + subsys_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "capsType": + value = item.getElementsByTagName("value") + for temp in value: + caps_api.append(str(temp.childNodes[0].data)) + if item.getAttribute('name') == "pciBackend": + value = item.getElementsByTagName("value") + for temp in value: + backend_api.append(str(temp.childNodes[0].data)) + #WIP, we need more codes to check vfio + if not (drive & device & scsi_generic): + allsubsys.remove("scsi") + if not (supportsPassthroughKVM(logger) & device): + allbackend.remove("default") + allbackend.remove("kvm") + if not (supportsPassthroughVFIO(logger) & vfio_pci): + allbackend.remove("default") + allbackend.remove("vfio") + logger.debug("Got mode list: %s" % mode_api) + logger.debug("Got startupPolicy list: %s" % policy_api) + logger.debug("Got subsysType list: %s" % subsys_api) + logger.debug("Got capsType list: %s" % caps_api) + logger.debug("Got pciBackend list: %s" % backend_api) + if not mode_api == allmode: + return False + if not policy_api == allpolicy: + return False + if not subsys_api == allsubsys: + return False + if not backend_api == allbackend: + return False + return True + +def connection_getDomainCapabilities(params): + """ + test API for getDomainCapabilities in class virConnect + """ + global ovmf_f + logger = params['logger'] + emulatorbin = params['emulatorbin'] + arch = params['arch'] + machine = params['machine'] + virttype = params['virttype'] + + try: + logger.info("The specified emulatorbin is %s" \ + % emulatorbin) + logger.info("The specified architecture is %s" \ + % arch) + logger.info("The specified machine is %s" \ + % machine) + logger.info("The specified virttype is %s" \ + % virttype) + + generate_hash(emulatorbin, logger) + if not os.path.exists(QEMU_CAPS): + logger.error("cache file, %s is not exist" % QEMU_CAPS) + return 1 + if os.path.exists(OVMF): + ovmf_f = True + else: + logger.warning("cache file, %s is not exist" % OVMF) + if not get_maxcpu(machine,logger): + logger.debug("get maxcpu: Fail") + return 1 + if not get_os_flags(logger): + logger.debug("get os: Fail") + return 1 + if not get_disk_flags(logger): + logger.debug("get disk: Fail") + return 1 + if not get_hostdev_flags(logger): + logger.debug("get hostdev: Fail") + return 1 + if not validate_caps_from_hv(emulatorbin,logger): + logger.error("Failed to compare caps") + return 1 + else: + logger.debug("Successed to compare caps") + + conn = sharedmod.libvirtobj['conn'] + caps_from_api = conn.getDomainCapabilities\ +(emulatorbin,arch,machine,virttype,0) + + logger.debug("The return of API: %s" % caps_from_api) + fd = open(API_FILE,"w+") + fd.write(caps_from_api) + fd.flush() + + given_list = [emulatorbin,machine,arch,maxcpu] + if not check_common_values(given_list,logger): + logger.info("Failed to validate common elements") + return 1 + else: + logger.info("Successed to validate common elements") + if not check_os(arch,logger): + logger.info("Failed to validate os block") + return 1 + else: + logger.info("Successed to validate os block") + if not check_disk(logger): + logger.info("Failed to validate disk block") + return 1 + else: + logger.info("Successed to validate disk block") + if not check_hostdev(logger): + logger.info("Failed to validate hostdev block") + return 1 + else: + logger.info("Successed to validate hostdev block") + + except libvirtError, e: + logger.error("API error message: %s" % e.message) + clean_env(logger) + return 1 + + clean_env(logger) + return 0 -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list