mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
bd56d0d813
Ever since the conversion to using only QMP for probing features of qemu 1.2 and newer, we have been unable to detect features that are added only by additional command line options. For example, we'd like to know if '-machine mem-merge=on' (added in qemu 1.5) is present. To do this, we will take advantage of qemu 1.5's query-command-line-parameters QMP call [1]. This patch wires up the framework for probing the command results; if the QMP command is missing, or if a particular command line option does not output any parameters (for example, -net uses a polymorphic parser, which showed up as no parameters as of qemu 1.5), we silently treat that command as having no results. [1] https://lists.gnu.org/archive/html/qemu-devel/2013-04/msg05180.html * src/qemu/qemu_monitor.h (qemuMonitorGetOptions) (qemuMonitorSetOptions) (qemuMonitorGetCommandLineOptionParameters): New functions. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONGetCommandLineOptionParameters): Likewise. * src/qemu/qemu_monitor.c (_qemuMonitor): Add cache field. (qemuMonitorDispose): Clean it. (qemuMonitorGetCommandLineOptionParameters): Implement new function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONGetCommandLineOptionParameters): Likewise. (testQemuMonitorJSONGetCommandLineParameters): Test it. Signed-off-by: Eric Blake <eblake@redhat.com>
633 lines
20 KiB
C
633 lines
20 KiB
C
/*
|
|
* Copyright (C) 2011-2013 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "testutils.h"
|
|
#include "testutilsqemu.h"
|
|
#include "qemumonitortestutils.h"
|
|
#include "qemu/qemu_conf.h"
|
|
#include "virthread.h"
|
|
#include "virerror.h"
|
|
#include "virstring.h"
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
static int
|
|
testQemuMonitorJSONGetStatus(const void *data)
|
|
{
|
|
virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
bool running = false;
|
|
virDomainPausedReason reason = 0;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-status",
|
|
"{ "
|
|
" \"return\": { "
|
|
" \"status\": \"running\", "
|
|
" \"singlestep\": false, "
|
|
" \"running\": true "
|
|
" } "
|
|
"}") < 0)
|
|
goto cleanup;
|
|
if (qemuMonitorTestAddItem(test, "query-status",
|
|
"{ "
|
|
" \"return\": { "
|
|
" \"singlestep\": false, "
|
|
" \"running\": false "
|
|
" } "
|
|
"}") < 0)
|
|
goto cleanup;
|
|
if (qemuMonitorTestAddItem(test, "query-status",
|
|
"{ "
|
|
" \"return\": { "
|
|
" \"status\": \"inmigrate\", "
|
|
" \"singlestep\": false, "
|
|
" \"running\": false "
|
|
" } "
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test),
|
|
&running, &reason) < 0)
|
|
goto cleanup;
|
|
|
|
if (!running) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
"Running was not true");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (reason != VIR_DOMAIN_PAUSED_UNKNOWN) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Reason was unexpectedly set to %d", reason);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test),
|
|
&running, &reason) < 0)
|
|
goto cleanup;
|
|
|
|
if (running) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
"Running was not false");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (reason != VIR_DOMAIN_PAUSED_UNKNOWN) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Reason was unexpectedly set to %d", reason);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (qemuMonitorGetStatus(qemuMonitorTestGetMonitor(test),
|
|
&running, &reason) < 0)
|
|
goto cleanup;
|
|
|
|
if (running) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
"Running was not false");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (reason != VIR_DOMAIN_PAUSED_MIGRATION) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Reason was unexpectedly set to %d", reason);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testQemuMonitorJSONGetVersion(const void *data)
|
|
{
|
|
virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
int major;
|
|
int minor;
|
|
int micro;
|
|
char *package = NULL;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-version",
|
|
"{ "
|
|
" \"return\":{ "
|
|
" \"qemu\":{ "
|
|
" \"major\":1, "
|
|
" \"minor\":2, "
|
|
" \"micro\":3 "
|
|
" },"
|
|
" \"package\":\"\""
|
|
" }"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-version",
|
|
"{ "
|
|
" \"return\":{ "
|
|
" \"qemu\":{ "
|
|
" \"major\":0, "
|
|
" \"minor\":11, "
|
|
" \"micro\":6 "
|
|
" },"
|
|
" \"package\":\"2.283.el6\""
|
|
" }"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if (qemuMonitorGetVersion(qemuMonitorTestGetMonitor(test),
|
|
&major, &minor, µ,
|
|
&package) < 0)
|
|
goto cleanup;
|
|
|
|
if (major != 1) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Major %d was not 1", major);
|
|
goto cleanup;
|
|
}
|
|
if (minor != 2) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Minor %d was not 2", major);
|
|
goto cleanup;
|
|
}
|
|
if (micro != 3) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Micro %d was not 3", major);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRNEQ(package, "")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Package %s was not ''", package);
|
|
goto cleanup;
|
|
}
|
|
VIR_FREE(package);
|
|
|
|
if (qemuMonitorGetVersion(qemuMonitorTestGetMonitor(test),
|
|
&major, &minor, µ,
|
|
&package) < 0)
|
|
goto cleanup;
|
|
|
|
if (major != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Major %d was not 0", major);
|
|
goto cleanup;
|
|
}
|
|
if (minor != 11) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Minor %d was not 11", major);
|
|
goto cleanup;
|
|
}
|
|
if (micro != 6) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Micro %d was not 6", major);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRNEQ(package, "2.283.el6")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"Package %s was not '2.283.el6'", package);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
VIR_FREE(package);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
testQemuMonitorJSONGetMachines(const void *data)
|
|
{
|
|
virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
qemuMonitorMachineInfoPtr *info;
|
|
int ninfo = 0;
|
|
const char *null = NULL;
|
|
int i;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-machines",
|
|
"{ "
|
|
" \"return\": [ "
|
|
" { "
|
|
" \"name\": \"pc-1.0\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"pc-1.1\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"pc-1.2\", "
|
|
" \"is-default\": true, "
|
|
" \"alias\": \"pc\" "
|
|
" } "
|
|
" ]"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if ((ninfo = qemuMonitorGetMachines(qemuMonitorTestGetMonitor(test),
|
|
&info)) < 0)
|
|
goto cleanup;
|
|
|
|
if (ninfo != 3) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"ninfo %d is not 3", ninfo);
|
|
goto cleanup;
|
|
}
|
|
|
|
#define CHECK(i, wantname, wantisDefault, wantalias) \
|
|
do { \
|
|
if (STRNEQ(info[i]->name, (wantname))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"name %s is not %s", \
|
|
info[i]->name, (wantname)); \
|
|
goto cleanup; \
|
|
} \
|
|
if (info[i]->isDefault != (wantisDefault)) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"isDefault %d is not %d", \
|
|
info[i]->isDefault, (wantisDefault)); \
|
|
goto cleanup; \
|
|
} \
|
|
if (STRNEQ_NULLABLE(info[i]->alias, (wantalias))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"alias %s is not %s", \
|
|
info[i]->alias, NULLSTR(wantalias)); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
CHECK(0, "pc-1.0", false, null);
|
|
CHECK(1, "pc-1.1", false, null);
|
|
CHECK(2, "pc-1.2", true, "pc");
|
|
|
|
#undef CHECK
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
for (i = 0; i < ninfo; i++)
|
|
qemuMonitorMachineInfoFree(info[i]);
|
|
VIR_FREE(info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testQemuMonitorJSONGetCPUDefinitions(const void *data)
|
|
{
|
|
virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
char **cpus = NULL;
|
|
int ncpus = 0;
|
|
int i;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-cpu-definitions",
|
|
"{ "
|
|
" \"return\": [ "
|
|
" { "
|
|
" \"name\": \"qemu64\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"Opteron_G4\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"Westmere\" "
|
|
" } "
|
|
" ]"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if ((ncpus = qemuMonitorGetCPUDefinitions(qemuMonitorTestGetMonitor(test),
|
|
&cpus)) < 0)
|
|
goto cleanup;
|
|
|
|
if (ncpus != 3) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"ncpus %d is not 3", ncpus);
|
|
goto cleanup;
|
|
}
|
|
|
|
#define CHECK(i, wantname) \
|
|
do { \
|
|
if (STRNEQ(cpus[i], (wantname))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"name %s is not %s", \
|
|
cpus[i], (wantname)); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
CHECK(0, "qemu64");
|
|
CHECK(1, "Opteron_G4");
|
|
CHECK(2, "Westmere");
|
|
|
|
#undef CHECK
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
for (i = 0; i < ncpus; i++)
|
|
VIR_FREE(cpus[i]);
|
|
VIR_FREE(cpus);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testQemuMonitorJSONGetCommands(const void *data)
|
|
{
|
|
virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
char **commands = NULL;
|
|
int ncommands = 0;
|
|
int i;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-commands",
|
|
"{ "
|
|
" \"return\": [ "
|
|
" { "
|
|
" \"name\": \"system_wakeup\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"cont\" "
|
|
" }, "
|
|
" { "
|
|
" \"name\": \"quit\" "
|
|
" } "
|
|
" ]"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if ((ncommands = qemuMonitorGetCommands(qemuMonitorTestGetMonitor(test),
|
|
&commands)) < 0)
|
|
goto cleanup;
|
|
|
|
if (ncommands != 3) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"ncommands %d is not 3", ncommands);
|
|
goto cleanup;
|
|
}
|
|
|
|
#define CHECK(i, wantname) \
|
|
do { \
|
|
if (STRNEQ(commands[i], (wantname))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"name %s is not %s", \
|
|
commands[i], (wantname)); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
CHECK(0, "system_wakeup");
|
|
CHECK(1, "cont");
|
|
CHECK(2, "quit");
|
|
|
|
#undef CHECK
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
for (i = 0; i < ncommands; i++)
|
|
VIR_FREE(commands[i]);
|
|
VIR_FREE(commands);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testQemuMonitorJSONGetTPMModels(const void *data)
|
|
{
|
|
const virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
char **tpmmodels = NULL;
|
|
int ntpmmodels = 0;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-tpm-models",
|
|
"{ "
|
|
" \"return\": [ "
|
|
" \"passthrough\""
|
|
" ]"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
if ((ntpmmodels = qemuMonitorGetTPMModels(qemuMonitorTestGetMonitor(test),
|
|
&tpmmodels)) < 0)
|
|
goto cleanup;
|
|
|
|
if (ntpmmodels != 1) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"ntpmmodels %d is not 1", ntpmmodels);
|
|
goto cleanup;
|
|
}
|
|
|
|
#define CHECK(i, wantname) \
|
|
do { \
|
|
if (STRNEQ(tpmmodels[i], (wantname))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"name %s is not %s", \
|
|
tpmmodels[i], (wantname)); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
CHECK(0, "passthrough");
|
|
|
|
#undef CHECK
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
virStringFreeList(tpmmodels);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
testQemuMonitorJSONGetCommandLineOptionParameters(const void *data)
|
|
{
|
|
const virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
|
|
qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt);
|
|
int ret = -1;
|
|
char **params = NULL;
|
|
int nparams = 0;
|
|
|
|
if (!test)
|
|
return -1;
|
|
|
|
if (qemuMonitorTestAddItem(test, "query-command-line-options",
|
|
"{ "
|
|
" \"return\": [ "
|
|
" {\"parameters\": [], \"option\": \"acpi\" },"
|
|
" {\"parameters\": ["
|
|
" {\"name\": \"romfile\", "
|
|
" \"type\": \"string\"}, "
|
|
" {\"name\": \"bootindex\", "
|
|
" \"type\": \"number\"}], "
|
|
" \"option\": \"option-rom\"}"
|
|
" ]"
|
|
"}") < 0)
|
|
goto cleanup;
|
|
|
|
/* present with params */
|
|
if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test),
|
|
"option-rom",
|
|
¶ms)) < 0)
|
|
goto cleanup;
|
|
|
|
if (nparams != 2) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"nparams was %d, expected 2", nparams);
|
|
goto cleanup;
|
|
}
|
|
|
|
#define CHECK(i, wantname) \
|
|
do { \
|
|
if (STRNEQ(params[i], (wantname))) { \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
"name was %s, expected %s", \
|
|
params[i], (wantname)); \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
CHECK(0, "romfile");
|
|
CHECK(1, "bootindex");
|
|
|
|
#undef CHECK
|
|
|
|
virStringFreeList(params);
|
|
params = NULL;
|
|
|
|
/* present but empty */
|
|
if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test),
|
|
"acpi",
|
|
¶ms)) < 0)
|
|
goto cleanup;
|
|
|
|
if (nparams != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"nparams was %d, expected 0", nparams);
|
|
goto cleanup;
|
|
}
|
|
if (params && params[0]) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
"unexpected array contents");
|
|
goto cleanup;
|
|
}
|
|
|
|
virStringFreeList(params);
|
|
params = NULL;
|
|
|
|
/* no such option */
|
|
if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test),
|
|
"foobar",
|
|
¶ms)) < 0)
|
|
goto cleanup;
|
|
|
|
if (nparams != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"nparams was %d, expected 0", nparams);
|
|
goto cleanup;
|
|
}
|
|
if (params && params[0]) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
"unexpected array contents");
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
qemuMonitorTestFree(test);
|
|
virStringFreeList(params);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
mymain(void)
|
|
{
|
|
int ret = 0;
|
|
virDomainXMLOptionPtr xmlopt;
|
|
|
|
#if !WITH_YAJL
|
|
fputs("libvirt not compiled with yajl, skipping this test\n", stderr);
|
|
return EXIT_AM_SKIP;
|
|
#endif
|
|
|
|
if (virThreadInitialize() < 0 ||
|
|
!(xmlopt = virQEMUDriverCreateXMLConf(NULL)))
|
|
return EXIT_FAILURE;
|
|
|
|
virEventRegisterDefaultImpl();
|
|
|
|
#define DO_TEST(name) \
|
|
if (virtTestRun(# name, 1, testQemuMonitorJSON ## name, xmlopt) < 0) \
|
|
ret = -1
|
|
|
|
DO_TEST(GetStatus);
|
|
DO_TEST(GetVersion);
|
|
DO_TEST(GetMachines);
|
|
DO_TEST(GetCPUDefinitions);
|
|
DO_TEST(GetCommands);
|
|
DO_TEST(GetTPMModels);
|
|
DO_TEST(GetCommandLineOptionParameters);
|
|
|
|
virObjectUnref(xmlopt);
|
|
|
|
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
VIRT_TEST_MAIN(mymain)
|