From 95f5ac9ae52455e9da47afc95fa31c9456ac27ae Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Wed, 13 Nov 2019 16:06:09 -0600 Subject: [PATCH] Add API to change qemu agent response timeout Some layered products such as oVirt have requested a way to avoid being blocked by guest agent commands when querying a loaded vm. For example, many guest agent commands are polled periodically to monitor changes, and rather than blocking the calling process, they'd prefer to simply time out when an agent query is taking too long. This patch adds a way for the user to specify a custom agent timeout that is applied to all agent commands. One special case to note here is the 'guest-sync' command. 'guest-sync' is issued internally prior to calling any other command. (For example, when libvirt wants to call 'guest-get-fsinfo', we first call 'guest-sync' and then call 'guest-get-fsinfo'). Previously, the 'guest-sync' command used a 5-second timeout (VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT), whereas the actual command that followed always blocked indefinitely (VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK). As part of this patch, if a custom timeout is specified that is shorter than 5 seconds, this new timeout is also used for 'guest-sync'. If there is no custom timeout or if the custom timeout is longer than 5 seconds, we will continue to use the 5-second timeout. Signed-off-by: Jonathon Jongsma Signed-off-by: Michal Privoznik Reviewed-by: Michal Privoznik --- include/libvirt/libvirt-domain.h | 10 +++ include/libvirt/libvirt-qemu.h | 8 +-- src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 49 +++++++++++++ src/libvirt_public.syms | 5 ++ src/qemu/qemu_agent.c | 70 ++++++++++--------- src/qemu/qemu_agent.h | 3 + src/qemu/qemu_domain.c | 10 +++ src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 56 +++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 ++++- src/remote_protocol-structs | 9 +++ .../blockjob-blockdev-in.xml | 1 + .../blockjob-mirror-in.xml | 1 + .../disk-secinfo-upgrade-out.xml | 1 + .../migration-in-params-in.xml | 1 + .../migration-out-nbd-out.xml | 1 + .../migration-out-nbd-tls-out.xml | 1 + .../migration-out-params-in.xml | 1 + tests/qemustatusxml2xmldata/modern-in.xml | 1 + .../qemustatusxml2xmldata/vcpus-multi-in.xml | 1 + tools/virsh-domain.c | 52 ++++++++++++++ tools/virsh.pod | 11 +++ 24 files changed, 281 insertions(+), 37 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 22277b0a84..a2f007568c 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4916,4 +4916,14 @@ int virDomainGetGuestInfo(virDomainPtr domain, int *nparams, unsigned int flags); +typedef enum { + VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK = -2, + VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_DEFAULT = -1, + VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_NOWAIT = 0, +} virDomainAgentResponseTimeoutValues; + +int virDomainAgentSetResponseTimeout(virDomainPtr domain, + int timeout, + unsigned int flags); + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 891617443f..0cc2872821 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -43,10 +43,10 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int flags); typedef enum { - VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN = -2, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = -2, - VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT = -1, - VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0, + VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK, + VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_DEFAULT, + VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_NOWAIT, VIR_DOMAIN_QEMU_AGENT_COMMAND_SHUTDOWN = 60, } virDomainQemuAgentCommandTimeoutValues; diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 015b2cd01c..4afd8f6ec5 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1372,6 +1372,11 @@ typedef int int *nparams, unsigned int flags); +typedef int +(*virDrvDomainAgentSetResponseTimeout)(virDomainPtr domain, + int timeout, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1632,4 +1637,5 @@ struct _virHypervisorDriver { virDrvDomainCheckpointGetParent domainCheckpointGetParent; virDrvDomainCheckpointDelete domainCheckpointDelete; virDrvDomainGetGuestInfo domainGetGuestInfo; + virDrvDomainAgentSetResponseTimeout domainAgentSetResponseTimeout; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index dcab179e6e..02622cb2ca 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12497,3 +12497,52 @@ int virDomainGetLaunchSecurityInfo(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainAgentSetResponseTimeout: + * @domain: a domain object + * @timeout: timeout in seconds + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Set how long to wait for a response from guest agent commands. By default, + * agent commands block forever waiting for a response. + * + * @timeout must be a value from virDomainAgentCommandTimeoutValues or + * positive: + * + * VIR_DOMAIN_AGENT_COMMAND_TIMEOUT_BLOCK(-2): meaning to block forever + * waiting for a result. + * VIR_DOMAIN_AGENT_COMMAND_TIMEOUT_DEFAULT(-1): use default timeout value. + * VIR_DOMAIN_AGENT_COMMAND_TIMEOUT_NOWAIT(0): does not wait. + * positive value: wait for @timeout seconds + * + * Returns 0 on success, -1 on failure + */ +int +virDomainAgentSetResponseTimeout(virDomainPtr domain, + int timeout, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "timeout=%i, flags=0x%x", + timeout, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + + if (conn->driver->domainAgentSetResponseTimeout) { + if (conn->driver->domainAgentSetResponseTimeout(domain, timeout, flags) < 0) + goto error; + return 0; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index f88a77892f..c92f083758 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -862,4 +862,9 @@ LIBVIRT_5.8.0 { virConnectSetIdentity; } LIBVIRT_5.7.0; +LIBVIRT_5.10.0 { + global: + virDomainAgentSetResponseTimeout; +} LIBVIRT_5.8.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index 5a50f7f3be..7905a74f90 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -30,6 +30,7 @@ #include #include "qemu_agent.h" +#include "qemu_domain.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" @@ -127,6 +128,7 @@ struct _qemuAgent { * but fire up an event on qemu monitor instead. * Take that as indication of successful completion */ qemuAgentEvent await_event; + int timeout; }; static virClassPtr qemuAgentClass; @@ -695,6 +697,7 @@ qemuAgentOpen(virDomainObjPtr vm, if (!(mon = virObjectLockableNew(qemuAgentClass))) return NULL; + mon->timeout = QEMU_DOMAIN_PRIVATE(vm)->agentTimeout; mon->fd = -1; if (virCondInit(&mon->notify) < 0) { virReportSystemError(errno, "%s", @@ -907,6 +910,12 @@ qemuAgentGuestSync(qemuAgentPtr mon) int send_ret; unsigned long long id; qemuAgentMessage sync_msg; + int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; + + /* if user specified a custom agent timeout that is lower than the + * default timeout, use the shorter timeout instead */ + if ((mon->timeout >= 0) && (mon->timeout < timeout)) + timeout = mon->timeout; memset(&sync_msg, 0, sizeof(sync_msg)); /* set only on first sync */ @@ -925,8 +934,7 @@ qemuAgentGuestSync(qemuAgentPtr mon) VIR_DEBUG("Sending guest-sync command with ID: %llu", id); - send_ret = qemuAgentSend(mon, &sync_msg, - VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT); + send_ret = qemuAgentSend(mon, &sync_msg, timeout); VIR_DEBUG("qemuAgentSend returned: %d", send_ret); @@ -1301,8 +1309,7 @@ int qemuAgentFSFreeze(qemuAgentPtr mon, const char **mountpoints, if (!cmd) goto cleanup; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) { @@ -1339,8 +1346,7 @@ int qemuAgentFSThaw(qemuAgentPtr mon) if (!cmd) return -1; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) { @@ -1377,8 +1383,7 @@ qemuAgentSuspend(qemuAgentPtr mon, return -1; mon->await_event = QEMU_AGENT_EVENT_SUSPEND; - ret = qemuAgentCommand(mon, cmd, &reply, false, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK); + ret = qemuAgentCommand(mon, cmd, &reply, false, mon->timeout); virJSONValueFree(cmd); virJSONValueFree(reply); @@ -1434,8 +1439,7 @@ qemuAgentFSTrim(qemuAgentPtr mon, if (!cmd) return ret; - ret = qemuAgentCommand(mon, cmd, &reply, false, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK); + ret = qemuAgentCommand(mon, cmd, &reply, false, mon->timeout); virJSONValueFree(cmd); virJSONValueFree(reply); @@ -1456,8 +1460,7 @@ qemuAgentGetVCPUs(qemuAgentPtr mon, if (!(cmd = qemuAgentMakeCommand("guest-get-vcpus", NULL))) return -1; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; if (!(data = virJSONValueObjectGetArray(reply, "return"))) { @@ -1572,8 +1575,7 @@ qemuAgentSetVCPUsCommand(qemuAgentPtr mon, NULL))) goto cleanup; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; /* All negative values are invalid. Return of 0 is bogus since we wouldn't @@ -1728,8 +1730,7 @@ qemuAgentGetHostname(qemuAgentPtr mon, if (!cmd) return ret; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) { + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) { if (qemuAgentErrorCommandUnsupported(reply)) ret = -2; goto cleanup; @@ -1773,8 +1774,7 @@ qemuAgentGetTime(qemuAgentPtr mon, if (!cmd) return ret; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) { @@ -1839,8 +1839,7 @@ qemuAgentSetTime(qemuAgentPtr mon, if (!cmd) return ret; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; ret = 0; @@ -2043,8 +2042,7 @@ qemuAgentGetFSInfoInternal(qemuAgentPtr mon, if (!cmd) return ret; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) { + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) { if (qemuAgentErrorCommandUnsupported(reply)) ret = -2; goto cleanup; @@ -2333,8 +2331,7 @@ qemuAgentGetInterfaces(qemuAgentPtr mon, if (!(cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL))) goto cleanup; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; if (!(ret_array = virJSONValueObjectGet(reply, "return"))) { @@ -2511,8 +2508,7 @@ qemuAgentSetUserPassword(qemuAgentPtr mon, NULL))) goto cleanup; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) goto cleanup; ret = 0; @@ -2543,8 +2539,7 @@ qemuAgentGetUsers(qemuAgentPtr mon, if (!(cmd = qemuAgentMakeCommand("guest-get-users", NULL))) return -1; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) { + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) { if (qemuAgentErrorCommandUnsupported(reply)) return -2; return -1; @@ -2633,8 +2628,7 @@ qemuAgentGetOSInfo(qemuAgentPtr mon, if (!(cmd = qemuAgentMakeCommand("guest-get-osinfo", NULL))) return -1; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) { + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) { if (qemuAgentErrorCommandUnsupported(reply)) return -2; return -1; @@ -2689,8 +2683,7 @@ qemuAgentGetTimezone(qemuAgentPtr mon, if (!(cmd = qemuAgentMakeCommand("guest-get-timezone", NULL))) return -1; - if (qemuAgentCommand(mon, cmd, &reply, true, - VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0) { + if (qemuAgentCommand(mon, cmd, &reply, true, mon->timeout) < 0) { if (qemuAgentErrorCommandUnsupported(reply)) return -2; return -1; @@ -2719,3 +2712,16 @@ qemuAgentGetTimezone(qemuAgentPtr mon, return 0; } + +/* qemuAgentSetResponseTimeout: + * mon: agent monitor + * timeout: number of seconds to wait for agent response + * + * The agent object must be locked prior to calling this function. + */ +void +qemuAgentSetResponseTimeout(qemuAgentPtr mon, + int timeout) +{ + mon->timeout = timeout; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index 78e648992a..85e436cf68 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -140,3 +140,6 @@ int qemuAgentGetTimezone(qemuAgentPtr mon, virTypedParameterPtr *params, int *nparams, int *maxparams); + +void qemuAgentSetResponseTimeout(qemuAgentPtr mon, + int timeout); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ace3761e20..e14b414518 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2093,6 +2093,8 @@ qemuDomainObjPrivateAlloc(void *opaque) if (!(priv->dbusVMStates = virHashCreate(5, dbusVMStateHashFree))) goto error; + /* agent commands block by default, user can choose different behavior */ + priv->agentTimeout = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK; priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX; priv->driver = opaque; @@ -2875,6 +2877,8 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, if (qemuDomainObjPrivateXMLFormatSlirp(buf, vm) < 0) return -1; + virBufferAsprintf(buf, "%i\n", priv->agentTimeout); + return 0; } @@ -3518,6 +3522,12 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, goto error; } + if (virXPathInt("string(./agentTimeout)", ctxt, &priv->agentTimeout) == -2) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse agent timeout")); + goto error; + } + if ((node = virXPathNode("./namespaces", ctxt))) { xmlNodePtr next; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index ab00b25789..98a9540275 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -293,6 +293,7 @@ struct _qemuDomainObjPrivate { virDomainChrSourceDefPtr monConfig; bool monError; unsigned long long monStart; + int agentTimeout; qemuAgentPtr agent; bool agentError; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index bb75fe5807..2f06217a5f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -22685,6 +22685,61 @@ qemuDomainGetGuestInfo(virDomainPtr dom, return ret; } + +static int +qemuDomainAgentSetResponseTimeout(virDomainPtr dom, + int timeout, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virDomainObjPtr vm = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + if (timeout < VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN) { + virReportError(VIR_ERR_INVALID_ARG, + _("guest agent timeout '%d' is " + "less than the minimum '%d'"), + timeout, VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN); + return -1; + } + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + cfg = virQEMUDriverGetConfig(driver); + + if (virDomainAgentSetResponseTimeoutEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + /* If domain has an agent, change its timeout. Otherwise just save the + * request so that we can set the timeout when the agent appears */ + if (qemuDomainAgentAvailable(vm, false)) { + /* We don't need to acquire a job since we're not interacting with the + * agent or the qemu monitor. We're only setting a struct member, so + * just acquire the mutex lock. Worst case, any in-process agent + * commands will use the newly-set agent timeout. */ + virObjectLock(QEMU_DOMAIN_PRIVATE(vm)->agent); + qemuAgentSetResponseTimeout(QEMU_DOMAIN_PRIVATE(vm)->agent, timeout); + virObjectUnlock(QEMU_DOMAIN_PRIVATE(vm)->agent); + } + + QEMU_DOMAIN_PRIVATE(vm)->agentTimeout = timeout; + + if (virDomainObjIsActive(vm) && + virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -22921,6 +22976,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainCheckpointGetParent = qemuDomainCheckpointGetParent, /* 5.6.0 */ .domainCheckpointDelete = qemuDomainCheckpointDelete, /* 5.6.0 */ .domainGetGuestInfo = qemuDomainGetGuestInfo, /* 5.7.0 */ + .domainAgentSetResponseTimeout = qemuDomainAgentSetResponseTimeout, /* 5.10.0 */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 503f49a902..a1384fc655 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8701,6 +8701,7 @@ static virHypervisorDriver hypervisor_driver = { .domainCheckpointGetParent = remoteDomainCheckpointGetParent, /* 5.6.0 */ .domainCheckpointDelete = remoteDomainCheckpointDelete, /* 5.6.0 */ .domainGetGuestInfo = remoteDomainGetGuestInfo, /* 5.7.0 */ + .domainAgentSetResponseTimeout = remoteDomainAgentSetResponseTimeout, /* 5.10.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f4e3392212..23e42d17b1 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3744,6 +3744,16 @@ struct remote_connect_set_identity_args { unsigned int flags; }; +struct remote_domain_agent_set_response_timeout_args { + remote_nonnull_domain dom; + int timeout; + unsigned int flags; +}; + +struct remote_domain_agent_set_response_timeout_ret { + int result; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6617,5 +6627,11 @@ enum remote_procedure { * @generate: client * @acl: connect:write */ - REMOTE_PROC_CONNECT_SET_IDENTITY = 419 + REMOTE_PROC_CONNECT_SET_IDENTITY = 419, + + /** + * @generate: both + * @acl: domain:write + */ + REMOTE_PROC_DOMAIN_AGENT_SET_RESPONSE_TIMEOUT = 420 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 51606e7473..9ad7a857e0 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -3114,6 +3114,14 @@ struct remote_connect_set_identity_args { } params; u_int flags; }; +struct remote_domain_agent_set_response_timeout_args { + remote_nonnull_domain dom; + int timeout; + u_int flags; +}; +struct remote_domain_agent_set_response_timeout_ret { + int result; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3534,4 +3542,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_CHECKPOINT_DELETE = 417, REMOTE_PROC_DOMAIN_GET_GUEST_INFO = 418, REMOTE_PROC_CONNECT_SET_IDENTITY = 419, + REMOTE_PROC_DOMAIN_AGENT_SET_RESPONSE_TIMEOUT = 420, }; diff --git a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml index e9031b7087..4f6930001e 100644 --- a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml +++ b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml @@ -338,6 +338,7 @@ + -2 copy 0439a4a8-db56-4933-9183-d8681d7b0746 diff --git a/tests/qemustatusxml2xmldata/blockjob-mirror-in.xml b/tests/qemustatusxml2xmldata/blockjob-mirror-in.xml index 0fba3b69e7..abd9f62965 100644 --- a/tests/qemustatusxml2xmldata/blockjob-mirror-in.xml +++ b/tests/qemustatusxml2xmldata/blockjob-mirror-in.xml @@ -23,6 +23,7 @@ + -2 QEMUGuest1 c7a5fdbd-edaf-9455-926a-d65c16db1809 diff --git a/tests/qemustatusxml2xmldata/disk-secinfo-upgrade-out.xml b/tests/qemustatusxml2xmldata/disk-secinfo-upgrade-out.xml index 3d5e8f1438..5d3287606f 100644 --- a/tests/qemustatusxml2xmldata/disk-secinfo-upgrade-out.xml +++ b/tests/qemustatusxml2xmldata/disk-secinfo-upgrade-out.xml @@ -258,6 +258,7 @@ + -2 upstream dcf47dbd-46d1-4d5b-b442-262a806a333a diff --git a/tests/qemustatusxml2xmldata/migration-in-params-in.xml b/tests/qemustatusxml2xmldata/migration-in-params-in.xml index 80cc4b4666..fdc2d39173 100644 --- a/tests/qemustatusxml2xmldata/migration-in-params-in.xml +++ b/tests/qemustatusxml2xmldata/migration-in-params-in.xml @@ -257,6 +257,7 @@ + -2 nest 994cee0d-2a70-4937-9693-0431e39d20f7 diff --git a/tests/qemustatusxml2xmldata/migration-out-nbd-out.xml b/tests/qemustatusxml2xmldata/migration-out-nbd-out.xml index 455c30be85..304fb1b77f 100644 --- a/tests/qemustatusxml2xmldata/migration-out-nbd-out.xml +++ b/tests/qemustatusxml2xmldata/migration-out-nbd-out.xml @@ -260,6 +260,7 @@ + -2 upstream dcf47dbd-46d1-4d5b-b442-262a806a333a diff --git a/tests/qemustatusxml2xmldata/migration-out-nbd-tls-out.xml b/tests/qemustatusxml2xmldata/migration-out-nbd-tls-out.xml index 409e97a918..d69796e029 100644 --- a/tests/qemustatusxml2xmldata/migration-out-nbd-tls-out.xml +++ b/tests/qemustatusxml2xmldata/migration-out-nbd-tls-out.xml @@ -289,6 +289,7 @@ + -2 upstream dcf47dbd-46d1-4d5b-b442-262a806a333a diff --git a/tests/qemustatusxml2xmldata/migration-out-params-in.xml b/tests/qemustatusxml2xmldata/migration-out-params-in.xml index 4a660281d2..1956eac120 100644 --- a/tests/qemustatusxml2xmldata/migration-out-params-in.xml +++ b/tests/qemustatusxml2xmldata/migration-out-params-in.xml @@ -271,6 +271,7 @@ + -2 nest 994cee0d-2a70-4937-9693-0431e39d20f7 diff --git a/tests/qemustatusxml2xmldata/modern-in.xml b/tests/qemustatusxml2xmldata/modern-in.xml index 2125b899f4..8a2718293f 100644 --- a/tests/qemustatusxml2xmldata/modern-in.xml +++ b/tests/qemustatusxml2xmldata/modern-in.xml @@ -261,6 +261,7 @@ + -2 upstream dcf47dbd-46d1-4d5b-b442-262a806a333a diff --git a/tests/qemustatusxml2xmldata/vcpus-multi-in.xml b/tests/qemustatusxml2xmldata/vcpus-multi-in.xml index b8ec650714..11ec74ecf8 100644 --- a/tests/qemustatusxml2xmldata/vcpus-multi-in.xml +++ b/tests/qemustatusxml2xmldata/vcpus-multi-in.xml @@ -309,6 +309,7 @@ + -2 QEMUGuest1 c7a5fdbd-edaf-9455-926a-d65c16db1809 diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 0feb21ef17..cfdaec1673 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13971,6 +13971,52 @@ cmdDomFSInfo(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "guest-agent-timeout" command + */ +static const vshCmdInfo info_guest_agent_timeout[] = { + {.name = "help", + .data = N_("Set the guest agent timeout") + }, + {.name = "desc", + .data = N_("Set the number of seconds to wait for a response from the guest agent.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_guest_agent_timeout[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "timeout", + .type = VSH_OT_INT, + .flags = VSH_OFLAG_REQ_OPT, + .help = N_("timeout seconds.") + }, + {.name = NULL} +}; + +static bool +cmdGuestAgentTimeout(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int timeout; + const unsigned int flags = 0; + bool ret = false; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptInt(ctl, cmd, "timeout", &timeout) < 0) + goto cleanup; + + if (virDomainAgentSetResponseTimeout(dom, timeout, flags) < 0) + goto cleanup; + + ret = true; + cleanup: + virshDomainFree(dom); + return ret; +} + /* * "guestinfo" command */ @@ -14492,6 +14538,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_qemu_agent_command, .flags = 0 }, + {.name = "guest-agent-timeout", + .handler = cmdGuestAgentTimeout, + .opts = opts_guest_agent_timeout, + .info = info_guest_agent_timeout, + .flags = 0 + }, {.name = "reboot", .handler = cmdReboot, .opts = opts_reboot, diff --git a/tools/virsh.pod b/tools/virsh.pod index cf2798e71a..c261d403d8 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1820,6 +1820,17 @@ events until a timeout or interrupt key. When I<--timestamp> is used, a human-readable timestamp will be printed before the event. +=item B I I<--timeout> B + +Set how long to wait for a response from guest agent commands. By default, +agent commands block forever waiting for a response. B must be a +positive value (wait for given amount of seconds) or one of the following +values: + + -2 - block forever waiting for a resuly, + -1 - reset timeout to the default value, + 0 - do not wait at all, + =item B I [I<--user>] [I<--os>] [I<--timezone>] [I<--hostname>] [I<--filesystem>]