This is basically a copy of the existing qemuxml2argvtest, with
a couple of obvious changes.
We're not using most of the features at the moment, but as the
JSON-based APIs evolve and get closer to feature-parity with
the XML-based ones we're definitely going to need more and more
of them, so we might as well start with the full package.
Signed-off-by: Andrea Bolognani <abologna(a)redhat.com>
---
tests/Makefile.am | 15 +
tests/qemujson2argvdata/tiny.json | 29 +
.../qemujson2argvdata/tiny.x86_64-latest.args | 33 +
tests/qemujson2argvtest.c | 1001 +++++++++++++++++
4 files changed, 1078 insertions(+)
create mode 100644 tests/qemujson2argvdata/tiny.json
create mode 100644 tests/qemujson2argvdata/tiny.x86_64-latest.args
create mode 100644 tests/qemujson2argvtest.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d3cdbff8bb..df82c7dc22 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -162,6 +162,7 @@ EXTRA_DIST = \
virstorageutildata \
virfilecachedata \
virresctrldata \
+ qemujson2argvdata \
$(NULL)
test_helpers = commandhelper ssh
@@ -283,6 +284,7 @@ test_programs += qemuxml2argvtest qemuxml2xmltest \
qemumigparamstest \
qemusecuritytest \
qemufirmwaretest \
+ qemujson2argvtest \
$(NULL)
test_helpers += qemucapsprobe
test_libraries += libqemumonitortestutils.la \
@@ -692,6 +694,18 @@ qemufirmwaretest_SOURCES = \
$(NULL)
qemufirmwaretest_LDADD = $(qemu_LDADDS) $(LDADDS)
+qemujson2argvtest_SOURCES = \
+ virfilewrapper.c virfilewrapper.h \
+ testutilsqemu.c testutilsqemu.h \
+ testutils.c testutils.h \
+ qemujson2argvtest.c \
+ $(NULL)
+qemujson2argvtest_LDADD = \
+ libqemutestdriver.la \
+ $(LIBXML_LIBS) \
+ $(LDADDS) \
+ $(NULL)
+
else ! WITH_QEMU
EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \
domainsnapshotxml2xmltest.c \
@@ -706,6 +720,7 @@ EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c
\
qemusecuritytest.c qemusecuritytest.h \
qemusecuritymock.c \
qemufirmwaretest.c \
+ qemujson2argvtest.c \
$(QEMUMONITORTESTUTILS_SOURCES)
endif ! WITH_QEMU
diff --git a/tests/qemujson2argvdata/tiny.json b/tests/qemujson2argvdata/tiny.json
new file mode 100644
index 0000000000..99071c5ec0
--- /dev/null
+++ b/tests/qemujson2argvdata/tiny.json
@@ -0,0 +1,29 @@
+{
+ "domain": {
+ "attributes": {
+ "type": "qemu"
+ },
+ "children": {
+ "name": {
+ "value": "guest"
+ },
+ "uuid": {
+ "value": "4f49aff1-4f74-45c3-87a7-51a6ad052a54"
+ },
+ "memory": {
+ "value": 4194304
+ },
+ "os": {
+ "children": {
+ "type": {
+ "attributes": {
+ "arch": "x86_64",
+ "machine": "pc"
+ },
+ "value": "hvm"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/qemujson2argvdata/tiny.x86_64-latest.args
b/tests/qemujson2argvdata/tiny.x86_64-latest.args
new file mode 100644
index 0000000000..e8e67d7727
--- /dev/null
+++ b/tests/qemujson2argvdata/tiny.x86_64-latest.args
@@ -0,0 +1,33 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-guest \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-guest/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-guest/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-guest/.config \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu-system-x86_64 \
+-name guest=guest,debug-threads=on \
+-S \
+-object secret,id=masterKey0,format=raw,\
+file=/tmp/lib/domain--1-guest/master-key.aes \
+-machine pc,accel=tcg,usb=off,dump-guest-core=off \
+-m 4096 \
+-realtime mlock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid 4f49aff1-4f74-45c3-87a7-51a6ad052a54 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server,nowait \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-reboot \
+-no-acpi \
+-boot strict=on \
+-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\
+resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemujson2argvtest.c b/tests/qemujson2argvtest.c
new file mode 100644
index 0000000000..e754813646
--- /dev/null
+++ b/tests/qemujson2argvtest.c
@@ -0,0 +1,1001 @@
+#include <config.h>
+
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "testutils.h"
+
+#ifdef WITH_QEMU
+
+# include "internal.h"
+# include "viralloc.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 "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"
+
+# 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 ATTRIBUTE_UNUSED,
+ size_t *value_size,
+ unsigned int fakeflags ATTRIBUTE_UNUSED,
+ unsigned int internalFlags ATTRIBUTE_UNUSED)
+{
+ char *secret;
+ if (VIR_STRDUP(secret, "AQCVn5hO6HzFAhAAq0NCv8jtJcIcE+HOBlMQ1A") < 0)
+ return NULL;
+ *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")) {
+ 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)
+{
+ char *xmlpath = NULL;
+ virStoragePoolPtr ret = NULL;
+
+ if (STRNEQ(name, "inactive")) {
+ if (virAsprintf(&xmlpath, "%s/%s%s.xml",
+ abs_srcdir,
+ STORAGE_POOL_XML_PATH,
+ name) < 0)
+ return NULL;
+
+ if (!virFileExists(xmlpath)) {
+ virReportError(VIR_ERR_NO_STORAGE_POOL,
+ "File '%s' not found", xmlpath);
+ goto cleanup;
+ }
+ }
+
+ ret = virGetStoragePool(conn, name, fakeUUID, NULL, NULL);
+
+ cleanup:
+ VIR_FREE(xmlpath);
+ return ret;
+}
+
+
+static virStorageVolPtr
+fakeStorageVolLookupByName(virStoragePoolPtr pool,
+ const char *name)
+{
+ char **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 = virStringSplit(name, "+", 2)))
+ return NULL;
+
+ if (!volinfo[1])
+ goto fallback;
+
+ ret = virGetStorageVol(pool->conn, pool->name, volinfo[1], volinfo[0],
+ NULL, NULL);
+
+ cleanup:
+ virStringListFree(volinfo);
+ 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)
+{
+ char *ret = NULL;
+
+ ignore_value(virAsprintf(&ret, "/some/%s/device/%s", vol->key,
vol->name));
+
+ return ret;
+}
+
+
+static char *
+fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool,
+ unsigned int flags_unused ATTRIBUTE_UNUSED)
+{
+ char *xmlpath = NULL;
+ char *xmlbuf = NULL;
+
+ if (STREQ(pool->name, "inactive")) {
+ virReportError(VIR_ERR_NO_STORAGE_POOL, NULL);
+ return NULL;
+ }
+
+ if (virAsprintf(&xmlpath, "%s/%s%s.xml",
+ abs_srcdir,
+ STORAGE_POOL_XML_PATH,
+ pool->name) < 0)
+ return NULL;
+
+ if (virTestLoadFile(xmlpath, &xmlbuf) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "failed to load XML file '%s'",
+ xmlpath);
+ goto cleanup;
+ }
+
+ cleanup:
+ VIR_FREE(xmlpath);
+
+ 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 ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+
+static virNWFilterDriver fakeNWFilterDriver = {
+ .nwfilterBindingLookupByPortDev = fakeNWFilterBindingLookupByPortDev,
+ .nwfilterBindingDelete = fakeNWFilterBindingDelete,
+};
+
+typedef enum {
+ FLAG_EXPECT_FAILURE = 1 << 0,
+ FLAG_EXPECT_PARSE_ERROR = 1 << 1,
+ FLAG_FIPS = 1 << 2,
+ FLAG_REAL_CAPS = 1 << 3,
+ FLAG_SKIP_LEGACY_CPUS = 1 << 4,
+} virQemuJSON2ArgvTestFlags;
+
+struct testInfo {
+ const char *name;
+ const char *suffix;
+ virQEMUCapsPtr qemuCaps;
+ const char *migrateFrom;
+ int migrateFd;
+ unsigned int flags;
+ unsigned int parseFlags;
+};
+
+
+static int
+testAddCPUModels(virQEMUCapsPtr 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,
+ ARRAY_CARDINALITY(x86Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 ||
+ virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, x86Models,
+ ARRAY_CARDINALITY(x86Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ return -1;
+
+ if (!skipLegacy) {
+ if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM,
+ x86LegacyModels,
+ ARRAY_CARDINALITY(x86LegacyModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 ||
+ virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU,
+ x86LegacyModels,
+ ARRAY_CARDINALITY(x86LegacyModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ return -1;
+ }
+ } else if (ARCH_IS_ARM(arch)) {
+ if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, armModels,
+ ARRAY_CARDINALITY(armModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 ||
+ virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, armModels,
+ ARRAY_CARDINALITY(armModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ return -1;
+ } else if (ARCH_IS_PPC64(arch)) {
+ if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, ppc64Models,
+ ARRAY_CARDINALITY(ppc64Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0 ||
+ virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, ppc64Models,
+ ARRAY_CARDINALITY(ppc64Models),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ return -1;
+ } else if (ARCH_IS_S390(arch)) {
+ if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, s390xModels,
+ ARRAY_CARDINALITY(s390xModels),
+ VIR_DOMCAPS_CPU_USABLE_UNKNOWN) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+testUpdateQEMUCaps(const struct testInfo *info,
+ virDomainObjPtr vm,
+ virCapsPtr caps)
+{
+ int ret = -1;
+
+ if (!caps)
+ goto cleanup;
+
+ virQEMUCapsSetArch(info->qemuCaps, vm->def->os.arch);
+
+ virQEMUCapsInitQMPBasicArch(info->qemuCaps);
+
+ if (testAddCPUModels(info->qemuCaps,
+ !!(info->flags & FLAG_SKIP_LEGACY_CPUS)) < 0)
+ goto cleanup;
+
+ virQEMUCapsInitHostCPUModel(info->qemuCaps, caps->host.arch,
+ VIR_DOMAIN_VIRT_KVM);
+ virQEMUCapsInitHostCPUModel(info->qemuCaps, caps->host.arch,
+ VIR_DOMAIN_VIRT_QEMU);
+
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+
+static int
+testCheckExclusiveFlags(int flags)
+{
+ virCheckFlags(FLAG_EXPECT_FAILURE |
+ FLAG_EXPECT_PARSE_ERROR |
+ FLAG_FIPS |
+ FLAG_REAL_CAPS |
+ FLAG_SKIP_LEGACY_CPUS |
+ 0, -1);
+
+ VIR_EXCLUSIVE_FLAGS_RET(FLAG_REAL_CAPS, FLAG_SKIP_LEGACY_CPUS, -1);
+ return 0;
+}
+
+
+# define JSON_BUFSIZE (10*1024*1024)
+
+
+static int
+testCompareJSONToArgv(const void *data)
+{
+ struct testInfo *info = (void *) data;
+ char *json = NULL;
+ char *args = NULL;
+ char *migrateURI = NULL;
+ char *actualargv = NULL;
+ const char *suffix = info->suffix;
+ unsigned int flags = info->flags;
+ unsigned int parseFlags = info->parseFlags;
+ int ret = -1;
+ virDomainObjPtr vm = NULL;
+ virDomainChrSourceDef monitor_chr;
+ virConnectPtr conn;
+ char *log = NULL;
+ virCommandPtr cmd = NULL;
+ size_t i;
+ qemuDomainObjPrivatePtr priv = NULL;
+ VIR_AUTOFREE(char *) buf = NULL;
+
+ memset(&monitor_chr, 0, sizeof(monitor_chr));
+
+ if (!(conn = virGetConnect()))
+ goto cleanup;
+
+ if (!suffix)
+ suffix = "";
+
+ conn->secretDriver = &fakeSecretDriver;
+ conn->storageDriver = &fakeStorageDriver;
+ conn->nwfilterDriver = &fakeNWFilterDriver;
+
+ virSetConnectInterface(conn);
+ virSetConnectNetwork(conn);
+ virSetConnectNWFilter(conn);
+ virSetConnectNodeDev(conn);
+ virSetConnectSecret(conn);
+ virSetConnectStorage(conn);
+
+ if (virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_ENABLE_FIPS))
+ flags |= FLAG_FIPS;
+
+ if (testCheckExclusiveFlags(info->flags) < 0)
+ goto cleanup;
+
+ if (qemuTestCapsCacheInsert(driver.qemuCapsCache, info->qemuCaps) < 0)
+ goto cleanup;
+
+ if (virAsprintf(&json, "%s/qemujson2argvdata/%s.json",
+ abs_srcdir, info->name) < 0 ||
+ virAsprintf(&args, "%s/qemujson2argvdata/%s%s.args",
+ abs_srcdir, info->name, suffix) < 0)
+ goto cleanup;
+
+ if (info->migrateFrom &&
+ !(migrateURI = qemuMigrationDstGetURI(info->migrateFrom,
+ info->migrateFd)))
+ goto cleanup;
+
+ if (!(vm = virDomainObjNew(driver.xmlopt)))
+ goto cleanup;
+
+ if (virFileReadAll(json, JSON_BUFSIZE, &buf) < 0)
+ goto cleanup;
+
+ parseFlags |= VIR_DOMAIN_DEF_PARSE_INACTIVE;
+ if (!(vm->def = virDomainDefParseJSONString(buf, driver.caps, driver.xmlopt,
+ NULL, parseFlags))) {
+ if (flags & FLAG_EXPECT_PARSE_ERROR)
+ 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", json);
+ goto cleanup;
+ }
+
+ vm->def->id = -1;
+
+ if (qemuProcessPrepareMonitorChr(&monitor_chr, priv->libDir) < 0)
+ goto cleanup;
+
+ if (!(info->flags & FLAG_REAL_CAPS) &&
+ testUpdateQEMUCaps(info, vm, driver.caps) < 0)
+ goto cleanup;
+
+ log = virTestLogContentAndReset();
+ VIR_FREE(log);
+ virResetLastError();
+
+ for (i = 0; i < vm->def->nhostdevs; i++) {
+ virDomainHostdevDefPtr 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_KVM;
+ }
+ }
+
+ if (vm->def->vsock) {
+ virDomainVsockDefPtr vsock = vm->def->vsock;
+ qemuDomainVsockPrivatePtr vsockPriv =
+ (qemuDomainVsockPrivatePtr)vsock->privateData;
+
+ if (vsock->auto_cid == VIR_TRISTATE_BOOL_YES)
+ vsock->guest_cid = 42;
+
+ vsockPriv->vhostfd = 6789;
+ }
+
+ if (vm->def->tpm) {
+ switch (vm->def->tpm->type) {
+ case VIR_DOMAIN_TPM_TYPE_EMULATOR:
+ VIR_FREE(vm->def->tpm->data.emulator.source.data.file.path);
+ if (VIR_STRDUP(vm->def->tpm->data.emulator.source.data.file.path,
+ "/dev/test") < 0)
+ goto cleanup;
+ vm->def->tpm->data.emulator.source.type = VIR_DOMAIN_CHR_TYPE_FILE;
+ break;
+ case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_TPM_TYPE_LAST:
+ break;
+ }
+ }
+
+ if (!(cmd = qemuProcessCreatePretendCmd(&driver, vm, migrateURI,
+ (flags & FLAG_FIPS), false,
+ VIR_QEMU_PROCESS_START_COLD))) {
+ if (flags & FLAG_EXPECT_FAILURE)
+ goto ok;
+ goto cleanup;
+ }
+ if (flags & FLAG_EXPECT_FAILURE) {
+ VIR_TEST_DEBUG("passed instead of expected failure");
+ goto cleanup;
+ }
+
+ if (!(actualargv = virCommandToString(cmd, false)))
+ goto cleanup;
+
+ if (virTestCompareToFile(actualargv, args) < 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.\n");
+ goto cleanup;
+ }
+ if (!virTestOOMActive()) {
+ if (flags & FLAG_EXPECT_FAILURE) {
+ if ((log = virTestLogContentAndReset()))
+ VIR_TEST_DEBUG("Got expected error: \n%s", log);
+ }
+ virResetLastError();
+ ret = 0;
+ }
+
+ cleanup:
+ VIR_FREE(log);
+ VIR_FREE(actualargv);
+ virDomainChrSourceDefClear(&monitor_chr);
+ virCommandFree(cmd);
+ virObjectUnref(vm);
+ virSetConnectSecret(NULL);
+ virSetConnectStorage(NULL);
+ virObjectUnref(conn);
+ VIR_FREE(migrateURI);
+ VIR_FREE(json);
+ VIR_FREE(args);
+ return ret;
+}
+
+# define TEST_CAPS_PATH abs_srcdir "/qemucapabilitiesdata"
+
+typedef enum {
+ ARG_QEMU_CAPS,
+ ARG_GIC,
+ ARG_MIGRATE_FROM,
+ ARG_MIGRATE_FD,
+ ARG_FLAGS,
+ ARG_PARSEFLAGS,
+ ARG_CAPS_ARCH,
+ ARG_CAPS_VER,
+ ARG_END,
+} testInfoArgName;
+
+static int
+testInfoSetArgs(struct testInfo *info,
+ virHashTablePtr capslatest, ...)
+{
+ va_list argptr;
+ testInfoArgName argname;
+ virQEMUCapsPtr qemuCaps = NULL;
+ int gic = GIC_NONE;
+ char *capsarch = NULL;
+ char *capsver = NULL;
+ VIR_AUTOFREE(char *) capsfile = NULL;
+ int flag;
+ int ret = -1;
+
+ va_start(argptr, capslatest);
+ argname = va_arg(argptr, testInfoArgName);
+ while (argname != ARG_END) {
+ switch (argname) {
+ case ARG_QEMU_CAPS:
+ if (qemuCaps || !(qemuCaps = virQEMUCapsNew()))
+ goto cleanup;
+
+ while ((flag = va_arg(argptr, int)) < QEMU_CAPS_LAST)
+ virQEMUCapsSet(qemuCaps, flag);
+
+ /* Some tests are run with NONE capabilities, which is just
+ * another name for QEMU_CAPS_LAST. If that is the case the
+ * arguments look like this :
+ *
+ * ARG_QEMU_CAPS, NONE, QEMU_CAPS_LAST, ARG_END
+ *
+ * Fetch one argument more and if it is QEMU_CAPS_LAST then
+ * break from the switch() to force getting next argument
+ * in the line. If it is not QEMU_CAPS_LAST then we've
+ * fetched real ARG_* and we must process it.
+ */
+ if ((flag = va_arg(argptr, int)) != QEMU_CAPS_LAST) {
+ argname = flag;
+ continue;
+ }
+
+ break;
+
+ case ARG_GIC:
+ gic = va_arg(argptr, int);
+ break;
+
+ case ARG_MIGRATE_FROM:
+ info->migrateFrom = va_arg(argptr, char *);
+ break;
+
+ case ARG_MIGRATE_FD:
+ info->migrateFd = va_arg(argptr, int);
+ break;
+
+ case ARG_FLAGS:
+ info->flags = va_arg(argptr, int);
+ break;
+
+ case ARG_PARSEFLAGS:
+ info->parseFlags = va_arg(argptr, int);
+ break;
+
+ case ARG_CAPS_ARCH:
+ capsarch = va_arg(argptr, char *);
+ break;
+
+ case ARG_CAPS_VER:
+ capsver = va_arg(argptr, char *);
+ break;
+
+ case ARG_END:
+ default:
+ fprintf(stderr, "Unexpected test info argument");
+ goto cleanup;
+ }
+
+ argname = va_arg(argptr, testInfoArgName);
+ }
+
+ if (!!capsarch ^ !!capsver) {
+ fprintf(stderr, "ARG_CAPS_ARCH and ARG_CAPS_VER "
+ "must be specified together.\n");
+ goto cleanup;
+ }
+
+ if (qemuCaps && (capsarch || capsver)) {
+ fprintf(stderr, "ARG_QEMU_CAPS can not be combined with ARG_CAPS_ARCH
"
+ "or ARG_CAPS_VER\n");
+ goto cleanup;
+ }
+
+ if (!qemuCaps && capsarch && capsver) {
+ bool stripmachinealiases = false;
+
+ if (STREQ(capsver, "latest")) {
+ if (VIR_STRDUP(capsfile, virHashLookup(capslatest, capsarch)) < 0)
+ goto cleanup;
+ stripmachinealiases = true;
+ } else if (virAsprintf(&capsfile, "%s/caps_%s.%s.xml",
+ TEST_CAPS_PATH, capsver, capsarch) < 0) {
+ goto cleanup;
+ }
+
+ if (!(qemuCaps = qemuTestParseCapabilitiesArch(virArchFromString(capsarch),
+ capsfile))) {
+ goto cleanup;
+ }
+
+ if (stripmachinealiases)
+ virQEMUCapsStripMachineAliases(qemuCaps);
+ info->flags |= FLAG_REAL_CAPS;
+ }
+
+ if (!qemuCaps) {
+ fprintf(stderr, "No qemuCaps generated\n");
+ goto cleanup;
+ }
+ VIR_STEAL_PTR(info->qemuCaps, qemuCaps);
+
+ if (gic != GIC_NONE && testQemuCapsSetGIC(info->qemuCaps, gic) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virObjectUnref(qemuCaps);
+ va_end(argptr);
+
+ return ret;
+}
+
+static void
+testInfoClear(struct testInfo *info)
+{
+ virObjectUnref(info->qemuCaps);
+}
+
+# define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
+
+static int
+mymain(void)
+{
+ int ret = 0, i;
+ char *fakerootdir;
+ const char *archs[] = {
+ "aarch64",
+ "ppc64",
+ "riscv64",
+ "s390x",
+ "x86_64",
+ };
+ virHashTablePtr capslatest = NULL;
+
+ if (VIR_STRDUP_QUIET(fakerootdir, FAKEROOTDIRTEMPLATE) < 0) {
+ fprintf(stderr, "Out of memory\n");
+ abort();
+ }
+
+ if (!mkdtemp(fakerootdir)) {
+ fprintf(stderr, "Cannot create fakerootdir");
+ abort();
+ }
+
+ setenv("LIBVIRT_FAKE_ROOT_DIR", fakerootdir, 1);
+
+ /* 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 (setenv("TZ", "VIR00:30", 1) < 0) {
+ perror("setenv");
+ return EXIT_FAILURE;
+ }
+
+ if (qemuTestDriverInit(&driver) < 0)
+ return EXIT_FAILURE;
+
+ driver.privileged = true;
+
+ VIR_FREE(driver.config->defaultTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->defaultTLSx509certdir,
"/etc/pki/qemu") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->vncTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->vncTLSx509certdir,
"/etc/pki/libvirt-vnc") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->spiceTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->spiceTLSx509certdir,
"/etc/pki/libvirt-spice") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->chardevTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->chardevTLSx509certdir,
"/etc/pki/libvirt-chardev") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->vxhsTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->vxhsTLSx509certdir,
"/etc/pki/libvirt-vxhs/dummy,path") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->nbdTLSx509certdir);
+ if (VIR_STRDUP_QUIET(driver.config->nbdTLSx509certdir,
"/etc/pki/libvirt-nbd/dummy,path") < 0)
+ return EXIT_FAILURE;
+
+ VIR_FREE(driver.config->hugetlbfs);
+ if (VIR_ALLOC_N(driver.config->hugetlbfs, 2) < 0)
+ return EXIT_FAILURE;
+ driver.config->nhugetlbfs = 2;
+ if (VIR_STRDUP(driver.config->hugetlbfs[0].mnt_dir, "/dev/hugepages2M")
< 0 ||
+ VIR_STRDUP(driver.config->hugetlbfs[1].mnt_dir, "/dev/hugepages1G")
< 0)
+ return EXIT_FAILURE;
+ driver.config->hugetlbfs[0].size = 2048;
+ driver.config->hugetlbfs[0].deflt = true;
+ driver.config->hugetlbfs[1].size = 1048576;
+ driver.config->spiceTLS = 1;
+ if (VIR_STRDUP_QUIET(driver.config->spicePassword, "123456") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->memoryBackingDir);
+ if (VIR_STRDUP_QUIET(driver.config->memoryBackingDir,
"/var/lib/libvirt/qemu/ram") < 0)
+ return EXIT_FAILURE;
+ VIR_FREE(driver.config->nvramDir);
+ if (VIR_STRDUP(driver.config->nvramDir, "/var/lib/libvirt/qemu/nvram")
< 0)
+ return EXIT_FAILURE;
+
+ capslatest = virHashCreate(4, virHashValueFree);
+ if (!capslatest)
+ return EXIT_FAILURE;
+
+ VIR_TEST_VERBOSE("\n");
+
+ for (i = 0; i < ARRAY_CARDINALITY(archs); ++i) {
+ char *cap = testQemuGetLatestCapsForArch(abs_srcdir
"/qemucapabilitiesdata",
+ archs[i], "xml");
+
+ if (!cap || virHashAddEntry(capslatest, archs[i], cap) < 0)
+ return EXIT_FAILURE;
+
+ VIR_TEST_VERBOSE("latest caps for %s: %s\n", archs[i], cap);
+ }
+
+ VIR_TEST_VERBOSE("\n");
+
+ 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");
+
+/**
+ * The following set of macros allows testing of JSON -> 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 testInfo info = { \
+ .name = _name, \
+ .suffix = _suffix, \
+ }; \
+ if (testInfoSetArgs(&info, capslatest, \
+ __VA_ARGS__, ARG_END) < 0) \
+ return EXIT_FAILURE; \
+ if (virTestRun("QEMU JSON-2-ARGV " _name _suffix, \
+ testCompareJSONToArgv, &info) < 0) \
+ ret = -1; \
+ testInfoClear(&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_VER(name, arch, ver) \
+ DO_TEST_CAPS_INTERNAL(name, arch, ver, ARG_END)
+
+# define DO_TEST_CAPS_VER(name, ver) \
+ DO_TEST_CAPS_ARCH_VER(name, "x86_64", ver)
+
+# define DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ...) \
+ DO_TEST_CAPS_INTERNAL(name, arch, "latest", __VA_ARGS__)
+
+# define DO_TEST_CAPS_ARCH_LATEST(name, arch) \
+ DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ARG_END)
+
+# define DO_TEST_CAPS_LATEST(name) \
+ DO_TEST_CAPS_ARCH_LATEST(name, "x86_64")
+
+# define DO_TEST_CAPS_LATEST_FAILURE(name) \
+ DO_TEST_CAPS_ARCH_LATEST_FULL(name, "x86_64", \
+ ARG_FLAGS, FLAG_EXPECT_FAILURE)
+
+# define DO_TEST_CAPS_LATEST_PARSE_ERROR(name) \
+ DO_TEST_CAPS_ARCH_LATEST_FULL(name, "x86_64", \
+ ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR)
+
+
+# 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 */
+ setenv("PATH", "/bin", 1);
+ setenv("USER", "test", 1);
+ setenv("LOGNAME", "test", 1);
+ setenv("HOME", "/home/test", 1);
+ unsetenv("TMPDIR");
+ unsetenv("LD_PRELOAD");
+ unsetenv("LD_LIBRARY_PATH");
+ unsetenv("QEMU_AUDIO_DRV");
+ unsetenv("SDL_AUDIODRIVER");
+
+ DO_TEST_CAPS_LATEST("tiny");
+
+ if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
+ virFileDeleteTree(fakerootdir);
+
+ VIR_FREE(driver.config->nbdTLSx509certdir);
+ qemuTestDriverFree(&driver);
+ VIR_FREE(fakerootdir);
+ virHashFree(capslatest);
+ virFileWrapperClearPrefixes();
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN_PRELOAD(mymain,
+ abs_builddir "/.libs/qemuxml2argvmock.so",
+ abs_builddir "/.libs/virrandommock.so",
+ abs_builddir "/.libs/qemucpumock.so",
+ abs_builddir "/.libs/virpcimock.so")
+
+#else
+
+int main(void)
+{
+ return EXIT_AM_SKIP;
+}
+
+#endif /* WITH_QEMU */
--
2.20.1