#include #include #include #include #include "testutils.h" #ifdef WITH_QEMU # include "internal.h" # include "viralloc.h" # include "viridentity.h" # include "qemu/qemu_alias.h" # include "qemu/qemu_capabilities.h" # include "qemu/qemu_command.h" # include "qemu/qemu_domain.h" # include "qemu/qemu_migration.h" # include "qemu/qemu_process.h" # include "qemu/qemu_slirp.h" # include "qemu/qemu_qapi.h" # include "datatypes.h" # include "conf/storage_conf.h" # include "cpu/cpu_map.h" # include "virstring.h" # include "storage/storage_driver.h" # include "virmock.h" # include "virfilewrapper.h" # include "configmake.h" # include "testutilsqemuschema.h" # include "qemu/qemu_monitor_json.h" # define LIBVIRT_QEMU_CAPSPRIV_H_ALLOW # include "qemu/qemu_capspriv.h" # include "testutilsqemu.h" # define VIR_FROM_THIS VIR_FROM_QEMU static virQEMUDriver driver; static unsigned char * fakeSecretGetValue(virSecretPtr obj G_GNUC_UNUSED, size_t *value_size, unsigned int fakeflags G_GNUC_UNUSED) { char *secret; secret = g_strdup("AQCVn5hO6HzFAhAAq0NCv8jtJcIcE+HOBlMQ1A"); *value_size = strlen(secret); return (unsigned char *) secret; } static virSecretPtr fakeSecretLookupByUsage(virConnectPtr conn, int usageType, const char *usageID) { unsigned char uuid[VIR_UUID_BUFLEN]; if (usageType == VIR_SECRET_USAGE_TYPE_VOLUME) { if (!STRPREFIX(usageID, "/storage/guest_disks/")) { virReportError(VIR_ERR_INTERNAL_ERROR, "test provided invalid volume storage prefix '%s'", usageID); return NULL; } } else if (STRNEQ(usageID, "mycluster_myname") && STRNEQ(usageID, "client.admin secret")) { virReportError(VIR_ERR_INTERNAL_ERROR, "test provided incorrect usage '%s'", usageID); return NULL; } if (virUUIDGenerate(uuid) < 0) return NULL; return virGetSecret(conn, uuid, usageType, usageID); } static virSecretPtr fakeSecretLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { /* NB: This mocked value could be "tls" or "volume" depending on * which test is being run, we'll leave at NONE (or 0) */ return virGetSecret(conn, uuid, VIR_SECRET_USAGE_TYPE_NONE, ""); } static virSecretDriver fakeSecretDriver = { .connectNumOfSecrets = NULL, .connectListSecrets = NULL, .secretLookupByUUID = fakeSecretLookupByUUID, .secretLookupByUsage = fakeSecretLookupByUsage, .secretDefineXML = NULL, .secretGetXMLDesc = NULL, .secretSetValue = NULL, .secretGetValue = fakeSecretGetValue, .secretUndefine = NULL, }; # define STORAGE_POOL_XML_PATH "storagepoolxml2xmlout/" static const unsigned char fakeUUID[VIR_UUID_BUFLEN] = "fakeuuid"; static virStoragePoolPtr fakeStoragePoolLookupByName(virConnectPtr conn, const char *name) { g_autofree char *xmlpath = NULL; virStoragePoolPtr ret = NULL; if (STRNEQ(name, "inactive")) { xmlpath = g_strdup_printf("%s/%s%s.xml", abs_srcdir, STORAGE_POOL_XML_PATH, name); if (!virFileExists(xmlpath)) { virReportError(VIR_ERR_NO_STORAGE_POOL, "File '%s' not found", xmlpath); goto cleanup; } } ret = virGetStoragePool(conn, name, fakeUUID, NULL, NULL); cleanup: return ret; } static virStorageVolPtr fakeStorageVolLookupByName(virStoragePoolPtr pool, const char *name) { g_auto(GStrv) volinfo = NULL; virStorageVolPtr ret = NULL; if (STREQ(pool->name, "inactive")) { virReportError(VIR_ERR_OPERATION_INVALID, "storage pool '%s' is not active", pool->name); return NULL; } if (STREQ(name, "nonexistent")) { virReportError(VIR_ERR_NO_STORAGE_VOL, "no storage vol with matching name '%s'", name); return NULL; } if (!strchr(name, '+')) goto fallback; if (!(volinfo = g_strsplit(name, "+", 2))) return NULL; if (!volinfo[1]) goto fallback; ret = virGetStorageVol(pool->conn, pool->name, volinfo[1], volinfo[0], NULL, NULL); cleanup: return ret; fallback: ret = virGetStorageVol(pool->conn, pool->name, name, "block", NULL, NULL); goto cleanup; } static int fakeStorageVolGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info) { memset(info, 0, sizeof(*info)); info->type = virStorageVolTypeFromString(vol->key); if (info->type < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "Invalid volume type '%s'", vol->key); return -1; } return 0; } static char * fakeStorageVolGetPath(virStorageVolPtr vol) { return g_strdup_printf("/some/%s/device/%s", vol->key, vol->name); } static char * fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags_unused G_GNUC_UNUSED) { g_autofree char *xmlpath = NULL; char *xmlbuf = NULL; if (STREQ(pool->name, "inactive")) { virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); return NULL; } xmlpath = g_strdup_printf("%s/%s%s.xml", abs_srcdir, STORAGE_POOL_XML_PATH, pool->name); if (virTestLoadFile(xmlpath, &xmlbuf) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "failed to load XML file '%s'", xmlpath); goto cleanup; } cleanup: return xmlbuf; } static int fakeStoragePoolIsActive(virStoragePoolPtr pool) { if (STREQ(pool->name, "inactive")) return 0; return 1; } /* Test storage pool implementation * * These functions aid testing of storage pool related stuff when creating a * qemu command line. * * There are a few "magic" values to pass to these functions: * * 1) "inactive" as a pool name to create an inactive pool. All other names are * interpreted as file names in storagepoolxml2xmlout/ and are used as the * definition for the pool. If the file doesn't exist the pool doesn't exist. * * 2) "nonexistent" returns an error while looking up a volume. Otherwise * pattern VOLUME_TYPE+VOLUME_PATH can be used to simulate a volume in a pool. * This creates a fake path for this volume. If the '+' sign is omitted, block * type is assumed. */ static virStorageDriver fakeStorageDriver = { .storagePoolLookupByName = fakeStoragePoolLookupByName, .storageVolLookupByName = fakeStorageVolLookupByName, .storagePoolGetXMLDesc = fakeStoragePoolGetXMLDesc, .storageVolGetPath = fakeStorageVolGetPath, .storageVolGetInfo = fakeStorageVolGetInfo, .storagePoolIsActive = fakeStoragePoolIsActive, }; /* virNetDevOpenvswitchGetVhostuserIfname mocks a portdev name - handle that */ static virNWFilterBindingPtr fakeNWFilterBindingLookupByPortDev(virConnectPtr conn, const char *portdev) { if (STREQ(portdev, "vhost-user0")) return virGetNWFilterBinding(conn, "fake_vnet0", "fakeFilterName"); virReportError(VIR_ERR_NO_NWFILTER_BINDING, "no nwfilter binding for port dev '%s'", portdev); return NULL; } static int fakeNWFilterBindingDelete(virNWFilterBindingPtr binding G_GNUC_UNUSED) { return 0; } static virNWFilterDriver fakeNWFilterDriver = { .nwfilterBindingLookupByPortDev = fakeNWFilterBindingLookupByPortDev, .nwfilterBindingDelete = fakeNWFilterBindingDelete, }; static int testAddCPUModels(virQEMUCaps *caps, bool skipLegacy) { virArch arch = virQEMUCapsGetArch(caps); const char *x86Models[] = { "Opteron_G3", "Opteron_G2", "Opteron_G1", "Nehalem", "Penryn", "Conroe", "Haswell-noTSX", "Haswell", }; const char *x86LegacyModels[] = { "n270", "athlon", "pentium3", "pentium2", "pentium", "486", "coreduo", "kvm32", "qemu32", "kvm64", "core2duo", "phenom", "qemu64", }; const char *armModels[] = { "cortex-a9", "cortex-a8", "cortex-a57", "cortex-a53", }; const char *ppc64Models[] = { "POWER8", "POWER7", }; const char *s390xModels[] = { "z990", "zEC12", "z13", }; if (ARCH_IS_X86(arch)) { if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, x86Models, G_N_ELEMENTS(x86Models), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 || virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, x86Models, G_N_ELEMENTS(x86Models), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0) return -1; if (!skipLegacy) { if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, x86LegacyModels, G_N_ELEMENTS(x86LegacyModels), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 || virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, x86LegacyModels, G_N_ELEMENTS(x86LegacyModels), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0) return -1; } } else if (ARCH_IS_ARM(arch)) { if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, armModels, G_N_ELEMENTS(armModels), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 || virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, armModels, G_N_ELEMENTS(armModels), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0) return -1; } else if (ARCH_IS_PPC64(arch)) { if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, ppc64Models, G_N_ELEMENTS(ppc64Models), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 || virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, ppc64Models, G_N_ELEMENTS(ppc64Models), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0) return -1; } else if (ARCH_IS_S390(arch)) { if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, s390xModels, G_N_ELEMENTS(s390xModels), VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0) return -1; } return 0; } static int testUpdateQEMUCaps(const struct testQemuInfo *info, virArch arch, virCaps *caps) { if (!caps) return -1; virQEMUCapsSetArch(info->qemuCaps, arch); virQEMUCapsInitQMPBasicArch(info->qemuCaps); if (testAddCPUModels(info->qemuCaps, !!(info->flags & FLAG_SKIP_LEGACY_CPUS)) < 0) return -1; virQEMUCapsUpdateHostCPUModel(info->qemuCaps, caps->host.arch, VIR_DOMAIN_VIRT_KVM); virQEMUCapsUpdateHostCPUModel(info->qemuCaps, caps->host.arch, VIR_DOMAIN_VIRT_QEMU); return 0; } static int testCheckExclusiveFlags(int flags) { virCheckFlags(FLAG_EXPECT_FAILURE | FLAG_EXPECT_PARSE_ERROR | FLAG_FIPS_HOST | FLAG_REAL_CAPS | FLAG_SKIP_LEGACY_CPUS | FLAG_SLIRP_HELPER | 0, -1); VIR_EXCLUSIVE_FLAGS_RET(FLAG_REAL_CAPS, FLAG_SKIP_LEGACY_CPUS, -1); return 0; } static virCommand * testCompareXMLToArgvCreateArgs(virQEMUDriver *drv, virDomainObj *vm, const char *migrateURI, struct testQemuInfo *info, unsigned int flags, bool jsonPropsValidation) { qemuDomainObjPrivate *priv = vm->privateData; bool enableFips = !!(flags & FLAG_FIPS_HOST); size_t i; if (qemuProcessCreatePretendCmdPrepare(drv, vm, migrateURI, false, VIR_QEMU_PROCESS_START_COLD) < 0) return NULL; for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDef *disk = vm->def->disks[i]; /* host cdrom requires special treatment in qemu, mock it */ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM && disk->src->format == VIR_STORAGE_FILE_RAW && virStorageSourceIsBlockLocal(disk->src) && STREQ(disk->src->path, "/dev/cdrom")) disk->src->hostcdrom = true; } for (i = 0; i < vm->def->nhostdevs; i++) { virDomainHostdevDef *hostdev = vm->def->hostdevs[i]; if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; } if (virHostdevIsSCSIDevice(hostdev)) { virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi; switch ((virDomainHostdevSCSIProtocolType) scsisrc->protocol) { case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE: scsisrc->u.host.src->path = g_strdup("/dev/sg0"); break; case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI: break; case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST: default: virReportEnumRangeError(virDomainHostdevSCSIProtocolType, scsisrc->protocol); return NULL; } } } for (i = 0; i < vm->def->nfss; i++) { virDomainFSDef *fs = vm->def->fss[i]; char *s; if (fs->fsdriver != VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS || QEMU_DOMAIN_FS_PRIVATE(fs)->vhostuser_fs_sock) continue; s = g_strdup_printf("/tmp/lib/domain--1-guest/fs%zu.vhost-fs.sock", i); QEMU_DOMAIN_FS_PRIVATE(fs)->vhostuser_fs_sock = s; } if (vm->def->vsock) { virDomainVsockDef *vsock = vm->def->vsock; qemuDomainVsockPrivate *vsockPriv = (qemuDomainVsockPrivate *)vsock->privateData; if (vsock->auto_cid == VIR_TRISTATE_BOOL_YES) vsock->guest_cid = 42; vsockPriv->vhostfd = 6789; } for (i = 0; i < vm->def->ntpms; i++) { if (vm->def->tpms[i]->type != VIR_DOMAIN_TPM_TYPE_EMULATOR) continue; VIR_FREE(vm->def->tpms[i]->data.emulator.source.data.file.path); vm->def->tpms[i]->data.emulator.source.data.file.path = g_strdup("/dev/test"); vm->def->tpms[i]->data.emulator.source.type = VIR_DOMAIN_CHR_TYPE_FILE; } for (i = 0; i < vm->def->nvideos; i++) { virDomainVideoDef *video = vm->def->videos[i]; if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) { qemuDomainVideoPrivate *vpriv = QEMU_DOMAIN_VIDEO_PRIVATE(video); vpriv->vhost_user_fd = 1729; } } if (flags & FLAG_SLIRP_HELPER) { for (i = 0; i < vm->def->nnets; i++) { virDomainNetDef *net = vm->def->nets[i]; if (net->type == VIR_DOMAIN_NET_TYPE_USER && virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) { qemuSlirp *slirp = qemuSlirpNew(); slirp->fd[0] = 42; QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp = slirp; } } } /* we can't use qemuCheckFips() directly as it queries host state */ if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_ENABLE_FIPS)) enableFips = false; return qemuProcessCreatePretendCmdBuild(drv, vm, migrateURI, enableFips, false, jsonPropsValidation); } static int testCompareXMLToArgvValidateSchema(virQEMUDriver *drv, const char *migrateURI, struct testQemuInfo *info, unsigned int flags) { g_auto(GStrv) args = NULL; g_autoptr(virDomainObj) vm = NULL; qemuDomainObjPrivate *priv = NULL; size_t nargs = 0; size_t i; GHashTable *schema = NULL; g_autoptr(virCommand) cmd = NULL; unsigned int parseFlags = info->parseFlags; bool netdevQAPIfied = false; /* comment out with line comment to enable schema checking for non _CAPS tests if (!info->schemafile) info->schemafile = testQemuGetLatestCapsForArch(virArchToString(info->arch), "replies"); // */ if (info->schemafile) { /* lookup and insert into cache if not found */ if (!g_hash_table_lookup_extended(info->qapiSchemaCache, info->schemafile, NULL, (void **) &schema)) { schema = testQEMUSchemaLoad(info->schemafile); g_hash_table_insert(info->qapiSchemaCache, g_strdup(info->schemafile), schema); } } if (!schema) return 0; if (!(vm = virDomainObjNew(driver.xmlopt))) return -1; parseFlags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; if (!(vm->def = virDomainDefParseFile(info->infile, driver.xmlopt, NULL, parseFlags))) return -1; priv = vm->privateData; if (virBitmapParse("0-3", &priv->autoNodeset, 4) < 0) return -1; if (!(cmd = testCompareXMLToArgvCreateArgs(drv, vm, migrateURI, info, flags, true))) return -1; if (virCommandGetArgList(cmd, &args, &nargs) < 0) return -1; netdevQAPIfied = !virQEMUQAPISchemaPathExists("netdev_add/arg-type/type/!string", schema); for (i = 0; i < nargs; i++) { g_auto(virBuffer) debug = VIR_BUFFER_INITIALIZER; g_autoptr(virJSONValue) jsonargs = NULL; if (STREQ(args[i], "-blockdev")) { if (!(jsonargs = virJSONValueFromString(args[i + 1]))) return -1; if (testQEMUSchemaValidateCommand("blockdev-add", jsonargs, schema, false, false, &debug) < 0) { VIR_TEST_VERBOSE("failed to validate -blockdev '%s' against QAPI schema: %s", args[i + 1], virBufferCurrentContent(&debug)); return -1; } i++; } else if (STREQ(args[i], "-netdev")) { if (!netdevQAPIfied) { i++; continue; } if (!(jsonargs = virJSONValueFromString(args[i + 1]))) return -1; if (testQEMUSchemaValidateCommand("netdev_add", jsonargs, schema, false, false, &debug) < 0) { VIR_TEST_VERBOSE("failed to validate -netdev '%s' against QAPI schema: %s", args[i + 1], virBufferCurrentContent(&debug)); return -1; } i++; } else if (STREQ(args[i], "-object")) { if (*args[i + 1] != '{') { i++; continue; } if (!(jsonargs = virJSONValueFromString(args[i + 1]))) return -1; if (testQEMUSchemaValidateCommand("object-add", jsonargs, schema, false, false, &debug) < 0) { VIR_TEST_VERBOSE("failed to validate -object '%s' against QAPI schema: %s", args[i + 1], virBufferCurrentContent(&debug)); return -1; } i++; } } return 0; } static int testCompareXMLToArgv(const void *data) { struct testQemuInfo *info = (void *) data; g_autofree char *migrateURI = NULL; g_auto(virBuffer) actualBuf = VIR_BUFFER_INITIALIZER; g_autofree char *actualargv = NULL; unsigned int flags = info->flags; unsigned int parseFlags = info->parseFlags; int ret = -1; virDomainObj *vm = NULL; virDomainChrSourceDef monitor_chr; g_autoptr(virConnect) conn = NULL; virError *err = NULL; char *log = NULL; g_autoptr(virCommand) cmd = NULL; qemuDomainObjPrivate *priv = NULL; g_autoptr(xmlDoc) xml = NULL; g_autoptr(xmlXPathContext) ctxt = NULL; xmlNodePtr root; g_autofree char *archstr = NULL; virArch arch = VIR_ARCH_NONE; g_autoptr(virIdentity) sysident = virIdentityGetSystem(); if (info->arch != VIR_ARCH_NONE && info->arch != VIR_ARCH_X86_64) qemuTestSetHostArch(&driver, info->arch); memset(&monitor_chr, 0, sizeof(monitor_chr)); if (!(conn = virGetConnect())) goto cleanup; conn->secretDriver = &fakeSecretDriver; conn->storageDriver = &fakeStorageDriver; conn->nwfilterDriver = &fakeNWFilterDriver; virSetConnectInterface(conn); virSetConnectNetwork(conn); virSetConnectNWFilter(conn); virSetConnectNodeDev(conn); virSetConnectSecret(conn); virSetConnectStorage(conn); if (virIdentitySetCurrent(sysident) < 0) goto cleanup; if (testCheckExclusiveFlags(info->flags) < 0) goto cleanup; if (!(xml = virXMLParse(info->infile, NULL, "(domain_definition)"))) goto cleanup; root = xmlDocGetRootElement(xml); if (!virXMLNodeNameEqual(root, "domain")) { VIR_TEST_VERBOSE("unexpected root element <%s>, expecting ", root->name); goto cleanup; } if (!(ctxt = virXMLXPathContextNew(xml))) goto cleanup; ctxt->node = root; if ((archstr = virXPathString("string(./os/type[1]/@arch)", ctxt))) { if ((arch = virArchFromString(archstr)) == VIR_ARCH_NONE) { arch = virArchFromHost(); } } if (!(info->flags & FLAG_REAL_CAPS)) { if (testUpdateQEMUCaps(info, arch, driver.caps) < 0) goto cleanup; } if (qemuTestCapsCacheInsert(driver.qemuCapsCache, info->qemuCaps) < 0) goto cleanup; if (info->migrateFrom && !(migrateURI = qemuMigrationDstGetURI(info->migrateFrom, info->migrateFd))) goto cleanup; if (!(vm = virDomainObjNew(driver.xmlopt))) goto cleanup; if (!virFileExists(info->infile)) { virReportError(VIR_ERR_INTERNAL_ERROR, "Input file '%s' not found", info->infile); goto cleanup; } parseFlags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; if (!(vm->def = virDomainDefParseNode(xml, root, driver.xmlopt, NULL, parseFlags))) { err = virGetLastError(); if (!err) { VIR_TEST_DEBUG("no error was reported for expected parse error"); goto cleanup; } if (flags & FLAG_EXPECT_PARSE_ERROR) { g_autofree char *tmperr = g_strdup_printf("%s\n", NULLSTR(err->message)); if (virTestCompareToFile(tmperr, info->errfile) >= 0) goto ok; } goto cleanup; } if (flags & FLAG_EXPECT_PARSE_ERROR) { VIR_TEST_DEBUG("passed instead of expected parse error"); goto cleanup; } priv = vm->privateData; if (virBitmapParse("0-3", &priv->autoNodeset, 4) < 0) goto cleanup; if (!virDomainDefCheckABIStability(vm->def, vm->def, driver.xmlopt)) { VIR_TEST_DEBUG("ABI stability check failed on %s", info->infile); goto cleanup; } vm->def->id = -1; if (qemuProcessPrepareMonitorChr(&monitor_chr, priv->libDir) < 0) goto cleanup; if (!(info->flags & FLAG_REAL_CAPS)) { if (testUpdateQEMUCaps(info, vm->def->os.arch, driver.caps) < 0) goto cleanup; if (qemuTestCapsCacheInsert(driver.qemuCapsCache, info->qemuCaps) < 0) goto cleanup; } log = virTestLogContentAndReset(); VIR_FREE(log); virResetLastError(); if (!(cmd = testCompareXMLToArgvCreateArgs(&driver, vm, migrateURI, info, flags, false))) { err = virGetLastError(); if (!err) { VIR_TEST_DEBUG("no error was reported for expected failure"); goto cleanup; } if (flags & FLAG_EXPECT_FAILURE) { g_autofree char *tmperr = g_strdup_printf("%s\n", NULLSTR(err->message)); if (virTestCompareToFile(tmperr, info->errfile) >= 0) goto ok; } goto cleanup; } if (flags & FLAG_EXPECT_FAILURE) { VIR_TEST_DEBUG("passed instead of expected failure"); goto cleanup; } if (testCompareXMLToArgvValidateSchema(&driver, migrateURI, info, flags) < 0) goto cleanup; if (virCommandToStringBuf(cmd, &actualBuf, true, false) < 0) goto cleanup; virBufferAddLit(&actualBuf, "\n"); actualargv = virBufferContentAndReset(&actualBuf); if (virTestCompareToFileFull(actualargv, info->outfile, false) < 0) goto cleanup; ret = 0; ok: if (ret == 0 && flags & FLAG_EXPECT_FAILURE) { ret = -1; VIR_TEST_DEBUG("Error expected but there wasn't any."); goto cleanup; } if (flags & FLAG_EXPECT_FAILURE) { if ((log = virTestLogContentAndReset())) VIR_TEST_DEBUG("Got expected error: \n%s", log); } virResetLastError(); ret = 0; cleanup: VIR_FREE(log); virDomainChrSourceDefClear(&monitor_chr); virObjectUnref(vm); virIdentitySetCurrent(NULL); virSetConnectSecret(NULL); virSetConnectStorage(NULL); if (info->arch != VIR_ARCH_NONE && info->arch != VIR_ARCH_X86_64) qemuTestSetHostArch(&driver, VIR_ARCH_NONE); return ret; } static void testInfoSetPaths(struct testQemuInfo *info, const char *suffix) { info->infile = g_strdup_printf("%s/qemuxml2argvdata/%s.xml", abs_srcdir, info->name); info->outfile = g_strdup_printf("%s/qemuxml2argvdata/%s%s.args", abs_srcdir, info->name, suffix ? suffix : ""); info->errfile = g_strdup_printf("%s/qemuxml2argvdata/%s%s.err", abs_srcdir, info->name, suffix ? suffix : ""); } # define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX" static int mymain(void) { int ret = 0; g_autofree char *fakerootdir = NULL; g_autoptr(GHashTable) capslatest = NULL; g_autoptr(GHashTable) qapiSchemaCache = virHashNew((GDestroyNotify) virHashFree); g_autoptr(GHashTable) capscache = virHashNew(virObjectFreeHashData); fakerootdir = g_strdup(FAKEROOTDIRTEMPLATE); if (!g_mkdtemp(fakerootdir)) { fprintf(stderr, "Cannot create fakerootdir"); abort(); } g_setenv("LIBVIRT_FAKE_ROOT_DIR", fakerootdir, TRUE); /* Set the timezone because we are mocking the time() function. * If we don't do that, then localtime() may return unpredictable * results. In order to detect things that just work by a blind * chance, we need to set an virtual timezone that no libvirt * developer resides in. */ if (g_setenv("TZ", "VIR00:30", TRUE) == FALSE) { perror("g_setenv"); return EXIT_FAILURE; } if (qemuTestDriverInit(&driver) < 0) return EXIT_FAILURE; driver.privileged = true; VIR_FREE(driver.config->defaultTLSx509certdir); driver.config->defaultTLSx509certdir = g_strdup("/etc/pki/qemu"); VIR_FREE(driver.config->vncTLSx509certdir); driver.config->vncTLSx509certdir = g_strdup("/etc/pki/libvirt-vnc"); VIR_FREE(driver.config->spiceTLSx509certdir); driver.config->spiceTLSx509certdir = g_strdup("/etc/pki/libvirt-spice"); VIR_FREE(driver.config->chardevTLSx509certdir); driver.config->chardevTLSx509certdir = g_strdup("/etc/pki/libvirt-chardev"); VIR_FREE(driver.config->vxhsTLSx509certdir); driver.config->vxhsTLSx509certdir = g_strdup("/etc/pki/libvirt-vxhs/dummy,path"); VIR_FREE(driver.config->nbdTLSx509certdir); driver.config->nbdTLSx509certdir = g_strdup("/etc/pki/libvirt-nbd/dummy,path"); VIR_FREE(driver.config->hugetlbfs); driver.config->hugetlbfs = g_new0(virHugeTLBFS, 2); driver.config->nhugetlbfs = 2; driver.config->hugetlbfs[0].mnt_dir = g_strdup("/dev/hugepages2M"); driver.config->hugetlbfs[1].mnt_dir = g_strdup("/dev/hugepages1G"); driver.config->hugetlbfs[0].size = 2048; driver.config->hugetlbfs[0].deflt = true; driver.config->hugetlbfs[1].size = 1048576; driver.config->spiceTLS = 1; driver.config->spicePassword = g_strdup("123456"); VIR_FREE(driver.config->memoryBackingDir); driver.config->memoryBackingDir = g_strdup("/var/lib/libvirt/qemu/ram"); VIR_FREE(driver.config->nvramDir); driver.config->nvramDir = g_strdup("/var/lib/libvirt/qemu/nvram"); capslatest = testQemuGetLatestCaps(); if (!capslatest) return EXIT_FAILURE; virFileWrapperAddPrefix(SYSCONFDIR "/qemu/firmware", abs_srcdir "/qemufirmwaredata/etc/qemu/firmware"); virFileWrapperAddPrefix(PREFIX "/share/qemu/firmware", abs_srcdir "/qemufirmwaredata/usr/share/qemu/firmware"); virFileWrapperAddPrefix("/home/user/.config/qemu/firmware", abs_srcdir "/qemufirmwaredata/home/user/.config/qemu/firmware"); virFileWrapperAddPrefix(SYSCONFDIR "/qemu/vhost-user", abs_srcdir "/qemuvhostuserdata/etc/qemu/vhost-user"); virFileWrapperAddPrefix(PREFIX "/share/qemu/vhost-user", abs_srcdir "/qemuvhostuserdata/usr/share/qemu/vhost-user"); virFileWrapperAddPrefix("/home/user/.config/qemu/vhost-user", abs_srcdir "/qemuvhostuserdata/home/user/.config/qemu/vhost-user"); virFileWrapperAddPrefix("/usr/libexec/qemu/vhost-user", abs_srcdir "/qemuvhostuserdata/usr/libexec/qemu/vhost-user"); /** * The following set of macros allows testing of XML -> argv conversion with a * real set of capabilities gathered from a real qemu copy. It is desired to use * these for positive test cases as it provides combinations of flags which * can be met in real life. * * The capabilities are taken from the real capabilities stored in * tests/qemucapabilitiesdata. * * It is suggested to use the DO_TEST_CAPS_LATEST macro which always takes the * most recent capability set. In cases when the new code would change behaviour * the test cases should be forked using DO_TEST_CAPS_VER with the appropriate * version. */ # define DO_TEST_INTERNAL(_name, _suffix, ...) \ do { \ static struct testQemuInfo info = { \ .name = _name, \ }; \ info.qapiSchemaCache = qapiSchemaCache; \ if (testQemuInfoSetArgs(&info, capscache, capslatest, \ __VA_ARGS__, ARG_END) < 0) \ ret = -1; \ testInfoSetPaths(&info, _suffix); \ if (virTestRun("QEMU XML-2-ARGV " _name _suffix, \ testCompareXMLToArgv, &info) < 0) \ ret = -1; \ testQemuInfoClear(&info); \ } while (0) # define DO_TEST_CAPS_INTERNAL(name, arch, ver, ...) \ DO_TEST_INTERNAL(name, "." arch "-" ver, \ ARG_CAPS_ARCH, arch, \ ARG_CAPS_VER, ver, \ __VA_ARGS__) # define DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ...) \ DO_TEST_CAPS_INTERNAL(name, arch, "latest", __VA_ARGS__) # define DO_TEST_CAPS_ARCH_VER_FULL(name, arch, ver, ...) \ DO_TEST_CAPS_INTERNAL(name, arch, ver, __VA_ARGS__) # define DO_TEST_CAPS_ARCH_LATEST(name, arch) \ DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ARG_END) # define DO_TEST_CAPS_ARCH_VER(name, arch, ver) \ DO_TEST_CAPS_ARCH_VER_FULL(name, arch, ver, ARG_END) # define DO_TEST_CAPS_LATEST(name) \ DO_TEST_CAPS_ARCH_LATEST(name, "x86_64") # define DO_TEST_CAPS_VER(name, ver) \ DO_TEST_CAPS_ARCH_VER(name, "x86_64", ver) # define DO_TEST_CAPS_LATEST_PPC64(name) \ DO_TEST_CAPS_ARCH_LATEST(name, "ppc64") # define DO_TEST_CAPS_ARCH_LATEST_FAILURE(name, arch) \ DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, \ ARG_FLAGS, FLAG_EXPECT_FAILURE) # define DO_TEST_CAPS_ARCH_VER_FAILURE(name, arch, ver) \ DO_TEST_CAPS_ARCH_VER_FULL(name, arch, ver, \ ARG_FLAGS, FLAG_EXPECT_FAILURE) # define DO_TEST_CAPS_LATEST_FAILURE(name) \ DO_TEST_CAPS_ARCH_LATEST_FAILURE(name, "x86_64") # define DO_TEST_CAPS_VER_FAILURE(name, ver) \ DO_TEST_CAPS_ARCH_VER_FAILURE(name, "x86_64", ver) # define DO_TEST_CAPS_ARCH_LATEST_PARSE_ERROR(name, arch) \ DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, \ ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR) # define DO_TEST_CAPS_ARCH_VER_PARSE_ERROR(name, arch, ver) \ DO_TEST_CAPS_ARCH_VER_FULL(name, arch, ver, \ ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR) # define DO_TEST_CAPS_LATEST_PARSE_ERROR(name) \ DO_TEST_CAPS_ARCH_LATEST_PARSE_ERROR(name, "x86_64") # define DO_TEST_CAPS_VER_PARSE_ERROR(name, ver) \ DO_TEST_CAPS_ARCH_VER_PARSE_ERROR(name, "x86_64", ver) # define DO_TEST_FULL(name, ...) \ DO_TEST_INTERNAL(name, "", \ __VA_ARGS__, QEMU_CAPS_LAST) /* All the following macros require an explicit QEMU_CAPS_* list * at the end of the argument list, or the NONE placeholder. * */ # define DO_TEST(name, ...) \ DO_TEST_FULL(name, \ ARG_QEMU_CAPS, __VA_ARGS__) # define DO_TEST_GIC(name, gic, ...) \ DO_TEST_FULL(name, \ ARG_GIC, gic, \ ARG_QEMU_CAPS, __VA_ARGS__) # define DO_TEST_FAILURE(name, ...) \ DO_TEST_FULL(name, \ ARG_FLAGS, FLAG_EXPECT_FAILURE, \ ARG_QEMU_CAPS, __VA_ARGS__) # define DO_TEST_PARSE_ERROR(name, ...) \ DO_TEST_FULL(name, \ ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR | FLAG_EXPECT_FAILURE, \ ARG_QEMU_CAPS, __VA_ARGS__) # define NONE QEMU_CAPS_LAST /* Unset or set all envvars here that are copied in qemudBuildCommandLine * using ADD_ENV_COPY, otherwise these tests may fail due to unexpected * values for these envvars */ g_setenv("PATH", "/bin", TRUE); g_setenv("USER", "test", TRUE); g_setenv("LOGNAME", "test", TRUE); g_setenv("HOME", "/home/test", TRUE); g_setenv("LC_ALL", "C", TRUE); g_unsetenv("TMPDIR"); g_unsetenv("LD_PRELOAD"); g_unsetenv("LD_LIBRARY_PATH"); g_unsetenv("QEMU_AUDIO_DRV"); g_unsetenv("SDL_AUDIODRIVER"); DO_TEST("minimal", NONE); DO_TEST("minimal-sandbox", QEMU_CAPS_SECCOMP_BLACKLIST); DO_TEST_PARSE_ERROR("minimal-no-memory", NONE); DO_TEST("minimal-msg-timestamp", QEMU_CAPS_MSG_TIMESTAMP); DO_TEST_CAPS_LATEST("genid"); DO_TEST_CAPS_LATEST("genid-auto"); DO_TEST("machine-aliases1", NONE); DO_TEST("machine-aliases2", QEMU_CAPS_KVM); DO_TEST("machine-core-on", NONE); driver.config->dumpGuestCore = true; DO_TEST("machine-core-off", NONE); driver.config->dumpGuestCore = false; DO_TEST("machine-smm-opt", QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, QEMU_CAPS_DEVICE_PCI_BRIDGE, QEMU_CAPS_DEVICE_IOH3420, QEMU_CAPS_ICH9_AHCI, QEMU_CAPS_VIRTIO_SCSI); DO_TEST("machine-vmport-opt", QEMU_CAPS_MACHINE_VMPORT_OPT); DO_TEST("default-kvm-host-arch", NONE); DO_TEST("default-qemu-host-arch", NONE); DO_TEST("x86-kvm-32-on-64", NONE); DO_TEST("boot-cdrom", NONE); DO_TEST("boot-network", NONE); DO_TEST("boot-floppy", NONE); DO_TEST("boot-floppy-q35", QEMU_CAPS_DEVICE_IOH3420, QEMU_CAPS_ICH9_AHCI); DO_TEST("boot-multi", NONE); DO_TEST("boot-menu-enable", NONE); DO_TEST("boot-menu-enable-with-timeout", QEMU_CAPS_SPLASH_TIMEOUT); DO_TEST_PARSE_ERROR("boot-menu-enable-with-timeout", NONE); DO_TEST_PARSE_ERROR("boot-menu-enable-with-timeout-invalid", QEMU_CAPS_SPLASH_TIMEOUT); DO_TEST("boot-menu-disable", NONE); DO_TEST("boot-menu-disable-drive", NONE); DO_TEST_PARSE_ERROR("boot-dev+order", QEMU_CAPS_VIRTIO_BLK_SCSI); DO_TEST("boot-order", QEMU_CAPS_VIRTIO_BLK_SCSI); DO_TEST("boot-complex", QEMU_CAPS_VIRTIO_BLK_SCSI); DO_TEST("boot-strict", QEMU_CAPS_BOOT_STRICT, QEMU_CAPS_VIRTIO_BLK_SCSI); /* Simplest possible