[libvirt] [PATCH v2 5/6] storage: add support for creating qcow3 images

Ján Tomko jtomko at redhat.com
Tue Feb 5 11:56:16 UTC 2013


This adds the <features> element to volume XML and allows
virStorageBackendCreateQemuImg to create qcow3 volumes.
For qcow3 images, it runs qemu-img with -f qcow2 -o compat=1.1,...
For qcow2 images, it specifies -o compat=0.10 if it's supported,
just in case the default of qemu-img changed to qcow3 in the future.
---
 docs/formatstorage.html.in       |  14 ++++-
 docs/schemas/Makefile.am         |   1 +
 docs/schemas/storagefeatures.rng |  17 ++++++
 docs/schemas/storagevol.rng      |   5 +-
 libvirt.spec.in                  |   1 +
 mingw-libvirt.spec.in            |   2 +
 src/conf/storage_conf.c          |  69 ++++++++++++++++++++++++
 src/conf/storage_conf.h          |   4 ++
 src/libvirt_private.syms         |   3 ++
 src/storage/storage_backend.c    | 114 ++++++++++++++++++++++++++++++++++++---
 src/storage/storage_backend_fs.c |   5 ++
 src/util/virstoragefile.c        |  27 ++++++++++
 src/util/virstoragefile.h        |   1 +
 13 files changed, 254 insertions(+), 9 deletions(-)
 create mode 100644 docs/schemas/storagefeatures.rng

diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in
index 9f93db8..37bf8ed 100644
--- a/docs/formatstorage.html.in
+++ b/docs/formatstorage.html.in
@@ -298,13 +298,16 @@
         ...
         <target>
           <path>/var/lib/virt/images/sparse.img</path>
-          <format type='qcow2'/>
+          <format type='qcow3'/>
           <permissions>
             <owner>107</owner>
             <group>107</group>
             <mode>0744</mode>
             <label>virt_image_t</label>
           </permissions>
+          <features>
+            <lazy_refcounts/>
+          </features>
         </target></pre>
 
     <dl>
@@ -333,6 +336,15 @@
         contains the MAC (eg SELinux) label string.
         <span class="since">Since 0.4.1</span>
       </dd>
+      <dt><code>features</code></dt>
+      <dd>Provides a format-specific list of features enabled in the volume.
+        So far, this is only supported for <code>qcow3</code>. Valid values
+        are:
+        <ul>
+          <li><code><lazy_refcounts></code> for delayed refcount updates
+          (This makes snapshot creation faster).<span class="since">Since
+          1.0.3</span></li>
+      </dd>
     </dl>
 
     <h3><a name="StorageVolBacking">Backing store elements</a></h3>
diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am
index 4413d9e..6e54048 100644
--- a/docs/schemas/Makefile.am
+++ b/docs/schemas/Makefile.am
@@ -15,6 +15,7 @@ schema_DATA = \
 	nwfilter.rng \
 	secret.rng \
 	storageencryption.rng \
+	storagefeatures.rng \
 	storagepool.rng \
 	storagevol.rng
 
diff --git a/docs/schemas/storagefeatures.rng b/docs/schemas/storagefeatures.rng
new file mode 100644
index 0000000..d8ee857
--- /dev/null
+++ b/docs/schemas/storagefeatures.rng
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt volume features XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+    datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+  <define name='diskFormatFeatures'>
+    <element name='features'>
+      <interleave>
+        <optional>
+          <element name="lazy_refcounts">
+            <empty/>
+          </element>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+</grammar>
diff --git a/docs/schemas/storagevol.rng b/docs/schemas/storagevol.rng
index 653491d..b426eca 100644
--- a/docs/schemas/storagevol.rng
+++ b/docs/schemas/storagevol.rng
@@ -8,7 +8,7 @@
   </start>
 
   <include href='storageencryption.rng'/>
-
+  <include href='storagefeatures.rng'/>
 
   <define name='vol'>
     <element name='volume'>
@@ -111,6 +111,9 @@
       <optional>
         <ref name='encryption'/>
       </optional>
+      <optional>
+        <ref name='diskFormatFeatures'/>
+      </optional>
     </element>
   </define>
 
diff --git a/libvirt.spec.in b/libvirt.spec.in
index c2da6ec..93591d1 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1937,6 +1937,7 @@ fi
 %{_datadir}/libvirt/schemas/nwfilter.rng
 %{_datadir}/libvirt/schemas/secret.rng
 %{_datadir}/libvirt/schemas/storageencryption.rng
+%{_datadir}/libvirt/schemas/storagefeatures.rng
 %{_datadir}/libvirt/schemas/storagepool.rng
 %{_datadir}/libvirt/schemas/storagevol.rng
 
diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in
index b27b0ee..9c7ba1e 100644
--- a/mingw-libvirt.spec.in
+++ b/mingw-libvirt.spec.in
@@ -213,6 +213,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
 %{mingw32_datadir}/libvirt/schemas/nwfilter.rng
 %{mingw32_datadir}/libvirt/schemas/secret.rng
 %{mingw32_datadir}/libvirt/schemas/storageencryption.rng
+%{mingw32_datadir}/libvirt/schemas/storagefeatures.rng
 %{mingw32_datadir}/libvirt/schemas/storagepool.rng
 %{mingw32_datadir}/libvirt/schemas/storagevol.rng
 %dir %{mingw32_datadir}/libvirt/api/
@@ -272,6 +273,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
 %{mingw64_datadir}/libvirt/schemas/nwfilter.rng
 %{mingw64_datadir}/libvirt/schemas/secret.rng
 %{mingw64_datadir}/libvirt/schemas/storageencryption.rng
+%{mingw64_datadir}/libvirt/schemas/storagefeatures.rng
 %{mingw64_datadir}/libvirt/schemas/storagepool.rng
 %{mingw64_datadir}/libvirt/schemas/storagevol.rng
 %dir %{mingw64_datadir}/libvirt/api/
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 7a39998..6fa536a 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -293,6 +293,7 @@ virStorageVolDefFree(virStorageVolDefPtr def) {
     }
     VIR_FREE(def->source.extents);
 
+    virBitmapFree(def->target.features);
     VIR_FREE(def->target.path);
     VIR_FREE(def->target.perms.label);
     VIR_FREE(def->target.timestamps);
@@ -1105,6 +1106,36 @@ virStorageSize(const char *unit,
     return 0;
 }
 
+static int
+virStorageVolDefParseTargetFeatures(virStorageVolTargetPtr target,
+                                    xmlNodePtr start_node)
+{
+    int value;
+    xmlNodePtr cur;
+
+    target->features = virBitmapNew(VIR_STORAGE_FILE_FEAT_QCOW3_LAST);
+
+    if (!target->features)
+        goto no_memory;
+
+    for (cur = start_node->children; cur; cur = cur->next) {
+        if (cur->type != XML_ELEMENT_NODE)
+            continue;
+
+        value = virStorageFileFeaturesQcow3TypeFromString((const char*)
+                                                          cur->name);
+        if (value >= 0 && virBitmapSetBit(target->features, value) < 0)
+            goto error;
+    }
+
+    return 0;
+no_memory:
+    virReportOOMError();
+error:
+    virBitmapFree(target->features);
+    return -1;
+}
+
 static virStorageVolDefPtr
 virStorageVolDefParseXML(virStoragePoolDefPtr pool,
                          xmlXPathContextPtr ctxt) {
@@ -1211,6 +1242,13 @@ virStorageVolDefParseXML(virStoragePoolDefPtr pool,
                                 DEFAULT_VOL_PERM_MODE) < 0)
         goto cleanup;
 
+    if (pool->type == VIR_STORAGE_POOL_DIR &&
+        ret->target.format == VIR_STORAGE_FILE_QCOW3 &&
+        (node = virXPathNode("./target/features", ctxt))) {
+        if (virStorageVolDefParseTargetFeatures(&(ret->target), node) < 0)
+            goto cleanup;
+    }
+
     return ret;
 
  cleanup:
@@ -1290,10 +1328,35 @@ virStorageVolTimestampFormat(virBufferPtr buf, const char *name,
 }
 
 static int
+virStorageVolDefFeaturesFormat(virBufferPtr buf,
+                               int format,
+                               virBitmapPtr features)
+{
+    int i;
+    bool tmp;
+    if (format != VIR_STORAGE_FILE_QCOW3)
+        return 0;
+
+    virBufferAddLit(buf, "<features>\n");
+    if (features) {
+        for (i = 0; i < VIR_STORAGE_FILE_FEAT_QCOW3_LAST; i++) {
+            if (virBitmapGetBit(features, i, &tmp) == 0 && tmp) {
+                virBufferEscapeString(buf, "    <%s/>\n",
+                                      virStorageFileFeaturesQcow3TypeToString(i));
+            }
+        }
+    }
+    virBufferAddLit(buf, "</features>\n");
+
+    return 0;
+}
+
+static int
 virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
                              virBufferPtr buf,
                              virStorageVolTargetPtr def,
                              const char *type) {
+
     virBufferAsprintf(buf, "  <%s>\n", type);
 
     if (def->path)
@@ -1341,6 +1404,12 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
         virBufferAdjustIndent(buf, -4);
     }
 
