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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list