[libvirt] [RFC PATCH 4/5] Qemu implementation of qemuMonitorCommand.

Chris Lalancette clalance at redhat.com
Tue Apr 13 18:36:49 UTC 2010


Signed-off-by: Chris Lalancette <clalance at redhat.com>
---
 src/qemu/qemu_conf.c         |   28 ++++++
 src/qemu/qemu_conf.h         |    6 ++
 src/qemu/qemu_driver.c       |  190 ++++++++++++++++++++++++++++++++++++------
 src/qemu/qemu_monitor.c      |   14 +++
 src/qemu/qemu_monitor.h      |    2 +
 src/qemu/qemu_monitor_json.c |   46 ++++++++++
 src/qemu/qemu_monitor_json.h |    4 +
 src/qemu/qemu_monitor_text.c |   11 +++
 src/qemu/qemu_monitor_text.h |    2 +
 9 files changed, 278 insertions(+), 25 deletions(-)

diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 48252a5..3093583 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -3363,6 +3363,9 @@ qemuBuildSmpArgStr(const virDomainDefPtr def,
     return virBufferContentAndReset(&buf);
 }
 
+static int qemuStringToArgvEnv(const char *args,
+                               const char ***retenv,
+                               const char ***retargv);
 
 /*
  * Constructs a argv suitable for launching qemu with config defined
@@ -4593,6 +4596,31 @@ int qemudBuildCommandLine(virConnectPtr conn,
         ADD_ARG_LIT(current_snapshot->def->name);
     }
 
+    if (def->namespaceData) {
+        qemuDomainAdvancedDefPtr adv;
+
+        adv = def->namespaceData;
+        if (adv->cmdline_extra) {
+            const char **progenv = NULL;
+            const char **progargv = NULL;
+
+            if (qemuStringToArgvEnv(adv->cmdline_extra, &progenv, &progargv) < 0)
+                goto error;
+
+            for (i = 0 ; progargv && progargv[i] ; i++) {
+                ADD_ARG_LIT(progargv[i]);
+                VIR_FREE(progargv[i]);
+            }
+            VIR_FREE(progargv);
+
+            for (i = 0 ; progenv && progenv[i] ; i++) {
+                ADD_ENV_LIT(progenv[i]);
+                VIR_FREE(progenv[i]);
+            }
+            VIR_FREE(progenv);
+        }
+    }
+
     ADD_ARG(NULL);
     ADD_ENV(NULL);
 
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index e0666cb..eef88c4 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -158,6 +158,12 @@ struct qemud_driver {
 typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet;
 typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr;
 
+typedef struct _qemuDomainAdvancedDef qemuDomainAdvancedDef;
+typedef qemuDomainAdvancedDef *qemuDomainAdvancedDefPtr;
+struct _qemuDomainAdvancedDef {
+    char *cmdline_extra;
+};
+
 /* Port numbers used for KVM migration. */
 # define QEMUD_MIGRATION_FIRST_PORT 49152
 # define QEMUD_MIGRATION_NUM_PORTS 64
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 74b200b..e0c17aa 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -47,6 +47,8 @@
 #include <sys/ioctl.h>
 #include <sys/un.h>
 
+#include <libxml/xpathInternals.h>
+
 #ifdef __linux__
 # include <sys/vfs.h>
 # ifndef NFS_SUPER_MAGIC
@@ -89,6 +91,9 @@
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
+#define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0"
+
+
 /* Only 1 job is allowed at any time
  * A job includes *all* monitor commands, even those just querying
  * information, not merely actions */
@@ -527,6 +532,103 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD
     }
 }
 
