mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-22 04:25:18 +00:00
qemu: query command line options in QMP
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>
This commit is contained in:
parent
082274ea41
commit
bd56d0d813
@ -79,6 +79,9 @@ struct _qemuMonitor {
|
||||
|
||||
bool json;
|
||||
bool waitGreeting;
|
||||
|
||||
/* cache of query-command-line-options results */
|
||||
virJSONValuePtr options;
|
||||
};
|
||||
|
||||
static virClassPtr qemuMonitorClass;
|
||||
@ -243,6 +246,7 @@ static void qemuMonitorDispose(void *obj)
|
||||
(mon->cb->destroy)(mon, mon->vm);
|
||||
virCondDestroy(&mon->notify);
|
||||
VIR_FREE(mon->buffer);
|
||||
virJSONValueFree(mon->options);
|
||||
}
|
||||
|
||||
|
||||
@ -912,6 +916,18 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
virJSONValuePtr
|
||||
qemuMonitorGetOptions(qemuMonitorPtr mon)
|
||||
{
|
||||
return mon->options;
|
||||
}
|
||||
|
||||
void
|
||||
qemuMonitorSetOptions(qemuMonitorPtr mon, virJSONValuePtr options)
|
||||
{
|
||||
mon->options = options;
|
||||
}
|
||||
|
||||
int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon,
|
||||
const char *cmd,
|
||||
int scm_fd,
|
||||
@ -3334,6 +3350,31 @@ int qemuMonitorGetEvents(qemuMonitorPtr mon,
|
||||
}
|
||||
|
||||
|
||||
/* Collect the parameters associated with a given command line option.
|
||||
* Return count of known parameters or -1 on error. */
|
||||
int
|
||||
qemuMonitorGetCommandLineOptionParameters(qemuMonitorPtr mon,
|
||||
const char *option,
|
||||
char ***params)
|
||||
{
|
||||
VIR_DEBUG("mon=%p option=%s params=%p", mon, option, params);
|
||||
|
||||
if (!mon) {
|
||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||
_("monitor must not be NULL"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!mon->json) {
|
||||
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
||||
_("JSON monitor is required"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return qemuMonitorJSONGetCommandLineOptionParameters(mon, option, params);
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorGetKVMState(qemuMonitorPtr mon,
|
||||
bool *enabled,
|
||||
bool *present)
|
||||
|
@ -168,6 +168,10 @@ int qemuMonitorSetLink(qemuMonitorPtr mon,
|
||||
char *qemuMonitorNextCommandID(qemuMonitorPtr mon);
|
||||
int qemuMonitorSend(qemuMonitorPtr mon,
|
||||
qemuMonitorMessagePtr msg);
|
||||
virJSONValuePtr qemuMonitorGetOptions(qemuMonitorPtr mon)
|
||||
ATTRIBUTE_NONNULL(1);
|
||||
void qemuMonitorSetOptions(qemuMonitorPtr mon, virJSONValuePtr options)
|
||||
ATTRIBUTE_NONNULL(1);
|
||||
int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon,
|
||||
const char *cmd,
|
||||
int scm_fd,
|
||||
@ -664,6 +668,9 @@ int qemuMonitorGetCommands(qemuMonitorPtr mon,
|
||||
char ***commands);
|
||||
int qemuMonitorGetEvents(qemuMonitorPtr mon,
|
||||
char ***events);
|
||||
int qemuMonitorGetCommandLineOptionParameters(qemuMonitorPtr mon,
|
||||
const char *option,
|
||||
char ***params);
|
||||
|
||||
int qemuMonitorGetKVMState(qemuMonitorPtr mon,
|
||||
bool *enabled,
|
||||
|
@ -4275,6 +4275,126 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon,
|
||||
const char *option,
|
||||
char ***params)
|
||||
{
|
||||
int ret;
|
||||
virJSONValuePtr cmd;
|
||||
virJSONValuePtr reply = NULL;
|
||||
virJSONValuePtr data = NULL;
|
||||
virJSONValuePtr array = NULL;
|
||||
char **paramlist = NULL;
|
||||
int n = 0;
|
||||
size_t i;
|
||||
|
||||
*params = NULL;
|
||||
|
||||
/* query-command-line-options has fixed output for a given qemu
|
||||
* binary; but since callers want to query parameters for one
|
||||
* option at a time, we cache the option list from qemu. */
|
||||
if (!(array = qemuMonitorGetOptions(mon))) {
|
||||
if (!(cmd = qemuMonitorJSONMakeCommand("query-command-line-options",
|
||||
NULL)))
|
||||
return -1;
|
||||
|
||||
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
||||
|
||||
if (ret == 0) {
|
||||
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
||||
goto cleanup;
|
||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectRemoveKey(reply, "return", &array) <= 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-command-line-options reply was missing "
|
||||
"return data"));
|
||||
goto cleanup;
|
||||
}
|
||||
qemuMonitorSetOptions(mon, array);
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
|
||||
if ((n = virJSONValueArraySize(array)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-command-line-options reply data was not "
|
||||
"an array"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < n ; i++) {
|
||||
virJSONValuePtr child = virJSONValueArrayGet(array, i);
|
||||
const char *tmp;
|
||||
|
||||
if (!(tmp = virJSONValueObjectGetString(child, "option"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-command-line-options reply data was "
|
||||
"missing 'option'"));
|
||||
goto cleanup;
|
||||
}
|
||||
if (STREQ(tmp, option)) {
|
||||
data = virJSONValueObjectGet(child, "parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
/* Option not found; return 0 parameters rather than an error. */
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((n = virJSONValueArraySize(data)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-command-line-options parameter data was not "
|
||||
"an array"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* null-terminated list */
|
||||
if (VIR_ALLOC_N(paramlist, n + 1) < 0) {
|
||||
virReportOOMError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < n ; i++) {
|
||||
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
||||
const char *tmp;
|
||||
|
||||
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-command-line-options parameter data was "
|
||||
"missing 'name'"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (VIR_STRDUP(paramlist[i], tmp) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = n;
|
||||
*params = paramlist;
|
||||
|
||||
cleanup:
|
||||
/* If we failed before getting the JSON array of options, we (try)
|
||||
* to cache an empty array to speed up the next failure. */
|
||||
if (!qemuMonitorGetOptions(mon))
|
||||
qemuMonitorSetOptions(mon, virJSONValueNewArray());
|
||||
|
||||
if (ret < 0)
|
||||
virStringFreeList(paramlist);
|
||||
virJSONValueFree(cmd);
|
||||
virJSONValueFree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon,
|
||||
bool *enabled,
|
||||
bool *present)
|
||||
|
@ -319,6 +319,10 @@ int qemuMonitorJSONGetCommands(qemuMonitorPtr mon,
|
||||
int qemuMonitorJSONGetEvents(qemuMonitorPtr mon,
|
||||
char ***events)
|
||||
ATTRIBUTE_NONNULL(2);
|
||||
int qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon,
|
||||
const char *option,
|
||||
char ***params)
|
||||
ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
|
||||
|
||||
int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon,
|
||||
bool *enabled,
|
||||
|
@ -493,6 +493,108 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@ -520,6 +622,7 @@ mymain(void)
|
||||
DO_TEST(GetCPUDefinitions);
|
||||
DO_TEST(GetCommands);
|
||||
DO_TEST(GetTPMModels);
|
||||
DO_TEST(GetCommandLineOptionParameters);
|
||||
|
||||
virObjectUnref(xmlopt);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user