From: Akihiko Odaki <akihiko.odaki(a)daynix.com>
usb-storage is a compound device that automatically creates a USB mass
storage device and a SCSI device as its backend. Unfortunately it lacks
some configuration options that are usually present with a SCSI device,
and cannot represent CD-ROM in particular.
Replace usb-storage with usb-bot, which can be combined with a manually
created SCSI device. libvirt will configure the SCSI device in a way
identical with how QEMU does for usb-storage except that now it respects
a configuration option to represent CD-ROM.
Resolves:
https://gitlab.com/libvirt/libvirt/-/issues/368
Signed-off-by: Akihiko Odaki <akihiko.odaki(a)daynix.com>
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/qemu/qemu_alias.c | 20 ++++-
src/qemu/qemu_capabilities.c | 3 +-
src/qemu/qemu_command.c | 86 ++++++++++++++++---
src/qemu/qemu_command.h | 5 ++
src/qemu/qemu_hotplug.c | 18 ++++
src/qemu/qemu_validate.c | 38 ++++++--
tests/qemuhotplugtest.c | 4 +-
...om-usb-empty.x86_64-latest.abi-update.args | 3 +-
.../disk-usb-device-model.x86_64-latest.args | 6 +-
...k-usb-device.x86_64-latest.abi-update.args | 12 ++-
10 files changed, 167 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c
index 9d39ebd63d..67cbddd470 100644
--- a/src/qemu/qemu_alias.c
+++ b/src/qemu/qemu_alias.c
@@ -268,8 +268,24 @@ qemuAssignDeviceDiskAlias(virDomainDef *def,
break;
case VIR_DOMAIN_DISK_BUS_USB:
- diskPriv->qomName =
g_strdup_printf("/machine/peripheral/%s/%s.0/legacy[0]",
- disk->info.alias,
disk->info.alias);
+ switch (disk->model) {
+ case VIR_DOMAIN_DISK_MODEL_USB_STORAGE:
+ diskPriv->qomName =
g_strdup_printf("/machine/peripheral/%s/%s.0/legacy[0]",
+ disk->info.alias,
disk->info.alias);
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_USB_BOT:
+ diskPriv->qomName =
g_strdup_printf("/machine/peripheral/%s-disk",
+ disk->info.alias);
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_DEFAULT:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_NON_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_LAST:
+ break;
+ }
break;
case VIR_DOMAIN_DISK_BUS_XEN:
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index f480060e50..ef6ec661ff 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -6472,7 +6472,8 @@ virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCaps *qemuCaps,
VIR_DOMAIN_DISK_BUS_VIRTIO,
/* VIR_DOMAIN_DISK_BUS_SD */);
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE))
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE) ||
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_BOT))
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI))
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 910242a389..98fd6fee85 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1626,6 +1626,33 @@ qemuBuildIothreadMappingProps(GSList *iothreads)
return g_steal_pointer(&ret);
}
+int
+qemuBuildDiskBusProps(const virDomainDef *def,
+ const virDomainDiskDef *disk,
+ virJSONValue **propsRet)
+{
+ g_autoptr(virJSONValue) props = NULL;
+
+ *propsRet = NULL;
+
+ if (disk->bus != VIR_DOMAIN_DISK_BUS_USB ||
+ disk->model != VIR_DOMAIN_DISK_MODEL_USB_BOT)
+ return 0;
+
+ if (virJSONValueObjectAdd(&props,
+ "s:driver", "usb-bot",
+ "s:id", disk->info.alias,
+ "S:serial", disk->serial,
+ NULL) < 0)
+ return -1;
+
+ if (qemuBuildDeviceAddressProps(props, def, &disk->info) < 0)
+ return -1;
+
+ *propsRet = g_steal_pointer(&props);
+
+ return 0;
+}
virJSONValue *
qemuBuildDiskDeviceProps(const virDomainDef *def,
@@ -1652,6 +1679,18 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
const char *rpolicy = NULL;
const char *model = NULL;
const char *product = NULL;
+ const char *alias = disk->info.alias;
+ g_autofree char *usbdiskalias = NULL;
+ const virDomainDeviceInfo *deviceinfo = &disk->info;
+ virDomainDeviceInfo usbSCSIinfo = {
+ .type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE,
+ .addr.drive = { .diskbus = VIR_DOMAIN_DISK_BUS_USB },
+ .effectiveBootIndex = deviceinfo->effectiveBootIndex,
+ .alias = deviceinfo->alias,
+ };
+
+ if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE)
+ disk->info.addr.drive.diskbus = disk->bus;
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
@@ -1735,13 +1774,35 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
break;
case VIR_DOMAIN_DISK_BUS_USB:
- driver = "usb-storage";
+ switch (disk->model) {
+ case VIR_DOMAIN_DISK_MODEL_USB_STORAGE:
+ driver = "usb-storage";
- if (disk->removable == VIR_TRISTATE_SWITCH_ABSENT)
- removable = VIR_TRISTATE_SWITCH_OFF;
- else
- removable = disk->removable;
+ if (disk->removable == VIR_TRISTATE_SWITCH_ABSENT)
+ removable = VIR_TRISTATE_SWITCH_OFF;
+ else
+ removable = disk->removable;
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_USB_BOT:
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
+ driver = "scsi-cd";
+ } else {
+ driver = "scsi-hd";
+ removable = disk->removable;
+ }
+ deviceinfo = &usbSCSIinfo;
+ alias = usbdiskalias = g_strdup_printf("%s-device",
disk->info.alias);
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_DEFAULT:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_NON_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_LAST:
+ break;
+ }
break;
case VIR_DOMAIN_DISK_BUS_FDC:
@@ -1771,10 +1832,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
return NULL;
}
- if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE)
- disk->info.addr.drive.diskbus = disk->bus;
-
- if (qemuBuildDeviceAddressProps(props, def, &disk->info) < 0)
+ if (qemuBuildDeviceAddressProps(props, def, deviceinfo) < 0)
return NULL;
if (disk->src->shared)
@@ -1836,7 +1894,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
"T:share-rw", shareRW,
"S:drive", drive,
"S:chardev", chardev,
- "s:id", disk->info.alias,
+ "s:id", alias,
"p:bootindex", bootindex,
"S:loadparm", bootLoadparm,
"p:logical_block_size", logical_block_size,
@@ -2201,6 +2259,7 @@ qemuBuildDiskCommandLine(virCommand *cmd,
virQEMUCaps *qemuCaps)
{
g_autoptr(virJSONValue) devprops = NULL;
+ g_autoptr(virJSONValue) busprops = NULL;
if (qemuBuildDiskSourceCommandLine(cmd, disk, qemuCaps) < 0)
return -1;
@@ -2216,6 +2275,13 @@ qemuBuildDiskCommandLine(virCommand *cmd,
if (qemuCommandAddExtDevice(cmd, &disk->info, def, qemuCaps) < 0)
return -1;
+ if (qemuBuildDiskBusProps(def, disk, &busprops) < 0)
+ return -1;
+
+ if (busprops &&
+ qemuBuildDeviceCommandlineFromJSON(cmd, busprops, def, qemuCaps) < 0)
+ return -1;
+
if (!(devprops = qemuBuildDiskDeviceProps(def, disk, qemuCaps)))
return -1;
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 2d43cf5506..574dffdc96 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -129,6 +129,11 @@ qemuBuildThrottleFiltersAttachPrepareBlockdev(virDomainDiskDef
*disk);
qemuBlockThrottleFiltersData *
qemuBuildThrottleFiltersDetachPrepareBlockdev(virDomainDiskDef *disk);
+int
+qemuBuildDiskBusProps(const virDomainDef *def,
+ const virDomainDiskDef *disk,
+ virJSONValue **propsRet);
+
virJSONValue *
qemuBuildDiskDeviceProps(const virDomainDef *def,
virDomainDiskDef *disk,
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index e1ed8181e3..4f6a3d3414 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -703,8 +703,10 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm,
g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
g_autoptr(qemuBlockThrottleFiltersData) filterData = NULL;
qemuDomainObjPrivate *priv = vm->privateData;
+ g_autoptr(virJSONValue) busprops = NULL;
g_autoptr(virJSONValue) devprops = NULL;
bool extensionDeviceAttached = false;
+ bool busAdded = false;
int rc;
g_autoptr(qemuSnapshotDiskContext) transientDiskSnapshotCtxt = NULL;
bool origReadonly = disk->src->readonly;
@@ -774,6 +776,9 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm,
}
}
+ if (qemuBuildDiskBusProps(vm->def, disk, &busprops) < 0)
+ goto rollback;
+
if (!(devprops = qemuBuildDiskDeviceProps(vm->def, disk, priv->qemuCaps)))
goto rollback;
@@ -783,6 +788,10 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm,
if ((rc = qemuDomainAttachExtensionDevice(priv->mon, &disk->info)) == 0)
extensionDeviceAttached = true;
+ if (rc == 0 && busprops &&
+ (rc = qemuMonitorAddDeviceProps(priv->mon, &busprops)) == 0)
+ busAdded = true;
+
if (rc == 0)
rc = qemuMonitorAddDeviceProps(priv->mon, &devprops);
@@ -811,6 +820,12 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm,
}
}
+ if (rc == 0) {
+ if (disk->bus == VIR_DOMAIN_DISK_BUS_USB &&
+ disk->model == VIR_DOMAIN_DISK_MODEL_USB_BOT)
+ rc = qemuMonitorSetUSBDiskAttached(priv->mon, disk->info.alias);
+ }
+
qemuDomainObjExitMonitor(vm);
if (rc < 0)
@@ -822,6 +837,9 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm,
if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
return -1;
+ if (busAdded)
+ ignore_value(qemuMonitorDelDevice(priv->mon, disk->info.alias));
+
if (extensionDeviceAttached)
ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &disk->info));
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index 7e0e4db516..4b8d4fc692 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -3183,16 +3183,40 @@ qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef
*disk,
break;
case VIR_DOMAIN_DISK_BUS_USB:
- if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("This QEMU doesn't support '-device
usb-storage'"));
+ switch (disk->model) {
+ case VIR_DOMAIN_DISK_MODEL_DEFAULT:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("USB disk model was not selected by selection
code"));
return -1;
- }
- if (disk->model == VIR_DOMAIN_DISK_MODEL_USB_STORAGE &&
- virStorageSourceIsEmpty(disk->src)) {
+ case VIR_DOMAIN_DISK_MODEL_USB_STORAGE:
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("This QEMU doesn't support '-device
usb-storage'"));
+ return -1;
+ }
+
+ if (virStorageSourceIsEmpty(disk->src)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("'usb' disk must not be empty"));
+ return -1;
+ }
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_USB_BOT:
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_BOT)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("This QEMU doesn't support '-device
usb-bot'"));
+ return -1;
+ }
+ break;
+
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO:
+ case VIR_DOMAIN_DISK_MODEL_VIRTIO_NON_TRANSITIONAL:
+ case VIR_DOMAIN_DISK_MODEL_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("'usb' disk must not be empty"));
+ _("USB disk supports only models: 'usb-storage',
'usb-bot''"));
return -1;
}
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
index fdb5093549..7881ccf327 100644
--- a/tests/qemuhotplugtest.c
+++ b/tests/qemuhotplugtest.c
@@ -746,7 +746,9 @@ mymain(void)
DO_TEST_ATTACH("x86_64", "base-live", "cdrom-usb",
false, true,
"blockdev-add", QMP_OK,
"device_add", QMP_OK,
- "query-block", QMP_EMPTY_ARRAY);
+ "device_add", QMP_OK,
+ "query-block", QMP_EMPTY_ARRAY,
+ "qom-set", QMP_OK);
DO_TEST_DETACH("x86_64", "base-live", "cdrom-usb",
true, true,
"device_del", QMP_OK);
DO_TEST_DETACH("x86_64", "base-live", "cdrom-usb",
false, false,
diff --git a/tests/qemuxmlconfdata/disk-cdrom-usb-empty.x86_64-latest.abi-update.args
b/tests/qemuxmlconfdata/disk-cdrom-usb-empty.x86_64-latest.abi-update.args
index cebc6e66e8..44bf27e9a4 100644
--- a/tests/qemuxmlconfdata/disk-cdrom-usb-empty.x86_64-latest.abi-update.args
+++ b/tests/qemuxmlconfdata/disk-cdrom-usb-empty.x86_64-latest.abi-update.args
@@ -27,7 +27,8 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
-no-shutdown \
-boot strict=on \
-device
'{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1","id":"usb-disk1","removable":false}'
\
+-device
'{"driver":"usb-bot","id":"usb-disk1","bus":"usb.0","port":"1"}'
\
+-device
'{"driver":"scsi-cd","bus":"usb-disk1.0","scsi-id":0,"lun":0,"id":"usb-disk1-device"}'
\
-audiodev
'{"id":"audio1","driver":"none"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/disk-usb-device-model.x86_64-latest.args
b/tests/qemuxmlconfdata/disk-usb-device-model.x86_64-latest.args
index 6d31319a49..f50b0d5849 100644
--- a/tests/qemuxmlconfdata/disk-usb-device-model.x86_64-latest.args
+++ b/tests/qemuxmlconfdata/disk-usb-device-model.x86_64-latest.args
@@ -33,9 +33,11 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
-blockdev
'{"driver":"file","filename":"/tmp/img2","node-name":"libvirt-5-storage","read-only":true}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.1","drive":"libvirt-5-storage","id":"usb-disk1","removable":false}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img3","node-name":"libvirt-4-storage","read-only":false}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.2","drive":"libvirt-4-storage","id":"usb-disk2","removable":false}'
\
+-device
'{"driver":"usb-bot","id":"usb-disk2","bus":"usb.0","port":"1.2"}'
\
+-device
'{"driver":"scsi-hd","bus":"usb-disk2.0","scsi-id":0,"lun":0,"drive":"libvirt-4-storage","id":"usb-disk2-device"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img4","node-name":"libvirt-3-storage","read-only":true}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.3","drive":"libvirt-3-storage","id":"usb-disk3","removable":false}'
\
+-device
'{"driver":"usb-bot","id":"usb-disk3","bus":"usb.0","port":"1.3"}'
\
+-device
'{"driver":"scsi-cd","bus":"usb-disk3.0","scsi-id":0,"lun":0,"drive":"libvirt-3-storage","id":"usb-disk3-device"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img5","node-name":"libvirt-2-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.4","drive":"libvirt-2-storage","id":"usb-disk4","removable":false}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img6","node-name":"libvirt-1-storage","read-only":true}'
\
diff --git a/tests/qemuxmlconfdata/disk-usb-device.x86_64-latest.abi-update.args
b/tests/qemuxmlconfdata/disk-usb-device.x86_64-latest.abi-update.args
index 079dfe5d99..d8208d29b6 100644
--- a/tests/qemuxmlconfdata/disk-usb-device.x86_64-latest.abi-update.args
+++ b/tests/qemuxmlconfdata/disk-usb-device.x86_64-latest.abi-update.args
@@ -32,19 +32,23 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
-blockdev
'{"driver":"file","filename":"/tmp/img1","node-name":"libvirt-12-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.1","drive":"libvirt-12-storage","id":"usb-disk0","bootindex":1,"removable":false}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img2","node-name":"libvirt-11-storage","read-only":true}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.2","drive":"libvirt-11-storage","id":"usb-disk1","removable":false}'
\
+-device
'{"driver":"usb-bot","id":"usb-disk1","bus":"usb.0","port":"1.2"}'
\
+-device
'{"driver":"scsi-cd","bus":"usb-disk1.0","scsi-id":0,"lun":0,"drive":"libvirt-11-storage","id":"usb-disk1-device"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img3","node-name":"libvirt-10-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.3","drive":"libvirt-10-storage","id":"usb-disk2","removable":false,"serial":"testserial1"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img4","node-name":"libvirt-9-storage","read-only":true}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.4","drive":"libvirt-9-storage","id":"usb-disk3","removable":false,"serial":"testserial2"}'
\
+-device
'{"driver":"usb-bot","id":"usb-disk3","serial":"testserial2","bus":"usb.0","port":"1.4"}'
\
+-device
'{"driver":"scsi-cd","bus":"usb-disk3.0","scsi-id":0,"lun":0,"drive":"libvirt-9-storage","id":"usb-disk3-device","serial":"testserial2"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img5","node-name":"libvirt-8-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.5","drive":"libvirt-8-storage","id":"ua-test1","removable":false}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img6","node-name":"libvirt-7-storage","read-only":true}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.6","drive":"libvirt-7-storage","id":"ua-test2","removable":false}'
\
+-device
'{"driver":"usb-bot","id":"ua-test2","bus":"usb.0","port":"1.6"}'
\
+-device
'{"driver":"scsi-cd","bus":"ua-test2.0","scsi-id":0,"lun":0,"drive":"libvirt-7-storage","id":"ua-test2-device"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img7","node-name":"libvirt-6-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"1.7","drive":"libvirt-6-storage","id":"ua-test3","removable":false,"serial":"testserial3"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img8","node-name":"libvirt-5-storage","read-only":true}'
\
--device
'{"driver":"usb-storage","bus":"usb.0","port":"1.8","drive":"libvirt-5-storage","id":"ua-test4","removable":false,"serial":"testserial4"}'
\
+-device
'{"driver":"usb-bot","id":"ua-test4","serial":"testserial4","bus":"usb.0","port":"1.8"}'
\
+-device
'{"driver":"scsi-cd","bus":"ua-test4.0","scsi-id":0,"lun":0,"drive":"libvirt-5-storage","id":"ua-test4-device","serial":"testserial4"}'
\
-blockdev
'{"driver":"file","filename":"/tmp/img9","node-name":"libvirt-4-storage","read-only":false}'
\
-device
'{"driver":"usb-storage","bus":"usb.0","port":"2.1","drive":"libvirt-4-storage","id":"usb-disk8","removable":true}'
\
-blockdev
'{"driver":"file","filename":"/tmp/imga","node-name":"libvirt-3-storage","read-only":false}'
\
--
2.49.0