+static void *qemuDomainDefNamespaceParse(xmlDocPtr xml,
+                                         xmlNodePtr root,
+                                         xmlXPathContextPtr ctxt)
+{
+    qemuDomainAdvancedDefPtr adv = NULL;
+    xmlNsPtr ns;
+
+    ns = xmlSearchNs(xml, root, BAD_CAST "qemu");
+    if (!ns)
+        /* this is fine; it just means there was no qemu namespace listed */
+        return NULL;
+
+    if (STRNEQ((const char *)ns->href, QEMU_NAMESPACE_HREF)) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("Found namespace '%s' doesn't match expected '%s'"),
+                        ns->href, QEMU_NAMESPACE_HREF);
+        return NULL;
+    }
+
+    if (VIR_ALLOC(adv) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    xmlXPathRegisterNs(ctxt, ns->prefix, ns->href);
+
+    adv->cmdline_extra = virXPathString("string(./qemu:advanced/qemu:commandline/qemu:extra[1])",
+                                        ctxt);
+
+    return adv;
+}
+
+static void qemuDomainDefNamespaceFree(void *nsdata)
+{
+    qemuDomainAdvancedDefPtr adv = nsdata;
+
+    if (!adv)
+        return;
+
+    VIR_FREE(adv->cmdline_extra);
+
+    VIR_FREE(adv);
+}
+
+static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf,
+                                           void *nsdata)
+{
+    qemuDomainAdvancedDefPtr adv = nsdata;
+
+    virBufferAddLit(buf, "  <qemu:advanced>\n");
+    if (adv->cmdline_extra) {
+        virBufferAddLit(buf, "    <qemu:commandline>\n");
+        virBufferVSprintf(buf, "      <qemu:extra>%s</qemu:extra>\n",
+                          adv->cmdline_extra);
+        virBufferAddLit(buf, "    </qemu:commandline>\n");
+    }
+    virBufferAddLit(buf, "  </qemu:advanced>\n");
+
+    return 0;
+}
+
+static const char *qemuDomainDefNamespaceHref(void)
+{
+    return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'";
+}
+
+static int qemuDomainLoadAllConfigs(virCapsPtr caps,
+                                    virDomainObjListPtr doms,
+                                    const char *configDir,
+                                    const char *autostartDir,
+                                    int liveStatus,
+                                    virDomainLoadConfigNotify notify,
+                                    void *opaque)
+{
+    struct xmlNamespace ns;
+
+    ns.parse = qemuDomainDefNamespaceParse;
+    ns.free = qemuDomainDefNamespaceFree;
+    ns.format = qemuDomainDefNamespaceFormatXML;
+    ns.href = qemuDomainDefNamespaceHref;
+
+    return virDomainLoadAllConfigs(caps, doms, configDir, autostartDir,
+                                   liveStatus, notify, &ns, opaque);
+}
+
+static virDomainDefPtr qemuDomainDefParseString(virCapsPtr caps,
+                                                const char *xml, int flags)
+{
+    struct xmlNamespace ns;
+
+    ns.parse = qemuDomainDefNamespaceParse;
+    ns.free = qemuDomainDefNamespaceFree;
+    ns.format = qemuDomainDefNamespaceFormatXML;
+    ns.href = qemuDomainDefNamespaceHref;
+
+    return virDomainDefParseString(caps, xml, &ns, flags);
+}
 
 static int qemuCgroupControllerActive(struct qemud_driver *driver,
                                       int controller)
@@ -1647,21 +1749,21 @@ qemudStartup(int privileged) {
     }
 
     /* Get all the running persistent or transient configs first */
-    if (virDomainLoadAllConfigs(qemu_driver->caps,
-                                &qemu_driver->domains,
-                                qemu_driver->stateDir,
-                                NULL,
-                                1, NULL, NULL) < 0)
+    if (qemuDomainLoadAllConfigs(qemu_driver->caps,
+                                 &qemu_driver->domains,
+                                 qemu_driver->stateDir,
+                                 NULL,
+                                 1, NULL, NULL) < 0)
         goto error;
 
     qemuReconnectDomains(qemu_driver);
 
     /* Then inactive persistent configs */
-    if (virDomainLoadAllConfigs(qemu_driver->caps,
-                                &qemu_driver->domains,
-                                qemu_driver->configDir,
-                                qemu_driver->autostartDir,
-                                0, NULL, NULL) < 0)
+    if (qemuDomainLoadAllConfigs(qemu_driver->caps,
+                                 &qemu_driver->domains,
+                                 qemu_driver->configDir,
+                                 qemu_driver->autostartDir,
+                                 0, NULL, NULL) < 0)
         goto error;
 
 
