[libvirt] [RFC PATCH 15/30] qemu: block: Add support for creating 'format' layer for blockdev-add

Peter Krempa posted 30 patches 7 years ago
[libvirt] [RFC PATCH 15/30] qemu: block: Add support for creating 'format' layer for blockdev-add
Posted by Peter Krempa 7 years ago
When using blockdev-add and friends, libvirt will need to create also
properties for the qcow2/raw/... format handler in qemu. This patch adds
the infrastructure and implements all formats known to libvirt including
all properties which are expressed at the format level in qemu.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
---
 src/qemu/qemu_block.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_block.h |   3 +
 2 files changed, 299 insertions(+)

diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 0357d93384..87f3cf9cbe 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -1168,3 +1168,299 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
     virJSONValueFree(fileprops);
     return ret;
 }
+
+
+static int
+qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src,
+                                        virJSONValuePtr props)
+{
+    qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+    const char *driver = "raw";
+    const char *secretalias = NULL;
+
+    if (src->encryption &&
+        src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
+        srcPriv &&
+        srcPriv->encinfo) {
+        driver = "luks";
+        secretalias = srcPriv->encinfo->s.aes.alias;
+    }
+
+    /* currently unhandled properties for the 'raw' driver:
+     * 'offset'
+     * 'size'
+     */
+
+    if (virJSONValueObjectAdd(props,
+                              "s:driver", driver,
+                              "S:key-secret", secretalias, NULL) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int
+qemuBlockStorageSourceGetCryptoProps(virStorageSourcePtr src,
+                                     virJSONValuePtr *encprops)
+{
+    qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+    const char *encformat = NULL;
+
+    *encprops = NULL;
+
+    /* qemu requires encrypted secrets regardless of encryption method used when
+     * passed using the blockdev infrastructure, thus only
+     * VIR_DOMAIN_SECRET_INFO_TYPE_AES works here. The correct type needs to be
+     * instantiated elsewhere. */
+    if (!src->encryption ||
+        !srcpriv ||
+        !srcpriv->encinfo ||
+        srcpriv->encinfo->type != VIR_DOMAIN_SECRET_INFO_TYPE_AES)
+        return 0;
+
+    switch ((virStorageEncryptionFormatType) src->encryption->format) {
+    case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW:
+        encformat = "aes";
+        break;
+
+    case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS:
+        encformat = "luks";
+        break;
+
+    case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT:
+    case VIR_STORAGE_ENCRYPTION_FORMAT_LAST:
+    default:
+        virReportEnumRangeError(virStorageEncryptionFormatType,
+                                src->encryption->format);
+        return -1;
+    }
+
+    return virJSONValueObjectCreate(encprops,
+                                    "s:format", encformat,
+                                    "s:key-secret", srcpriv->encinfo->s.aes.alias,
+                                    NULL);
+}
+
+
+static int
+qemuBlockStorageSourceGetFormatQcowGenericProps(virStorageSourcePtr src,
+                                                const char *format,
+                                                virJSONValuePtr props)
+{
+    virJSONValuePtr encprops = NULL;
+    int ret = -1;
+
+    if (qemuBlockStorageSourceGetCryptoProps(src, &encprops) < 0)
+        return -1;
+
+    if (virJSONValueObjectAdd(props,
+                              "s:driver", format,
+                              "A:encrypt", &encprops, NULL) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(encprops);
+    return ret;
+}
+
+
+static int
+qemuBlockStorageSourceGetFormatQcow2Props(virStorageSourcePtr src,
+                                          virJSONValuePtr props)
+{
+    /* currently unhandled qcow2 props:
+     *
+     * 'lazy-refcounts'
+     * 'pass-discard-request'
+     * 'pass-discard-snapshot'
+     * 'pass-discard-other'
+     * 'overlap-check'
+     * 'l2-cache-size'
+     * 'l2-cache-entry-size'
+     * 'refcount-cache-size'
+     * 'cache-clean-interval'
+     */
+
+    if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow2", props) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static virJSONValuePtr
+qemuBlockStorageSourceGetBlockdevFormatCommonProps(virStorageSourcePtr src)
+{
+    const char *detectZeroes = NULL;
+    const char *discard = NULL;
+    int detectZeroesMode = virDomainDiskGetDetectZeroesMode(src->discard,
+                                                            src->detect_zeroes);
+    virJSONValuePtr props = NULL;
+    virJSONValuePtr ret = NULL;
+
+    if (qemuBlockNodeNameValidate(src->nodeformat) < 0)
+        return NULL;
+
+    if (src->discard)
+        discard = virDomainDiskDiscardTypeToString(src->discard);
+
+    if (detectZeroesMode)
+        detectZeroes = virDomainDiskDetectZeroesTypeToString(detectZeroesMode);
+
+    /* currently unhandled global properties:
+     * '*force-share': 'bool'
+     */
+
+    if (virJSONValueObjectCreate(&props,
+                                 "s:node-name", src->nodeformat,
+                                 "b:read-only", src->readonly,
+                                 "S:discard", discard,
+                                 "S:detect-zeroes", detectZeroes,
+                                 NULL) < 0)
+        return NULL;
+
+    if (qemuBlockStorageSourceGetBlockdevGetCacheProps(src, props) < 0)
+        goto cleanup;
+
+    VIR_STEAL_PTR(ret, props);
+
+ cleanup:
+    virJSONValueFree(props);
+    return ret;
+}
+
+
+static virJSONValuePtr
+qemuBlockStorageSourceGetBlockdevFormatProps(virStorageSourcePtr src)
+{
+    const char *driver = NULL;
+    virJSONValuePtr props = NULL;
+    virJSONValuePtr ret = NULL;
+
+    if (!(props = qemuBlockStorageSourceGetBlockdevFormatCommonProps(src)))
+        goto cleanup;
+
+    switch ((virStorageFileFormat) src->format) {
+    case VIR_STORAGE_FILE_FAT:
+        /* The fat layer is emulated by the storage access layer, so we need to
+         * put a raw layer on top */
+    case VIR_STORAGE_FILE_RAW:
+        if (qemuBlockStorageSourceGetFormatRawProps(src, props) < 0)
+            goto cleanup;
+
+        break;
+
+    case VIR_STORAGE_FILE_QCOW2:
+        if (qemuBlockStorageSourceGetFormatQcow2Props(src, props) < 0)
+            goto cleanup;
+        break;
+
+    case VIR_STORAGE_FILE_QCOW:
+        if (qemuBlockStorageSourceGetFormatQcowGenericProps(src, "qcow", props) < 0)
+            goto cleanup;
+        break;
+
+    /* formats without any special parameters */
+    case VIR_STORAGE_FILE_PLOOP:
+        driver = "parallels";
+        break;
+    case VIR_STORAGE_FILE_VHD:
+        driver = "vhdx";
+        break;
+    case VIR_STORAGE_FILE_BOCHS:
+    case VIR_STORAGE_FILE_CLOOP:
+    case VIR_STORAGE_FILE_DMG:
+    case VIR_STORAGE_FILE_VDI:
+    case VIR_STORAGE_FILE_VPC:
+    case VIR_STORAGE_FILE_QED:
+    case VIR_STORAGE_FILE_VMDK:
+        driver = virStorageFileFormatTypeToString(src->format);
+        break;
+
+    case VIR_STORAGE_FILE_AUTO_SAFE:
+    case VIR_STORAGE_FILE_AUTO:
+    case VIR_STORAGE_FILE_NONE:
+    case VIR_STORAGE_FILE_COW:
+    case VIR_STORAGE_FILE_ISO:
+    case VIR_STORAGE_FILE_DIR:
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("mishandled storage format '%s'"),
+                       virStorageFileFormatTypeToString(src->format));
+        goto cleanup;
+
+    case VIR_STORAGE_FILE_LAST:
+    default:
+        virReportEnumRangeError(virStorageFileFormat, src->format);
+        goto cleanup;
+    }
+
+    if (driver &&
+        virJSONValueObjectAdd(props, "s:driver", driver, NULL) < 0)
+        goto cleanup;
+
+    VIR_STEAL_PTR(ret, props);
+
+ cleanup:
+    virJSONValueFree(props);
+
+    return ret;
+}
+
+
+/**
+ * qemuBlockStorageSourceGetBlockdevProps:
+ *
+ * @src: storage source to format
+ *
+ * Formats @src into a JSON object which can be used with blockdev-add or
+ * -blockdev. The formatted object contains both the storage and format layer
+ * in nested form including link to the backing chain layer if necessary.
+ */
+virJSONValuePtr
+qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src)
+{
+    bool backingSupported = src->format >= VIR_STORAGE_FILE_BACKING;
+    virJSONValuePtr storage = NULL;
+    virJSONValuePtr props = NULL;
+    virJSONValuePtr ret = NULL;
+
+    if (virStorageSourceHasBacking(src) && !backingSupported) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("storage format '%s' does not support backing store"),
+                       virStorageFileFormatTypeToString(src->format));
+        goto cleanup;
+    }
+
+    if (!(props = qemuBlockStorageSourceGetBlockdevFormatProps(src)))
+        goto cleanup;
+
+    if (!(storage = qemuBlockStorageSourceGetBackendProps(src, false)))
+        goto cleanup;
+
+    if (virJSONValueObjectAppend(props, "file", storage) < 0)
+        goto cleanup;
+    storage = NULL;
+
+    if (src->backingStore && backingSupported) {
+        if (virStorageSourceHasBacking(src)) {
+            if (virJSONValueObjectAppendString(props, "backing",
+                                               src->backingStore->nodeformat) < 0)
+                goto cleanup;
+        } else {
+            /* chain is terminated, indicate that no detection should happen
+             * in qemu */
+            if (virJSONValueObjectAppendNull(props, "backing") < 0)
+                goto cleanup;
+        }
+    }
+
+    VIR_STEAL_PTR(ret, props);
+
+ cleanup:
+    virJSONValueFree(storage);
+    virJSONValueFree(props);
+    return ret;
+}
diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h
index 0e674437f4..f819c6f907 100644
--- a/src/qemu/qemu_block.h
+++ b/src/qemu/qemu_block.h
@@ -64,4 +64,7 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
 virURIPtr
 qemuBlockStorageSourceGetURI(virStorageSourcePtr src);

