[libvirt] [PATCH RFC 3/4] libxl: implement qemu-agent-command

Joao Martins joao.m.martins at oracle.com
Wed Feb 8 16:44:39 UTC 2017


Signed-off-by: Joao Martins <joao.m.martins at 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




More information about the libvir-list mailing list