Signed-off-by: Joao Martins <joao.m.martins(a)oracle.com>
---
src/libxl/libxl_domain.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++-
src/libxl/libxl_domain.h | 16 ++++
src/libxl/libxl_driver.c | 51 ++++++++++
3 files changed, 305 insertions(+), 1 deletion(-)
diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c
index ed73cd2..6bdd0ec 100644
--- a/src/libxl/libxl_domain.c
+++ b/src/libxl/libxl_domain.c
@@ -782,6 +782,12 @@ libxlDomainCleanup(libxlDriverPrivatePtr driver,
}
}
+ if (priv->agent) {
+ qemuAgentClose(priv->agent);
+ priv->agent = NULL;
+ priv->agentError = false;
+ }
+
if ((vm->def->nnets)) {
size_t i;
@@ -940,6 +946,228 @@ libxlDomainFreeMem(libxl_ctx *ctx, libxl_domain_config *d_config)
return -1;
}
+/*
+ * This is a callback registered with a qemuAgentPtr instance,
+ * and to be invoked when the agent console hits an end of file
+ * condition, or error, thus indicating VM shutdown should be
+ * performed
+ */
+static void
+libxlHandleAgentEOF(qemuAgentPtr agent,
+ virDomainObjPtr vm)
+{
+ libxlDomainObjPrivatePtr priv;
+
+ VIR_DEBUG("Received EOF from agent on %p '%s'", vm,
vm->def->name);
+
+ virObjectLock(vm);
+
+ priv = vm->privateData;
+
+ if (!priv->agent) {
+ VIR_DEBUG("Agent freed already");
+ goto unlock;
+ }
+
+ qemuAgentClose(agent);
+ priv->agent = NULL;
+
+ unlock:
+ virObjectUnlock(vm);
+ return;
+}
+
+
+/*
+ * This is invoked when there is some kind of error
+ * parsing data to/from the agent. The VM can continue
+ * to run, but no further agent commands will be
+ * allowed
+ */
+static void
+libxlHandleAgentError(qemuAgentPtr agent ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ libxlDomainObjPrivatePtr priv;
+
+ VIR_DEBUG("Received error from agent on %p '%s'", vm,
vm->def->name);
+
+ virObjectLock(vm);
+
+ priv = vm->privateData;
+
+ priv->agentError = true;
+
+ virObjectUnlock(vm);
+}
+
+static void libxlHandleAgentDestroy(qemuAgentPtr agent,
+ virDomainObjPtr vm)
+{
+ VIR_DEBUG("Received destroy agent=%p vm=%p", agent, vm);
+
+ virObjectUnref(vm);
+}
+
+static qemuAgentCallbacks agentCallbacks = {
+ .destroy = libxlHandleAgentDestroy,
+ .eofNotify = libxlHandleAgentEOF,
+ .errorNotify = libxlHandleAgentError,
+};
+
+static virDomainChrDefPtr
+libxlFindAgentConfig(virDomainDefPtr def)
+{
+ size_t i;
+
+ for (i = 0; i < def->nchannels; i++) {
+ virDomainChrDefPtr channel = def->channels[i];
+
+ if (channel->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN)
+ continue;
+
+ if (STREQ_NULLABLE(channel->target.name, "org.qemu.guest_agent.0"))
+ return channel;
+ }
+
+ return NULL;
+}
+
+bool
+libxlDomainAgentAvailable(virDomainObjPtr vm, bool reportError)
+{
+ libxlDomainObjPrivatePtr priv = vm->privateData;
+
+ if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
+ if (reportError) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ }
+ return false;
+ }
+ if (priv->agentError) {
+ if (reportError) {
+ virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+ _("QEMU guest agent is not "
+ "available due to an error"));
+ }
+ return false;
+ }
+ if (!priv->agent) {
+ if (libxlFindAgentConfig(vm->def)) {
+ if (reportError) {
+ virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+ _("QEMU guest agent is not connected"));
+ }
+ return false;
+ } else {
+ if (reportError) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("QEMU guest agent is not configured"));
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+static int
+libxlConnectAgent(virDomainObjPtr vm)
+{
+ virDomainChrDefPtr config = libxlFindAgentConfig(vm->def);
+ libxlDomainObjPrivatePtr priv = vm->privateData;
+ qemuAgentPtr agent = NULL;
+ int ret = -1;
+
+ if (!config)
+ return 0;
+
+ if (priv->agent)
+ return 0;
+
+ /* Hold an extra reference because we can't allow 'vm' to be
+ * deleted while the agent is active */
+ virObjectRef(vm);
+
+ ignore_value(virTimeMillisNow(&priv->agentStart));
+ virObjectUnlock(vm);
+
+ agent = qemuAgentOpen(vm, config->source, &agentCallbacks);
+
+ virObjectLock(vm);
+ priv->agentStart = 0;
+
+ if (agent == NULL)
+ virObjectUnref(vm);
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuAgentClose(agent);
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest crashed while connecting to the guest
agent"));
+ ret = -2;
+ goto cleanup;
+ }
+
+ priv->agent = agent;
+
+ if (priv->agent == NULL) {
+ VIR_INFO("Failed to connect agent for %s", vm->def->name);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+/*
+ * obj must be locked before calling
+ *
+ * To be called immediately before any QEMU agent API call.
+ * Must have already called libxlDomainObjBeginJob() and checked
+ * that the VM is still active.
+ *
+ * To be followed with libxlDomainObjExitAgent() once complete
+ */
+void
+libxlDomainObjEnterAgent(virDomainObjPtr obj)
+{
+ libxlDomainObjPrivatePtr priv = obj->privateData;
+
+ VIR_DEBUG("Entering agent (agent=%p vm=%p name=%s)",
+ priv->agent, obj, obj->def->name);
+ virObjectLock(priv->agent);
+ virObjectRef(priv->agent);
+ ignore_value(virTimeMillisNow(&priv->agentStart));
+ virObjectUnlock(obj);
+}
+
+
+/* obj must NOT be locked before calling
+ *
+ * Should be paired with an earlier qemuDomainObjEnterAgent() call
+ */
+void
+libxlDomainObjExitAgent(virDomainObjPtr obj)
+{
+ libxlDomainObjPrivatePtr priv = obj->privateData;
+ bool hasRefs;
+
+ hasRefs = virObjectUnref(priv->agent);
+
+ if (hasRefs)
+ virObjectUnlock(priv->agent);
+
+ virObjectLock(obj);
+ VIR_DEBUG("Exited agent (agent=%p vm=%p name=%s)",
+ priv->agent, obj, obj->def->name);
+
+ priv->agentStart = 0;
+ if (!hasRefs)
+ priv->agent = NULL;
+}
+
static int
libxlNetworkPrepareDevices(virDomainDefPtr def)
{
@@ -1312,8 +1540,17 @@ libxlDomainStart(libxlDriverPrivatePtr driver,
libxlDomainCreateIfaceNames(vm->def, &d_config);
#ifdef LIBXL_HAVE_DEVICE_CHANNEL
- if (vm->def->nchannels > 0)
+ if (vm->def->nchannels > 0) {
libxlDomainCreateChannelPTY(vm->def, cfg->ctx);
+
+ /* Failure to connect to agent shouldn't be fatal */
+ if (libxlConnectAgent(vm) < 0) {
+ VIR_WARN("Cannot connect to QEMU guest agent for %s",
+ vm->def->name);
+ virResetLastError();
+ priv->agentError = true;
+ }
+ }
#endif
if ((dom_xml = virDomainDefFormat(vm->def, cfg->caps, 0)) == NULL)
diff --git a/src/libxl/libxl_domain.h b/src/libxl/libxl_domain.h
index 3a3890b..59f9f8d 100644
--- a/src/libxl/libxl_domain.h
+++ b/src/libxl/libxl_domain.h
@@ -29,6 +29,7 @@
# include "domain_conf.h"
# include "libxl_conf.h"
# include "virchrdev.h"
+# include "virqemuagent.h"
# define JOB_MASK(job) (1 << (job - 1))
# define DEFAULT_JOB_MASK \
@@ -62,6 +63,11 @@ typedef libxlDomainObjPrivate *libxlDomainObjPrivatePtr;
struct _libxlDomainObjPrivate {
virObjectLockable parent;
+ /* agent */
+ qemuAgentPtr agent;
+ bool agentError;
+ unsigned long long agentStart;
+
/* console */
virChrdevsPtr devs;
libxl_evgen_domain_death *deathW;
@@ -91,6 +97,16 @@ void
libxlDomainObjEndJob(libxlDriverPrivatePtr driver,
virDomainObjPtr obj);
+bool
+libxlDomainAgentAvailable(virDomainObjPtr vm,
+ bool reportError);
+
+void
+libxlDomainObjEnterAgent(virDomainObjPtr obj);
+
+void
+libxlDomainObjExitAgent(virDomainObjPtr obj);
+
int
libxlDomainJobUpdateTime(struct libxlDomainJobObj *job)
ATTRIBUTE_RETURN_CHECK;
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index 3a69720..cf5e702 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -57,6 +57,7 @@
#include "virstring.h"
#include "virsysinfo.h"
#include "viraccessapicheck.h"
+#include "viraccessapicheckqemu.h"
#include "viratomic.h"
#include "virhostdev.h"
#include "network/bridge_driver.h"
@@ -6415,6 +6416,55 @@ libxlConnectBaselineCPU(virConnectPtr conn,
return cpu;
}
+static char *
+libxlDomainQemuAgentCommand(virDomainPtr domain,
+ const char *cmd,
+ int timeout,
+ unsigned int flags)
+{
+ libxlDriverPrivatePtr driver = domain->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+ char *result = NULL;
+ libxlDomainObjPrivatePtr priv;
+
+ virCheckFlags(0, NULL);
+
+ if (!(vm = libxlDomObjFromDomain(domain)))
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (virDomainQemuAgentCommandEnsureACL(domain->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ if (!libxlDomainAgentAvailable(vm, true))
+ goto endjob;
+
+ libxlDomainObjEnterAgent(vm);
+ ret = qemuAgentArbitraryCommand(priv->agent, cmd, &result, timeout);
+ libxlDomainObjExitAgent(vm);
+ if (ret < 0)
+ VIR_FREE(result);
+
+ endjob:
+ libxlDomainObjEndJob(driver, vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return result;
+}
+
+
static virHypervisorDriver libxlHypervisorDriver = {
.name = LIBXL_DRIVER_NAME,
.connectOpen = libxlConnectOpen, /* 0.9.0 */
@@ -6522,6 +6572,7 @@ static virHypervisorDriver libxlHypervisorDriver = {
.connectGetDomainCapabilities = libxlConnectGetDomainCapabilities, /* 2.0.0 */
.connectCompareCPU = libxlConnectCompareCPU, /* 2.3.0 */
.connectBaselineCPU = libxlConnectBaselineCPU, /* 2.3.0 */
+ .domainQemuAgentCommand = libxlDomainQemuAgentCommand, /* 3.1.0 */
};
static virConnectDriver libxlConnectDriver = {
--
2.1.4