[PATCH 16/23] qemu: block: Add code to allow detection of auto-allocated node names

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



qemu for some time already sets node names automatically for the block
nodes. This patch adds code that attempts a best-effort detection of the
node names for the backing chain from the output of
'query-named-block-nodes'. The only drawback is that the data provided
by qemu needs to be matched by the filename as seen by qemu and thus
if two disks share a single backing store file the detection won't work.

This will allow us to use qemu commands such as
'block-set-write-threshold' which only accepts node names.

In this patch only the detection code is added, it will be used later.
---
 src/Makefile.am       |   1 +
 src/qemu/qemu_block.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_block.h |  47 +++++++++
 3 files changed, 328 insertions(+)
 create mode 100644 src/qemu/qemu_block.c
 create mode 100644 src/qemu/qemu_block.h

diff --git a/src/Makefile.am b/src/Makefile.am
index f0d8efe50..b4ecd6366 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -823,6 +823,7 @@ VBOX_DRIVER_EXTRA_DIST =					\
 QEMU_DRIVER_SOURCES =							\
 		qemu/qemu_agent.c qemu/qemu_agent.h			\
 		qemu/qemu_alias.c qemu/qemu_alias.h			\
+		qemu/qemu_block.c qemu/qemu_block.h			\
 		qemu/qemu_blockjob.c qemu/qemu_blockjob.h		\
 		qemu/qemu_capabilities.c qemu/qemu_capabilities.h	\
 		qemu/qemu_command.c qemu/qemu_command.h			\
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
new file mode 100644
index 000000000..91c04ab7f
--- /dev/null
+++ b/src/qemu/qemu_block.c
@@ -0,0 +1,280 @@
+/*
+ * qemu_block.c: helper functions for QEMU block subsystem
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "qemu_block.h"
+#include "qemu_domain.h"
+
+#include "viralloc.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+
+static void
+qemuBlockNodeNameBackingChainDataFree(qemuBlockNodeNameBackingChainDataPtr data)
+{
+    size_t i;
+
+    if (!data)
+        return;
+
+    for (i = 0; i < data->nelems; i++)
+        virJSONValueFree(data->elems[i]);
+
+    VIR_FREE(data->nodeformat);
+    VIR_FREE(data->nodestorage);
+    VIR_FREE(data->nodebacking);
+
+    VIR_FREE(data->qemufilename);
+    VIR_FREE(data->backingstore);
+
+    VIR_FREE(data);
+}
+
+
+static void
+qemuBlockNodeNameBackingChainDataHashEntryFree(void *opaque,
+                                               const void *name ATTRIBUTE_UNUSED)
+{
+    qemuBlockNodeNameBackingChainDataFree(opaque);
+}
+
+
+struct qemuBlockNodeNameGetBackingChainData {
+    virHashTablePtr table;
+    qemuBlockNodeNameBackingChainDataPtr *entries;
+    size_t nentries;
+};
+
+
+static int
+qemuBlockNodeNameDetectProcessByFilename(size_t pos ATTRIBUTE_UNUSED,
+                                         virJSONValuePtr item,
+                                         void *opaque)
+{
+    struct qemuBlockNodeNameGetBackingChainData *data = opaque;
+    qemuBlockNodeNameBackingChainDataPtr entry;
+    const char *file;
+
+    if (!(file = virJSONValueObjectGetString(item, "file")))
+        return 1;
+
+    if (!(entry = virHashLookup(data->table, file))) {
+        if (VIR_ALLOC(entry) < 0)
+            return -1;
+
+        if (VIR_APPEND_ELEMENT_COPY(data->entries, data->nentries, entry) < 0) {
+            VIR_FREE(entry);
+            return -1;
+        }
+
+        if (VIR_STRDUP(entry->qemufilename, file) < 0)
+            return -1;
+
+        if (virHashAddEntry(data->table, file, entry) < 0)
+            return -1;
+    }
+
+    if (VIR_APPEND_ELEMENT(entry->elems, entry->nelems, item) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static const char *qemuBlockDriversFormat[] = {
+    "qcow2", "raw", "qcow", "luks", "qed", "bochs", "cloop", "dmg", "parallels",
+    "vdi", "vhdx", "vmdk", "vpc", "vvfat", NULL};
+static const char *qemuBlockDriversStorage[] = {
+    "file", "iscsi", "nbd", "host_cdrom", "host_device", "ftp", "ftps",
+    "gluster", "http", "https", "nfs", "rbd", "sheepdog", "ssh", "tftp", NULL};
+
+
+static bool
+qemuBlockDriverMatch(const char *drvname,
+                     const char **drivers)
+{
+    while (*drivers) {
+        if (STREQ(drvname, *drivers))
+            return true;
+
+        drivers++;
+    }
+
+    return false;
+}
+
+
+static int
+qemuBlockNodeNameDetectProcessExtract(qemuBlockNodeNameBackingChainDataPtr data)
+{
+    const char *drv;
+    const char *nodename;
+    const char *backingstore;
+    size_t i;
+
+    /* Since the only way to construct the backing chain is to look up the files
+     * by file name, if two disks share a backing image we can't know which node
+     * belongs to which backing chain. Refuse to detect such chains. */
+    if (data->nelems > 2)
+        return 0;
+
+    for (i = 0; i < data->nelems; i++) {
+        drv = virJSONValueObjectGetString(data->elems[i], "drv");
+        nodename = virJSONValueObjectGetString(data->elems[i], "node-name");
+        backingstore = virJSONValueObjectGetString(data->elems[i], "backing_file");
+
+        if (!drv || !nodename)
+            continue;
+
+        if (qemuBlockDriverMatch(drv, qemuBlockDriversFormat)) {
+            if (data->nodeformat)
+                continue;
+
+            if (VIR_STRDUP(data->nodeformat, nodename) < 0)
+                return -1;
+
+            /* extract the backing store file name for the protocol layer */
+            if (VIR_STRDUP(data->backingstore, backingstore) < 0)
+                return -1;
+        } else if (qemuBlockDriverMatch(drv, qemuBlockDriversStorage)) {
+            if (data->nodestorage)
+                continue;
+
+            if (VIR_STRDUP(data->nodestorage, nodename) < 0)
+                return -1;
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+qemuBlockNodeNameDetectProcessLinkBacking(qemuBlockNodeNameBackingChainDataPtr data,
+                                          virHashTablePtr table)
+{
+    qemuBlockNodeNameBackingChainDataPtr backing;
+
+    if (!data->backingstore)
+        return 0;
+
+    if (!(backing = virHashLookup(table, data->backingstore)))
+        return 0;
+
+    if (VIR_STRDUP(data->nodebacking, backing->nodeformat) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static void
+qemuBlockNodeNameGetBackingChainDataClearLookup(qemuBlockNodeNameBackingChainDataPtr data)
+{
+    size_t i;
+
+    for (i = 0; i < data->nelems; i++)
+        virJSONValueFree(data->elems[i]);
+
+    VIR_FREE(data->elems);
+    data->nelems = 0;
+}
+
+
+/**
+ * qemuBlockNodeNameGetBackingChain:
+ * @json: JSON array of data returned from 'query-named-block-nodes'
+ *
+ * Tries to reconstruct the backing chain from @json to allow detection of
+ * node names that were auto-assigned by qemu. This is a best-effort operation
+ * and may not be successful. The returned hash table contains the entries as
+ * qemuBlockNodeNameBackingChainDataPtr accessible by the node name. The fields
+ * then can be used to recover the full backing chain.
+ *
+ * Returns a hash table on success and NULL on failure.
+ */
+virHashTablePtr
+qemuBlockNodeNameGetBackingChain(virJSONValuePtr json)
+{
+    struct qemuBlockNodeNameGetBackingChainData data;
+    virHashTablePtr nodetable = NULL;
+    virHashTablePtr ret = NULL;
+    size_t i;
+
+    memset(&data, 0, sizeof(data));
+
+    /* hash table keeps the entries accessible by the 'file' in qemu */
+    if (!(data.table = virHashCreate(50, NULL)))
+        goto cleanup;
+
+    /* first group the named entries by the 'file' field */
+    if (virJSONValueArrayForeachSteal(json,
+                                      qemuBlockNodeNameDetectProcessByFilename,
+                                      &data) < 0)
+        goto cleanup;
+
+    /* extract the node names for the format and storage layer */
+    for (i = 0; i < data.nentries; i++) {
+        if (qemuBlockNodeNameDetectProcessExtract(data.entries[i]) < 0)
+            goto cleanup;
+    }
+
+    /* extract the node name for the backing file */
+    for (i = 0; i < data.nentries; i++) {
+        if (qemuBlockNodeNameDetectProcessLinkBacking(data.entries[i],
+                                                      data.table) < 0)
+            goto cleanup;
+    }
+
+    /* clear JSON data necessary only for the lookup procedure */
+    for (i = 0; i < data.nentries; i++)
+        qemuBlockNodeNameGetBackingChainDataClearLookup(data.entries[i]);
+
+    /* create hash table hashed by the format node name */
+    if (!(nodetable = virHashCreate(50,
+                                    qemuBlockNodeNameBackingChainDataHashEntryFree)))
+        goto cleanup;
+
+    /* fill the entries */
+    for (i = 0; i < data.nentries; i++) {
+        if (!data.entries[i]->nodeformat)
+            continue;
+
+        if (virHashAddEntry(nodetable, data.entries[i]->nodeformat,
+                            data.entries[i]) < 0)
+            goto cleanup;
+
+        /* hash table steals the entry and then frees it by itself */
+        data.entries[i] = NULL;
+    }
+
+    VIR_STEAL_PTR(ret, nodetable);
+
+ cleanup:
+     virHashFree(data.table);
+     virHashFree(nodetable);
+     for (i = 0; i < data.nentries; i++)
+         qemuBlockNodeNameBackingChainDataFree(data.entries[i]);
+
+    VIR_FREE(data.entries);
+
+     return ret;
+}
diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h
new file mode 100644
index 000000000..26f5ae062
--- /dev/null
+++ b/src/qemu/qemu_block.h
@@ -0,0 +1,47 @@
+/*
+ * qemu_block.h: helper functions for QEMU block subsystem
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __QEMU_BLOCK_H__
+# define __QEMU_BLOCK_H__
+
+# include "internal.h"
+
+# include "qemu_conf.h"
+
+# include "virhash.h"
+# include "virjson.h"
+
+typedef struct qemuBlockNodeNameBackingChainData qemuBlockNodeNameBackingChainData;
+typedef qemuBlockNodeNameBackingChainData *qemuBlockNodeNameBackingChainDataPtr;
+struct qemuBlockNodeNameBackingChainData {
+    char *qemufilename; /* name of the image from qemu */
+    char *backingstore;
+    char *nodeformat;   /* node name of the format layer */
+    char *nodestorage;  /* node name of the storage backing the format node */
+
+    char *nodebacking; /* node name of the backing file format layer */
+
+    /* data necessary for detection of the node names from qemu */
+    virJSONValuePtr *elems;
+    size_t nelems;
+};
+
+virHashTablePtr
+qemuBlockNodeNameGetBackingChain(virJSONValuePtr data);
+
+#endif /* __QEMU_BLOCK_H__ */
-- 
2.12.0

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]
  Powered by Linux