+    if (def->format == VIR_STORAGE_FILE_QCOW3) {
+        virBufferAdjustIndent(buf, 4);
+        virStorageVolDefFeaturesFormat(buf, def->format, def->features);
+        virBufferAdjustIndent(buf, -4);
+    }
+
     virBufferAsprintf(buf, "  </%s>\n", type);
 
     return 0;
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index ad16eca..e7f0744 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -28,6 +28,7 @@
 # include "virutil.h"
 # include "storage_encryption_conf.h"
 # include "virthread.h"
+# include "virstoragefile.h"
 
 # include <libxml/tree.h>
 
@@ -93,6 +94,9 @@ struct _virStorageVolTarget {
     int type; /* only used by disk backend for partition type */
     /* Currently used only in virStorageVolDef.target, not in .backingstore. */
     virStorageEncryptionPtr encryption;
+    /* Format-specific features. Currently only used for qcow3.
+     * Only in virStorageVolDef.target, not in .backingstore. */
+    virBitmapPtr features;
 };
 
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4504ccd..60d224c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1168,6 +1168,8 @@ virStorageGenerateQcowPassphrase;
 
 # storage_file.h
 virStorageFileChainLookup;
+virStorageFileFeaturesQcow3TypeFromString;
+virStorageFileFeaturesQcow3TypeToString;
 virStorageFileFormatToStringQemu;
 virStorageFileFormatTypeFromString;
 virStorageFileFormatTypeToString;
@@ -1181,6 +1183,7 @@ virStorageFileIsSharedFS;
 virStorageFileIsSharedFSType;
 virStorageFileProbeFormat;
 virStorageFileProbeFormatFromFD;
+virStorageFileQemuImgQcow3Options;
 virStorageFileResize;
 
 # sysinfo.h
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index f9e604a..e4c586d 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -654,6 +654,75 @@ cleanup:
     return ret;
 }
 
+static int virStorageBackendQEMUImgOpts(const char *qemuimg, virBitmapPtr opts)
+{
+    char *buf = NULL;
+    char *p, *q = NULL, *r;
+    int ret = -1;
+    int val;
+    virCommandPtr cmd = virCommandNewArgList(qemuimg, "create", "-o", "?",
+                                             "-f", "qcow2", "/dev/null", NULL);
+
+    virCommandAddEnvString(cmd, "LC_ALL=C");
+    virCommandSetOutputBuffer(cmd, &buf);
+
+    if (virCommandRun(cmd, NULL) < 0)
+        goto cleanup;
+
+
+    q = strchr(buf, '\n');
+    while (q) {
+        p = q + 1;
+        q = strchr(p, '\n');
+        if (STRPREFIX(p, "compat ")) {
+            ret = 1;
+            if (opts)
+                continue;
+            else
+                goto cleanup;
+        }
+        r = strchr(p, ' ');
+        if (!r)
+            goto cleanup;
+        *r = '\0';
+        val = virStorageFileFeaturesQcow3TypeFromString(p);
+        if (val >= 0)
+            ignore_value(virBitmapSetBit(opts, val));
+    }
+
+    if (ret < 0)
+        ret = 0;
+
+cleanup:
+    virCommandFree(cmd);
+    VIR_FREE(buf);
+    return ret;
+}
+
+static int virStorageBackendQemuCheckFeatures(virBitmapPtr opts, virBitmapPtr feats)
+{
+    int i;
+    bool val;
+    int ret = -1;
+
+    for (i = 0; i < VIR_STORAGE_FILE_FEAT_QCOW3_LAST; i++) {
+        if (virBitmapGetBit(feats, i, &val) < 0)
+            goto cleanup;
+        if (val) {
+            if (virBitmapGetBit(opts, i, &val) < 0)
+                goto cleanup;
+            if (!val) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("feature %s not supported by qemu-img"),
+                               virStorageFileFeaturesQcow3TypeToString(i));
+                goto cleanup;
+            }
+        }
+    }
+    ret = 0;
+cleanup:
+    return ret;
+}
 
 static int
 virStorageBackendCreateQemuImg(virConnectPtr conn,
@@ -665,27 +734,29 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
     int ret = -1;
     char *create_tool;
     int imgformat = -1;
+    int compat = 0;
     virCommandPtr cmd = NULL;
     bool do_encryption = (vol->target.encryption != NULL);
     unsigned long long int size_arg;
     bool preallocate = false;
-    char *options = NULL;
+    const char *options = NULL;
     virBuffer buf = VIR_BUFFER_INITIALIZER;
+    virBitmapPtr opts = NULL;
 
     virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
 
     preallocate = !!(flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA);
 
-    const char *type = virStorageFileFormatTypeToString(vol->target.format);
+    const char *type = virStorageFileFormatToStringQemu(vol->target.format);
     const char *backingType = vol->backingStore.path ?
-        virStorageFileFormatTypeToString(vol->backingStore.format) : NULL;
+        virStorageFileFormatToStringQemu(vol->backingStore.format) : NULL;
 
     const char *inputBackingPath = (inputvol ? inputvol->backingStore.path
                                              : NULL);
     const char *inputPath = inputvol ? inputvol->target.path : NULL;
     /* Treat input block devices as 'raw' format */
     const char *inputType = inputPath ?
-        virStorageFileFormatTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ?
+        virStorageFileFormatToStringQemu(inputvol->type == VIR_STORAGE_VOL_BLOCK ?
                                          VIR_STORAGE_FILE_RAW :
                                          inputvol->target.format) :
         NULL;
@@ -702,9 +773,11 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
                        inputvol->target.format);
         return -1;
     }
