[libvirt] [RFC] Add API to change qemu agent response timeout

Michal Privoznik mprivozn at redhat.com
Fri Oct 4 09:48:11 UTC 2019


On 10/3/19 11:52 PM, Jonathon Jongsma wrote:
> 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 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.
> 
> See https://bugzilla.redhat.com/show_bug.cgi?id=1705426 for additional details.
> 
> Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> ---
>   include/libvirt/libvirt-qemu.h |  2 +
>   src/driver-hypervisor.h        |  5 +++
>   src/libvirt-qemu.c             | 40 ++++++++++++++++++++
>   src/libvirt_qemu.syms          |  4 ++
>   src/qemu/qemu_agent.c          | 69 +++++++++++++++++++---------------
>   src/qemu/qemu_agent.h          |  3 ++
>   src/qemu/qemu_driver.c         | 24 ++++++++++++
>   src/qemu_protocol-structs      |  8 ++++
>   src/remote/qemu_protocol.x     | 18 ++++++++-
>   src/remote/remote_driver.c     |  1 +
>   10 files changed, 143 insertions(+), 31 deletions(-)
> 
> diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h
> index 891617443f..8d3cc776e9 100644
> --- a/include/libvirt/libvirt-qemu.h
> +++ b/include/libvirt/libvirt-qemu.h
> @@ -53,6 +53,8 @@ typedef enum {
>   char *virDomainQemuAgentCommand(virDomainPtr domain, const char *cmd,
>                                   int timeout, unsigned int flags);
>   
> +int virDomainQemuAgentSetTimeout(virDomainPtr domain, int timeout);
> +
>   /**
>    * virConnectDomainQemuMonitorEventCallback:
>    * @conn: the connection pointer
> diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
> index 015b2cd01c..2f17bff844 100644
> --- a/src/driver-hypervisor.h
> +++ b/src/driver-hypervisor.h
> @@ -1372,6 +1372,10 @@ typedef int
>                               int *nparams,
>                               unsigned int flags);
>   
> +typedef int
> +(*virDrvDomainQemuAgentSetTimeout)(virDomainPtr domain,
> +                                   int timeout);
> +
>   typedef struct _virHypervisorDriver virHypervisorDriver;
>   typedef virHypervisorDriver *virHypervisorDriverPtr;
>   
> @@ -1632,4 +1636,5 @@ struct _virHypervisorDriver {
>       virDrvDomainCheckpointGetParent domainCheckpointGetParent;
>       virDrvDomainCheckpointDelete domainCheckpointDelete;
>       virDrvDomainGetGuestInfo domainGetGuestInfo;
> +    virDrvDomainQemuAgentSetTimeout domainQemuAgentSetTimeout;
>   };
> diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c
> index 1afb5fe529..73f119cb23 100644
> --- a/src/libvirt-qemu.c
> +++ b/src/libvirt-qemu.c
> @@ -216,6 +216,46 @@ virDomainQemuAgentCommand(virDomainPtr domain,
>       return NULL;
>   }
>   
> +/**
> + * virDomainQemuAgentSetTimeout:
> + * @domain: a domain object
> + * @timeout: timeout in seconds
> + *
> + * Set how long to wait for a response from qemu agent commands. By default,
> + * agent commands block forever waiting for a response.
> + *
> + * @timeout must be -2, -1, 0 or positive.
> + * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2): meaning to block forever waiting for
> + * a result.
> + * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1): use default timeout value.
> + * VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0): does not wait.
> + * positive value: wait for @timeout seconds
> + *
> + * Returns 0 on success, -1 on failure
> + */
> +int
> +virDomainQemuAgentSetTimeout(virDomainPtr domain,
> +                             int timeout)

This definitely deserves some @flags argument. Even though we don't have 
any flags to set now, it has proven to save us in the future couple of 
times. Look at "extra flags; not used yet ...." occurring in docs for 
our public APIs.

> +{
> +    virConnectPtr conn;
> +
> +    virResetLastError();
> +
> +    virCheckDomainReturn(domain, -1);
> +    conn = domain->conn;
> +
> +    if (conn->driver->domainQemuAgentSetTimeout) {
> +        if (conn->driver->domainQemuAgentSetTimeout(domain, timeout) < 0)
> +            goto error;
> +        return 0;
> +    }
> +
> +    virReportUnsupportedError();
> +
> + error:
> +    virDispatchError(conn);
> +    return -1;
> +}
>   
>   /**
>    * virConnectDomainQemuMonitorEventRegister:
> diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms
> index 3a297e3a2b..348caea72e 100644
> --- a/src/libvirt_qemu.syms
> +++ b/src/libvirt_qemu.syms
> @@ -30,3 +30,7 @@ LIBVIRT_QEMU_1.2.3 {
>           virConnectDomainQemuMonitorEventDeregister;
>           virConnectDomainQemuMonitorEventRegister;
>   } LIBVIRT_QEMU_0.10.0;
> +LIBVIRT_QEMU_5.8.0 {
> +    global:
> +        virDomainQemuAgentSetTimeout;
> +} LIBVIRT_QEMU_1.2.3;
> diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
> index 34e1a85d64..86352aaec5 100644
> --- a/src/qemu/qemu_agent.c
> +++ b/src/qemu/qemu_agent.c
> @@ -128,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;
> @@ -696,6 +697,8 @@ qemuAgentOpen(virDomainObjPtr vm,
>       if (!(mon = virObjectLockableNew(qemuAgentClass)))
>           return NULL;
>   
> +    /* agent commands block by default, user can choose different behavior */
> +    mon->timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK;
>       mon->fd = -1;
>       if (virCondInit(&mon->notify) < 0) {
>           virReportSystemError(errno, "%s",
> @@ -851,6 +854,11 @@ static int qemuAgentSend(qemuAgentPtr mon,
>               return -1;
>           if (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT)
>               seconds = QEMU_AGENT_WAIT_TIME;
> +
> +        /* 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 < seconds))
> +            seconds = mon->timeout;
>           then = now + seconds * 1000ull;
>       }
>   
> @@ -1305,8 +1313,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)


This looks redundant - you're already passing @mon, qemuAgentCommand can 
grab ->timeout from it instead of having it as an argument.

>           goto cleanup;
>   

Michal




More information about the libvir-list mailing list