[libvirt RFC 0/2] qemu: handle 'raw' format options 'size' and 'offset'

See patch 2 please. Peter Krempa (2): tests: storage: Add test data for json specified raw image with offset/size qemu: Handle 'offset' and 'size' parameters of 'raw' driver src/conf/domain_conf.c | 19 +++++++++++++++++++ src/qemu/qemu_block.c | 10 ++++------ src/util/virstoragefile.c | 21 +++++++++++++++++---- src/util/virstoragefile.h | 4 ++++ tests/virstoragetest.c | 15 +++++++++++++++ 5 files changed, 59 insertions(+), 10 deletions(-) -- 2.24.1

QEMU allows specifying the offset and size into a raw file to expose a sub-slice of the image to the guest with the raw driver. Libvirt currently doesn't support it but we can add test case for future reference. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tests/virstoragetest.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 2862758752..58e15c0cc4 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -1559,6 +1559,16 @@ mymain(void) "<source protocol='vxhs' name='c6718f6b-0401-441d-a8c3-1f0064d75ee0'>\n" " <host name='example.com' port='9999'/>\n" "</source>\n"); + TEST_BACKING_PARSE_FULL("json:{ \"file\": { \"driver\": \"raw\"," + "\"offset\": 10752," + "\"size\": 4063232," + "\"file\": { \"driver\": \"file\"," + "\"filename\": \"/tmp/testfle\"" + "}" + "}" + "}", + "<source file='/tmp/testfle'/>\n", 0); + #endif /* WITH_YAJL */ cleanup: -- 2.24.1

Prior to using -blockdev if a user created an overlay file with some json properties we'd not put them on the command line at all. This meant that qemu then obeyed them. Now since we do configure all known backing files, we must also pass in all of the parameters. Otherwise we'd configure something else than the user expected. The most prominent one now is the 'offset' and 'size' of the raw driver which we completely ignored. This is an RFC to implement properly the parameters above to see whether upstream accepts it or not. This patch is not meant to be pushed like this but to start discussion. Another option is to detect when we know that the image can't be passed through (we do that partially for images with authentication) and possibly just skip formatting them and let qemu handle it. But that really seems like a step back now that we've put this much effort into blockdev. Obviously it's missing the parser bits and schema and docs but I'd like to know whether the design of the XML makes any sense. The bug was reported by Rich Jones here: https://bugzilla.redhat.com/show_bug.cgi?id=1791788 --- src/conf/domain_conf.c | 19 +++++++++++++++++++ src/qemu/qemu_block.c | 10 ++++------ src/util/virstoragefile.c | 21 +++++++++++++++++---- src/util/virstoragefile.h | 4 ++++ tests/virstoragetest.c | 7 ++++++- 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2a8a04cacb..62872dfb2f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -24183,6 +24183,23 @@ virDomainDiskSourceFormatPrivateData(virBufferPtr buf, } +static void +virDomainDiskSourceFormatOptions(virBufferPtr buf, + virStorageSourcePtr src) +{ + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + + if (src->format == VIR_STORAGE_FILE_RAW) { + if (src->offset > 0) + virBufferAsprintf(&attrBuf, " offset='%llu'", src->offset); + if (src->size > 0) + virBufferAsprintf(&attrBuf, " size='%llu'", src->size); + } + + virXMLFormatElement(buf, "options", &attrBuf, NULL); +} + + /** * virDomainDiskSourceFormat: * @buf: output buffer @@ -24253,6 +24270,8 @@ virDomainDiskSourceFormat(virBufferPtr buf, return -1; } + virDomainDiskSourceFormatOptions(&childBuf, src); + if (src->type != VIR_STORAGE_TYPE_NETWORK) virDomainSourceDefFormatSeclabel(&childBuf, src->nseclabels, src->seclabels, flags); diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index eab21bc107..21977a319e 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -1190,14 +1190,12 @@ qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src, 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) + "S:key-secret", secretalias, + "P:offset", src->offset, + "P:size", src->size, + NULL) < 0) return -1; return 0; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 1397f532fd..22a243a2eb 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2270,6 +2270,8 @@ virStorageSourceCopy(const virStorageSource *src, def->type = src->type; def->protocol = src->protocol; def->format = src->format; + def->offset = src->offset; + def->size = src->size; def->capacity = src->capacity; def->allocation = src->allocation; def->has_allocation = src->has_allocation; @@ -3511,10 +3513,21 @@ virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, virJSONValuePtr json, int opaque G_GNUC_UNUSED) { - /* There are no interesting attributes in raw driver. - * Treat it as pass-through. - */ - return virStorageSourceParseBackingJSONInternal(src, json); + int rc; + + if ((rc = virStorageSourceParseBackingJSONInternal(src, json)) < 0) + return rc; + + if ((virJSONValueObjectHasKey(json, "offset") && + virJSONValueObjectGetNumberUlong(json, "offset", &src->offset) < 0) || + (virJSONValueObjectHasKey(json, "size") && + virJSONValueObjectGetNumberUlong(json, "size", &src->size) < 0)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'size' or 'offset' property of 'raw' driver")); + return -1; + } + + return rc; } diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 39e50a989d..5a86f32e95 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -286,6 +286,10 @@ struct _virStorageSource { bool nocow; bool sparse; + /* properties of the raw driver. Unspecified if 0. */ + unsigned long long offset; + unsigned long long size; + virStoragePermsPtr perms; virStorageTimestampsPtr timestamps; unsigned long long capacity; /* in bytes, 0 if unknown */ diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 58e15c0cc4..d9af2628d0 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -606,6 +606,9 @@ testBackingParse(const void *args) if (!src) return 0; + /* allow tests pass format */ + src->format = VIR_STORAGE_FILE_RAW; + if (src && !data->expect) { fprintf(stderr, "parsing of backing store string '%s' should " "have failed\n", data->backing); @@ -1567,7 +1570,9 @@ mymain(void) "}" "}" "}", - "<source file='/tmp/testfle'/>\n", 0); + "<source file='/tmp/testfle'>\n" + " <options offset='10752' size='4063232'/>\n" + "</source>\n", 0); #endif /* WITH_YAJL */ -- 2.24.1

