[PATCH] qemu: Add guest load average to domain stats

Since this is already in QEMU we can add this stat which might be useful for people. Resolves: https://issues.redhat.com/browse/RHEL-71883 Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/manpages/virsh.rst | 3 +++ src/libvirt-domain.c | 3 +++ src/qemu/qemu_agent.c | 59 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 +++++ src/qemu/qemu_driver.c | 32 ++++++++++++++++++++++ tests/qemuagenttest.c | 53 ++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 06c2802b3f9f..60b556cb0cd0 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2408,6 +2408,9 @@ When selecting the *--state* group the following fields are returned: for bank <index> in cache monitor <num> * ``cpu.cache.monitor.<num>.bank.<index>.bytes`` - the number of bytes of last level cache that the domain is using on cache bank <index> +* ``cpu.load.1m`` - average load in guest for last 1 minute +* ``cpu.load.5m`` - average load in guest for last 5 minutes +* ``cpu.load.15m`` - average load in guest for last 15 minutes *--balloon* returns: diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc3225579..35f5e926e52b 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12272,6 +12272,9 @@ virConnectGetDomainCapabilities(virConnectPtr conn, * last level cache that the * domain is using on cache * bank <index> + * "cpu.load.1m" - average load in guest for last 1 minute + * "cpu.load.5m" - average load in guest for last 5 minutes + * "cpu.load.15m" - average load in guest for last 15 minutes * * VIR_DOMAIN_STATS_BALLOON: * Return memory balloon device information. diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 43fca86f10ed..6dbe8c52d738 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -2568,3 +2568,62 @@ int qemuAgentGetDisks(qemuAgent *agent, g_free(*disks); return -1; } + + +int +qemuAgentGetLoadAvg(qemuAgent *agent, + double *load1m, + double *load5m, + double *load15m, + bool report_unsupported) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + virJSONValue *data = NULL; + int rc; + + if (load1m) + *load1m = 0; + + if (load5m) + *load5m = 0; + + if (load15m) + *load15m = 0; + + if (!(cmd = qemuAgentMakeCommand("guest-get-load", NULL))) + return -1; + + if ((rc = qemuAgentCommandFull(agent, cmd, &reply, agent->timeout, + report_unsupported)) < 0) + return rc; + + if (!(data = virJSONValueObjectGetObject(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent didn't return an array of loads")); + return -1; + } + + if (load1m && + virJSONValueObjectGetNumberDouble(data, "load1m", load1m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load1m' missing in reply of guest-get-load")); + return -1; + } + + if (load5m && + virJSONValueObjectGetNumberDouble(data, "load5m", load5m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load5m' missing in reply of guest-get-load")); + return -1; + } + + if (load15m && + virJSONValueObjectGetNumberDouble(data, "load15m", load15m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load15m' missing in reply of guest-get-load")); + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index f98586e8f8ab..cd17a98d3924 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -195,3 +195,9 @@ int qemuAgentSSHRemoveAuthorizedKeys(qemuAgent *agent, int qemuAgentGetDisks(qemuAgent *mon, qemuAgentDiskInfo ***disks, bool report_unsupported); + +int qemuAgentGetLoadAvg(qemuAgent *agent, + double *load1m, + double *load5m, + double *load15m, + bool report_unsupported); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 80c918312b8f..d9279b3a5804 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16937,6 +16937,36 @@ qemuDomainGetStatsCpuHaltPollTime(virDomainObj *dom, return; } + +static void +qemuDomainGetStatsCpuLoadAvg(virDomainObj *dom, + virTypedParamList *params, + unsigned int privflags) +{ + qemuAgent *agent = NULL; + double load1m = 0; + double load5m = 0; + double load15m = 0; + int rc = 0; + + if (!HAVE_JOB(privflags) || + !virDomainObjIsActive(dom) || + !qemuDomainAgentAvailable(dom, false)) + return; + + agent = qemuDomainObjEnterAgent(dom); + rc = qemuAgentGetLoadAvg(agent, &load1m, &load5m, &load15m, false); + qemuDomainObjExitAgent(dom, agent); + + if (rc < 0) + return; + + virTypedParamListAddDouble(params, load1m, "cpu.load.1m"); + virTypedParamListAddDouble(params, load5m, "cpu.load.5m"); + virTypedParamListAddDouble(params, load15m, "cpu.load.15m"); +} + + static void qemuDomainGetStatsCpu(virQEMUDriver *driver, virDomainObj *dom, @@ -16954,6 +16984,8 @@ qemuDomainGetStatsCpu(virQEMUDriver *driver, qemuDomainGetStatsCpuCache(driver, dom, params); qemuDomainGetStatsCpuHaltPollTime(dom, params, privflags); + + qemuDomainGetStatsCpuLoadAvg(dom, params, privflags); } diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c index 328788024197..566571cf1107 100644 --- a/tests/qemuagenttest.c +++ b/tests/qemuagenttest.c @@ -1356,6 +1356,58 @@ testQemuAgentTimezone(const void *data) virTypedParamsFree(params, nparams); return ret; } + + +static const char testQemuAgentGetLoadAvgResponse[] = + "{" + " \"return\": {" + " \"load15m\": 0.03564453125," + " \"load5m\": 0.064453125," + " \"load1m\": 0.00390625" + " }" + "}"; + +static int +testQemuAgentGetLoadAvg(const void *data) +{ + virDomainXMLOption *xmlopt = (virDomainXMLOption *)data; + g_autoptr(qemuMonitorTest) test = qemuMonitorTestNewAgent(xmlopt); + double load1m = 0; + double load5m = 0; + double load15m = 0; + + if (!test) + return -1; + + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) + return -1; + + if (qemuMonitorTestAddItem(test, "guest-get-load", + testQemuAgentGetLoadAvgResponse) < 0) + return -1; + + if (qemuAgentGetLoadAvg(qemuMonitorTestGetAgent(test), + &load1m, &load5m, &load15m, true) < 0) + return -1; + +#define VALIDATE_LOAD(value_, expected_) \ + do { \ + if (value_ != expected_) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + "Expected " #value_ " '%.11f', got '%.11f'", \ + expected_, value_); \ + return -1; \ + } \ + } while (0) + + VALIDATE_LOAD(load1m, 0.00390625); + VALIDATE_LOAD(load5m, 0.064453125); + VALIDATE_LOAD(load15m, 0.03564453125); + + return 0; +} + + static int mymain(void) { @@ -1392,6 +1444,7 @@ mymain(void) DO_TEST(Timezone); DO_TEST(SSHKeys); DO_TEST(GetDisks); + DO_TEST(GetLoadAvg); DO_TEST(Timeout); /* Timeout should always be called last */ -- 2.48.1

