/** * virArchFromHost: * * Return the host architecture. Prefer this to the * uname 'machine' field, since this will canonicalize * architecture names like 'amd64' into 'x86_64'. */ virArch virArchFromHost(void) { struct utsname ut; virArch arch; uname(&ut); /* Some special cases we need to handle first * for non-canonical names */ if (strlen(ut.machine) == 4 && ut.machine[0] == 'i' && ut.machine[2] == '8' && ut.machine[3] == '6' && ut.machine[4] == '\0') { arch = VIR_ARCH_I686; } else if (STREQ(ut.machine, "amd64")) { arch = VIR_ARCH_X86_64; } else { /* Otherwise assume the canonical name */ if ((arch = virArchFromString(ut.machine)) == VIR_ARCH_NONE) { VIR_WARN("Unknown host arch %s, report to [email protected]", ut.machine); } } VIR_DEBUG("Mapped %s to %d (%s)", ut.machine, arch, virArchToString(arch)); return arch; }
/** * cpuGetModels: * * @archName: CPU architecture string * @models: where to store the list of supported models * * Fetches all CPU models supported by libvirt on @archName. * * Returns number of supported CPU models or -1 on error. */ int cpuGetModels(const char *archName, char ***models) { struct cpuArchDriver *driver; virArch arch; VIR_DEBUG("arch=%s", archName); arch = virArchFromString(archName); if (arch == VIR_ARCH_NONE) { virReportError(VIR_ERR_INVALID_ARG, _("cannot find architecture %s"), archName); return -1; } driver = cpuGetSubDriver(arch); if (driver == NULL) { virReportError(VIR_ERR_INVALID_ARG, _("cannot find a driver for the architecture %s"), archName); return -1; } if (!driver->getModels) { virReportError(VIR_ERR_NO_SUPPORT, _("CPU driver for %s has no CPU model support"), virArchToString(arch)); return -1; } return driver->getModels(models); }
static int test_virDomainCapsFormat(const void *opaque) { const struct testData *data = opaque; virDomainCapsPtr domCaps = NULL; char *path = NULL; char *domCapsXML = NULL; int ret = -1; if (virAsprintf(&path, "%s/domaincapsschemadata/%s.xml", abs_srcdir, data->name) < 0) goto cleanup; if (!(domCaps = virDomainCapsNew(data->emulator, data->machine, virArchFromString(data->arch), data->type))) goto cleanup; switch (data->capsType) { case CAPS_NONE: break; case CAPS_ALL: if (fillAllCaps(domCaps) < 0) goto cleanup; break; case CAPS_QEMU: #if WITH_QEMU if (fillQemuCaps(domCaps, data->capsName, data->arch, data->machine, data->capsOpaque) < 0) goto cleanup; #endif break; case CAPS_LIBXL: #if WITH_LIBXL if (fillXenCaps(domCaps) < 0) goto cleanup; #endif break; } if (!(domCapsXML = virDomainCapsFormat(domCaps))) goto cleanup; if (virTestCompareToFile(domCapsXML, path) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(domCapsXML); VIR_FREE(path); virObjectUnref(domCaps); return ret; }
static int testQemuCapsCopy(const void *opaque) { int ret = -1; const testQemuData *data = opaque; char *capsFile = NULL; virCapsPtr caps = NULL; virQEMUCapsPtr orig = NULL; virQEMUCapsPtr copy = NULL; char *actual = NULL; if (virAsprintf(&capsFile, "%s/qemucapabilitiesdata/%s.%s.xml", abs_srcdir, data->base, data->archName) < 0) goto cleanup; if (!(caps = virCapabilitiesNew(virArchFromString(data->archName), false, false))) goto cleanup; if (!(orig = qemuTestParseCapabilities(caps, capsFile))) goto cleanup; if (!(copy = virQEMUCapsNewCopy(orig))) goto cleanup; if (!(actual = virQEMUCapsFormatCache(copy))) goto cleanup; if (virTestCompareToFile(actual, capsFile) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(capsFile); virObjectUnref(caps); virObjectUnref(orig); virObjectUnref(copy); VIR_FREE(actual); return ret; }
static int fillAllCaps(virDomainCapsPtr domCaps) { virDomainCapsOSPtr os = &domCaps->os; virDomainCapsLoaderPtr loader = &os->loader; virDomainCapsCPUPtr cpu = &domCaps->cpu; virDomainCapsDeviceDiskPtr disk = &domCaps->disk; virDomainCapsDeviceGraphicsPtr graphics = &domCaps->graphics; virDomainCapsDeviceVideoPtr video = &domCaps->video; virDomainCapsDeviceHostdevPtr hostdev = &domCaps->hostdev; virCPUDef host = { .type = VIR_CPU_TYPE_HOST, .arch = VIR_ARCH_X86_64, .model = (char *) "host", .vendor = (char *) "CPU Vendorrr", }; domCaps->maxvcpus = 255; os->supported = true; loader->supported = true; SET_ALL_BITS(loader->type); SET_ALL_BITS(loader->readonly); if (fillStringValues(&loader->values, "/foo/bar", "/tmp/my_path", NULL) < 0) return -1; cpu->hostPassthrough = true; cpu->hostModel = virCPUDefCopy(&host); if (!(cpu->custom = virDomainCapsCPUModelsNew(3)) || virDomainCapsCPUModelsAdd(cpu->custom, "Model1", -1, VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 || virDomainCapsCPUModelsAdd(cpu->custom, "Model2", -1, VIR_DOMCAPS_CPU_USABLE_NO) < 0 || virDomainCapsCPUModelsAdd(cpu->custom, "Model3", -1, VIR_DOMCAPS_CPU_USABLE_YES) < 0) return -1; disk->supported = true; SET_ALL_BITS(disk->diskDevice); SET_ALL_BITS(disk->bus); graphics->supported = true; SET_ALL_BITS(graphics->type); video->supported = true; SET_ALL_BITS(video->modelType); hostdev->supported = true; SET_ALL_BITS(hostdev->mode); SET_ALL_BITS(hostdev->startupPolicy); SET_ALL_BITS(hostdev->subsysType); SET_ALL_BITS(hostdev->capsType); SET_ALL_BITS(hostdev->pciBackend); return 0; } #if WITH_QEMU # include "testutilsqemu.h" static virCPUDef aarch64Cpu = { .sockets = 1, .cores = 1, .threads = 1, }; static virCPUDef ppc64leCpu = { .type = VIR_CPU_TYPE_HOST, .arch = VIR_ARCH_PPC64LE, .model = (char *) "POWER8", .sockets = 1, .cores = 1, .threads = 1, }; static virCPUDef x86Cpu = { .type = VIR_CPU_TYPE_HOST, .arch = VIR_ARCH_X86_64, .model = (char *) "Broadwell", .sockets = 1, .cores = 1, .threads = 1, }; static virCPUDef s390Cpu = { .type = VIR_CPU_TYPE_HOST, .arch = VIR_ARCH_S390X, .sockets = 1, .cores = 1, .threads = 1, }; static int fakeHostCPU(virCapsPtr caps, virArch arch) { virCPUDefPtr cpu; switch (arch) { case VIR_ARCH_AARCH64: cpu = &aarch64Cpu; break; case VIR_ARCH_PPC64LE: cpu = &ppc64leCpu; break; case VIR_ARCH_X86_64: cpu = &x86Cpu; break; case VIR_ARCH_S390X: cpu = &s390Cpu; break; default: virReportError(VIR_ERR_INTERNAL_ERROR, "cannot fake host CPU for arch %s", virArchToString(arch)); return -1; } if (!(caps->host.cpu = virCPUDefCopy(cpu))) return -1; return 0; } static int fillQemuCaps(virDomainCapsPtr domCaps, const char *name, const char *arch, const char *machine, virQEMUDriverConfigPtr cfg) { int ret = -1; char *path = NULL; virCapsPtr caps = NULL; virQEMUCapsPtr qemuCaps = NULL; virDomainCapsLoaderPtr loader = &domCaps->os.loader; if (!(caps = virCapabilitiesNew(domCaps->arch, false, false)) || fakeHostCPU(caps, domCaps->arch) < 0) goto cleanup; if (virAsprintf(&path, "%s/qemucapabilitiesdata/%s.%s.xml", abs_srcdir, name, arch) < 0 || !(qemuCaps = qemuTestParseCapabilities(caps, path))) goto cleanup; if (machine && VIR_STRDUP(domCaps->machine, virQEMUCapsGetCanonicalMachine(qemuCaps, machine)) < 0) goto cleanup; if (!domCaps->machine && VIR_STRDUP(domCaps->machine, virQEMUCapsGetDefaultMachine(qemuCaps)) < 0) goto cleanup; if (virQEMUCapsFillDomainCaps(caps, domCaps, qemuCaps, cfg->firmwares, cfg->nfirmwares) < 0) goto cleanup; /* The function above tries to query host's KVM & VFIO capabilities by * calling qemuHostdevHostSupportsPassthroughLegacy() and * qemuHostdevHostSupportsPassthroughVFIO() which, however, can't be * successfully mocked as they are not exposed as internal APIs. Therefore, * instead of mocking set the expected values here by hand. */ VIR_DOMAIN_CAPS_ENUM_SET(domCaps->hostdev.pciBackend, VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO); /* As of f05b6a918e28 we are expecting to see OVMF_CODE.fd file which * may not exists everywhere. */ while (loader->values.nvalues) VIR_FREE(loader->values.values[--loader->values.nvalues]); if (fillStringValues(&loader->values, "/usr/share/AAVMF/AAVMF_CODE.fd", "/usr/share/OVMF/OVMF_CODE.fd", NULL) < 0) goto cleanup; ret = 0; cleanup: virObjectUnref(caps); virObjectUnref(qemuCaps); VIR_FREE(path); return ret; } #endif /* WITH_QEMU */ #ifdef WITH_LIBXL # include "testutilsxen.h" static int fillXenCaps(virDomainCapsPtr domCaps) { virFirmwarePtr *firmwares; int ret = -1; if (VIR_ALLOC_N(firmwares, 2) < 0) return ret; if (VIR_ALLOC(firmwares[0]) < 0 || VIR_ALLOC(firmwares[1]) < 0) goto cleanup; if (VIR_STRDUP(firmwares[0]->name, "/usr/lib/xen/boot/hvmloader") < 0 || VIR_STRDUP(firmwares[1]->name, "/usr/lib/xen/boot/ovmf.bin") < 0) goto cleanup; if (libxlMakeDomainCapabilities(domCaps, firmwares, 2) < 0) goto cleanup; ret = 0; cleanup: virFirmwareFreeList(firmwares, 2); return ret; } #endif /* WITH_LIBXL */ #ifdef WITH_BHYVE # include "bhyve/bhyve_capabilities.h" static int fillBhyveCaps(virDomainCapsPtr domCaps, unsigned int *bhyve_caps) { virDomainCapsStringValuesPtr firmwares = NULL; int ret = -1; if (VIR_ALLOC(firmwares) < 0) return -1; if (fillStringValues(firmwares, "/foo/bar", "/foo/baz", NULL) < 0) goto cleanup; if (virBhyveDomainCapsFill(domCaps, *bhyve_caps, firmwares) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(firmwares); return ret; } #endif /* WITH_BHYVE */ enum testCapsType { CAPS_NONE, CAPS_ALL, CAPS_QEMU, CAPS_LIBXL, CAPS_BHYVE, }; struct testData { const char *name; const char *emulator; const char *machine; const char *arch; virDomainVirtType type; enum testCapsType capsType; const char *capsName; void *capsOpaque; }; static int test_virDomainCapsFormat(const void *opaque) { const struct testData *data = opaque; virDomainCapsPtr domCaps = NULL; char *path = NULL; char *domCapsXML = NULL; int ret = -1; if (virAsprintf(&path, "%s/domaincapsschemadata/%s.xml", abs_srcdir, data->name) < 0) goto cleanup; if (!(domCaps = virDomainCapsNew(data->emulator, data->machine, virArchFromString(data->arch), data->type))) goto cleanup; switch (data->capsType) { case CAPS_NONE: break; case CAPS_ALL: if (fillAllCaps(domCaps) < 0) goto cleanup; break; case CAPS_QEMU: #if WITH_QEMU if (fillQemuCaps(domCaps, data->capsName, data->arch, data->machine, data->capsOpaque) < 0) goto cleanup; #endif break; case CAPS_LIBXL: #if WITH_LIBXL if (fillXenCaps(domCaps) < 0) goto cleanup; #endif break; case CAPS_BHYVE: #if WITH_BHYVE if (fillBhyveCaps(domCaps, data->capsOpaque) < 0) goto cleanup; #endif break; } if (!(domCapsXML = virDomainCapsFormat(domCaps))) goto cleanup; if (virTestCompareToFile(domCapsXML, path) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(domCapsXML); VIR_FREE(path); virObjectUnref(domCaps); return ret; } static int mymain(void) { int ret = 0; #if WITH_BHYVE unsigned int bhyve_caps = 0; #endif #if WITH_QEMU virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false); if (!cfg) return EXIT_FAILURE; #endif #define DO_TEST(Name, Emulator, Machine, Arch, Type, CapsType) \ do { \ struct testData data = { \ .name = Name, \ .emulator = Emulator, \ .machine = Machine, \ .arch = Arch, \ .type = Type, \ .capsType = CapsType, \ }; \ if (virTestRun(Name, test_virDomainCapsFormat, &data) < 0) \ ret = -1; \ } while (0) #define DO_TEST_QEMU(Name, CapsName, Emulator, Machine, Arch, Type) \ do { \ char *name = NULL; \ if (virAsprintf(&name, "qemu_%s%s%s.%s", \ Name, \ Machine ? "-" : "", Machine ? Machine : "", \ Arch) < 0) { \ ret = -1; \ break; \ } \ struct testData data = { \ .name = name, \ .emulator = Emulator, \ .machine = Machine, \ .arch = Arch, \ .type = Type, \ .capsType = CAPS_QEMU, \ .capsName = CapsName, \ .capsOpaque = cfg, \ }; \ if (virTestRun(name, test_virDomainCapsFormat, &data) < 0) \ ret = -1; \ VIR_FREE(name); \ } while (0) #define DO_TEST_LIBXL(Name, Emulator, Machine, Arch, Type) \ do { \ struct testData data = { \ .name = Name, \ .emulator = Emulator, \ .machine = Machine, \ .arch = Arch, \ .type = Type, \ .capsType = CAPS_LIBXL, \ }; \ if (virTestRun(Name, test_virDomainCapsFormat, &data) < 0) \ ret = -1; \ } while (0) DO_TEST("basic", "/bin/emulatorbin", "my-machine-type", "x86_64", VIR_DOMAIN_VIRT_UML, CAPS_NONE); DO_TEST("full", "/bin/emulatorbin", "my-machine-type", "x86_64", VIR_DOMAIN_VIRT_KVM, CAPS_ALL); #define DO_TEST_BHYVE(Name, Emulator, BhyveCaps, Type) \ do { \ char *name = NULL; \ if (virAsprintf(&name, "bhyve_%s.x86_64", Name) < 0) { \ ret = -1; \ break; \ } \ struct testData data = { \ .name = name, \ .emulator = Emulator, \ .arch = "x86_64", \ .type = Type, \ .capsType = CAPS_BHYVE, \ .capsOpaque = BhyveCaps, \ }; \ if (virTestRun(name, test_virDomainCapsFormat, &data) < 0) \ ret = -1; \ VIR_FREE(name); \ } while (0) #if WITH_QEMU DO_TEST_QEMU("1.7.0", "caps_1.7.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.6.0", "caps_2.6.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.6.0", "caps_2.6.0-gicv2", "/usr/bin/qemu-system-aarch64", NULL, "aarch64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.6.0-gicv2", "caps_2.6.0-gicv2", "/usr/bin/qemu-system-aarch64", "virt", "aarch64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.6.0-gicv3", "caps_2.6.0-gicv3", "/usr/bin/qemu-system-aarch64", "virt", "aarch64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.6.0", "caps_2.6.0", "/usr/bin/qemu-system-ppc64", NULL, "ppc64le", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.8.0", "caps_2.8.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.8.0-tcg", "caps_2.8.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_QEMU); DO_TEST_QEMU("2.9.0", "caps_2.9.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.9.0", "caps_2.9.0", "/usr/bin/qemu-system-x86_64", "q35", "x86_64", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.9.0-tcg", "caps_2.9.0", "/usr/bin/qemu-system-x86_64", NULL, "x86_64", VIR_DOMAIN_VIRT_QEMU); DO_TEST_QEMU("2.7.0", "caps_2.7.0", "/usr/bin/qemu-system-s390x", NULL, "s390x", VIR_DOMAIN_VIRT_KVM); DO_TEST_QEMU("2.8.0", "caps_2.8.0", "/usr/bin/qemu-system-s390x", NULL, "s390x", VIR_DOMAIN_VIRT_KVM); virObjectUnref(cfg); #endif /* WITH_QEMU */ #if WITH_LIBXL # ifdef LIBXL_HAVE_PVUSB # define LIBXL_XENPV_CAPS "libxl-xenpv-usb" # define LIBXL_XENFV_CAPS "libxl-xenfv-usb" # else # define LIBXL_XENPV_CAPS "libxl-xenpv" # define LIBXL_XENFV_CAPS "libxl-xenfv" # endif DO_TEST_LIBXL(LIBXL_XENPV_CAPS, "/usr/bin/qemu-system-x86_64", "xenpv", "x86_64", VIR_DOMAIN_VIRT_XEN); DO_TEST_LIBXL(LIBXL_XENFV_CAPS, "/usr/bin/qemu-system-x86_64", "xenfv", "x86_64", VIR_DOMAIN_VIRT_XEN); #endif /* WITH_LIBXL */ #if WITH_BHYVE DO_TEST_BHYVE("basic", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE); bhyve_caps |= BHYVE_CAP_LPC_BOOTROM; DO_TEST_BHYVE("uefi", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE); bhyve_caps |= BHYVE_CAP_FBUF; DO_TEST_BHYVE("fbuf", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE); #endif /* WITH_BHYVE */ return ret; }
virDomainDefPtr lxcParseConfigString(const char *config, virCapsPtr caps, virDomainXMLOptionPtr xmlopt) { virDomainDefPtr vmdef = NULL; virConfPtr properties = NULL; virConfValuePtr value; if (!(properties = virConfReadMem(config, 0, VIR_CONF_FLAG_LXC_FORMAT))) return NULL; if (!(vmdef = virDomainDefNew())) goto error; if (virUUIDGenerate(vmdef->uuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to generate uuid")); goto error; } vmdef->id = -1; virDomainDefSetMemoryTotal(vmdef, 64 * 1024); vmdef->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; vmdef->onCrash = VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY; vmdef->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; vmdef->virtType = VIR_DOMAIN_VIRT_LXC; /* Value not handled by the LXC driver, setting to * minimum required to make XML parsing pass */ if (virDomainDefSetVcpusMax(vmdef, 1) < 0) goto error; if (virDomainDefSetVcpus(vmdef, 1) < 0) goto error; vmdef->nfss = 0; vmdef->os.type = VIR_DOMAIN_OSTYPE_EXE; if ((value = virConfGetValue(properties, "lxc.arch")) && value->str) { virArch arch = virArchFromString(value->str); if (arch == VIR_ARCH_NONE && STREQ(value->str, "x86")) arch = VIR_ARCH_I686; else if (arch == VIR_ARCH_NONE && STREQ(value->str, "amd64")) arch = VIR_ARCH_X86_64; vmdef->os.arch = arch; } if (VIR_STRDUP(vmdef->os.init, "/sbin/init") < 0) goto error; if (!(value = virConfGetValue(properties, "lxc.utsname")) || !value->str || (VIR_STRDUP(vmdef->name, value->str) < 0)) goto error; if (!vmdef->name && (VIR_STRDUP(vmdef->name, "unnamed") < 0)) goto error; if (lxcSetRootfs(vmdef, properties) < 0) goto error; /* Look for fstab: we shouldn't have it */ if (virConfGetValue(properties, "lxc.mount")) { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", _("lxc.mount found, use lxc.mount.entry lines instead")); goto error; } /* Loop over lxc.mount.entry to add filesystem devices for them */ if (virConfWalk(properties, lxcFstabWalkCallback, vmdef) < 0) goto error; /* Network configuration */ if (lxcConvertNetworkSettings(vmdef, properties) < 0) goto error; /* Consoles */ if (lxcCreateConsoles(vmdef, properties) < 0) goto error; /* lxc.id_map */ if (virConfWalk(properties, lxcIdmapWalkCallback, vmdef) < 0) goto error; /* lxc.cgroup.memory.* */ if (lxcSetMemTune(vmdef, properties) < 0) goto error; /* lxc.cgroup.cpu.* */ if (lxcSetCpuTune(vmdef, properties) < 0) goto error; /* lxc.cgroup.cpuset.* */ if (lxcSetCpusetTune(vmdef, properties) < 0) goto error; /* lxc.cgroup.blkio.* */ if (lxcSetBlkioTune(vmdef, properties) < 0) goto error; /* lxc.cap.drop */ lxcSetCapDrop(vmdef, properties); if (virDomainDefPostParse(vmdef, caps, VIR_DOMAIN_DEF_PARSE_ABI_UPDATE, xmlopt) < 0) goto cleanup; goto cleanup; error: virDomainDefFree(vmdef); vmdef = NULL; cleanup: virConfFree(properties); return vmdef; }
virCPUDefPtr virCPUDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, enum virCPUType mode) { virCPUDefPtr def; xmlNodePtr *nodes = NULL; int n; size_t i; char *cpuMode; char *fallback = NULL; char *vendor_id = NULL; if (!xmlStrEqual(node->name, BAD_CAST "cpu")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'cpu' element")); return NULL; } if (VIR_ALLOC(def) < 0) return NULL; if (mode == VIR_CPU_TYPE_AUTO) { if (virXPathBoolean("boolean(./arch)", ctxt)) { if (virXPathBoolean("boolean(./@match)", ctxt)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'arch' element element cannot be used inside 'cpu'" " element with 'match' attribute'")); goto error; } def->type = VIR_CPU_TYPE_HOST; } else { def->type = VIR_CPU_TYPE_GUEST; } } else { def->type = mode; } if ((cpuMode = virXMLPropString(node, "mode"))) { if (def->type == VIR_CPU_TYPE_HOST) { VIR_FREE(cpuMode); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Attribute mode is only allowed for guest CPU")); goto error; } else { def->mode = virCPUModeTypeFromString(cpuMode); if (def->mode < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid mode attribute '%s'"), cpuMode); VIR_FREE(cpuMode); goto error; } VIR_FREE(cpuMode); } } else { if (def->type == VIR_CPU_TYPE_HOST) def->mode = -1; else def->mode = VIR_CPU_MODE_CUSTOM; } if (def->type == VIR_CPU_TYPE_GUEST) { char *match = virXMLPropString(node, "match"); if (!match) { if (virXPathBoolean("boolean(./model)", ctxt)) def->match = VIR_CPU_MATCH_EXACT; else def->match = -1; } else { def->match = virCPUMatchTypeFromString(match); VIR_FREE(match); if (def->match < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid match attribute for CPU " "specification")); goto error; } } } if (def->type == VIR_CPU_TYPE_HOST) { char *arch = virXPathString("string(./arch[1])", ctxt); if (!arch) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU architecture")); goto error; } if ((def->arch = virArchFromString(arch)) == VIR_ARCH_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown architecture %s"), arch); VIR_FREE(arch); goto error; } VIR_FREE(arch); } if (!(def->model = virXPathString("string(./model[1])", ctxt)) && def->type == VIR_CPU_TYPE_HOST) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU model name")); goto error; } if (def->type == VIR_CPU_TYPE_GUEST && def->mode != VIR_CPU_MODE_HOST_PASSTHROUGH) { if ((fallback = virXPathString("string(./model[1]/@fallback)", ctxt))) { if ((def->fallback = virCPUFallbackTypeFromString(fallback)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid fallback attribute")); goto error; } } if ((vendor_id = virXPathString("string(./model[1]/@vendor_id)", ctxt))) { if (strlen(vendor_id) != VIR_CPU_VENDOR_ID_LENGTH) { virReportError(VIR_ERR_XML_ERROR, _("vendor_id must be exactly %d characters long"), VIR_CPU_VENDOR_ID_LENGTH); goto error; } /* ensure that the string can be passed to qemu*/ if (strchr(vendor_id, ',')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("vendor id is invalid")); goto error; } def->vendor_id = vendor_id; vendor_id = NULL; } } def->vendor = virXPathString("string(./vendor[1])", ctxt); if (def->vendor && !def->model) { virReportError(VIR_ERR_XML_ERROR, "%s", _("CPU vendor specified without CPU model")); goto error; } if (virXPathNode("./topology[1]", ctxt)) { int ret; unsigned long ul; ret = virXPathULong("string(./topology[1]/@sockets)", ctxt, &ul); if (ret < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'sockets' attribute in CPU topology")); goto error; } def->sockets = (unsigned int) ul; ret = virXPathULong("string(./topology[1]/@cores)", ctxt, &ul); if (ret < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'cores' attribute in CPU topology")); goto error; } def->cores = (unsigned int) ul; ret = virXPathULong("string(./topology[1]/@threads)", ctxt, &ul); if (ret < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'threads' attribute in CPU topology")); goto error; } def->threads = (unsigned int) ul; if (!def->sockets || !def->cores || !def->threads) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU topology")); goto error; } } if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) goto error; if (n > 0) { if (!def->model && def->mode != VIR_CPU_MODE_HOST_MODEL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Non-empty feature list specified without " "CPU model")); goto error; } if (VIR_RESIZE_N(def->features, def->nfeatures_max, def->nfeatures, n) < 0) goto error; def->nfeatures = n; } for (i = 0; i < n; i++) { char *name; int policy; /* enum virDomainCPUFeaturePolicy */ size_t j; if (def->type == VIR_CPU_TYPE_GUEST) { char *strpolicy; strpolicy = virXMLPropString(nodes[i], "policy"); if (strpolicy == NULL) policy = VIR_CPU_FEATURE_REQUIRE; else policy = virCPUFeaturePolicyTypeFromString(strpolicy); VIR_FREE(strpolicy); if (policy < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid CPU feature policy")); goto error; } } else { policy = -1; } if (!(name = virXMLPropString(nodes[i], "name")) || *name == 0) { VIR_FREE(name); virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU feature name")); goto error; } for (j = 0; j < i; j++) { if (STREQ(name, def->features[j].name)) { virReportError(VIR_ERR_XML_ERROR, _("CPU feature `%s' specified more than once"), name); VIR_FREE(name); goto error; } } def->features[i].name = name; def->features[i].policy = policy; } if (virXPathNode("./numa[1]", ctxt)) { VIR_FREE(nodes); n = virXPathNodeSet("./numa[1]/cell", ctxt, &nodes); if (n <= 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("NUMA topology defined without NUMA cells")); goto error; } if (VIR_RESIZE_N(def->cells, def->ncells_max, def->ncells, n) < 0) goto error; def->ncells = n; for (i = 0; i < n; i++) { char *cpus, *memory; int ret, ncpus = 0; def->cells[i].cellid = i; cpus = virXMLPropString(nodes[i], "cpus"); if (!cpus) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'cpus' attribute in NUMA cell")); goto error; } def->cells[i].cpustr = cpus; ncpus = virBitmapParse(cpus, 0, &def->cells[i].cpumask, VIR_DOMAIN_CPUMASK_LEN); if (ncpus <= 0) goto error; def->cells_cpus += ncpus; memory = virXMLPropString(nodes[i], "memory"); if (!memory) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'memory' attribute in NUMA cell")); goto error; } ret = virStrToLong_ui(memory, NULL, 10, &def->cells[i].mem); if (ret == -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid 'memory' attribute in NUMA cell")); VIR_FREE(memory); goto error; } VIR_FREE(memory); } } cleanup: VIR_FREE(fallback); VIR_FREE(vendor_id); VIR_FREE(nodes); return def; error: virCPUDefFree(def); def = NULL; goto cleanup; }
/* * Parses CPU definition XML from a node pointed to by @xpath. If @xpath is * NULL, the current node of @ctxt is used (i.e., it is a shortcut to "."). * * Missing <cpu> element in the XML document is not considered an error unless * @xpath is NULL in which case the function expects it was provided with a * valid <cpu> element already. In other words, the function returns success * and sets @cpu to NULL if @xpath is not NULL and the node pointed to by * @xpath is not found. * * Returns 0 on success, -1 on error. */ int virCPUDefParseXML(xmlXPathContextPtr ctxt, const char *xpath, virCPUType type, virCPUDefPtr *cpu) { virCPUDefPtr def = NULL; xmlNodePtr *nodes = NULL; xmlNodePtr oldnode = ctxt->node; int n; size_t i; char *cpuMode; char *fallback = NULL; char *vendor_id = NULL; int ret = -1; *cpu = NULL; if (xpath && !(ctxt->node = virXPathNode(xpath, ctxt))) { ret = 0; goto cleanup; } if (!virXMLNodeNameEqual(ctxt->node, "cpu")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'cpu' element")); goto cleanup; } if (VIR_ALLOC(def) < 0) goto cleanup; if (type == VIR_CPU_TYPE_AUTO) { if (virXPathBoolean("boolean(./arch)", ctxt)) { if (virXPathBoolean("boolean(./@match)", ctxt)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'arch' element cannot be used inside 'cpu'" " element with 'match' attribute'")); goto cleanup; } def->type = VIR_CPU_TYPE_HOST; } else { def->type = VIR_CPU_TYPE_GUEST; } } else { def->type = type; } if ((cpuMode = virXMLPropString(ctxt->node, "mode"))) { if (def->type == VIR_CPU_TYPE_HOST) { VIR_FREE(cpuMode); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Attribute mode is only allowed for guest CPU")); goto cleanup; } else { def->mode = virCPUModeTypeFromString(cpuMode); if (def->mode < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid mode attribute '%s'"), cpuMode); VIR_FREE(cpuMode); goto cleanup; } VIR_FREE(cpuMode); } } else { if (def->type == VIR_CPU_TYPE_HOST) def->mode = -1; else def->mode = VIR_CPU_MODE_CUSTOM; } if (def->type == VIR_CPU_TYPE_GUEST) { char *match = virXMLPropString(ctxt->node, "match"); char *check; if (!match) { if (virXPathBoolean("boolean(./model)", ctxt)) def->match = VIR_CPU_MATCH_EXACT; else def->match = -1; } else { def->match = virCPUMatchTypeFromString(match); VIR_FREE(match); if (def->match < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid match attribute for CPU " "specification")); goto cleanup; } } if ((check = virXMLPropString(ctxt->node, "check"))) { int value = virCPUCheckTypeFromString(check); VIR_FREE(check); if (value < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid check attribute for CPU " "specification")); goto cleanup; } def->check = value; } } if (def->type == VIR_CPU_TYPE_HOST) { char *arch = virXPathString("string(./arch[1])", ctxt); if (!arch) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU architecture")); goto cleanup; } if ((def->arch = virArchFromString(arch)) == VIR_ARCH_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown architecture %s"), arch); VIR_FREE(arch); goto cleanup; } VIR_FREE(arch); if (virXPathBoolean("boolean(./microcode[1]/@version)", ctxt) > 0 && virXPathUInt("string(./microcode[1]/@version)", ctxt, &def->microcodeVersion) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("invalid microcode version")); goto cleanup; } } if (!(def->model = virXPathString("string(./model[1])", ctxt)) && def->type == VIR_CPU_TYPE_HOST) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU model name")); goto cleanup; } if (def->type == VIR_CPU_TYPE_GUEST && def->mode != VIR_CPU_MODE_HOST_PASSTHROUGH) { if ((fallback = virXPathString("string(./model[1]/@fallback)", ctxt))) { if ((def->fallback = virCPUFallbackTypeFromString(fallback)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid fallback attribute")); goto cleanup; } } if ((vendor_id = virXPathString("string(./model[1]/@vendor_id)", ctxt))) { if (strlen(vendor_id) != VIR_CPU_VENDOR_ID_LENGTH) { virReportError(VIR_ERR_XML_ERROR, _("vendor_id must be exactly %d characters long"), VIR_CPU_VENDOR_ID_LENGTH); goto cleanup; } /* ensure that the string can be passed to qemu*/ if (strchr(vendor_id, ',')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("vendor id is invalid")); goto cleanup; } def->vendor_id = vendor_id; vendor_id = NULL; } } def->vendor = virXPathString("string(./vendor[1])", ctxt); if (def->vendor && !def->model) { virReportError(VIR_ERR_XML_ERROR, "%s", _("CPU vendor specified without CPU model")); goto cleanup; } if (virXPathNode("./topology[1]", ctxt)) { unsigned long ul; if (virXPathULong("string(./topology[1]/@sockets)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'sockets' attribute in CPU topology")); goto cleanup; } def->sockets = (unsigned int) ul; if (virXPathULong("string(./topology[1]/@cores)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'cores' attribute in CPU topology")); goto cleanup; } def->cores = (unsigned int) ul; if (virXPathULong("string(./topology[1]/@threads)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'threads' attribute in CPU topology")); goto cleanup; } def->threads = (unsigned int) ul; if (!def->sockets || !def->cores || !def->threads) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU topology")); goto cleanup; } } if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) goto cleanup; if (n > 0) { if (!def->model && def->mode == VIR_CPU_MODE_CUSTOM) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Non-empty feature list specified without " "CPU model")); goto cleanup; } if (VIR_RESIZE_N(def->features, def->nfeatures_max, def->nfeatures, n) < 0) goto cleanup; def->nfeatures = n; } for (i = 0; i < n; i++) { char *name; int policy; /* enum virDomainCPUFeaturePolicy */ size_t j; if (def->type == VIR_CPU_TYPE_GUEST) { char *strpolicy; strpolicy = virXMLPropString(nodes[i], "policy"); if (strpolicy == NULL) policy = VIR_CPU_FEATURE_REQUIRE; else policy = virCPUFeaturePolicyTypeFromString(strpolicy); VIR_FREE(strpolicy); if (policy < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid CPU feature policy")); goto cleanup; } } else { policy = -1; } if (!(name = virXMLPropString(nodes[i], "name")) || *name == 0) { VIR_FREE(name); virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU feature name")); goto cleanup; } for (j = 0; j < i; j++) { if (STREQ(name, def->features[j].name)) { virReportError(VIR_ERR_XML_ERROR, _("CPU feature '%s' specified more than once"), name); VIR_FREE(name); goto cleanup; } } def->features[i].name = name; def->features[i].policy = policy; } if (virXPathInt("count(./cache)", ctxt, &n) < 0) { goto cleanup; } else if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("at most one CPU cache element may be specified")); goto cleanup; } else if (n == 1) { int level = -1; char *strmode; int mode; if (virXPathBoolean("boolean(./cache[1]/@level)", ctxt) == 1 && (virXPathInt("string(./cache[1]/@level)", ctxt, &level) < 0 || level < 1 || level > 3)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid CPU cache level, must be in range [1,3]")); goto cleanup; } if (!(strmode = virXPathString("string(./cache[1]/@mode)", ctxt)) || (mode = virCPUCacheModeTypeFromString(strmode)) < 0) { VIR_FREE(strmode); virReportError(VIR_ERR_XML_ERROR, "%s", _("missing or invalid CPU cache mode")); goto cleanup; } VIR_FREE(strmode); if (VIR_ALLOC(def->cache) < 0) goto cleanup; def->cache->level = level; def->cache->mode = mode; } VIR_STEAL_PTR(*cpu, def); ret = 0; cleanup: ctxt->node = oldnode; VIR_FREE(fallback); VIR_FREE(vendor_id); VIR_FREE(nodes); virCPUDefFree(def); return ret; }
static int mymain(void) { int ret = 0; virQEMUDriver driver; struct testBackingXMLjsonXMLdata xmljsonxmldata; struct testQemuDiskXMLToJSONData diskxmljsondata; char *capslatest_x86_64 = NULL; virQEMUCapsPtr caps_x86_64 = NULL; if (qemuTestDriverInit(&driver) < 0) return EXIT_FAILURE; diskxmljsondata.driver = &driver; if (!(capslatest_x86_64 = testQemuGetLatestCapsForArch("x86_64", "xml"))) return EXIT_FAILURE; VIR_TEST_VERBOSE("\nlatest caps x86_64: %s\n", capslatest_x86_64); if (!(caps_x86_64 = qemuTestParseCapabilitiesArch(virArchFromString("x86_64"), capslatest_x86_64))) return EXIT_FAILURE; diskxmljsondata.qemuCaps = caps_x86_64; virTestCounterReset("qemu storage source xml->json->xml "); # define TEST_JSON_FORMAT(tpe, xmlstr) \ do { \ xmljsonxmldata.type = tpe; \ xmljsonxmldata.xml = xmlstr; \ if (virTestRun(virTestCounterNext(), testBackingXMLjsonXML, \ &xmljsonxmldata) < 0) \ ret = -1; \ } while (0) # define TEST_JSON_FORMAT_NET(xmlstr) \ TEST_JSON_FORMAT(VIR_STORAGE_TYPE_NETWORK, xmlstr) TEST_JSON_FORMAT(VIR_STORAGE_TYPE_FILE, "<source file='/path/to/file'/>\n"); /* type VIR_STORAGE_TYPE_BLOCK is not tested since it parses back to 'file' */ /* type VIR_STORAGE_TYPE_DIR it is a 'format' driver in qemu */ TEST_JSON_FORMAT_NET("<source protocol='http' name=''>\n" " <host name='example.com' port='80'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='http' name='file'>\n" " <host name='example.com' port='80'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='https' name='file'>\n" " <host name='example.com' port='432'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='gluster' name='vol/file'>\n" " <host name='example.com' port='24007'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='gluster' name='testvol/img.qcow2'>\n" " <host name='example.com' port='1234'/>\n" " <host transport='unix' socket='/path/socket'/>\n" " <host name='example.com' port='24007'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='nbd'>\n" " <host transport='unix' socket='/path/to/socket'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='nbd' name='blah'>\n" " <host name='example.org' port='6000'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='nbd'>\n" " <host name='example.org' port='6000'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='ssh' name='blah'>\n" " <host name='example.org' port='6000'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='rbd' name='libvirt/test'>\n" " <host name='example.com' port='1234'/>\n" " <host name='example2.com'/>\n" " <snapshot name='snapshotname'/>\n" " <config file='/path/to/conf'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='iscsi' name='iqn.2016-12.com.virttest:emulated-iscsi-noauth.target/0'>\n" " <host name='test.org' port='3260'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='iscsi' name='iqn.2016-12.com.virttest:emulated-iscsi-noauth.target/6'>\n" " <host name='test.org' port='1234'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='sheepdog' name='test'>\n" " <host name='example.com' port='321'/>\n" "</source>\n"); TEST_JSON_FORMAT_NET("<source protocol='vxhs' name='c6718f6b-0401-441d-a8c3-1f0064d75ee0'>\n" " <host name='example.com' port='9999'/>\n" "</source>\n"); # define TEST_DISK_TO_JSON_FULL(nme, fl) \ do { \ diskxmljsondata.name = nme; \ diskxmljsondata.props = NULL; \ diskxmljsondata.nprops = 0; \ diskxmljsondata.fail = fl; \ if (virTestRun("disk xml to props " nme, testQemuDiskXMLToProps, \ &diskxmljsondata) < 0) \ ret = -1; \ if (virTestRun("disk xml to props validate schema " nme, \ testQemuDiskXMLToPropsValidateSchema, &diskxmljsondata) < 0) \ ret = -1; \ if (virTestRun("disk xml to props validate file " nme, \ testQemuDiskXMLToPropsValidateFile, &diskxmljsondata) < 0) \ ret = -1; \ testQemuDiskXMLToPropsClear(&diskxmljsondata); \ } while (0) # define TEST_DISK_TO_JSON(nme) TEST_DISK_TO_JSON_FULL(nme, false) if (!(diskxmljsondata.schema = testQEMUSchemaLoad())) { ret = -1; goto cleanup; } if (virQEMUQAPISchemaPathGet("blockdev-add/arg-type", diskxmljsondata.schema, &diskxmljsondata.schemaroot) < 0 || !diskxmljsondata.schemaroot) { VIR_TEST_VERBOSE("failed to find schema entry for blockdev-add\n"); ret = -1; goto cleanup; } TEST_DISK_TO_JSON_FULL("nodename-long-format", true); TEST_DISK_TO_JSON_FULL("nodename-long-protocol", true); TEST_DISK_TO_JSON("file-raw-noopts"); TEST_DISK_TO_JSON("file-bochs-noopts"); TEST_DISK_TO_JSON("file-cloop-noopts"); TEST_DISK_TO_JSON("file-dmg-noopts"); TEST_DISK_TO_JSON("file-ploop-noopts"); TEST_DISK_TO_JSON("file-vdi-noopts"); TEST_DISK_TO_JSON("file-vhd-noopts"); TEST_DISK_TO_JSON("file-vpc-noopts"); TEST_DISK_TO_JSON("file-backing_basic-noopts"); TEST_DISK_TO_JSON("dir-fat-readonly"); TEST_DISK_TO_JSON("dir-fat-floppy"); TEST_DISK_TO_JSON("file-raw-aio_native"); TEST_DISK_TO_JSON("file-backing_basic-aio_threads"); TEST_DISK_TO_JSON("file-raw-luks"); TEST_DISK_TO_JSON("file-qcow2-backing-chain-noopts"); TEST_DISK_TO_JSON("file-qcow2-backing-chain-unterminated"); TEST_DISK_TO_JSON("file-qcow2-backing-chain-encryption"); TEST_DISK_TO_JSON("network-qcow2-backing-chain-encryption_auth"); TEST_DISK_TO_JSON("file-backing_basic-unmap"); TEST_DISK_TO_JSON("file-backing_basic-unmap-detect"); TEST_DISK_TO_JSON("file-backing_basic-unmap-ignore"); TEST_DISK_TO_JSON("file-backing_basic-detect"); TEST_DISK_TO_JSON("file-backing_basic-cache-none"); TEST_DISK_TO_JSON("file-backing_basic-cache-writethrough"); TEST_DISK_TO_JSON("file-backing_basic-cache-writeback"); TEST_DISK_TO_JSON("file-backing_basic-cache-directsync"); TEST_DISK_TO_JSON("file-backing_basic-cache-unsafe"); TEST_DISK_TO_JSON("network-qcow2-backing-chain-cache-unsafe"); TEST_DISK_TO_JSON("dir-fat-cache"); TEST_DISK_TO_JSON("network-nbd-tls"); TEST_DISK_TO_JSON("block-raw-noopts"); TEST_DISK_TO_JSON("block-raw-reservations"); cleanup: virHashFree(diskxmljsondata.schema); qemuTestDriverFree(&driver); VIR_FREE(capslatest_x86_64); virObjectUnref(caps_x86_64); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }