[PATCH v2 0/4] Add support for getting load averages from guest agent

I'm drawing blanks when it comes to commit messages today. But this still has time, because even though the patches are in QEMU upstream, but not a part of a release yet. However nothing should break if we add support earlier... Right? O:-) v2: - split patches - add it to guestinfo instead of domstats - some more macro deduplication Martin Kletzander (4): Add load average information type into virDomainGetGuestInfo qemu_agent: Add qemuAgentGetLoadAvg() qemu: Add support for VIR_DOMAIN_GUEST_INFO_LOAD virsh: Add support for VIR_DOMAIN_GUEST_INFO_LOAD docs/manpages/virsh.rst | 8 ++++- include/libvirt/libvirt-domain.h | 1 + src/libvirt-domain.c | 8 +++++ src/qemu/qemu_agent.c | 55 ++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 ++++ src/qemu/qemu_driver.c | 21 +++++++++++- tests/qemuagenttest.c | 53 ++++++++++++++++++++++++++++++ tools/virsh-domain.c | 6 ++++ 8 files changed, 156 insertions(+), 2 deletions(-) -- 2.48.1

The public API part. Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- include/libvirt/libvirt-domain.h | 1 + src/libvirt-domain.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index f5420bca6e23..2d27f96be94d 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -6455,6 +6455,7 @@ typedef enum { VIR_DOMAIN_GUEST_INFO_FILESYSTEM = (1 << 4), /* return filesystem information (Since: 5.7.0) */ VIR_DOMAIN_GUEST_INFO_DISKS = (1 << 5), /* return disks information (Since: 7.0.0) */ VIR_DOMAIN_GUEST_INFO_INTERFACES = (1 << 6), /* return interfaces information (Since: 7.10.0) */ + VIR_DOMAIN_GUEST_INFO_LOAD = (1 << 7), /* return load averages (Since: 11.2.0) */ } virDomainGuestInfoTypes; int virDomainGetGuestInfo(virDomainPtr domain, diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc3225579..ae15ab1e5a94 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -13292,6 +13292,14 @@ virDomainSetVcpu(virDomainPtr domain, * "if.<num>.addr.<num1>.addr" - the IP address of addr <num1> * "if.<num>.addr.<num1>.prefix" - the prefix of IP address of addr <num1> * + * VIR_DOMAIN_GUEST_INFO_LOAD: + * Returns load (the number of processes in the runqueue or waiting for disk + * I/O) as double values: + * + * "load.1m" - load averaged over 1 minute + * "load.5m" - load averaged over 5 minutes + * "load.15m" - load averaged over 15 minutes + * * Using 0 for @types returns all information groups supported by the given * hypervisor. * -- 2.48.1

With qemu guest agent 9.3 we are able to get the load averages with a new command. Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- src/qemu/qemu_agent.c | 55 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 +++++ tests/qemuagenttest.c | 53 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 43fca86f10ed..27efb4b389ee 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -2568,3 +2568,58 @@ 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; + } + +#define GET_NUMBER_PARAM(param_) \ + do { \ + if (param_ && \ + virJSONValueObjectGetNumberDouble(data, #param_, param_) < 0) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + _("'%1$s' missing in reply of guest-get-load"), \ + #param_); \ + return -1; \ + } \ + } while (0) + + GET_NUMBER_PARAM(load1m); + GET_NUMBER_PARAM(load5m); + GET_NUMBER_PARAM(load15m); + +#undef GET_NUMBER_PARAM + + 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/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 15:52:30 +0100, Martin Kletzander wrote:
With qemu guest agent 9.3 we are able to get the load averages with a new command.
Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- src/qemu/qemu_agent.c | 55 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 6 +++++ tests/qemuagenttest.c | 53 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+)
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 43fca86f10ed..27efb4b389ee 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -2568,3 +2568,58 @@ 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;
Clearing these is pointless ...
+ + 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; + } + +#define GET_NUMBER_PARAM(param_) \ + do { \ + if (param_ && \ + virJSONValueObjectGetNumberDouble(data, #param_, param_) < 0) { \
... as they are unconditionally overwritten ...
+ virReportError(VIR_ERR_INTERNAL_ERROR, \ + _("'%1$s' missing in reply of guest-get-load"), \
How about: parameter '%1%s' missing ...
+ #param_); \ + return -1; \
... or error is returned.
+ } \ + } while (0) + + GET_NUMBER_PARAM(load1m); + GET_NUMBER_PARAM(load5m); + GET_NUMBER_PARAM(load15m);

Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- src/qemu/qemu_driver.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 80c918312b8f..3b8b2c0a1eda 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19181,7 +19181,8 @@ static const unsigned int qemuDomainGetGuestInfoSupportedTypes = VIR_DOMAIN_GUEST_INFO_HOSTNAME | VIR_DOMAIN_GUEST_INFO_FILESYSTEM | VIR_DOMAIN_GUEST_INFO_DISKS | - VIR_DOMAIN_GUEST_INFO_INTERFACES; + VIR_DOMAIN_GUEST_INFO_INTERFACES | + VIR_DOMAIN_GUEST_INFO_LOAD; static int qemuDomainGetGuestInfoCheckSupport(unsigned int types, @@ -19468,6 +19469,10 @@ qemuDomainGetGuestInfo(virDomainPtr dom, qemuAgentDiskInfo **agentdiskinfo = NULL; virDomainInterfacePtr *ifaces = NULL; size_t nifaces = 0; + double load1m = 0; + double load5m = 0; + double load15m = 0; + bool format_load = false; size_t i; virCheckFlags(0, -1); @@ -19538,6 +19543,14 @@ qemuDomainGetGuestInfo(virDomainPtr dom, nifaces = rc; } + if (supportedTypes & VIR_DOMAIN_GUEST_INFO_LOAD) { + rc = qemuAgentGetLoadAvg(agent, &load1m, &load5m, &load15m, report_unsupported); + if (rc == -1) + goto exitagent; + if (rc >= 0) + format_load = true; + } + qemuDomainObjExitAgent(vm, agent); virDomainObjEndAgentJob(vm); @@ -19564,6 +19577,12 @@ qemuDomainGetGuestInfo(virDomainPtr dom, virDomainInterfaceFormatParams(ifaces, nifaces, params, nparams, &maxparams); } + if (format_load) { + virTypedParamsAddDouble(params, nparams, &maxparams, "load.1m", load1m); + virTypedParamsAddDouble(params, nparams, &maxparams, "load.5m", load5m); + virTypedParamsAddDouble(params, nparams, &maxparams, "load.15m", load15m); + } + ret = 0; cleanup: -- 2.48.1

Resolves: https://issues.redhat.com/browse/RHEL-71883 Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/manpages/virsh.rst | 8 +++++++- tools/virsh-domain.c | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 06c2802b3f9f..f159c40631ec 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2930,7 +2930,7 @@ Success is always reported in this case. You can limit the types of information that are returned by specifying one or more flags. Available information types flags are *--user*, *--os*, -*--timezone*, *--hostname*, *--filesystem*, *--disk* and *--interface*. +*--timezone*, *--hostname*, *--filesystem*, *--disk*, *--interface* and *--load*. If an explicitly requested information type is not supported by the guest agent at that point, the processes will provide an exit code of 1. @@ -3009,6 +3009,12 @@ returned: * ``if.<num>.addr.<num1>.addr`` - the IP address of addr <num1> * ``if.<num>.addr.<num1>.prefix`` - the prefix of IP address of addr <num1> +*--load* returns: +* ``load.1m`` - average load in guest for last 1 minute +* ``load.5m`` - average load in guest for last 5 minutes +* ``load.15m`` - average load in guest for last 15 minutes + + guestvcpus ---------- diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index cc5ae6053606..93c34c497133 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13095,6 +13095,10 @@ static const vshCmdOptDef opts_guestinfo[] = { .type = VSH_OT_BOOL, .help = N_("report interface information"), }, + {.name = "load", + .type = VSH_OT_BOOL, + .help = N_("report load averages information"), + }, {.name = NULL} }; @@ -13122,6 +13126,8 @@ cmdGuestInfo(vshControl *ctl, const vshCmd *cmd) types |= VIR_DOMAIN_GUEST_INFO_DISKS; if (vshCommandOptBool(cmd, "interface")) types |= VIR_DOMAIN_GUEST_INFO_INTERFACES; + if (vshCommandOptBool(cmd, "load")) + types |= VIR_DOMAIN_GUEST_INFO_LOAD; if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; -- 2.48.1

On Tue, Feb 25, 2025 at 15:52:28 +0100, Martin Kletzander wrote:
I'm drawing blanks when it comes to commit messages today. But this still has time, because even though the patches are in QEMU upstream, but not a part of a release yet. However nothing should break if we add support earlier... Right? O:-)
No it doesn't qualify to be pushed during freeze ;)
v2: - split patches - add it to guestinfo instead of domstats - some more macro deduplication
Martin Kletzander (4): Add load average information type into virDomainGetGuestInfo qemu_agent: Add qemuAgentGetLoadAvg() qemu: Add support for VIR_DOMAIN_GUEST_INFO_LOAD virsh: Add support for VIR_DOMAIN_GUEST_INFO_LOAD
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

On Tue, Feb 25, 2025 at 05:02:35PM +0100, Peter Krempa wrote:
On Tue, Feb 25, 2025 at 15:52:28 +0100, Martin Kletzander wrote:
I'm drawing blanks when it comes to commit messages today. But this still has time, because even though the patches are in QEMU upstream, but not a part of a release yet. However nothing should break if we add support earlier... Right? O:-)
No it doesn't qualify to be pushed during freeze ;)
I meant earlier as in "before it's released in qemu" not "during freeze", I even specifically mention 11.2.0 in the docs ;)
v2: - split patches - add it to guestinfo instead of domstats - some more macro deduplication
Martin Kletzander (4): Add load average information type into virDomainGetGuestInfo qemu_agent: Add qemuAgentGetLoadAvg() qemu: Add support for VIR_DOMAIN_GUEST_INFO_LOAD virsh: Add support for VIR_DOMAIN_GUEST_INFO_LOAD
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Thanks, I'll fixup what you suggested and wait for after the release.

On Tue, Feb 25, 2025 at 17:17:46 +0100, Martin Kletzander wrote:
On Tue, Feb 25, 2025 at 05:02:35PM +0100, Peter Krempa wrote:
On Tue, Feb 25, 2025 at 15:52:28 +0100, Martin Kletzander wrote:
I'm drawing blanks when it comes to commit messages today. But this still has time, because even though the patches are in QEMU upstream, but not a part of a release yet. However nothing should break if we add support earlier... Right? O:-)
No it doesn't qualify to be pushed during freeze ;)
I meant earlier as in "before it's released in qemu" not "during freeze", I even specifically mention 11.2.0 in the docs ;)
You see cover-letters are pointless because nobody reads them properly. Anyways we do integration with pushed-but-not-yet-released qemu stuff all the time so there's definitely precedent. And stuff gets changed; libvirt can still adapt it without breaking compatibility.

On a Tuesday in 2025, Peter Krempa wrote:
On Tue, Feb 25, 2025 at 17:17:46 +0100, Martin Kletzander wrote:
On Tue, Feb 25, 2025 at 05:02:35PM +0100, Peter Krempa wrote:
On Tue, Feb 25, 2025 at 15:52:28 +0100, Martin Kletzander wrote:
I'm drawing blanks when it comes to commit messages today. But this still has time, because even though the patches are in QEMU upstream, but not a part of a release yet. However nothing should break if we add support earlier... Right? O:-)
No it doesn't qualify to be pushed during freeze ;)
I meant earlier as in "before it's released in qemu" not "during freeze", I even specifically mention 11.2.0 in the docs ;)
You see cover-letters are pointless because nobody reads them properly.
Anyways we do integration with pushed-but-not-yet-released qemu stuff all the time so there's definitely precedent. And stuff gets changed; libvirt can still adapt it without breaking compatibility.
I think that only happened in a handful of cases in the past. Also, there's still almost 5 weeks until the release of 11.2.0 so plenty of time to change stuff. Jano
participants (3)
-
Ján Tomko
-
Martin Kletzander
-
Peter Krempa