[libvirt PATCH v5 21/32] qemu: use nbdkit to serve network disks if available

Jonathon Jongsma posted 32 patches 2 years, 12 months ago
There is a newer version of this series
[libvirt PATCH v5 21/32] qemu: use nbdkit to serve network disks if available
Posted by Jonathon Jongsma 2 years, 12 months ago
For virStorageSource objects that contain an nbdkitProcess, start that
nbdkit process to serve that network drive and then pass the nbdkit
socket to qemu rather than sending the network url to qemu directly.

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
---
 src/qemu/qemu_block.c                         | 162 +++++++++++-------
 src/qemu/qemu_domain.c                        |  13 +-
 src/qemu/qemu_extdevice.c                     |  56 ++++++
 src/qemu/qemu_hotplug.c                       |   7 +
 src/qemu/qemu_nbdkit.c                        |  63 +++++++
 src/qemu/qemu_nbdkit.h                        |  13 ++
 ...sk-cdrom-network-nbdkit.x86_64-latest.args |  42 +++++
 .../disk-cdrom-network-nbdkit.xml             |   1 +
 ...isk-network-http-nbdkit.x86_64-latest.args |  45 +++++
 .../disk-network-http-nbdkit.xml              |   1 +
 ...rce-curl-nbdkit-backing.x86_64-latest.args |  38 ++++
 ...isk-network-source-curl-nbdkit-backing.xml |  45 +++++
 ...work-source-curl-nbdkit.x86_64-latest.args |  50 ++++++
 .../disk-network-source-curl-nbdkit.xml       |   1 +
 ...isk-network-source-curl.x86_64-latest.args |  53 ++++++
 .../disk-network-source-curl.xml              |  71 ++++++++
 ...disk-network-ssh-nbdkit.x86_64-latest.args |  36 ++++
 .../disk-network-ssh-nbdkit.xml               |   1 +
 tests/qemuxml2argvtest.c                      |   6 +
 19 files changed, 638 insertions(+), 66 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.x86_64-latest.args
 create mode 120000 tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.xml
 create mode 100644 tests/qemuxml2argvdata/disk-network-http-nbdkit.x86_64-latest.args
 create mode 120000 tests/qemuxml2argvdata/disk-network-http-nbdkit.xml
 create mode 100644 tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.x86_64-latest.args
 create mode 100644 tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.xml
 create mode 100644 tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.x86_64-latest.args
 create mode 120000 tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.xml
 create mode 100644 tests/qemuxml2argvdata/disk-network-source-curl.x86_64-latest.args
 create mode 100644 tests/qemuxml2argvdata/disk-network-source-curl.xml
 create mode 100644 tests/qemuxml2argvdata/disk-network-ssh-nbdkit.x86_64-latest.args
 create mode 120000 tests/qemuxml2argvdata/disk-network-ssh-nbdkit.xml

diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 5e700eff99..125470f5e2 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -438,6 +438,32 @@ qemuBlockStorageSourceGetCURLProps(virStorageSource *src,
 }
 
 
+static virJSONValue *
+qemuBlockStorageSourceGetNbdkitProps(virStorageSource *src)
+{
+    qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+    virJSONValue *ret = NULL;
+    g_autoptr(virJSONValue) serverprops = NULL;
+    virStorageNetHostDef host = { .transport = VIR_STORAGE_NET_HOST_TRANS_UNIX };
+
+    /* srcPriv->nbdkitProcess will already be initialized if we can use nbdkit
+     * to proxy this storage source */
+    if (!(srcPriv  && srcPriv->nbdkitProcess))
+        return NULL;
+
+    host.socket = srcPriv->nbdkitProcess->socketfile;
+    serverprops = qemuBlockStorageSourceBuildJSONSocketAddress(&host);
+
+    if (!serverprops)
+        return NULL;
+
+    if (virJSONValueObjectAdd(&ret, "a:server", &serverprops, NULL) < 0)
+        return NULL;
+
+    return ret;
+}
+
+
 static virJSONValue *
 qemuBlockStorageSourceGetISCSIProps(virStorageSource *src,
                                     bool onlytarget)
@@ -875,69 +901,75 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
         return NULL;
 
     case VIR_STORAGE_TYPE_NETWORK:
