#include #include #include "internal.h" #include "testutils.h" #include "testutilsqemu.h" #include "qemu/qemu_domain.h" #include "qemu/qemu_nbdkit.h" #define LIBVIRT_QEMU_NBDKITPRIV_H_ALLOW #include "qemu/qemu_nbdkitpriv.h" #include "vircommand.h" #define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW #include "vircommandpriv.h" #include "virutil.h" #include "virsecret.h" #include "datatypes.h" #include "virmock.h" #define VIR_FROM_THIS VIR_FROM_QEMU static virQEMUDriver driver; /* Some mock implementations for testing */ #define PIPE_FD_START 777 static int mockpipefd = PIPE_FD_START; static int (*real_virPipeQuiet)(int fds[2]); static void init_syms(void) { VIR_MOCK_REAL_INIT(virPipeQuiet); } static int moveToStableFd(int fd) { int newfd; /* don't overwrite an existing fd */ if (fcntl(mockpipefd, F_GETFD) != -1) abort(); newfd = dup2(fd, mockpipefd++); VIR_FORCE_CLOSE(fd); return newfd; } int virPipeQuiet(int fds[2]) { int tempfds[2]; init_syms(); if (real_virPipeQuiet(tempfds) < 0) return -1; if ((fds[0] = moveToStableFd(tempfds[0])) < 0 || (fds[1] = moveToStableFd(tempfds[1])) < 0) return -1; return 0; } int virSecretGetSecretString(virConnectPtr conn G_GNUC_UNUSED, virSecretLookupTypeDef *seclookupdef, virSecretUsageType secretUsageType, uint8_t **secret, size_t *secret_size) { char uuidstr[VIR_UUID_BUFLEN]; const char *secretname = NULL; char *tmp = NULL; switch (seclookupdef->type) { case VIR_SECRET_LOOKUP_TYPE_UUID: virUUIDFormat(seclookupdef->u.uuid, uuidstr); secretname = uuidstr; break; case VIR_SECRET_LOOKUP_TYPE_USAGE: secretname = seclookupdef->u.usage; break; case VIR_SECRET_LOOKUP_TYPE_NONE: case VIR_SECRET_LOOKUP_TYPE_LAST: default: virReportEnumRangeError(virSecretLookupType, seclookupdef->type); return -1; }; /* For testing, just generate a value for the secret that includes the type * and the id of the secret */ tmp = g_strdup_printf("%s-%s-secret", virSecretUsageTypeToString(secretUsageType), secretname); *secret = (uint8_t*)tmp; *secret_size = strlen(tmp) + 1; return 0; } virConnectPtr virGetConnectSecret(void) { return virGetConnect(); } /* end of mock implementations */ typedef struct { const char *name; char* infile; char* outtemplate; qemuNbdkitCaps *nbdkitcaps; bool expectFail; } TestInfo; typedef enum { NBDKIT_ARG_CAPS, NBDKIT_ARG_EXPECT_FAIL, NBDKIT_ARG_END } NbdkitArgName; static void testInfoSetPaths(TestInfo *info) { info->infile = g_strdup_printf("%s/qemuxml2argvdata/%s.xml", abs_srcdir, info->name); info->outtemplate = g_strdup_printf("%s/qemunbdkitdata/%s", abs_srcdir, info->name); } static void testInfoClear(TestInfo *info) { g_free(info->infile); g_free(info->outtemplate); g_clear_object(&info->nbdkitcaps); } static void testInfoSetArgs(TestInfo *info, ...) { va_list argptr; NbdkitArgName argname; unsigned int cap; va_start(argptr, info); while ((argname = va_arg(argptr, NbdkitArgName)) != NBDKIT_ARG_END) { switch (argname) { case NBDKIT_ARG_CAPS: while ((cap = va_arg(argptr, unsigned int)) < QEMU_NBDKIT_CAPS_LAST) qemuNbdkitCapsSet(info->nbdkitcaps, cap); break; case NBDKIT_ARG_EXPECT_FAIL: info->expectFail = va_arg(argptr, unsigned int); break; case NBDKIT_ARG_END: default: break; } } } static int testNbdkit(const void *data) { const TestInfo *info = data; g_autoptr(virDomainDef) def = NULL; size_t i; int ret = 0; /* restart mock pipe fds so tests are consistent */ mockpipefd = PIPE_FD_START; if (!virFileExists(info->infile)) { virReportError(VIR_ERR_INTERNAL_ERROR, "Test input file '%s' is missing", info->infile); return -1; } if (!(def = virDomainDefParseFile(info->infile, driver.xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) return -1; for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; g_autofree char *statedir = g_strdup_printf("/tmp/statedir-%zi", i); g_autofree char *alias = g_strdup_printf("test-disk-%zi", i); g_autofree char *cmdfile = g_strdup_printf("%s.args.disk%zi", info->outtemplate, i); if (qemuNbdkitInitStorageSource(info->nbdkitcaps, disk->src, statedir, alias, 101, 101)) { qemuDomainStorageSourcePrivate *srcPriv = qemuDomainStorageSourcePrivateFetch(disk->src); g_autoptr(virCommand) cmd = NULL; g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew(); g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; g_autofree char *actualCmdline = NULL; virCommandSendBuffer *sendbuffers; int nsendbuffers; size_t j; virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL); cmd = qemuNbdkitProcessBuildCommand(srcPriv->nbdkitProcess); if (virCommandRun(cmd, NULL) < 0) { ret = -1; continue; } virCommandPeekSendBuffers(cmd, &sendbuffers, &nsendbuffers); if (!(actualCmdline = virBufferContentAndReset(&buf))) { ret = -1; continue; } if (virTestCompareToFileFull(actualCmdline, cmdfile, false) < 0) ret = -1; for (j = 0; j < nsendbuffers; j++) { virCommandSendBuffer *buffer = &sendbuffers[j]; g_autofree char *pipefile = g_strdup_printf("%s.pipe.%i", cmdfile, buffer->fd); if (virTestCompareToFile((const char*)buffer->buffer, pipefile) < 0) ret = -1; } } else { if (virFileExists(cmdfile)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "qemuNbdkitInitStorageSource() was not expected to fail"); ret = -1; } } } if (info->expectFail) { if (ret == 0) { ret = -1; VIR_TEST_DEBUG("Error expected but there wasn't any."); } else { ret = 0; } } return ret; } static int mymain(void) { g_autoptr(GHashTable) capslatest = testQemuGetLatestCaps(); g_autoptr(GHashTable) capscache = virHashNew(virObjectUnref); int ret = 0; if (qemuTestDriverInit(&driver) < 0) return EXIT_FAILURE; if (testQemuInsertRealCaps(driver.qemuCapsCache, "x86_64", "latest", "", capslatest, capscache, NULL, NULL) < 0) { ret = -1; goto cleanup; } #define DO_TEST_FULL(_name, ...) \ do { \ TestInfo info = { \ .name = _name, \ .nbdkitcaps = qemuNbdkitCapsNew(TEST_NBDKIT_PATH), \ }; \ testInfoSetPaths(&info); \ testInfoSetArgs(&info, __VA_ARGS__); \ virTestRunLog(&ret, "nbdkit " _name, testNbdkit, &info); \ testInfoClear(&info); \ } while (0) #define DO_TEST(_name, ...) \ DO_TEST_FULL(_name, NBDKIT_ARG_CAPS, __VA_ARGS__, QEMU_NBDKIT_CAPS_LAST, NBDKIT_ARG_END) #define DO_TEST_FAILURE(_name, ...) \ DO_TEST_FULL(_name, \ NBDKIT_ARG_EXPECT_FAIL, 1, \ NBDKIT_ARG_CAPS, __VA_ARGS__, QEMU_NBDKIT_CAPS_LAST, NBDKIT_ARG_END) #define DO_TEST_NOCAPS(_name) \ DO_TEST_FULL(_name, NBDKIT_ARG_END) DO_TEST("disk-cdrom-network", QEMU_NBDKIT_CAPS_PLUGIN_CURL); DO_TEST("disk-network-http", QEMU_NBDKIT_CAPS_PLUGIN_CURL); DO_TEST("disk-network-source-curl-nbdkit-backing", QEMU_NBDKIT_CAPS_PLUGIN_CURL); DO_TEST("disk-network-source-curl", QEMU_NBDKIT_CAPS_PLUGIN_CURL); DO_TEST("disk-network-ssh", QEMU_NBDKIT_CAPS_PLUGIN_SSH); DO_TEST("disk-network-ssh-password", QEMU_NBDKIT_CAPS_PLUGIN_SSH); DO_TEST("disk-network-ssh-key", QEMU_NBDKIT_CAPS_PLUGIN_SSH); cleanup: qemuTestDriverFree(&driver); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } VIR_TEST_MAIN(mymain)