[libvirt] [PATCH 10/15] qemu: capabilities: Add support for QMP schema introspection

Peter Krempa pkrempa at redhat.com
Thu Oct 20 15:25:02 UTC 2016


Allow detecting capabilities according to the qemu QMP schema. This is
necessary as sometimes the availability of certain options depends on
the presence of a field in the schema.

This patch adds support for loading the QMP schema when detecting qemu
capabilities and adds a very simple query language to allow traversing
the schema and selecting a certain element from it.

The infrastructure in this patch allows to use a query path and set a
specific capability flag according to the availability of the given
element in the schema.
---
 src/qemu/qemu_capabilities.c                      | 182 ++++++++++++++++++++++
 src/qemu/qemu_capabilities.h                      |   1 +
 tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml  |   1 +
 tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml |   1 +
 tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml  |   1 +
 tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml  |   1 +
 6 files changed, 187 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 9132469..97e29db 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -346,6 +346,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
               "virtio-net.rx_queue_size", /* 235 */
               "machine-iommu",
               "virtio-vga",
+              "query-qmp-schema",
     );


@@ -1487,6 +1488,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = {
     { "rtc-reset-reinjection", QEMU_CAPS_RTC_RESET_REINJECTION },
     { "migrate-incoming", QEMU_CAPS_INCOMING_DEFER },
     { "query-hotpluggable-cpus", QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS },
+    { "query-qmp-schema", QEMU_CAPS_QUERY_QMP_SCHEMA }
 };

 struct virQEMUCapsStringFlags virQEMUCapsMigration[] = {
@@ -1691,6 +1693,10 @@ static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsUSBNECXHCI[] = {
     { "p3", QEMU_CAPS_NEC_USB_XHCI_PORTS },
 };

+static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = {
+    { "bogus/path/to/satisfy/compiler", 0 },
+};
+
 struct virQEMUCapsObjectTypeProps {
     const char *type;
     struct virQEMUCapsStringFlags *props;
@@ -3691,6 +3697,179 @@ virQEMUCapsInitArchQMPBasic(virQEMUCapsPtr qemuCaps,
     return ret;
 }

+
+static const char *
+virQEMUCapsQMPSchemaObjectGetType(const char *name,
+                                  const char *field,
+                                  const char *namefield,
+                                  virJSONValuePtr elem)
+{
+    virJSONValuePtr arr;
+    virJSONValuePtr cur;
+    const char *curname;
+    const char *type;
+    size_t i;
+
+    if (!(arr = virJSONValueObjectGetArray(elem, field)))
+        return NULL;
+
+    for (i = 0; i < virJSONValueArraySize(arr); i++) {
+        if (!(cur = virJSONValueArrayGet(arr, i)) ||
+            !(curname = virJSONValueObjectGetString(cur, namefield)) ||
+            !(type = virJSONValueObjectGetString(cur, "type")))
+            continue;
+
+        if (STREQ(name, curname))
+            return type;
+    }
+
+    return NULL;
+}
+
+
+static virJSONValuePtr
+virQEMUCapsQMPSchemaTraverse(const char *basename,
+                             char **query,
+                             virHashTablePtr schema)
+{
+    virJSONValuePtr base;
+    const char *metatype;
+
+    do {
+        if (!(base = virHashLookup(schema, basename)))
+            return NULL;
+
+        if (!*query)
+            return base;
+
+        if (!(metatype = virJSONValueObjectGetString(base, "meta-type")))
+            return NULL;
+
+        /* flatten arrays by default */
+        if (STREQ(metatype, "array")) {
+            if (!(basename = virJSONValueObjectGetString(base, "element-type")))
+                return NULL;
+
+            continue;
+        } else if (STREQ(metatype, "object")) {
+            if (**query == '+')
+                basename = virQEMUCapsQMPSchemaObjectGetType(*query + 1,
+                                                             "variants",
+                                                             "case", base);
+            else
+                basename = virQEMUCapsQMPSchemaObjectGetType(*query,
+                                                             "members",
+                                                             "name",
+                                                             base);
+
+            if (!basename)
+                return NULL;
+        } else if (STREQ(metatype, "command") ||
+                   STREQ(metatype, "event")) {
+            if (!(basename = virJSONValueObjectGetString(base, *query)))
+                return NULL;
+        } else {
+            /* alternates, basic types and enums can't be entered */
+            return NULL;
+        }
+
+        query++;
+    } while (*query);
+
+    return base;
+}
+
+
+/**
+ * virQEMUCapsQMPSchemaGetByPath:
+ * @query: string specifying the required data type (see below)
+ * @schema: hash table containing the schema data
+ * @entry: filled with the located schema object requested by @query
+ *
+ * Retrieves the requested schema entry specified by @query to @entry. The
+ * @query parameter has the following syntax which is very closely tied to the
+ * qemu schema syntax entries separated by slashes with a few special characters:
+ *
+ * "command_or_event/attribute/subattribute/+variant_discriminator/subattribute"
+ *
+ * command_or_event: name of the event or attribute to introspect
+ * attribute: selects whether arguments or return type should be introspected
+ *            ("arg-type" or "ret-type" for commands, "arg-type" for events)
+ * subattribute: specifies member name of object types
+ * +variant_discriminator: In case of unionized objects allows to select a
+ *                         specific case to introspect.
+ *
+ * Array types are automatically flattened to the singular type. Alternate
+ * types are currently not supported.
+ *
+ * The above types can be chained arbitrarily using slashes to construct any
+ * path into the schema tree.
+ *
+ * Returns 0 on success (including if the requested schema was not found) and
+ * fills @entry appropriately. On failure returns -1 and sets an appropriate
+ * error message.
+ */
+static int
+virQEMUCapsQMPSchemaGetByPath(const char *query,
+                              virHashTablePtr schema,
+                              virJSONValuePtr *entry)
+{
+    char **elems = NULL;
+
+    *entry = NULL;
+
+    if (!(elems = virStringSplit(query, "/", 0)))
+        return -1;
+
+    if (!*elems) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed query string"));
+        virStringFreeList(elems);
+        return -1;
+    }
+
+    *entry = virQEMUCapsQMPSchemaTraverse(*elems, elems + 1, schema);
+
+    virStringFreeList(elems);
+    return 0;
+}
+
+
+static bool
+virQEMUCapsQMPSchemaQueryPath(const char *query,
+                              virHashTablePtr schema)
+{
+    virJSONValuePtr entry;
+
+    if (virQEMUCapsQMPSchemaGetByPath(query, schema, &entry))
+        return false;
+
+    return !!entry;
+}
+
+
+static int
+virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCapsPtr qemuCaps,
+                                      qemuMonitorPtr mon)
+{
+    struct virQEMUCapsStringFlags *entry;
+    virHashTablePtr schema;
+    size_t i;
+
+    if (!(schema = qemuMonitorQueryQMPSchema(mon)))
+        return -1;
+
+    for (i = 0; i < ARRAY_CARDINALITY(virQEMUCapsQMPSchemaQueries); i++) {
+        entry = virQEMUCapsQMPSchemaQueries + i;
+
+        if (virQEMUCapsQMPSchemaQueryPath(entry->value, schema))
+            virQEMUCapsSet(qemuCaps, entry->flag);
+    }
+
+    virHashFree(schema);
+    return 0;
+}
+
+
 int
 virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps,
                           qemuMonitorPtr mon)