-    if (preallocate && vol->target.format != VIR_STORAGE_FILE_QCOW2) {
+    if (preallocate && vol->target.format != VIR_STORAGE_FILE_QCOW2
+        && vol->target.format != VIR_STORAGE_FILE_QCOW3) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("metadata preallocation only available with qcow2"));
+                       _("metadata preallocation only available with qcow2"
+                         " or qcow3"));
         return -1;
     }
 
@@ -763,7 +836,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
         virStorageEncryptionPtr enc;
 
         if (vol->target.format != VIR_STORAGE_FILE_QCOW &&
-            vol->target.format != VIR_STORAGE_FILE_QCOW2) {
+            vol->target.format != VIR_STORAGE_FILE_QCOW2 &&
+            vol->target.format != VIR_STORAGE_FILE_QCOW3) {
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                            _("qcow volume encryption unsupported with "
                              "volume format %s"), type);
@@ -807,6 +881,18 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
     if (imgformat < 0)
         goto cleanup;
 
+    if (vol->target.format == VIR_STORAGE_FILE_QCOW3 &&
+        (opts = virBitmapNew(VIR_STORAGE_FILE_FEAT_QCOW3_LAST)) == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS) {
+       compat = virStorageBackendQEMUImgOpts(create_tool, opts);
+       if (compat < 0)
+           goto cleanup;
+    }
+
     cmd = virCommandNew(create_tool);
 
     if (inputvol) {
@@ -831,6 +917,18 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
         else if (preallocate)
             virBufferAddLit(&buf, ",preallocation=metadata");
 
+        if (vol->target.format == VIR_STORAGE_FILE_QCOW3) {
+            if (virStorageBackendQemuCheckFeatures(opts, vol->target.features) < 0)
+                goto cleanup;
+            options = virStorageFileQemuImgQcow3Options(vol->target.features);
+            if (!options)
+                goto cleanup;
+            virBufferAdd(&buf, options, strlen(options));
+            VIR_FREE(options);
+        } else if (compat && vol->target.format == VIR_STORAGE_FILE_QCOW2) {
+            virBufferAddLit(&buf, ",compat=0.10");
+        }
+
         if (virBufferError(&buf) > 0) {
             virReportOOMError();
             goto cleanup;
@@ -854,6 +952,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
 
     ret = virStorageBackendCreateExecCommand(pool, vol, cmd);
 cleanup:
+    virBitmapFree(opts);
+    virBufferFreeAndReset(&buf);
     VIR_FREE(create_tool);
     virCommandFree(cmd);
 
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index f356173..538f540 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -150,6 +150,11 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
          */
     }
 
+    if (target->format == VIR_STORAGE_FILE_QCOW3) {
+        target->features = meta->features;
+        meta->features = NULL;
+    }
+
     virStorageFileFreeMetadata(meta);
 
     return ret;
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 1d6a13e..7e49ba3 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1482,3 +1482,30 @@ const char *virStorageFileFormatToStringQemu(enum virStorageFileFormat format)
     else
         return virStorageFileFormatTypeToString(format);
 }
+
+const char *virStorageFileQemuImgQcow3Options(virBitmapPtr features)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    bool value;
+
+    virBufferAddLit(&buf, ",compat=1.1");
+
+    if (features) {
+        if (virBitmapGetBit(features,
+                            VIR_STORAGE_FILE_FEAT_QCOW3_LAZY_REFCOUNTS,
+                            &value) < 0)
+            goto no_memory;
+        if (value)
+            virBufferAddLit(&buf, ",lazy_refcounts=on");
+    }
+
+    if (virBufferError(&buf) > 0)
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+no_memory:
+    virBufferFreeAndReset(&buf);
+    virReportOOMError();
+    return NULL;
+}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index f765e71..1a8cb53 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -118,5 +118,6 @@ int virStorageFileGetLVMKey(const char *path,
 int virStorageFileGetSCSIKey(const char *path,
                              char **key);
 const char *virStorageFileFormatToStringQemu(enum virStorageFileFormat format);
+const char *virStorageFileQemuImgQcow3Options(virBitmapPtr features);
 
 #endif /* __VIR_STORAGE_FILE_H__ */
-- 
1.7.12.4




More information about the libvir-list mailing list