mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 11:01:19 +00:00
For ssh disks that are served by nbdkit, we can support logging in with an ssh key file. Pass the path to the configured key file and the username to the nbdkit process. Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com> Reviewed-by: Peter Krempa <pkrempa@redhat.com>
311 lines
8.6 KiB
C
311 lines
8.6 KiB
C
#include <config.h>
|
|
|
|
#include <fcntl.h>
|
|
#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)
|