-        switch ((virStorageNetProtocol) src->protocol) {
-        case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
-            driver = "gluster";
-            if (!(fileprops = qemuBlockStorageSourceGetGlusterProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_VXHS:
-            driver = "vxhs";
-            if (!(fileprops = qemuBlockStorageSourceGetVxHSProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_HTTP:
-        case VIR_STORAGE_NET_PROTOCOL_HTTPS:
-        case VIR_STORAGE_NET_PROTOCOL_FTP:
-        case VIR_STORAGE_NET_PROTOCOL_FTPS:
-        case VIR_STORAGE_NET_PROTOCOL_TFTP:
-            driver = virStorageNetProtocolTypeToString(src->protocol);
-            if (!(fileprops = qemuBlockStorageSourceGetCURLProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_ISCSI:
-            driver = "iscsi";
-            if (!(fileprops = qemuBlockStorageSourceGetISCSIProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_NBD:
+        /* prefer using nbdkit for sources that are supported */
+        if ((fileprops = qemuBlockStorageSourceGetNbdkitProps(src))) {
             driver = "nbd";
-            if (!(fileprops = qemuBlockStorageSourceGetNBDProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_RBD:
-            driver = "rbd";
-            if (!(fileprops = qemuBlockStorageSourceGetRBDProps(src, onlytarget)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
-            driver = "sheepdog";
-            if (!(fileprops = qemuBlockStorageSourceGetSheepdogProps(src)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_SSH:
-            driver = "ssh";
-            if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
-                return NULL;
             break;
-
-        case VIR_STORAGE_NET_PROTOCOL_NFS:
-            driver = "nfs";
-            if (!(fileprops = qemuBlockStorageSourceGetNFSProps(src)))
-                return NULL;
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_NONE:
-        case VIR_STORAGE_NET_PROTOCOL_LAST:
-            virReportEnumRangeError(virStorageNetProtocol, src->protocol);
-            return NULL;
+        } else {
+            switch ((virStorageNetProtocol) src->protocol) {
+                case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
+                    driver = "gluster";
+                    if (!(fileprops = qemuBlockStorageSourceGetGlusterProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_VXHS:
+                    driver = "vxhs";
+                    if (!(fileprops = qemuBlockStorageSourceGetVxHSProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_HTTP:
+                case VIR_STORAGE_NET_PROTOCOL_HTTPS:
+                case VIR_STORAGE_NET_PROTOCOL_FTP:
+                case VIR_STORAGE_NET_PROTOCOL_FTPS:
+                case VIR_STORAGE_NET_PROTOCOL_TFTP:
+                    driver = virStorageNetProtocolTypeToString(src->protocol);
+                    if (!(fileprops = qemuBlockStorageSourceGetCURLProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_ISCSI:
+                    driver = "iscsi";
+                    if (!(fileprops = qemuBlockStorageSourceGetISCSIProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_NBD:
+                    driver = "nbd";
+                    if (!(fileprops = qemuBlockStorageSourceGetNBDProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_RBD:
+                    driver = "rbd";
+                    if (!(fileprops = qemuBlockStorageSourceGetRBDProps(src, onlytarget)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
+                    driver = "sheepdog";
+                    if (!(fileprops = qemuBlockStorageSourceGetSheepdogProps(src)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_SSH:
+                    driver = "ssh";
+                    if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_NFS:
+                    driver = "nfs";
+                    if (!(fileprops = qemuBlockStorageSourceGetNFSProps(src)))
+                        return NULL;
+                    break;
+
+                case VIR_STORAGE_NET_PROTOCOL_NONE:
+                case VIR_STORAGE_NET_PROTOCOL_LAST:
+                    virReportEnumRangeError(virStorageNetProtocol, src->protocol);
+                    return NULL;
+            }
         }
         break;
     }
@@ -2227,6 +2259,7 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
     g_autoptr(virJSONValue) location = NULL;
     const char *driver = NULL;
     const char *filename = NULL;
+    qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
 
     switch (actualType) {
     case VIR_STORAGE_TYPE_FILE:
@@ -2255,6 +2288,13 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
             break;
 
         case VIR_STORAGE_NET_PROTOCOL_SSH:
+            if (srcPriv->nbdkitProcess) {
+                /* disk creation not yet supported with nbdkit, and even if it
+                 * was supported, it would not be done with blockdev-create
+                 * props */
+                return 0;
+            }
+
             driver = "ssh";
             if (!(location = qemuBlockStorageSourceGetSshProps(src)))
                 return -1;
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 781b80b46d..ef1789062d 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10880,9 +10880,14 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDomainDiskDef *disk,
     if (qemuDomainSecretStorageSourcePrepareEncryption(priv, src,
                                                        src->nodeformat) < 0)
         return -1;
-    if (qemuDomainSecretStorageSourcePrepareAuth(priv, src,
-                                                 src->nodestorage) < 0)
-        return -1;
+
+    if (!qemuDomainPrepareStorageSourceNbdkit(src, cfg, src->nodestorage, priv)) {
+        /* If we're using nbdkit to serve the storage source, we don't pass
+         * authentication secrets to qemu, but will pass them to nbdkit instead */
+        if (qemuDomainSecretStorageSourcePrepareAuth(priv, src,
+                                                     src->nodestorage) < 0)
+            return -1;
+    }
 
     if (qemuDomainPrepareStorageSourcePR(src, priv, src->nodestorage) < 0)
         return -1;
@@ -10897,8 +10902,6 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDomainDiskDef *disk,
     if (qemuDomainPrepareStorageSourceFDs(src, priv) < 0)
         return -1;
 
-    qemuDomainPrepareStorageSourceNbdkit(src, cfg, src->nodestorage, priv);
-
     return 0;
 }
 
diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c
index fdefe59215..13604904a0 100644
--- a/src/qemu/qemu_extdevice.c
+++ b/src/qemu/qemu_extdevice.c
@@ -232,6 +232,17 @@ qemuExtDevicesStart(virQEMUDriver *driver,
             return -1;
     }
 
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainDiskDef *disk = def->disks[i];
+        if (qemuNbdkitStartStorageSource(driver, vm, disk->src) < 0)
+            return -1;
+    }
+
+    if (def->os.loader && def->os.loader->nvram) {
+        if (qemuNbdkitStartStorageSource(driver, vm, def->os.loader->nvram) < 0)
+            return -1;
+    }
+
     return 0;
 }
 
@@ -283,6 +294,14 @@ qemuExtDevicesStop(virQEMUDriver *driver,
             fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS)
             qemuVirtioFSStop(driver, vm, fs);
     }
+
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainDiskDef *disk = def->disks[i];
+        qemuNbdkitStopStorageSource(disk->src);
+    }
+
+    if (def->os.loader && def->os.loader->nvram)
+        qemuNbdkitStopStorageSource(def->os.loader->nvram);
 }
 
 
@@ -308,10 +327,36 @@ qemuExtDevicesHasDevice(virDomainDef *def)
             return true;
     }
 
+    for (i = 0; i < def->ndisks; i++) {
+        qemuDomainStorageSourcePrivate *priv =
+            QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(def->disks[i]->src);
+        if (priv->nbdkitProcess)
+            return true;
+    }
+
+
     return false;
 }
 
 
+/* recursively setup nbdkit cgroups for backing chain of src */
+static int qemuExtDevicesSetupCgroupNbdkit(virStorageSource *src,
+                                           virCgroup *cgroup)
+{
+        qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+
+        if (src->backingStore)
+            if (qemuExtDevicesSetupCgroupNbdkit(src->backingStore, cgroup) < 0)
+                return -1;
+
+        if (priv && priv->nbdkitProcess &&
+            qemuNbdkitProcessSetupCgroup(priv->nbdkitProcess, cgroup) < 0)
+            return -1;
+
+        return 0;
+}
+
+
 int
 qemuExtDevicesSetupCgroup(virQEMUDriver *driver,
                           virDomainObj *vm,
@@ -351,6 +396,17 @@ qemuExtDevicesSetupCgroup(virQEMUDriver *driver,
             return -1;
     }
 
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainDiskDef *disk = def->disks[i];
+        if (qemuExtDevicesSetupCgroupNbdkit(disk->src, cgroup) < 0)
+            return -1;
+    }
+
+    if (def->os.loader && def->os.loader->nvram) {
+        if (qemuExtDevicesSetupCgroupNbdkit(def->os.loader->nvram, cgroup) < 0)
+            return -1;
+    }
+
     for (i = 0; i < def->nfss; i++) {
         virDomainFSDef *fs = def->fss[i];
 
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index c490e2b97a..113bd5436c 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -993,6 +993,9 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver,
     if (qemuHotplugAttachManagedPR(vm, disk->src, VIR_ASYNC_JOB_NONE) < 0)
         goto cleanup;
 
+    if (qemuNbdkitStartStorageSource(driver, vm, disk->src) < 0)
+        goto cleanup;
+
     ret = qemuDomainAttachDiskGeneric(vm, disk, VIR_ASYNC_JOB_NONE);
 
     virDomainAuditDisk(vm, NULL, disk->src, "attach", ret == 0);
@@ -1015,6 +1018,8 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver,
 
         if (virStorageSourceChainHasManagedPR(disk->src))
             ignore_value(qemuHotplugRemoveManagedPR(vm, VIR_ASYNC_JOB_NONE));
+
+        qemuNbdkitStopStorageSource(disk->src);
     }
     qemuDomainSecretDiskDestroy(disk);
     qemuDomainCleanupStorageSourceFD(disk->src);
@@ -4332,6 +4337,8 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver,
         qemuHotplugRemoveManagedPR(vm, VIR_ASYNC_JOB_NONE) < 0)
         goto cleanup;
 
+    qemuNbdkitStopStorageSource(disk->src);
+
     if (disk->transient) {
         VIR_DEBUG("Removing transient overlay '%s' of disk '%s'",
                   disk->src->path, disk->dst);
diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
index b17f9e772f..dcdd38d119 100644
--- a/src/qemu/qemu_nbdkit.c
+++ b/src/qemu/qemu_nbdkit.c
@@ -754,6 +754,61 @@ qemuNbdkitInitStorageSource(qemuNbdkitCaps *caps,
 }
 
 
+static int
+qemuNbdkitStartStorageSourceOne(virQEMUDriver *driver,
+                                virDomainObj *vm,
+                                virStorageSource *src)
+{
+    qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+
+    if (priv && priv->nbdkitProcess &&
+        qemuNbdkitProcessStart(priv->nbdkitProcess, vm, driver) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+/* recursively start nbdkit for backing chain of src */
+int
+qemuNbdkitStartStorageSource(virQEMUDriver *driver,
+                             virDomainObj *vm,
+                             virStorageSource *src)
+{
+    virStorageSource *backing;
+
+    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
+        if (qemuNbdkitStartStorageSourceOne(driver, vm, backing) < 0)
+            return -1;
+
+    return qemuNbdkitStartStorageSourceOne(driver, vm, src);
+}
+
+
+static void
+qemuNbdkitStopStorageSourceOne(virStorageSource *src)
+{
+    qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+
+    if (priv && priv->nbdkitProcess &&
+        qemuNbdkitProcessStop(priv->nbdkitProcess) < 0)
+        VIR_WARN("Unable to stop nbdkit for storage source '%s'", src->nodestorage);
+}
+
+
+/* recursively stop nbdkit processes for backing chain of src */
+void
+qemuNbdkitStopStorageSource(virStorageSource *src)
+{
+    virStorageSource *backing;
+
+    qemuNbdkitStopStorageSourceOne(src);
+
+    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
+        qemuNbdkitStopStorageSourceOne(backing);
+}
+
+
 static int
 qemuNbdkitCommandPassDataByPipe(virCommand *cmd,
                                 const char *argName,
@@ -941,6 +996,14 @@ qemuNbdkitProcessFree(qemuNbdkitProcess *proc)
 }
 
 
+int
+qemuNbdkitProcessSetupCgroup(qemuNbdkitProcess *proc,
+                             virCgroup *cgroup)
+{
+    return virCgroupAddProcess(cgroup, proc->pid);
+}
+
+
 int
 qemuNbdkitProcessStart(qemuNbdkitProcess *proc,
                        virDomainObj *vm,
diff --git a/src/qemu/qemu_nbdkit.h b/src/qemu/qemu_nbdkit.h
index 7e2aeed4eb..36a2219d82 100644
--- a/src/qemu/qemu_nbdkit.h
+++ b/src/qemu/qemu_nbdkit.h
@@ -21,6 +21,7 @@
 
 #include "internal.h"
 #include "storage_source_conf.h"
+#include "vircgroup.h"
 #include "virenum.h"
 #include "virfilecache.h"
 
@@ -59,6 +60,14 @@ qemuNbdkitReconnectStorageSource(virStorageSource *source,
                                  const char *pidfile,
                                  const char *socketfile);
 
+int
+qemuNbdkitStartStorageSource(virQEMUDriver *driver,
+                             virDomainObj *vm,
+                             virStorageSource *src);
+
+void
+qemuNbdkitStopStorageSource(virStorageSource *src);
+
 void
 qemuNbdkitStorageSourceManageProcess(virStorageSource *src);
 
@@ -95,4 +104,8 @@ qemuNbdkitProcessStop(qemuNbdkitProcess *proc);
 void
 qemuNbdkitProcessFree(qemuNbdkitProcess *proc);
 
+int
+qemuNbdkitProcessSetupCgroup(qemuNbdkitProcess *proc,
+                             virCgroup *cgroup);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuNbdkitProcess, qemuNbdkitProcessFree);
diff --git a/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.x86_64-latest.args b/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.x86_64-latest.args
new file mode 100644
index 0000000000..eec7ef2af7
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.x86_64-latest.args
@@ -0,0 +1,42 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel kvm \
+-cpu qemu64 \
+-m 1024 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":1073741824}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-3-storage.socket"},"node-name":"libvirt-3-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-3-format","read-only":true,"driver":"raw","file":"libvirt-3-storage"}' \
+-device '{"driver":"ide-cd","bus":"ide.0","unit":0,"drive":"libvirt-3-format","id":"ide0-0-0","bootindex":1}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-2-storage.socket"},"node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-2-format","read-only":true,"driver":"raw","file":"libvirt-2-storage"}' \
+-device '{"driver":"ide-cd","bus":"ide.0","unit":1,"drive":"libvirt-2-format","id":"ide0-0-1"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-1-storage.socket"},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"ide-cd","bus":"ide.1","unit":0,"drive":"libvirt-1-format","id":"ide0-1-0"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x2"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.xml b/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.xml
new file mode 120000
index 0000000000..55f677546f
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-cdrom-network-nbdkit.xml
@@ -0,0 +1 @@
+disk-cdrom-network.xml
\ No newline at end of file
diff --git a/tests/qemuxml2argvdata/disk-network-http-nbdkit.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-http-nbdkit.x86_64-latest.args
new file mode 100644
index 0000000000..25d476d3ce
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-http-nbdkit.x86_64-latest.args
@@ -0,0 +1,45 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel kvm \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-4-storage.socket"},"node-name":"libvirt-4-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-4-format","read-only":false,"driver":"raw","file":"libvirt-4-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-4-format","id":"virtio-disk0","bootindex":1}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-3-storage.socket"},"node-name":"libvirt-3-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-3-format","read-only":false,"driver":"raw","file":"libvirt-3-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-3-format","id":"virtio-disk1"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-2-storage.socket"},"node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-2-format","read-only":false,"driver":"raw","file":"libvirt-2-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x4","drive":"libvirt-2-format","id":"virtio-disk2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-1-storage.socket"},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x5","drive":"libvirt-1-format","id":"virtio-disk3"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-network-http-nbdkit.xml b/tests/qemuxml2argvdata/disk-network-http-nbdkit.xml
new file mode 120000
index 0000000000..6a05204e8a
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-http-nbdkit.xml
@@ -0,0 +1 @@
+disk-network-http.xml
\ No newline at end of file
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.x86_64-latest.args
new file mode 100644
index 0000000000..98cfcd219a
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.x86_64-latest.args
@@ -0,0 +1,38 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel tcg \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-2-storage.socket"},"node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-2-format","read-only":true,"driver":"qcow2","file":"libvirt-2-storage"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-1-storage.socket"},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"qcow2","file":"libvirt-1-storage","backing":"libvirt-2-format"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.xml b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.xml
new file mode 100644
index 0000000000..37a30fcbd6
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit-backing.xml
@@ -0,0 +1,45 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='network' device='disk'>
+      <driver name='qemu' type='qcow2'/>
+      <source protocol='https' name='path/to/disk1.qcow2'>
+        <host name='https.example.org' port='8443'/>
+        <cookies>
+            <cookie name='cookie1'>cookievalue1</cookie>
+            <cookie name='cookie2'>cookievalue2</cookie>
+        </cookies>
+      </source>
+      <backingStore type='network'>
+          <format type='qcow2'/>
+          <source protocol='https' name='path/to/backing.qcow2'>
+              <host name='https.example2.org' port='8444'/>
+              <cookies>
+                  <cookie name='cookie3'>cookievalue3</cookie>
+                  <cookie name='cookie4'>cookievalue4</cookie>
+              </cookies>
+          </source>
+      </backingStore>
+      <target dev='vda' bus='virtio'/>
+      <readonly/>
+    </disk>
+    <controller type='usb' index='0'/>
+    <controller type='pci' index='0' model='pci-root'/>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.x86_64-latest.args
new file mode 100644
index 0000000000..ec193bb10a
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.x86_64-latest.args
@@ -0,0 +1,50 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel tcg \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-device '{"driver":"ahci","id":"sata0","bus":"pci.0","addr":"0x2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-5-storage.socket"},"node-name":"libvirt-5-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-5-format","read-only":true,"driver":"raw","file":"libvirt-5-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-5-format","id":"virtio-disk0","bootindex":1}' \
+-object '{"qom-type":"secret","id":"libvirt-4-format-encryption-secret0","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-4-storage.socket"},"node-name":"libvirt-4-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-4-format","read-only":false,"driver":"luks","key-secret":"libvirt-4-format-encryption-secret0","file":"libvirt-4-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x4","drive":"libvirt-4-format","id":"virtio-disk4"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-3-storage.socket"},"node-name":"libvirt-3-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-3-format","read-only":true,"driver":"raw","file":"libvirt-3-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.1","drive":"libvirt-3-format","id":"sata0-0-1"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-2-storage.socket"},"node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-2-format","read-only":true,"driver":"raw","file":"libvirt-2-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.2","drive":"libvirt-2-format","id":"sata0-0-2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-1-storage.socket"},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.3","drive":"libvirt-1-format","id":"sata0-0-3"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.xml b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.xml
new file mode 120000
index 0000000000..4a1e40bd70
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl-nbdkit.xml
@@ -0,0 +1 @@
+disk-network-source-curl.xml
\ No newline at end of file
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-source-curl.x86_64-latest.args
new file mode 100644
index 0000000000..ec6dd13f6c
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl.x86_64-latest.args
@@ -0,0 +1,53 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel tcg \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-device '{"driver":"ahci","id":"sata0","bus":"pci.0","addr":"0x2"}' \
+-object '{"qom-type":"secret","id":"libvirt-5-storage-httpcookie-secret0","data":"BUU0KmnWfonHdjzhYhwVQZ5iTI1KweTJ22q8XWUVoBCVu1z70reDuczPBIabZtC3","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
+-blockdev '{"driver":"https","url":"https://https.example.org:8443/path/to/disk1.iso","cookie-secret":"libvirt-5-storage-httpcookie-secret0","node-name":"libvirt-5-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-5-format","read-only":true,"driver":"raw","file":"libvirt-5-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-5-format","id":"virtio-disk0","bootindex":1}' \
+-object '{"qom-type":"secret","id":"libvirt-4-format-encryption-secret0","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
+-object '{"qom-type":"secret","id":"libvirt-4-storage-httpcookie-secret0","data":"BUU0KmnWfonHdjzhYhwVQZ5iTI1KweTJ22q8XWUVoBCVu1z70reDuczPBIabZtC3","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
+-blockdev '{"driver":"https","url":"https://https.example.org:8443/path/to/disk5.iso?foo=bar","sslverify":false,"cookie-secret":"libvirt-4-storage-httpcookie-secret0","node-name":"libvirt-4-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-4-format","read-only":false,"driver":"luks","key-secret":"libvirt-4-format-encryption-secret0","file":"libvirt-4-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x4","drive":"libvirt-4-format","id":"virtio-disk4"}' \
+-object '{"qom-type":"secret","id":"libvirt-3-storage-httpcookie-secret0","data":"BUU0KmnWfonHdjzhYhwVQZ5iTI1KweTJ22q8XWUVoBBv7TuTgTkyAyOPpC2P5qLbOIypLoHpppjz+u5O+X8oT+jA1m7q/OJQ8dk2EFD5c0A=","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
+-blockdev '{"driver":"http","url":"http://http.example.org:8080/path/to/disk2.iso","cookie-secret":"libvirt-3-storage-httpcookie-secret0","node-name":"libvirt-3-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-3-format","read-only":true,"driver":"raw","file":"libvirt-3-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.1","drive":"libvirt-3-format","id":"sata0-0-1"}' \
+-blockdev '{"driver":"ftp","url":"ftp://ftp.example.org:20/path/to/disk3.iso","node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-2-format","read-only":true,"driver":"raw","file":"libvirt-2-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.2","drive":"libvirt-2-format","id":"sata0-0-2"}' \
+-blockdev '{"driver":"ftps","url":"ftps://ftps.example.org:22/path/to/disk4.iso","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"ide-cd","bus":"sata0.3","drive":"libvirt-1-format","id":"sata0-0-3"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-network-source-curl.xml b/tests/qemuxml2argvdata/disk-network-source-curl.xml
new file mode 100644
index 0000000000..1e50314abe
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-source-curl.xml
@@ -0,0 +1,71 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='network' device='disk'>
+      <source protocol='https' name='path/to/disk1.iso'>
+        <host name='https.example.org' port='8443'/>
+        <cookies>
+            <cookie name='cookie1'>cookievalue1</cookie>
+            <cookie name='cookie2'>cookievalue2</cookie>
+        </cookies>
+      </source>
+      <target dev='vda' bus='virtio'/>
+      <readonly/>
+    </disk>
+    <disk type='network' device='cdrom'>
+        <source protocol='http' name='path/to/disk2.iso'>
+        <host name='http.example.org' port='8080'/>
+        <cookies>
+            <cookie name='cookie1'>cookievalue1</cookie>
+            <cookie name='cookie2'>cookievalue2</cookie>
+            <cookie name='cookie3'>cookievalue3</cookie>
+        </cookies>
+      </source>
+      <target dev='hdb' bus='sata'/>
+    </disk>
+    <disk type='network' device='cdrom'>
+        <source protocol='ftp' name='path/to/disk3.iso'>
+        <host name='ftp.example.org' port='20'/>
+      </source>
+      <target dev='hdc' bus='sata'/>
+    </disk>
+    <disk type='network' device='cdrom'>
+        <source protocol='ftps' name='path/to/disk4.iso'>
+        <host name='ftps.example.org' port='22'/>
+      </source>
+      <target dev='hdd' bus='sata'/>
+    </disk>
+    <disk type='network' device='disk'>
+      <source protocol='https' name='path/to/disk5.iso' query='foo=bar'>
+        <host name='https.example.org' port='8443'/>
+        <ssl verify='no'/>
+        <cookies>
+            <cookie name='cookie1'>cookievalue1</cookie>
+            <cookie name='cookie2'>cookievalue2</cookie>
+        </cookies>
+        <encryption format='luks'>
+          <secret type='passphrase' uuid='1148b693-0843-4cef-9f97-8feb4e1ae365'/>
+        </encryption>
+      </source>
+      <target dev='vde' bus='virtio'/>
+    </disk>
+    <controller type='usb' index='0'/>
+    <controller type='pci' index='0' model='pci-root'/>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.x86_64-latest.args
new file mode 100644
index 0000000000..e22ba095b1
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.x86_64-latest.args
@@ -0,0 +1,36 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel kvm \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-blockdev '{"driver":"nbd","server":{"type":"unix","path":"/tmp/lib/domain--1-QEMUGuest1/nbdkit-libvirt-1-storage.socket"},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.xml b/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.xml
new file mode 120000
index 0000000000..b0589bdfb5
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-ssh-nbdkit.xml
@@ -0,0 +1 @@
+disk-network-ssh.xml
\ No newline at end of file
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index f2f7467102..6b9785b32d 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1262,6 +1262,7 @@ mymain(void)
     DO_TEST_CAPS_LATEST("disk-cdrom-empty-network-invalid");
     DO_TEST_CAPS_LATEST("disk-cdrom-bus-other");
     DO_TEST_CAPS_LATEST("disk-cdrom-network");
+    DO_TEST_CAPS_LATEST_NBDKIT("disk-cdrom-network-nbdkit", QEMU_NBDKIT_CAPS_PLUGIN_CURL);
     DO_TEST_CAPS_LATEST("disk-cdrom-tray");
     DO_TEST_CAPS_LATEST("disk-floppy");
     DO_TEST_CAPS_LATEST("disk-floppy-q35");
@@ -1302,6 +1303,9 @@ mymain(void)
     /* qemu-6.0 is the last qemu version supporting sheepdog */
     DO_TEST_CAPS_VER("disk-network-sheepdog", "6.0.0");
     DO_TEST_CAPS_LATEST("disk-network-source-auth");
+    DO_TEST_CAPS_LATEST("disk-network-source-curl");
+    DO_TEST_CAPS_LATEST_NBDKIT("disk-network-source-curl-nbdkit", QEMU_NBDKIT_CAPS_PLUGIN_CURL);
+    DO_TEST_CAPS_LATEST_NBDKIT("disk-network-source-curl-nbdkit-backing", QEMU_NBDKIT_CAPS_PLUGIN_CURL);
     DO_TEST_CAPS_LATEST("disk-network-nfs");
     driver.config->vxhsTLS = 1;
     driver.config->nbdTLSx509secretUUID = g_strdup("6fd3f62d-9fe7-4a4e-a869-7acd6376d8ea");
@@ -1312,7 +1316,9 @@ mymain(void)
     DO_TEST_CAPS_LATEST("disk-network-tlsx509-nbd-hostname");
     DO_TEST_CAPS_VER("disk-network-tlsx509-vxhs", "5.0.0");
     DO_TEST_CAPS_LATEST("disk-network-http");
+    DO_TEST_CAPS_LATEST_NBDKIT("disk-network-http-nbdkit", QEMU_NBDKIT_CAPS_PLUGIN_CURL);
     DO_TEST_CAPS_LATEST("disk-network-ssh");
+    DO_TEST_CAPS_LATEST_NBDKIT("disk-network-ssh-nbdkit", QEMU_NBDKIT_CAPS_PLUGIN_SSH);
     driver.config->vxhsTLS = 0;
     VIR_FREE(driver.config->vxhsTLSx509certdir);
     DO_TEST_CAPS_LATEST("disk-no-boot");
-- 
2.39.1
Re: [libvirt PATCH v5 21/32] qemu: use nbdkit to serve network disks if available
Posted by Peter Krempa 2 years, 11 months ago
On Tue, Feb 14, 2023 at 11:08:08 -0600, Jonathon Jongsma wrote:
> For virStorageSource objects that contain an nbdkitProcess, start that
> nbdkit process to serve that network drive and then pass the nbdkit
> socket to qemu rather than sending the network url to qemu directly.
> 
> Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
> ---

[...]

> @@ -308,10 +327,36 @@ qemuExtDevicesHasDevice(virDomainDef *def)
>              return true;
>      }
>  
> +    for (i = 0; i < def->ndisks; i++) {
> +        qemuDomainStorageSourcePrivate *priv =
> +            QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(def->disks[i]->src);
> +        if (priv->nbdkitProcess)
> +            return true;

This is insufficient. Also the backing images of 'src' can have 'nbdkit'

> +    }
> +
> +
>      return false;
>  }
>  
>  
> +/* recursively setup nbdkit cgroups for backing chain of src */
> +static int qemuExtDevicesSetupCgroupNbdkit(virStorageSource *src,
> +                                           virCgroup *cgroup)

Header style doesn't conform to our coding style and the rest of the
file.

> +{
> +        qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
> +
> +        if (src->backingStore)
> +            if (qemuExtDevicesSetupCgroupNbdkit(src->backingStore, cgroup) < 0)
> +                return -1;
> +
> +        if (priv && priv->nbdkitProcess &&
> +            qemuNbdkitProcessSetupCgroup(priv->nbdkitProcess, cgroup) < 0)
> +            return -1;

Any particular reason why you need to setup the cgroups starting from
the bottom-most image?

Specifically for nbdkit it should not matter too much as we need to
start them before starting qemu anyways.

In QEMU we indeed must construct the backing chain from the bottom image
first.

> +
> +        return 0;
> +}
> +
> +
>  int
>  qemuExtDevicesSetupCgroup(virQEMUDriver *driver,
>                            virDomainObj *vm,

[...]

> +/* recursively start nbdkit for backing chain of src */
> +int
> +qemuNbdkitStartStorageSource(virQEMUDriver *driver,
> +                             virDomainObj *vm,
> +                             virStorageSource *src)
> +{
> +    virStorageSource *backing;
> +
> +    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
> +        if (qemuNbdkitStartStorageSourceOne(driver, vm, backing) < 0)
> +            return -1;
> +
> +    return qemuNbdkitStartStorageSourceOne(driver, vm, src);
> +}

This is again the weird iteration mechanism where backing images are
iterated from top to bottom and then the topmost image is iterated.

Make it into a linear one please. Either from the end or from the start.

> +/* recursively stop nbdkit processes for backing chain of src */
> +void
> +qemuNbdkitStopStorageSource(virStorageSource *src)
> +{
> +    virStorageSource *backing;
> +
> +    qemuNbdkitStopStorageSourceOne(src);
> +
> +    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
> +        qemuNbdkitStopStorageSourceOne(backing);
> +}

Same here.


Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Re: [libvirt PATCH v5 21/32] qemu: use nbdkit to serve network disks if available
Posted by Jonathon Jongsma 2 years, 11 months ago
On 2/16/23 9:55 AM, Peter Krempa wrote:
> On Tue, Feb 14, 2023 at 11:08:08 -0600, Jonathon Jongsma wrote:
>> For virStorageSource objects that contain an nbdkitProcess, start that
>> nbdkit process to serve that network drive and then pass the nbdkit
>> socket to qemu rather than sending the network url to qemu directly.
>>
>> Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
>> ---
> 
> [...]
> 
>> @@ -308,10 +327,36 @@ qemuExtDevicesHasDevice(virDomainDef *def)
>>               return true;
>>       }
>>   
>> +    for (i = 0; i < def->ndisks; i++) {
>> +        qemuDomainStorageSourcePrivate *priv =
>> +            QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(def->disks[i]->src);
>> +        if (priv->nbdkitProcess)
>> +            return true;
> 
> This is insufficient. Also the backing images of 'src' can have 'nbdkit'
> 
>> +    }
>> +
>> +
>>       return false;
>>   }
>>   
>>   
>> +/* recursively setup nbdkit cgroups for backing chain of src */
>> +static int qemuExtDevicesSetupCgroupNbdkit(virStorageSource *src,
>> +                                           virCgroup *cgroup)
> 
> Header style doesn't conform to our coding style and the rest of the
> file.
> 
>> +{
>> +        qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
>> +
>> +        if (src->backingStore)
>> +            if (qemuExtDevicesSetupCgroupNbdkit(src->backingStore, cgroup) < 0)
>> +                return -1;
>> +
>> +        if (priv && priv->nbdkitProcess &&
>> +            qemuNbdkitProcessSetupCgroup(priv->nbdkitProcess, cgroup) < 0)
>> +            return -1;
> 
> Any particular reason why you need to setup the cgroups starting from
> the bottom-most image?
> 
> Specifically for nbdkit it should not matter too much as we need to
> start them before starting qemu anyways.
> 
> In QEMU we indeed must construct the backing chain from the bottom image
> first.

I just figured that logically the backing source should always be 
available to the upper layers, so I would try to be consistent and 
always set them up that way. But I agree that this is not really 
necessary here and makes the code less concise, so I'll change them.

> 
>> +
>> +        return 0;
>> +}
>> +
>> +
>>   int
>>   qemuExtDevicesSetupCgroup(virQEMUDriver *driver,
>>                             virDomainObj *vm,
> 
> [...]
> 
>> +/* recursively start nbdkit for backing chain of src */
>> +int
>> +qemuNbdkitStartStorageSource(virQEMUDriver *driver,
>> +                             virDomainObj *vm,
>> +                             virStorageSource *src)
>> +{
>> +    virStorageSource *backing;
>> +
>> +    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
>> +        if (qemuNbdkitStartStorageSourceOne(driver, vm, backing) < 0)
>> +            return -1;
>> +
>> +    return qemuNbdkitStartStorageSourceOne(driver, vm, src);
>> +}
> 
> This is again the weird iteration mechanism where backing images are
> iterated from top to bottom and then the topmost image is iterated.
> 
> Make it into a linear one please. Either from the end or from the start.
> 
>> +/* recursively stop nbdkit processes for backing chain of src */
>> +void
>> +qemuNbdkitStopStorageSource(virStorageSource *src)
>> +{
>> +    virStorageSource *backing;
>> +
>> +    qemuNbdkitStopStorageSourceOne(src);
>> +
>> +    for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
>> +        qemuNbdkitStopStorageSourceOne(backing);
>> +}
> 
> Same here.
> 
> 
> Reviewed-by: Peter Krempa <pkrempa@redhat.com>
>
Re: [libvirt PATCH v5 21/32] qemu: use nbdkit to serve network disks if available
Posted by Peter Krempa 2 years, 11 months ago
On Thu, Feb 16, 2023 at 10:13:27 -0600, Jonathon Jongsma wrote:
> On 2/16/23 9:55 AM, Peter Krempa wrote:
> > On Tue, Feb 14, 2023 at 11:08:08 -0600, Jonathon Jongsma wrote:
> > > For virStorageSource objects that contain an nbdkitProcess, start that
> > > nbdkit process to serve that network drive and then pass the nbdkit
> > > socket to qemu rather than sending the network url to qemu directly.
> > > 
> > > Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
> > > ---

[...]

> > 
> > Any particular reason why you need to setup the cgroups starting from
> > the bottom-most image?
> > 
> > Specifically for nbdkit it should not matter too much as we need to
> > start them before starting qemu anyways.
> > 
> > In QEMU we indeed must construct the backing chain from the bottom image
> > first.
> 
> I just figured that logically the backing source should always be available
> to the upper layers, so I would try to be consistent and always set them up
> that way. But I agree that this is not really necessary here and makes the
> code less concise, so I'll change them.

Well, that makes sense, but for actual starting of nbdkit you use this
algorithm:



  for (backing = src->backingStore; backing != NULL; backing = backing->backingStore)
     if (qemuNbdkitStartStorageSourceOne(driver, vm, backing) < 0)
        return -1;

   return qemuNbdkitStartStorageSourceOne(driver, vm, src);

So a chain of images such as

 [qemu] -> top -> b1 -> b2

Gets started as

b1, b2, top

But yeah, it doesn't matter as long as you start everything before
talking to qemu as nbdkit doesn't actually try to do anything with the
actual image itself.