@@ -3793,6 +3972,9 @@ virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps,
         goto cleanup;
     if (virQEMUCapsProbeQMPMigrationCapabilities(qemuCaps, mon) < 0)
         goto cleanup;
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_QMP_SCHEMA) &&
+        virQEMUCapsProbeQMPSchemaCapabilities(qemuCaps, mon) < 0)
+        goto cleanup;

     /* 'intel-iommu' shows up as a device since 2.2.0, but can
      * not be used with -device until 2.7.0. Before that it
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 4d84ef4..2db9b92 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -380,6 +380,7 @@ typedef enum {
     QEMU_CAPS_VIRTIO_NET_RX_QUEUE_SIZE, /* virtio-net-*.rx_queue_size */
     QEMU_CAPS_MACHINE_IOMMU, /* -machine iommu=on */
     QEMU_CAPS_DEVICE_VIRTIO_VGA, /* -device virtio-vga */
+    QEMU_CAPS_QUERY_QMP_SCHEMA, /* query-qmp-schema command */

     QEMU_CAPS_LAST /* this must always be the last item */
 } virQEMUCapsFlags;
diff --git a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml
index 62d42c3..bea01c7 100644
--- a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml
+++ b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml
@@ -186,6 +186,7 @@
   <flag name='virtio-pci-disable-legacy'/>
   <flag name='machine-iommu'/>
   <flag name='virtio-vga'/>
+  <flag name='query-qmp-schema'/>
   <version>2005000</version>
   <kvmVersion>0</kvmVersion>
   <package></package>
diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml
index 721bdee..ea3745e 100644
--- a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml
+++ b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml
@@ -155,6 +155,7 @@
   <flag name='smm'/>
   <flag name='virtio-pci-disable-legacy'/>
   <flag name='virtio-vga'/>
+  <flag name='query-qmp-schema'/>
   <version>2006000</version>
   <kvmVersion>0</kvmVersion>
   <package></package>
diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml
index 0fc9c2f..3f0f59f 100644
--- a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml
+++ b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml
@@ -192,6 +192,7 @@
   <flag name='virtio-pci-disable-legacy'/>
   <flag name='machine-iommu'/>
   <flag name='virtio-vga'/>
+  <flag name='query-qmp-schema'/>
   <version>2006000</version>
   <kvmVersion>0</kvmVersion>
   <package></package>
diff --git a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml
index dc11677..f0ba496 100644
--- a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml
+++ b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml
@@ -193,6 +193,7 @@
   <flag name='virtio-pci-disable-legacy'/>
   <flag name='query-hotpluggable-cpus'/>
   <flag name='virtio-vga'/>
+  <flag name='query-qmp-schema'/>
   <version>2007000</version>
   <kvmVersion>0</kvmVersion>
   <package> (v2.7.0)</package>
-- 
2.10.0




More information about the libvir-list mailing list