@@ -1711,11 +1813,11 @@ qemudReload(void) {
         return 0;
 
     qemuDriverLock(qemu_driver);
-    virDomainLoadAllConfigs(qemu_driver->caps,
-                            &qemu_driver->domains,
-                            qemu_driver->configDir,
-                            qemu_driver->autostartDir,
-                            0, qemudNotifyLoadDomain, qemu_driver);
+    qemuDomainLoadAllConfigs(qemu_driver->caps,
+                             &qemu_driver->domains,
+                             qemu_driver->configDir,
+                             qemu_driver->autostartDir,
+                             0, qemudNotifyLoadDomain, qemu_driver);
     qemuDriverUnlock(qemu_driver);
 
     qemudAutostartConfigs(qemu_driver);
@@ -4027,8 +4129,8 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml,
     virDomainEventPtr event = NULL;
 
     qemuDriverLock(driver);
-    if (!(def = virDomainDefParseString(driver->caps, xml,
-                                        VIR_DOMAIN_XML_INACTIVE)))
+    if (!(def = qemuDomainDefParseString(driver->caps, xml,
+                                         VIR_DOMAIN_XML_INACTIVE)))
         goto cleanup;
 
     if (virSecurityDriverVerify(def) < 0)
@@ -5861,8 +5963,8 @@ static int qemudDomainRestore(virConnectPtr conn,
     }
 
     /* Create a domain from this XML */
-    if (!(def = virDomainDefParseString(driver->caps, xml,
-                                        VIR_DOMAIN_XML_INACTIVE))) {
+    if (!(def = qemuDomainDefParseString(driver->caps, xml,
+                                         VIR_DOMAIN_XML_INACTIVE))) {
         qemuReportError(VIR_ERR_OPERATION_FAILED,
                         "%s", _("failed to parse XML"));
         goto cleanup;
@@ -6146,7 +6248,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn,
         goto cleanup;
     }
 
-    def = virDomainDefParseString(driver->caps, xmlData, 0);
+    def = qemuDomainDefParseString(driver->caps, xmlData, 0);
     if (!def)
         goto cleanup;
 
@@ -6463,7 +6565,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) {
     int dupVM;
 
     qemuDriverLock(driver);
-    if (!(def = virDomainDefParseString(driver->caps, xml,
+    if (!(def = qemuDomainDefParseString(driver->caps, xml,
                                         VIR_DOMAIN_XML_INACTIVE)))
         goto cleanup;
 
@@ -9335,7 +9437,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
     }
 
     /* Parse the domain XML. */
-    if (!(def = virDomainDefParseString(driver->caps, dom_xml,
+    if (!(def = qemuDomainDefParseString(driver->caps, dom_xml,
                                         VIR_DOMAIN_XML_INACTIVE))) {
         qemuReportError(VIR_ERR_OPERATION_FAILED,
                         "%s", _("failed to parse XML"));
@@ -9566,8 +9668,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
         VIR_DEBUG("Generated uri_out=%s", *uri_out);
 
     /* Parse the domain XML. */
-    if (!(def = virDomainDefParseString(driver->caps, dom_xml,
-                                        VIR_DOMAIN_XML_INACTIVE))) {
+    if (!(def = qemuDomainDefParseString(driver->caps, dom_xml,
+                                         VIR_DOMAIN_XML_INACTIVE))) {
         qemuReportError(VIR_ERR_OPERATION_FAILED,
                         "%s", _("failed to parse XML"));
         goto cleanup;
@@ -11249,6 +11351,44 @@ cleanup:
     return ret;
 }
 
+static int qemuMonitorCommand(virDomainPtr domain, const char *cmd,
+                              char **result, unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct qemud_driver *driver = domain->conn->privateData;
+    virDomainObjPtr vm = NULL;
+    int ret = -1;
+    qemuDomainObjPrivatePtr priv;
+
+    VIR_WARN(_("Qemu monitor command '%s' executed; libvirt results may be unpredictable!"), cmd);
+
+    qemuDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+        virUUIDFormat(domain->uuid, uuidstr);
+        qemuReportError(VIR_ERR_NO_DOMAIN,
+                        _("no domain with matching uuid '%s'"), uuidstr);
+        goto cleanup;
+    }
+
+    if (!virDomainObjIsActive(vm)) {
+        qemuReportError(VIR_ERR_OPERATION_INVALID,
+                        "%s", _("domain is not running"));
+        goto cleanup;
+   }
+
+    priv = vm->privateData;
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    ret = qemuMonitorArbitraryCommand(priv->mon, cmd, result);
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+cleanup:
+    if (vm)
+        virDomainObjUnlock(vm);
+    qemuDriverUnlock(driver);
+    return ret;
+}
+
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
     "QEMU",
@@ -11346,7 +11486,7 @@ static virDriver qemuDriver = {
     qemuDomainSnapshotCurrent, /* domainSnapshotCurrent */
     qemuDomainRevertToSnapshot, /* domainRevertToSnapshot */
     qemuDomainSnapshotDelete, /* domainSnapshotDelete */
-    NULL, /* qemuMonitorCommand */
+    qemuMonitorCommand, /* qemuMonitorCommand */
 };
 
 
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 01e3a46..a47932d 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1530,3 +1530,17 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name)
         ret = qemuMonitorTextDeleteSnapshot(mon, name);
     return ret;
 }
+
+int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply)
+{
+    int ret;
+
+    DEBUG("mon=%p, cmd=%s, reply=%p", mon, cmd, reply);
+
+    if (mon->json)
+        ret = qemuMonitorJSONArbitraryCommand(mon, cmd, reply);
+    else
+        ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply);
+    return ret;
+}
+
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 21b8989..74b0f60 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -348,4 +348,6 @@ int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply);
+
 #endif /* QEMU_MONITOR_H */
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 2904201..92f9dd0 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2222,3 +2222,49 @@ int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name)
     virJSONValueFree(reply);
     return ret;
 }
+
+int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
+                                    const char *cmd,
+                                    char **reply)
+{
+    int ret = -1;
+    qemuMonitorMessage msg;
+
+    *reply = NULL;
+
+    memset(&msg, 0, sizeof msg);
+
+    if (virAsprintf(&msg.txBuffer, "%s\r\n", cmd) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    msg.txLength = strlen(msg.txBuffer);
+    msg.txFD = -1;
+
+    ret = qemuMonitorSend(mon, &msg);
+
+    /* If we got ret==0, but not reply data something rather bad
+     * went wrong, so lets fake an EIO error */
+    if (!msg.rxBuffer && ret == 0) {
+        msg.lastErrno = EIO;
+        ret = -1;
+    }
+
+    if (ret == 0) {
+        *reply = strdup(msg.rxBuffer);
+        if (*reply == NULL) {
+            ret = -1;
+            virReportOOMError();
+            goto cleanup;
+        }
+    }
+    else
+        virReportSystemError(msg.lastErrno,
+                             _("cannot send monitor command '%s'"), cmd);
+
+cleanup:
+    VIR_FREE(msg.txBuffer);
+    VIR_FREE(msg.rxBuffer);
+
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index e7baf84..a0814e8 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -179,4 +179,8 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
+                                    const char *cmd,
+                                    char **reply);
+
 #endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 9942768..65760f5 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -2438,3 +2438,14 @@ cleanup:
     VIR_FREE(reply);
     return ret;
 }
+
+int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply)
+{
+    if (qemuMonitorCommand(mon, cmd, reply)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("failed to run cmd '%s'"), cmd);
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index fb7d08b..27a9500 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -181,4 +181,6 @@ int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply);
+
 #endif /* QEMU_MONITOR_TEXT_H */
-- 
1.6.6.1




More information about the libvir-list mailing list