+virJSONValuePtr
+qemuBlockStorageSourceGetBlockdevProps(virStorageSourcePtr src);
+
 #endif /* __QEMU_BLOCK_H__ */
-- 
2.14.3

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [RFC PATCH 15/30] qemu: block: Add support for creating 'format' layer for blockdev-add
Posted by Kevin Wolf 7 years ago
Am 19.04.2018 um 17:25 hat Peter Krempa geschrieben:
> When using blockdev-add and friends, libvirt will need to create also
> properties for the qcow2/raw/... format handler in qemu. This patch adds
> the infrastructure and implements all formats known to libvirt including
> all properties which are expressed at the format level in qemu.
> 
> Signed-off-by: Peter Krempa <pkrempa@redhat.com>
> ---
>  src/qemu/qemu_block.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_block.h |   3 +
>  2 files changed, 299 insertions(+)
> 
> diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
> index 0357d93384..87f3cf9cbe 100644
> --- a/src/qemu/qemu_block.c
> +++ b/src/qemu/qemu_block.c
> @@ -1168,3 +1168,299 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
>      virJSONValueFree(fileprops);
>      return ret;
>  }
> +
> +
> +static int
> +qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src,
> +                                        virJSONValuePtr props)
> +{
> +    qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
> +    const char *driver = "raw";
> +    const char *secretalias = NULL;
> +
> +    if (src->encryption &&
> +        src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
> +        srcPriv &&
> +        srcPriv->encinfo) {
> +        driver = "luks";
> +        secretalias = srcPriv->encinfo->s.aes.alias;
> +    }

Hm, so libvirt treats luks images as "encrypted raw" (which is a
contradiction in itself)? I suppose it's too late to change this, but
it's a bit unfortunate, because...

> +    /* currently unhandled properties for the 'raw' driver:
> +     * 'offset'
> +     * 'size'
> +     */

...if you ever want to implement these features, the luks driver won't
be offering them and you'll need to create two layers at once in this
function (raw over luks, or luks over raw - and it's not clear which
one).

Kevin

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