On Thu, Jan 16, 2020 at 05:56:40PM +0100, Peter Krempa wrote:
Prior to using -blockdev if a user created an overlay file with some json properties we'd not put them on the command line at all. This meant that qemu then obeyed them.
Now since we do configure all known backing files, we must also pass in all of the parameters. Otherwise we'd configure something else than the user expected.
The most prominent one now is the 'offset' and 'size' of the raw driver which we completely ignored.
This is an RFC to implement properly the parameters above to see whether upstream accepts it or not.
This patch is not meant to be pushed like this but to start discussion.
Another option is to detect when we know that the image can't be passed through (we do that partially for images with authentication) and possibly just skip formatting them and let qemu handle it. But that really seems like a step back now that we've put this much effort into blockdev.
Obviously it's missing the parser bits and schema and docs but I'd like to know whether the design of the XML makes any sense.
So you're suggesting <disk type="file" media="disk"> ... <source file='/tmp/testfle'> <options offset='10752' size='4063232'/> </source> ... </disk> First a very minor question is whether to have a general <options> element vs a more specific <slice offset="nnn" size="nnn"/>. I would probably have gone for the latter myself, so I feel like more general options elements end up being a dumping ground. Now the more important question... The offset/size applies to the 'raw' driver in QEMU, which IIUC, is something that can be plugged into the block layer driver stack at any point - either above or below a format driver, or a above a protocol protocol driver IIUC this lets us conceptually support many things 1. Guest -> raw + offet/set -> file 2. Guest -> qcow2 -> raw + offet/set -> file 3. Guest -> raw + offset/size -> qcow2 -> file 4. Guest -> raw + offset/size -> qcow2 -> raw + offset/set -> file Add in protocol drivers (iscsi) 5. Guest -> raw + offet/set -> iscsi 6. Guest -> qcow2 -> raw + offet/set -> iscsi 7. Guest -> raw + offset/size -> qcow2 -> iscsi 8. Guest -> raw + offset/size -> qcow2 -> raw + offset/set -> iscsi Add in backing files 9. Guest -> qcow2 -> backing raw + offset/size -> file 10. Guest -> qcow2 -> backing qcow2 -> raw + offset/size -> file 11. Guest -> raw + offset/size qcow2 -> backing raw + offset/size -> file 12. Guest -> raw + offset/size qcow2 -> backing qcow2 -> raw + offet/size -> file Some of these combinations look much more likely to be used than others. ie, i'd expect offset+size on top of the protocol driver to be much more common than offset+size on top of a format driver. So the question is do we need to cope with both of those possibilities, or can we restrict ourselves to only worry about offset+size on top of the prootocol driver ? With the syntax you're suggesting we can only cope with it on top of the protocol driver, or on top of the format driver, but not both. So we have pick one, or decide on a syntax that can cope with both. One way we can support both is to say that size/offset as a child of <source> refers to the protocol driver, and that size/offset at same level as <source> refers to the format driver. eg <disk type='file' device='disk'> <driver name='qemu' type='qcow2' queues='4'/> <options size="nnn" offset="nnn"/> <source file='/var/lib/libvirt/images/domain.qcow'> <options size="nnn" offset="nnn"/> </source> <backingStore type='file'> <options size="nnn" offset="nnn"/> <format type='qcow2'/> <source file='/var/lib/libvirt/images/snapshot.qcow'> <options size="nnn" offset="nnn"/> </source> <backingStore type='block'> <format type='raw'/> <options size="nnn" offset="nnn"/> <source dev='/dev/mapper/base'> <options size="nnn" offset="nnn"/> </source> <backingStore/> </backingStore> </backingStore> <target dev='vdd' bus='virtio'/> </disk> If we think this is a viable approach, then it has the nice characteristic that we can be lazy and only implement the protocol driver size/offset as a child of <source> for now. We'll know that we have the design flexiblility to add format driver size/offset in future, if anyone ever turns up and requests it.
The bug was reported by Rich Jones here: https://bugzilla.redhat.com/show_bug.cgi?id=1791788 --- src/conf/domain_conf.c | 19 +++++++++++++++++++ src/qemu/qemu_block.c | 10 ++++------ src/util/virstoragefile.c | 21 +++++++++++++++++---- src/util/virstoragefile.h | 4 ++++ tests/virstoragetest.c | 7 ++++++- 5 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2a8a04cacb..62872dfb2f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -24183,6 +24183,23 @@ virDomainDiskSourceFormatPrivateData(virBufferPtr buf, }
+static void +virDomainDiskSourceFormatOptions(virBufferPtr buf, + virStorageSourcePtr src) +{ + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + + if (src->format == VIR_STORAGE_FILE_RAW) { + if (src->offset > 0) + virBufferAsprintf(&attrBuf, " offset='%llu'", src->offset); + if (src->size > 0) + virBufferAsprintf(&attrBuf, " size='%llu'", src->size); + } + + virXMLFormatElement(buf, "options", &attrBuf, NULL); +} + + /** * virDomainDiskSourceFormat: * @buf: output buffer @@ -24253,6 +24270,8 @@ virDomainDiskSourceFormat(virBufferPtr buf, return -1; }
+ virDomainDiskSourceFormatOptions(&childBuf, src); + if (src->type != VIR_STORAGE_TYPE_NETWORK) virDomainSourceDefFormatSeclabel(&childBuf, src->nseclabels, src->seclabels, flags); diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index eab21bc107..21977a319e 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -1190,14 +1190,12 @@ qemuBlockStorageSourceGetFormatRawProps(virStorageSourcePtr src, 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) + "S:key-secret", secretalias, + "P:offset", src->offset, + "P:size", src->size, + NULL) < 0) return -1;
return 0; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 1397f532fd..22a243a2eb 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2270,6 +2270,8 @@ virStorageSourceCopy(const virStorageSource *src, def->type = src->type; def->protocol = src->protocol; def->format = src->format; + def->offset = src->offset; + def->size = src->size; def->capacity = src->capacity; def->allocation = src->allocation; def->has_allocation = src->has_allocation; @@ -3511,10 +3513,21 @@ virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, virJSONValuePtr json, int opaque G_GNUC_UNUSED) { - /* There are no interesting attributes in raw driver. - * Treat it as pass-through. - */ - return virStorageSourceParseBackingJSONInternal(src, json); + int rc; + + if ((rc = virStorageSourceParseBackingJSONInternal(src, json)) < 0) + return rc; + + if ((virJSONValueObjectHasKey(json, "offset") && + virJSONValueObjectGetNumberUlong(json, "offset", &src->offset) < 0) || + (virJSONValueObjectHasKey(json, "size") && + virJSONValueObjectGetNumberUlong(json, "size", &src->size) < 0)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'size' or 'offset' property of 'raw' driver")); + return -1; + } + + return rc; }
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 39e50a989d..5a86f32e95 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -286,6 +286,10 @@ struct _virStorageSource { bool nocow; bool sparse;
+ /* properties of the raw driver. Unspecified if 0. */ + unsigned long long offset; + unsigned long long size; + virStoragePermsPtr perms; virStorageTimestampsPtr timestamps; unsigned long long capacity; /* in bytes, 0 if unknown */ diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 58e15c0cc4..d9af2628d0 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -606,6 +606,9 @@ testBackingParse(const void *args) if (!src) return 0;
+ /* allow tests pass format */ + src->format = VIR_STORAGE_FILE_RAW; + if (src && !data->expect) { fprintf(stderr, "parsing of backing store string '%s' should " "have failed\n", data->backing); @@ -1567,7 +1570,9 @@ mymain(void) "}" "}" "}", - "<source file='/tmp/testfle'/>\n", 0); + "<source file='/tmp/testfle'>\n" + " <options offset='10752' size='4063232'/>\n" + "</source>\n", 0);
#endif /* WITH_YAJL */
-- 2.24.1
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On Thu, Jan 16, 2020 at 05:56:38PM +0100, Peter Krempa wrote:
See patch 2 please.
Peter Krempa (2): tests: storage: Add test data for json specified raw image with offset/size qemu: Handle 'offset' and 'size' parameters of 'raw' driver
src/conf/domain_conf.c | 19 +++++++++++++++++++ src/qemu/qemu_block.c | 10 ++++------ src/util/virstoragefile.c | 21 +++++++++++++++++---- src/util/virstoragefile.h | 4 ++++ tests/virstoragetest.c | 15 +++++++++++++++ 5 files changed, 59 insertions(+), 10 deletions(-)
Can confirm that this fixes the issue in the virt-v2v test suite. Tested-by: Richard W.M. Jones <rjones@redhat.com> Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html

On Thu, Jan 16, 2020 at 05:17:43PM +0000, Richard W.M. Jones wrote:
On Thu, Jan 16, 2020 at 05:56:38PM +0100, Peter Krempa wrote:
See patch 2 please.
Peter Krempa (2): tests: storage: Add test data for json specified raw image with offset/size qemu: Handle 'offset' and 'size' parameters of 'raw' driver
src/conf/domain_conf.c | 19 +++++++++++++++++++ src/qemu/qemu_block.c | 10 ++++------ src/util/virstoragefile.c | 21 +++++++++++++++++---- src/util/virstoragefile.h | 4 ++++ tests/virstoragetest.c | 15 +++++++++++++++ 5 files changed, 59 insertions(+), 10 deletions(-)
Can confirm that this fixes the issue in the virt-v2v test suite.
Actually I'm sorry to say it didn't fix it. I'm not sure what exactly happened there. I might have missed the error message or I ran the test suite in the wrong way, or perhaps the error is intermittent. Anyway I have to withdraw the "Tested-by" until I can take a closer look at this. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
participants (3)
-
Daniel P. Berrangé
-
Peter Krempa
-
Richard W.M. Jones