mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-20 11:35:19 +00:00
qemu: agent: Make setting of vcpus more robust
Documentation for the "guest-set-vcpus" command describes a proper algorithm how to set vcpus. This patch makes the following changes: - state of cpus that has not changed is not updated - if the command was partially successful the command is re-tried with the rest of the arguments to get a proper error message - code is more robust against malicious guest agent - fix testsuite to the new semantics
This commit is contained in:
parent
69e0cd3397
commit
b1aa91e140
@ -1523,16 +1523,13 @@ qemuAgentGetVCPUs(qemuAgentPtr mon,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the VCPU state using guest agent.
|
/* returns the value provided by the guest agent or -1 on internal error */
|
||||||
*
|
static int
|
||||||
* Returns -1 on error, ninfo in case everything was successful and less than
|
qemuAgentSetVCPUsCommand(qemuAgentPtr mon,
|
||||||
* ninfo on a partial failure.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|
||||||
qemuAgentCPUInfoPtr info,
|
qemuAgentCPUInfoPtr info,
|
||||||
size_t ninfo)
|
size_t ninfo,
|
||||||
|
int *nmodified)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
virJSONValuePtr cmd = NULL;
|
virJSONValuePtr cmd = NULL;
|
||||||
@ -1541,6 +1538,8 @@ qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|||||||
virJSONValuePtr cpu = NULL;
|
virJSONValuePtr cpu = NULL;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
*nmodified = 0;
|
||||||
|
|
||||||
/* create the key data array */
|
/* create the key data array */
|
||||||
if (!(cpus = virJSONValueNewArray()))
|
if (!(cpus = virJSONValueNewArray()))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1548,6 +1547,12 @@ qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|||||||
for (i = 0; i < ninfo; i++) {
|
for (i = 0; i < ninfo; i++) {
|
||||||
qemuAgentCPUInfoPtr in = &info[i];
|
qemuAgentCPUInfoPtr in = &info[i];
|
||||||
|
|
||||||
|
/* don't set state for cpus that were not touched */
|
||||||
|
if (!in->modified)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(*nmodified)++;
|
||||||
|
|
||||||
/* create single cpu object */
|
/* create single cpu object */
|
||||||
if (!(cpu = virJSONValueNewObject()))
|
if (!(cpu = virJSONValueNewObject()))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1564,6 +1569,11 @@ qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|||||||
cpu = NULL;
|
cpu = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*nmodified == 0) {
|
||||||
|
ret = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(cmd = qemuAgentMakeCommand("guest-set-vcpus",
|
if (!(cmd = qemuAgentMakeCommand("guest-set-vcpus",
|
||||||
"a:vcpus", cpus,
|
"a:vcpus", cpus,
|
||||||
NULL)))
|
NULL)))
|
||||||
@ -1575,9 +1585,17 @@ qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|||||||
VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
|
VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) {
|
if (qemuAgentCheckError(cmd, reply) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* All negative values are invalid. Return of 0 is bogus since we wouldn't
|
||||||
|
* call the guest agent so that 0 cpus would be set successfully. Reporting
|
||||||
|
* more successfully set vcpus that we've asked for is invalid. */
|
||||||
|
if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0 ||
|
||||||
|
ret <= 0 || ret > *nmodified) {
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
_("malformed return value"));
|
_("guest agent returned malformed or invalid return value"));
|
||||||
|
ret = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@ -1589,6 +1607,45 @@ qemuAgentSetVCPUs(qemuAgentPtr mon,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the VCPU state using guest agent.
|
||||||
|
*
|
||||||
|
* Attempts to set the guest agent state for all cpus or until a proper error is
|
||||||
|
* reported by the guest agent. This may require multiple calls.
|
||||||
|
*
|
||||||
|
* Returns -1 on error, 0 on success.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
qemuAgentSetVCPUs(qemuAgentPtr mon,
|
||||||
|
qemuAgentCPUInfoPtr info,
|
||||||
|
size_t ninfo)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
int nmodified;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((rv = qemuAgentSetVCPUsCommand(mon, info, ninfo, &nmodified)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* all vcpus were set successfully */
|
||||||
|
if (rv == nmodified)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* un-mark vcpus that were already set */
|
||||||
|
for (i = 0; i < ninfo && rv > 0; i++) {
|
||||||
|
if (!info[i].modified)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
info[i].modified = false;
|
||||||
|
rv--;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* modify the cpu info structure to set the correct amount of cpus */
|
/* modify the cpu info structure to set the correct amount of cpus */
|
||||||
int
|
int
|
||||||
qemuAgentUpdateCPUInfo(unsigned int nvcpus,
|
qemuAgentUpdateCPUInfo(unsigned int nvcpus,
|
||||||
@ -1647,12 +1704,14 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus,
|
|||||||
/* unplug */
|
/* unplug */
|
||||||
if (cpuinfo[i].offlinable && cpuinfo[i].online) {
|
if (cpuinfo[i].offlinable && cpuinfo[i].online) {
|
||||||
cpuinfo[i].online = false;
|
cpuinfo[i].online = false;
|
||||||
|
cpuinfo[i].modified = true;
|
||||||
nonline--;
|
nonline--;
|
||||||
}
|
}
|
||||||
} else if (nvcpus > nonline) {
|
} else if (nvcpus > nonline) {
|
||||||
/* plug */
|
/* plug */
|
||||||
if (!cpuinfo[i].online) {
|
if (!cpuinfo[i].online) {
|
||||||
cpuinfo[i].online = true;
|
cpuinfo[i].online = true;
|
||||||
|
cpuinfo[i].modified = true;
|
||||||
nonline++;
|
nonline++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -95,6 +95,8 @@ struct _qemuAgentCPUInfo {
|
|||||||
unsigned int id; /* logical cpu ID */
|
unsigned int id; /* logical cpu ID */
|
||||||
bool online; /* true if the CPU is activated */
|
bool online; /* true if the CPU is activated */
|
||||||
bool offlinable; /* true if the CPU can be offlined */
|
bool offlinable; /* true if the CPU can be offlined */
|
||||||
|
|
||||||
|
bool modified; /* set to true if the vcpu state needs to be changed */
|
||||||
};
|
};
|
||||||
|
|
||||||
int qemuAgentGetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr *info);
|
int qemuAgentGetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr *info);
|
||||||
|
@ -4804,19 +4804,6 @@ qemuDomainSetVcpusAgent(virDomainObjPtr vm,
|
|||||||
ret = qemuAgentSetVCPUs(qemuDomainGetAgent(vm), cpuinfo, ncpuinfo);
|
ret = qemuAgentSetVCPUs(qemuDomainGetAgent(vm), cpuinfo, ncpuinfo);
|
||||||
qemuDomainObjExitAgent(vm);
|
qemuDomainObjExitAgent(vm);
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (ret < ncpuinfo) {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("failed to set state of cpu %d via guest agent"),
|
|
||||||
cpuinfo[ret-1].id);
|
|
||||||
ret = -1;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(cpuinfo);
|
VIR_FREE(cpuinfo);
|
||||||
|
|
||||||
|
@ -517,17 +517,15 @@ static const char testQemuAgentCPUResponse[] =
|
|||||||
"}";
|
"}";
|
||||||
|
|
||||||
static const char testQemuAgentCPUArguments1[] =
|
static const char testQemuAgentCPUArguments1[] =
|
||||||
"[{\"logical-id\":0,\"online\":true},"
|
"[{\"logical-id\":1,\"online\":false}]";
|
||||||
"{\"logical-id\":1,\"online\":false},"
|
|
||||||
"{\"logical-id\":2,\"online\":true},"
|
|
||||||
"{\"logical-id\":3,\"online\":false}]";
|
|
||||||
|
|
||||||
static const char testQemuAgentCPUArguments2[] =
|
static const char testQemuAgentCPUArguments2[] =
|
||||||
"[{\"logical-id\":0,\"online\":true},"
|
"[{\"logical-id\":1,\"online\":true},"
|
||||||
"{\"logical-id\":1,\"online\":true},"
|
|
||||||
"{\"logical-id\":2,\"online\":true},"
|
|
||||||
"{\"logical-id\":3,\"online\":true}]";
|
"{\"logical-id\":3,\"online\":true}]";
|
||||||
|
|
||||||
|
static const char testQemuAgentCPUArguments3[] =
|
||||||
|
"[{\"logical-id\":3,\"online\":true}]";
|
||||||
|
|
||||||
static int
|
static int
|
||||||
testQemuAgentCPU(const void *data)
|
testQemuAgentCPU(const void *data)
|
||||||
{
|
{
|
||||||
@ -566,44 +564,40 @@ testQemuAgentCPU(const void *data)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (qemuMonitorTestAddItemParams(test, "guest-set-vcpus",
|
if (qemuMonitorTestAddItemParams(test, "guest-set-vcpus",
|
||||||
"{ \"return\" : 4 }",
|
"{ \"return\" : 1 }",
|
||||||
"vcpus", testQemuAgentCPUArguments1,
|
"vcpus", testQemuAgentCPUArguments1,
|
||||||
NULL) < 0)
|
NULL) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if ((nvcpus = qemuAgentSetVCPUs(qemuMonitorTestGetAgent(test),
|
if (qemuAgentSetVCPUs(qemuMonitorTestGetAgent(test), cpuinfo, nvcpus) < 0)
|
||||||
cpuinfo, nvcpus)) < 0)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (nvcpus != 4) {
|
/* try to hotplug two, second one will fail*/
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"Expected '4' cpus updated , got '%d'", nvcpus);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try to hotplug two */
|
|
||||||
if (qemuMonitorTestAddAgentSyncResponse(test) < 0)
|
if (qemuMonitorTestAddAgentSyncResponse(test) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (qemuMonitorTestAddItemParams(test, "guest-set-vcpus",
|
if (qemuMonitorTestAddItemParams(test, "guest-set-vcpus",
|
||||||
"{ \"return\" : 4 }",
|
"{ \"return\" : 1 }",
|
||||||
"vcpus", testQemuAgentCPUArguments2,
|
"vcpus", testQemuAgentCPUArguments2,
|
||||||
NULL) < 0)
|
NULL) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
if (qemuMonitorTestAddAgentSyncResponse(test) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (qemuMonitorTestAddItemParams(test, "guest-set-vcpus",
|
||||||
|
"{ \"error\" : \"random error\" }",
|
||||||
|
"vcpus", testQemuAgentCPUArguments3,
|
||||||
|
NULL) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
if (qemuAgentUpdateCPUInfo(4, cpuinfo, nvcpus) < 0)
|
if (qemuAgentUpdateCPUInfo(4, cpuinfo, nvcpus) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if ((nvcpus = qemuAgentSetVCPUs(qemuMonitorTestGetAgent(test),
|
/* this should fail */
|
||||||
cpuinfo, nvcpus)) < 0)
|
if (qemuAgentSetVCPUs(qemuMonitorTestGetAgent(test), cpuinfo, nvcpus) != -1)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (nvcpus != 4) {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"Expected '4' cpus updated , got '%d'", nvcpus);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user