On Tue, Feb 25, 2025 at 11:51:28 +0100, Martin Kletzander wrote:
Since this is already in QEMU we can add this stat which might be useful for people.
Resolves: https://issues.redhat.com/browse/RHEL-71883 Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/manpages/virsh.rst | 3 +++ src/libvirt-domain.c | 3 +++ src/qemu/qemu_agent.c | 59 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 +++++ src/qemu/qemu_driver.c | 32 ++++++++++++++++++++++ tests/qemuagenttest.c | 53 ++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 06c2802b3f9f..60b556cb0cd0 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2408,6 +2408,9 @@ When selecting the *--state* group the following fields are returned: for bank <index> in cache monitor <num> * ``cpu.cache.monitor.<num>.bank.<index>.bytes`` - the number of bytes of last level cache that the domain is using on cache bank <index> +* ``cpu.load.1m`` - average load in guest for last 1 minute +* ``cpu.load.5m`` - average load in guest for last 5 minutes +* ``cpu.load.15m`` - average load in guest for last 15 minutes
*--balloon* returns: diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc3225579..35f5e926e52b 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12272,6 +12272,9 @@ virConnectGetDomainCapabilities(virConnectPtr conn, * last level cache that the * domain is using on cache * bank <index> + * "cpu.load.1m" - average load in guest for last 1 minute + * "cpu.load.5m" - average load in guest for last 5 minutes + * "cpu.load.15m" - average load in guest for last 15 minutes
Data fetched via the guest agent doesn't belong in virConnectGetAllDomainStats but rather into virDomainGetGuestInfo. Also you should mention which typed parameter type is returned.
* * VIR_DOMAIN_STATS_BALLOON: * Return memory balloon device information. diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 43fca86f10ed..6dbe8c52d738 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -2568,3 +2568,62 @@ int qemuAgentGetDisks(qemuAgent *agent, g_free(*disks); return -1; } + + +int +qemuAgentGetLoadAvg(qemuAgent *agent, + double *load1m, + double *load5m, + double *load15m, + bool report_unsupported) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + virJSONValue *data = NULL; + int rc; + + if (load1m) + *load1m = 0; + + if (load5m) + *load5m = 0; + + if (load15m) + *load15m = 0; + + if (!(cmd = qemuAgentMakeCommand("guest-get-load", NULL))) + return -1; + + if ((rc = qemuAgentCommandFull(agent, cmd, &reply, agent->timeout, + report_unsupported)) < 0) + return rc; + + if (!(data = virJSONValueObjectGetObject(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent didn't return an array of loads")); + return -1; + } + + if (load1m && + virJSONValueObjectGetNumberDouble(data, "load1m", load1m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load1m' missing in reply of guest-get-load")); + return -1; + } + + if (load5m && + virJSONValueObjectGetNumberDouble(data, "load5m", load5m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load5m' missing in reply of guest-get-load")); + return -1; + } + + if (load15m && + virJSONValueObjectGetNumberDouble(data, "load15m", load15m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load15m' missing in reply of guest-get-load"));
Is there value having this as a translatable string for each entry?
+ return -1; + } + + return 0; +}

On Tue, Feb 25, 2025 at 12:11:21PM +0100, Peter Krempa wrote:
On Tue, Feb 25, 2025 at 11:51:28 +0100, Martin Kletzander wrote:
Since this is already in QEMU we can add this stat which might be useful for people.
Resolves: https://issues.redhat.com/browse/RHEL-71883 Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/manpages/virsh.rst | 3 +++ src/libvirt-domain.c | 3 +++ src/qemu/qemu_agent.c | 59 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 +++++ src/qemu/qemu_driver.c | 32 ++++++++++++++++++++++ tests/qemuagenttest.c | 53 ++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 06c2802b3f9f..60b556cb0cd0 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2408,6 +2408,9 @@ When selecting the *--state* group the following fields are returned: for bank <index> in cache monitor <num> * ``cpu.cache.monitor.<num>.bank.<index>.bytes`` - the number of bytes of last level cache that the domain is using on cache bank <index> +* ``cpu.load.1m`` - average load in guest for last 1 minute +* ``cpu.load.5m`` - average load in guest for last 5 minutes +* ``cpu.load.15m`` - average load in guest for last 15 minutes
*--balloon* returns: diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc3225579..35f5e926e52b 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12272,6 +12272,9 @@ virConnectGetDomainCapabilities(virConnectPtr conn, * last level cache that the * domain is using on cache * bank <index> + * "cpu.load.1m" - average load in guest for last 1 minute + * "cpu.load.5m" - average load in guest for last 5 minutes + * "cpu.load.15m" - average load in guest for last 15 minutes
Data fetched via the guest agent doesn't belong in virConnectGetAllDomainStats but rather into virDomainGetGuestInfo.
I wasn't sure and thanks for pointing this out. I'll gladly change it, it seemed weird in domstats.
Also you should mention which typed parameter type is returned.
Good point.
* * VIR_DOMAIN_STATS_BALLOON: * Return memory balloon device information. diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 43fca86f10ed..6dbe8c52d738 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -2568,3 +2568,62 @@ int qemuAgentGetDisks(qemuAgent *agent, g_free(*disks); return -1; } + + +int +qemuAgentGetLoadAvg(qemuAgent *agent, + double *load1m, + double *load5m, + double *load15m, + bool report_unsupported) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + virJSONValue *data = NULL; + int rc; + + if (load1m) + *load1m = 0; + + if (load5m) + *load5m = 0; + + if (load15m) + *load15m = 0; + + if (!(cmd = qemuAgentMakeCommand("guest-get-load", NULL))) + return -1; + + if ((rc = qemuAgentCommandFull(agent, cmd, &reply, agent->timeout, + report_unsupported)) < 0) + return rc; + + if (!(data = virJSONValueObjectGetObject(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent didn't return an array of loads")); + return -1; + } + + if (load1m && + virJSONValueObjectGetNumberDouble(data, "load1m", load1m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load1m' missing in reply of guest-get-load")); + return -1; + } + + if (load5m && + virJSONValueObjectGetNumberDouble(data, "load5m", load5m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load5m' missing in reply of guest-get-load")); + return -1; + } + + if (load15m && + virJSONValueObjectGetNumberDouble(data, "load15m", load15m) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'load15m' missing in reply of guest-get-load"));
Is there value having this as a translatable string for each entry?
I thought I changed it to a macro, but that was the tests. I'll fix it up to also avoid duplication.
+ return -1; + } + + return 0; +}
participants (2)
-
Martin Kletzander
-
Peter Krempa