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 */
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;
int *nparams,
unsigned int flags);
+typedef int
+(*virDrvDomainAgentSetResponseTimeout)(virDomainPtr domain,
+ int timeout,
+ unsigned int flags);
+
typedef struct _virHypervisorDriver virHypervisorDriver;
typedef virHypervisorDriver *virHypervisorDriverPtr;
virDrvDomainCheckpointGetParent domainCheckpointGetParent;
virDrvDomainCheckpointDelete domainCheckpointDelete;
virDrvDomainGetGuestInfo domainGetGuestInfo;
+ virDrvDomainAgentSetResponseTimeout domainAgentSetResponseTimeout;
};
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;
+}
virConnectSetIdentity;
} LIBVIRT_5.7.0;
+LIBVIRT_5.10.0 {
+ global:
+ virDomainAgentSetResponseTimeout;
+} LIBVIRT_5.8.0;
+
# .... define new API here using predicted next version number ....
#include <sys/time.h>
#include "qemu_agent.h"
+#include "qemu_domain.h"
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
* but fire up an event on qemu monitor instead.
* Take that as indication of successful completion */
qemuAgentEvent await_event;
+ int timeout;
};
static virClassPtr qemuAgentClass;
if (!(mon = virObjectLockableNew(qemuAgentClass)))
return NULL;
+ mon->timeout = QEMU_DOMAIN_PRIVATE(vm)->agentTimeout;
mon->fd = -1;
if (virCondInit(&mon->notify) < 0) {
virReportSystemError(errno, "%s",
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 */
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);
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) {
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) {
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);
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);
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"))) {
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
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;
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) {
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;
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;
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"))) {
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;
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;
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;
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;
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;
+}
virTypedParameterPtr *params,
int *nparams,
int *maxparams);
+
+void qemuAgentSetResponseTimeout(qemuAgentPtr mon,
+ int timeout);
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;
if (qemuDomainObjPrivateXMLFormatSlirp(buf, vm) < 0)
return -1;
+ virBufferAsprintf(buf, "<agentTimeout>%i</agentTimeout>\n", priv->agentTimeout);
+
return 0;
}
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;
virDomainChrSourceDefPtr monConfig;
bool monError;
unsigned long long monStart;
+ int agentTimeout;
qemuAgentPtr agent;
bool agentError;
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,
.domainCheckpointGetParent = qemuDomainCheckpointGetParent, /* 5.6.0 */
.domainCheckpointDelete = qemuDomainCheckpointDelete, /* 5.6.0 */
.domainGetGuestInfo = qemuDomainGetGuestInfo, /* 5.7.0 */
+ .domainAgentSetResponseTimeout = qemuDomainAgentSetResponseTimeout, /* 5.10.0 */
};
.domainCheckpointGetParent = remoteDomainCheckpointGetParent, /* 5.6.0 */
.domainCheckpointDelete = remoteDomainCheckpointDelete, /* 5.6.0 */
.domainGetGuestInfo = remoteDomainGetGuestInfo, /* 5.7.0 */
+ .domainAgentSetResponseTimeout = remoteDomainAgentSetResponseTimeout, /* 5.10.0 */
};
static virNetworkDriver network_driver = {
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. */
* @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
};
} 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,
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,
};
</chains>
</blockjob>
</blockjobs>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='4'>
<name>copy</name>
<uuid>0439a4a8-db56-4933-9183-d8681d7b0746</uuid>
<channelTargetDir path='/tmp/channel'/>
<allowReboot value='yes'/>
<blockjobs active='yes'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='qemu' id='1'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<chardevStdioLogd/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='1'>
<name>upstream</name>
<uuid>dcf47dbd-46d1-4d5b-b442-262a806a333a</uuid>
<chardevStdioLogd/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='1'>
<name>nest</name>
<uuid>994cee0d-2a70-4937-9693-0431e39d20f7</uuid>
<chardevStdioLogd/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='4'>
<name>upstream</name>
<uuid>dcf47dbd-46d1-4d5b-b442-262a806a333a</uuid>
<chardevStdioLogd/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='3'>
<name>upstream</name>
<uuid>dcf47dbd-46d1-4d5b-b442-262a806a333a</uuid>
<chardevStdioLogd/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='7'>
<name>nest</name>
<uuid>994cee0d-2a70-4937-9693-0431e39d20f7</uuid>
<allowReboot value='yes'/>
<nodename index='123'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm' id='1'>
<name>upstream</name>
<uuid>dcf47dbd-46d1-4d5b-b442-262a806a333a</uuid>
<channelTargetDir path='/tmp/channel'/>
<allowReboot value='yes'/>
<blockjobs active='no'/>
+ <agentTimeout>-2</agentTimeout>
<domain type='kvm'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
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
*/
.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,
When I<--timestamp> is used, a human-readable timestamp will be printed
before the event.
+=item B<guest-agent-timeout> I<domain> I<--timeout> B<value>
+
+Set how long to wait for a response from guest agent commands. By default,
+agent commands block forever waiting for a response. B<value> 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<guestinfo> I<domain> [I<--user>] [I<--os>] [I<--timezone>]
[I<--hostname>] [I<--filesystem>]