[libvirt] [PATCH 00/23] Add block write threshold event

This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this. The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time. Peter Krempa (23): qemu: driver: Don't call qemuDomainDetermineDiskChain on block jobs util: buffer: Add API to set indentation level to a given value util: storage: Split out useful bits of virStorageFileParseChainIndex util: storage: Add variables for node names into virStorageSource lib: Introduce event for tracking disk backing file write threshold qemu: monitor: Add support for BLOCK_WRITE_THRESHOLD event qemu: domain: Add helper to lookup disk by node name qemu: domain: Add helper to generate indexed backing store names qemu: process: Wire up firing of the VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD event lib: Add API for setting the threshold size for VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD virsh: Implement 'domblkthreshold' command to call virDomainSetBlockThreshold qemu: domain: Add helper to look up disk soruce by the backing store string qemu: implement qemuDomainSetBlockThreshold qemu: capabilities: add capability for query-named-block-nodes qmp cmd qemu: monitor: Add monitor infrastructure for query-named-block-nodes qemu: block: Add code to allow detection of auto-allocated node names tests: qemumonitorjson: Add test case for node name detection code tests: qemumonitorjson: Add long backing chain test case for node name detection tests: qemumonitorjson: Add case for two disks sharing a backing image tests: qemumonitorjson: Add relative image names for node name detection tests: qemumonitorjson: Test node name detection on networked storage qemu: monitor: Extract the top level format node when querying disks qemu: block: Add code to detect node names when necessary daemon/remote.c | 45 + examples/object-events/event-test.c | 19 + include/libvirt/libvirt-domain.h | 36 + src/Makefile.am | 1 + src/conf/domain_event.c | 97 + src/conf/domain_event.h | 15 + src/driver-hypervisor.h | 8 + src/libvirt-domain.c | 51 + src/libvirt_private.syms | 5 + src/libvirt_public.syms | 1 + src/qemu/qemu_block.c | 378 ++++ src/qemu/qemu_block.h | 51 + src/qemu/qemu_blockjob.c | 2 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_domain.c | 102 + src/qemu/qemu_domain.h | 14 + src/qemu/qemu_driver.c | 80 +- src/qemu/qemu_monitor.c | 54 +- src/qemu/qemu_monitor.h | 20 + src/qemu/qemu_monitor_json.c | 91 +- src/qemu/qemu_monitor_json.h | 9 + src/qemu/qemu_process.c | 44 + src/remote/remote_driver.c | 34 + src/remote/remote_protocol.x | 33 +- src/remote_protocol-structs | 16 + src/util/virbuffer.c | 19 + src/util/virbuffer.h | 2 + src/util/virstoragefile.c | 108 +- src/util/virstoragefile.h | 15 + tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml | 2 + tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml | 2 + .../caps_2.6.0-gicv2.aarch64.xml | 2 + .../caps_2.6.0-gicv3.aarch64.xml | 2 + tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml | 2 + tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml | 2 + tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml | 2 + tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml | 2 + tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml | 2 + tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml | 2 + tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml | 2 + .../qemumonitorjson-nodename-1.json | 268 +++ .../qemumonitorjson-nodename-1.result | 15 + .../qemumonitorjson-nodename-2.json | 2270 ++++++++++++++++++++ .../qemumonitorjson-nodename-2.result | 60 + .../qemumonitorjson-nodename-gluster.json | 135 ++ .../qemumonitorjson-nodename-gluster.result | 10 + .../qemumonitorjson-nodename-relative.json | 554 +++++ .../qemumonitorjson-nodename-relative.result | 31 + .../qemumonitorjson-nodename-same-backing.json | 316 +++ .../qemumonitorjson-nodename-same-backing.result | 11 + tests/qemumonitorjsontest.c | 120 ++ tools/virsh-domain.c | 85 + tools/virsh.pod | 8 + 55 files changed, 5243 insertions(+), 19 deletions(-) create mode 100644 src/qemu/qemu_block.c create mode 100644 src/qemu/qemu_block.h create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result -- 2.12.0

Our code calls it when starting or re-starting the domain or when hotplugging the disk so there's nothing to be detected. --- src/qemu/qemu_driver.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2032fac71..06bd442ee 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16508,9 +16508,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, goto endjob; } - if (qemuDomainDetermineDiskChain(driver, vm, disk, false, true) < 0) - goto endjob; - /* clear the _SHALLOW flag if there is only one layer */ if (!disk->src->backingStore) flags &= ~VIR_DOMAIN_BLOCK_COPY_SHALLOW; @@ -16890,8 +16887,6 @@ qemuDomainBlockCommit(virDomainPtr dom, if (qemuDomainDiskBlockJobIsActive(disk)) goto endjob; - if (qemuDomainDetermineDiskChain(driver, vm, disk, false, true) < 0) - goto endjob; if (!top) topSource = disk->src; -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Our code calls it when starting or re-starting the domain or when hotplugging the disk so there's nothing to be detected. --- src/qemu/qemu_driver.c | 5 ----- 1 file changed, 5 deletions(-)
I think I added it here because block jobs have the tendency to rewrite the backing chain (think active commit, for example, which reduces the backing chain). Forcing a redetermination is therefore a way to ensure no stale data associated with the pre-job chain is left in memory. So I'm not sure this is correct. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Wed, Mar 22, 2017 at 14:21:29 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Our code calls it when starting or re-starting the domain or when hotplugging the disk so there's nothing to be detected. --- src/qemu/qemu_driver.c | 5 ----- 1 file changed, 5 deletions(-)
I think I added it here because block jobs have the tendency to rewrite the backing chain (think active commit, for example, which reduces the backing chain). Forcing a redetermination is therefore a way to ensure no stale data associated with the pre-job chain is left in memory.
So I'm not sure this is correct.
We re-detect the backing chain when the VM is started, when we reconnect to it and when we get any of the blockjob-related events. Additionally if the backing chain is rewritten the code would not be run since the argument that would rewrite any previously detected data is false.

On 03/23/2017 02:13 AM, Peter Krempa wrote:
On Wed, Mar 22, 2017 at 14:21:29 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Our code calls it when starting or re-starting the domain or when hotplugging the disk so there's nothing to be detected. --- src/qemu/qemu_driver.c | 5 ----- 1 file changed, 5 deletions(-)
I think I added it here because block jobs have the tendency to rewrite the backing chain (think active commit, for example, which reduces the backing chain). Forcing a redetermination is therefore a way to ensure no stale data associated with the pre-job chain is left in memory.
So I'm not sure this is correct.
We re-detect the backing chain when the VM is started, when we reconnect to it and when we get any of the blockjob-related events.
Do we still care about qemu old enough where events did not appear, and therefore where we had to fake events ourselves when we detect that the job is complete? If so, then we want to redetect the chain (but only for those older qemu); if not, then I can agree to your cleanup because redetection at the event is sufficient.
Additionally if the backing chain is rewritten the code would not be run since the argument that would rewrite any previously detected data is false.
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Mar 23, 2017 at 10:31:06 -0500, Eric Blake wrote:
On 03/23/2017 02:13 AM, Peter Krempa wrote:
On Wed, Mar 22, 2017 at 14:21:29 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Our code calls it when starting or re-starting the domain or when hotplugging the disk so there's nothing to be detected. --- src/qemu/qemu_driver.c | 5 ----- 1 file changed, 5 deletions(-)
I think I added it here because block jobs have the tendency to rewrite the backing chain (think active commit, for example, which reduces the backing chain). Forcing a redetermination is therefore a way to ensure no stale data associated with the pre-job chain is left in memory.
So I'm not sure this is correct.
We re-detect the backing chain when the VM is started, when we reconnect to it and when we get any of the blockjob-related events.
Do we still care about qemu old enough where events did not appear, and therefore where we had to fake events ourselves when we detect that the job is complete? If so, then we want to redetect the chain (but only for those older qemu); if not, then I can agree to your cleanup because redetection at the event is sufficient.
Since nobody complained that it would not work properly with such old qemus I don't think that anybody uses it.

It will be useful to set indentation level to 0 after formatting a nested structure rather than having to track the depth. --- src/libvirt_private.syms | 1 + src/util/virbuffer.c | 19 +++++++++++++++++++ src/util/virbuffer.h | 2 ++ 3 files changed, 22 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4efea0098..39e2b40c5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1303,6 +1303,7 @@ virBufferEscapeShell; virBufferEscapeString; virBufferFreeAndReset; virBufferGetIndent; +virBufferSetIndent; virBufferStrcat; virBufferTrim; virBufferURIEncodeString; diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c index 41d541b32..8f0f49d7f 100644 --- a/src/util/virbuffer.c +++ b/src/util/virbuffer.c @@ -88,6 +88,25 @@ virBufferAdjustIndent(virBufferPtr buf, int indent) buf->indent += indent; } + +/** + * virBufferAdjustIndent: + * @buf: the buffer + * @indent: new indentation size. + * + * Set the auto-indent value to @indent. See virBufferAdjustIndent on how auto + * indentation is applied. + */ +void +virBufferSetIndent(virBufferPtr buf, int indent) +{ + if (!buf || buf->error) + return; + + buf->indent = indent; +} + + /** * virBufferGetIndent: * @buf: the buffer diff --git a/src/util/virbuffer.h b/src/util/virbuffer.h index 94f14b5b1..d1b64ca3a 100644 --- a/src/util/virbuffer.h +++ b/src/util/virbuffer.h @@ -95,6 +95,8 @@ void virBufferURIEncodeString(virBufferPtr buf, const char *str); virBufferAdd(buf_, "" literal_string_ "", sizeof(literal_string_) - 1) void virBufferAdjustIndent(virBufferPtr buf, int indent); +void virBufferSetIndent(virBufferPtr, int indent); + int virBufferGetIndent(const virBuffer *buf, bool dynamic); void virBufferTrim(virBufferPtr buf, const char *trim, int len); -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
It will be useful to set indentation level to 0 after formatting a nested structure rather than having to track the depth. --- src/libvirt_private.syms | 1 + src/util/virbuffer.c | 19 +++++++++++++++++++ src/util/virbuffer.h | 2 ++ 3 files changed, 22 insertions(+)
+ +/** + * virBufferAdjustIndent:
Too much copy-and-paste.
+ * @buf: the buffer + * @indent: new indentation size. + * + * Set the auto-indent value to @indent. See virBufferAdjustIndent on how auto + * indentation is applied. + */ +void +virBufferSetIndent(virBufferPtr buf, int indent) +{ + if (!buf || buf->error) + return; + + buf->indent = indent; +} +
No testsuite addition? -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Wed, Mar 22, 2017 at 14:27:18 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
It will be useful to set indentation level to 0 after formatting a nested structure rather than having to track the depth. --- src/libvirt_private.syms | 1 + src/util/virbuffer.c | 19 +++++++++++++++++++ src/util/virbuffer.h | 2 ++ 3 files changed, 22 insertions(+)
+ +/** + * virBufferAdjustIndent:
Too much copy-and-paste.
+ * @buf: the buffer + * @indent: new indentation size. + * + * Set the auto-indent value to @indent. See virBufferAdjustIndent on how auto + * indentation is applied. + */ +void +virBufferSetIndent(virBufferPtr buf, int indent) +{ + if (!buf || buf->error) + return; + + buf->indent = indent; +} +
No testsuite addition?
For such trivial operation? I think we have pretty strong coverage of the indentation code. This simple setter isn't worth it IMO.

On 03/23/2017 02:14 AM, Peter Krempa wrote:
No testsuite addition?
For such trivial operation? I think we have pretty strong coverage of the indentation code. This simple setter isn't worth it IMO.
I don't see how adding one or two lines to the testsuite, to ensure we don't break this new interface, is not worth it. Yes, we have a lot of good coverage already, so let's keep it good, rather than introducing holes in the coverage as we add new stuff. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The function has very specific semantics. Split out the part that parses the backing store specification string into a separate helper so that it can be reused later while keeping the wrapper with existing semantics. Note that virStorageFileParseChainIndex is pretty well covered by the test suite. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 68 +++++++++++++++++++++++++++++++++++++++-------- src/util/virstoragefile.h | 5 ++++ 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 39e2b40c5..31d6085cb 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2476,6 +2476,7 @@ virStorageFileGetMetadataInternal; virStorageFileGetRelativeBackingPath; virStorageFileGetSCSIKey; virStorageFileIsClusterFS; +virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; virStorageFileProbeFormat; virStorageFileResize; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index c9420fdb7..c8eb26aa7 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1442,32 +1442,78 @@ int virStorageFileGetSCSIKey(const char *path, } #endif + +/** + * virStorageFileParseBackingStoreStr: + * @str: backing store specifier string to parse + * @target: returns target device portion of the string + * @chainIndex: returns the backing store portion of the string + * + * Parses the backing store specifier string such as vda[1], or sda into + * components and returns them via arguments. If the string did not specify an + * index, 0 is assumed. + * + * Returns 0 on success -1 on error + */ +int +virStorageFileParseBackingStoreStr(const char *str, + char **target, + unsigned int *chainIndex) +{ + char **strings = NULL; + size_t nstrings; + unsigned int idx = 0; + char *suffix; + int ret = -1; + + *chainIndex = 0; + + if (!(strings = virStringSplitCount(str, "[", 2, &nstrings))) + return -1; + + if (nstrings == 2) { + if (virStrToLong_uip(strings[1], &suffix, 10, &idx) < 0 || + STRNEQ(suffix, "]")) + goto cleanup; + } + + if (target && + VIR_STRDUP(*target, strings[0]) < 0) + goto cleanup; + + *chainIndex = idx; + ret = 0; + + cleanup: + virStringListFreeCount(strings, nstrings); + return ret; +} + + int virStorageFileParseChainIndex(const char *diskTarget, const char *name, unsigned int *chainIndex) { - char **strings = NULL; unsigned int idx = 0; - char *suffix; + char *target = NULL; int ret = 0; *chainIndex = 0; - if (name && diskTarget) - strings = virStringSplit(name, "[", 2); + if (!name || !diskTarget) + return 0; - if (virStringListLength((const char * const *)strings) != 2) - goto cleanup; + if (virStorageFileParseBackingStoreStr(name, &target, &idx) < 0) + return 0; - if (virStrToLong_uip(strings[1], &suffix, 10, &idx) < 0 || - STRNEQ(suffix, "]")) + if (idx == 0) goto cleanup; - if (STRNEQ(diskTarget, strings[0])) { + if (STRNEQ(diskTarget, target)) { virReportError(VIR_ERR_INVALID_ARG, _("requested target '%s' does not match target '%s'"), - strings[0], diskTarget); + target, diskTarget); ret = -1; goto cleanup; } @@ -1475,7 +1521,7 @@ virStorageFileParseChainIndex(const char *diskTarget, *chainIndex = idx; cleanup: - virStringListFree(strings); + VIR_FREE(target); return ret; } diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index bea1c35a2..5f6e41911 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -309,6 +309,11 @@ int virStorageFileParseChainIndex(const char *diskTarget, unsigned int *chainIndex) ATTRIBUTE_NONNULL(3); +int virStorageFileParseBackingStoreStr(const char *str, + char **target, + unsigned int *chainIndex) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, virStorageSourcePtr startFrom, const char *name, -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
The function has very specific semantics. Split out the part that parses the backing store specification string into a separate helper so that it can be reused later while keeping the wrapper with existing semantics.
Note that virStorageFileParseChainIndex is pretty well covered by the test suite. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 68 +++++++++++++++++++++++++++++++++++++++-------- src/util/virstoragefile.h | 5 ++++ 3 files changed, 63 insertions(+), 11 deletions(-)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

'nodeformat' should be used for strings which describe the storage format object, and 'nodebacking' for the actual storage object itself. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/util/virstoragefile.h | 10 ++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 31d6085cb..bf2039a75 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2490,6 +2490,7 @@ virStorageNetProtocolTypeToString; virStorageSourceBackingStoreClear; virStorageSourceClear; virStorageSourceCopy; +virStorageSourceFindByNodeName; virStorageSourceFree; virStorageSourceGetActualType; virStorageSourceGetSecurityLabelDef; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index c8eb26aa7..3bcb69bf6 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2012,6 +2012,8 @@ virStorageSourceCopy(const virStorageSource *src, VIR_STRDUP(ret->backingStoreRaw, src->backingStoreRaw) < 0 || VIR_STRDUP(ret->snapshot, src->snapshot) < 0 || VIR_STRDUP(ret->configFile, src->configFile) < 0 || + VIR_STRDUP(ret->nodeformat, src->nodeformat) < 0 || + VIR_STRDUP(ret->nodebacking, src->nodebacking) < 0 || VIR_STRDUP(ret->compat, src->compat) < 0) goto error; @@ -2232,6 +2234,9 @@ virStorageSourceClear(virStorageSourcePtr def) virStorageNetHostDefFree(def->nhosts, def->hosts); virStorageAuthDefFree(def->auth); + VIR_FREE(def->nodebacking); + VIR_FREE(def->nodeformat); + virStorageSourceBackingStoreClear(def); } @@ -3781,3 +3786,38 @@ virStorageSourceIsRelative(virStorageSourcePtr src) return false; } + + +/** + * virStorageSourceFindByNodeName: + * @top: backing chain top + * @nodeName: node name to find in backing chain + * @index: if provided the index in the backing chain + * + * Looks up the given storage source in the backing chain and returns the + * pointer to it. If @index is passed then it's filled by the index in the + * backing chain. On failure NULL is returned and no error is reported. + */ +virStorageSourcePtr +virStorageSourceFindByNodeName(virStorageSourcePtr top, + const char *nodeName, + unsigned int *index) +{ + virStorageSourcePtr tmp; + + if (index) + *index = 0; + + for (tmp = top; tmp; tmp = tmp->backingStore) { + if (STREQ_NULLABLE(tmp->nodeformat, nodeName) || + STREQ_NULLABLE(tmp->nodebacking, nodeName)) + return tmp; + + if (index) + (*index)++; + } + + if (index) + *index = 0; + return NULL; +} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 5f6e41911..9ebfc1108 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -276,6 +276,10 @@ struct _virStorageSource { /* Name of the child backing store recorded in metadata of the * current file. */ char *backingStoreRaw; + + /* metadata that allows identifying given storage source */ + char *nodeformat; /* name of the format handler object */ + char *nodebacking; /* name of the backing storage object */ }; @@ -395,4 +399,10 @@ virStorageSourcePtr virStorageSourceNewFromBackingAbsolute(const char *path); bool virStorageSourceIsRelative(virStorageSourcePtr src); +virStorageSourcePtr +virStorageSourceFindByNodeName(virStorageSourcePtr top, + const char *nodeName, + unsigned int *index) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + #endif /* __VIR_STORAGE_FILE_H__ */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
'nodeformat' should be used for strings which describe the storage format object, and 'nodebacking' for the actual storage object itself. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/util/virstoragefile.h | 10 ++++++++++ 3 files changed, 51 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

When using thin provisioning, management tools need to resize the disk in certain cases. To avoid having them to poll disk usage introduce an event which will be fired when a given offset of the storage is written by the hypervisor. Together with the API which will be added later, it will allow registering thresholds for given storage backing volumes and this event will then notify management if the threshold is exceeded. --- daemon/remote.c | 45 +++++++++++++++++ examples/object-events/event-test.c | 19 ++++++++ include/libvirt/libvirt-domain.h | 31 ++++++++++++ src/conf/domain_event.c | 97 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 15 ++++++ src/libvirt_private.syms | 2 + src/remote/remote_driver.c | 33 +++++++++++++ src/remote/remote_protocol.x | 18 ++++++- src/remote_protocol-structs | 9 ++++ tools/virsh-domain.c | 21 ++++++++ 10 files changed, 289 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index f2b9b9aec..7888f85b5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1295,6 +1295,50 @@ remoteRelayDomainEventMetadataChange(virConnectPtr conn, } +static int +remoteRelayDomainEventBlockThreshold(virConnectPtr conn, + virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_block_threshold_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain block threshold event %s %d %s %s %llu %llu, callback %d", + dom->name, dom->id, dev, NULLSTR(path), threshold, excess, callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + if (VIR_STRDUP(data.dev, dev) < 0) + goto error; + if (path) { + if (VIR_ALLOC(data.path) < 0) + goto error; + if (VIR_STRDUP(*(data.path), path) < 0) + goto error; + } + data.threshold = threshold; + data.excess = excess; + make_nonnull_domain(&data.dom, dom); + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD, + (xdrproc_t)xdr_remote_domain_event_block_threshold_msg, &data); + + return 0; + error: + VIR_FREE(data.dev); + return -1; +} + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), @@ -1321,6 +1365,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 55c004f93..12690cac0 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -15,6 +15,7 @@ #define ARRAY_CARDINALITY(Array) (sizeof(Array) / sizeof(*(Array))) #define STREQ(a, b) (strcmp(a, b) == 0) +#define NULLSTR(s) ((s) ? (s) : "<null>") #ifndef ATTRIBUTE_UNUSED # define ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -925,6 +926,23 @@ myDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSED, static int +myDomainEventBlockThresholdCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) block threshold callback dev '%s'(%s), " + "threshold: '%llu', excess: '%llu'", + __func__, virDomainGetName(dom), virDomainGetID(dom), + dev, NULLSTR(path), threshold, excess); + return 0; +} + + +static int myDomainEventMigrationIterationCallback(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int iteration, @@ -1053,6 +1071,7 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_JOB_COMPLETED, myDomainEventJobCompletedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback), }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index c490d712f..12f8ac21c 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4267,6 +4267,36 @@ typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, /** + * virConnectDomainEventBlockThresholdCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @dev: name associated with the affected disk or storage backing chain + * element + * @path: for local storage, the path of the backing chain element + * @threshold: threshold offset in bytes + * @excess: number of bytes written beyond the threshold + * @opaque: application specified data + * + * The callback occurs when the hypervisor detects that the given storage + * element was written beyond the point specified by @threshold. The excess + * data size written beyond @threshold is reported by @excess (if supported + * by the hypervisor, 0 otherwise). The event is useful for thin-provisioned + * storage. + * + * The threshold size can be set via the virDomainSetBlockThreshold API. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess, + void *opaque); + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -4307,6 +4337,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_JOB_COMPLETED = 21, /* virConnectDomainEventJobCompletedCallback */ VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */ VIR_DOMAIN_EVENT_ID_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */ + VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index da503f3ee..6243b4262 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -60,6 +60,7 @@ static virClassPtr virDomainEventMigrationIterationClass; static virClassPtr virDomainEventJobCompletedClass; static virClassPtr virDomainEventDeviceRemovalFailedClass; static virClassPtr virDomainEventMetadataChangeClass; +static virClassPtr virDomainEventBlockThresholdClass; static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); @@ -81,6 +82,7 @@ static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDeviceRemovalFailedDispose(void *obj); static void virDomainEventMetadataChangeDispose(void *obj); +static void virDomainEventBlockThresholdDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -277,6 +279,17 @@ struct _virDomainEventMetadataCange { typedef struct _virDomainEventMetadataCange virDomainEventMetadataChange; typedef virDomainEventMetadataChange *virDomainEventMetadataChangePtr; +struct _virDomainEventBlockThreshold { + virDomainEvent parent; + + char *dev; + char *path; + + unsigned long long threshold; + unsigned long long excess; +}; +typedef struct _virDomainEventBlockThreshold virDomainEventBlockThreshold; +typedef virDomainEventBlockThreshold *virDomainEventBlockThresholdPtr; static int @@ -402,6 +415,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventMetadataChange), virDomainEventMetadataChangeDispose))) return -1; + if (!(virDomainEventBlockThresholdClass = + virClassNew(virDomainEventClass, + "virDomainEventBlockThreshold", + sizeof(virDomainEventBlockThreshold), + virDomainEventBlockThresholdDispose))) + return -1; return 0; } @@ -600,6 +619,17 @@ virDomainEventMetadataChangeDispose(void *obj) } +static void +virDomainEventBlockThresholdDispose(void *obj) +{ + virDomainEventBlockThresholdPtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->dev); + VIR_FREE(event->path); +} + + static void * virDomainEventNew(virClassPtr klass, int eventID, @@ -1674,6 +1704,60 @@ virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, } +static virObjectEventPtr +virDomainEventBlockThresholdNew(int id, + const char *name, + unsigned char *uuid, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess) +{ + virDomainEventBlockThresholdPtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventBlockThresholdClass, + VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, + id, name, uuid))) + return NULL; + + if (VIR_STRDUP(ev->dev, dev) < 0 || + VIR_STRDUP(ev->path, path) < 0) { + virObjectUnref(ev); + return NULL; + } + ev->threshold = threshold; + ev->excess = excess; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventBlockThresholdNewFromObj(virDomainObjPtr obj, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess) +{ + return virDomainEventBlockThresholdNew(obj->def->id, obj->def->name, + obj->def->uuid, dev, path, + threshold, excess); +} + +virObjectEventPtr +virDomainEventBlockThresholdNewFromDom(virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess) +{ + return virDomainEventBlockThresholdNew(dom->id, dom->name, dom->uuid, + dev, path, threshold, excess); +} + + static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, @@ -1943,6 +2027,19 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD: + { + virDomainEventBlockThresholdPtr blockThresholdEvent; + + blockThresholdEvent = (virDomainEventBlockThresholdPtr)event; + ((virConnectDomainEventBlockThresholdCallback)cb)(conn, dom, + blockThresholdEvent->dev, + blockThresholdEvent->path, + blockThresholdEvent->threshold, + blockThresholdEvent->excess, + cbopaque); + goto cleanup; + } case VIR_DOMAIN_EVENT_ID_LAST: break; } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 1933f4724..3992a29c5 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -244,6 +244,21 @@ virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, int type, const char *nsuri); + +virObjectEventPtr +virDomainEventBlockThresholdNewFromObj(virDomainObjPtr obj, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess); + +virObjectEventPtr +virDomainEventBlockThresholdNewFromDom(virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bf2039a75..fcc87675b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -536,6 +536,8 @@ virDomainEventBlockJob2NewFromDom; virDomainEventBlockJob2NewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventBlockJobNewFromObj; +virDomainEventBlockThresholdNewFromDom; +virDomainEventBlockThresholdNewFromObj; virDomainEventControlErrorNewFromDom; virDomainEventControlErrorNewFromObj; virDomainEventDeviceAddedNewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 0c8bfeed1..efa47beaf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -396,6 +396,11 @@ remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, void *evdata, void *opaque); static void +remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); @@ -602,6 +607,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteSecretBuildEventValueChanged, sizeof(remote_secret_event_value_changed_msg), (xdrproc_t)xdr_remote_secret_event_value_changed_msg }, + { REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD, + remoteDomainBuildEventBlockThreshold, + sizeof(remote_domain_event_block_threshold_msg), + (xdrproc_t)xdr_remote_domain_event_block_threshold_msg }, }; static void @@ -5577,6 +5586,30 @@ remoteSecretGetValue(virSecretPtr secret, size_t *value_size, } +static void +remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_block_threshold_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventBlockThresholdNewFromDom(dom, msg->dev, + msg->path ? *msg->path : NULL, + msg->threshold, msg->excess); + + virObjectUnref(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + + static int remoteStreamSend(virStreamPtr st, const char *data, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index abe63af07..39dd2b728 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3071,6 +3071,15 @@ struct remote_domain_event_block_job_2_msg { int status; }; +struct remote_domain_event_block_threshold_msg { + int callbackID; + remote_nonnull_domain dom; + remote_nonnull_string dev; + remote_string path; + unsigned hyper threshold; + unsigned hyper excess; +}; + struct remote_domain_event_callback_tunable_msg { int callbackID; remote_nonnull_domain dom; @@ -6033,5 +6042,12 @@ enum remote_procedure { * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG */ - REMOTE_PROC_DOMAIN_SET_VCPU = 384 + REMOTE_PROC_DOMAIN_SET_VCPU = 384, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385 + }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index e1e53d21b..67e43a4ac 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2516,6 +2516,14 @@ struct remote_domain_event_block_job_2_msg { int type; int status; }; +struct remote_domain_event_block_threshold_msg { + int callbackID; + remote_nonnull_domain dom; + remote_nonnull_string dev; + remote_string path; + uint64_t threshold; + uint64_t excess; +}; struct remote_domain_event_callback_tunable_msg { int callbackID; remote_nonnull_domain dom; @@ -3217,4 +3225,5 @@ enum remote_procedure { REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382, REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383, REMOTE_PROC_DOMAIN_SET_VCPU = 384, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 09a9f8203..ee702f3c4 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12870,6 +12870,25 @@ virshEventMetadataChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, } +static void +virshEventBlockThresholdPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'block-threshold' for domain %s: " + "dev: %s(%s) %llu %llu\n"), + virDomainGetName(dom), + dev, NULLSTR(path), threshold, excess); + virshEventPrint(opaque, &buf); +} + + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, @@ -12917,6 +12936,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, { "metadata-change", VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, + { "block-threshold", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
When using thin provisioning, management tools need to resize the disk in certain cases. To avoid having them to poll disk usage introduce an event which will be fired when a given offset of the storage is written by the hypervisor. Together with the API which will be added later, it will allow registering thresholds for given storage backing volumes and this event will then notify management if the threshold is exceeded. ---
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The event is fired when a given block backend node (identified by the node name) experiences a write beyond the bound set via block-set-write-threshold QMP command. This wires up the monitor code to extract the data and allow us receiving the events and the capability. --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_monitor.c | 18 +++++++++++++++ src/qemu/qemu_monitor.h | 14 ++++++++++++ src/qemu/qemu_monitor_json.c | 26 ++++++++++++++++++++++ tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml | 1 + .../caps_2.6.0-gicv2.aarch64.xml | 1 + .../caps_2.6.0-gicv3.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml | 1 + 16 files changed, 72 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 70f9ed777..c4695693a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -359,6 +359,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "query-cpu-model-expansion", /* 245 */ "virtio-net.host_mtu", "spice-rendernode", + "block-write-threshold", ); @@ -1529,6 +1530,7 @@ struct virQEMUCapsStringFlags virQEMUCapsEvents[] = { { "MIGRATION", QEMU_CAPS_MIGRATION_EVENT }, { "VSERPORT_CHANGE", QEMU_CAPS_VSERPORT_CHANGE }, { "DEVICE_TRAY_MOVED", QEMU_CAPS_DEVICE_TRAY_MOVED }, + { "BLOCK_WRITE_THRESHOLD", QEMU_CAPS_BLOCK_WRITE_THRESHOLD }, }; struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index cc9f46e65..eb72ad646 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -394,6 +394,7 @@ typedef enum { QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION, /* qmp query-cpu-model-expansion */ QEMU_CAPS_VIRTIO_NET_HOST_MTU, /* virtio-net-*.host_mtu */ QEMU_CAPS_SPICE_RENDERNODE, /* -spice rendernode */ + QEMU_CAPS_BLOCK_WRITE_THRESHOLD, /* BLOCK_WRITE_THRESHOLD event */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d71f84c80..f43d79b98 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1580,6 +1580,24 @@ qemuMonitorEmitAcpiOstInfo(qemuMonitorPtr mon, int +qemuMonitorEmitBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold, + unsigned long long excess) +{ + int ret = -1; + + VIR_DEBUG("mon=%p, node-name='%s', threshold='%llu', excess='%llu'", + mon, nodename, threshold, excess); + + QEMU_MONITOR_CALLBACK(mon, ret, domainBlockThreshold, mon->vm, + nodename, threshold, excess); + + return ret; +} + + +int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { QEMU_CHECK_MONITOR(mon); diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 847e9458a..66dcd2468 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -207,6 +207,14 @@ typedef int (*qemuMonitorDomainAcpiOstInfoCallback)(qemuMonitorPtr mon, void *opaque); +typedef int (*qemuMonitorDomainBlockThresholdCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *nodename, + unsigned long long threshold, + unsigned long long excess, + void *opaque); + + typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; struct _qemuMonitorCallbacks { @@ -238,6 +246,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainMigrationStatusCallback domainMigrationStatus; qemuMonitorDomainMigrationPassCallback domainMigrationPass; qemuMonitorDomainAcpiOstInfoCallback domainAcpiOstInfo; + qemuMonitorDomainBlockThresholdCallback domainBlockThreshold; }; char *qemuMonitorEscapeArg(const char *in); @@ -357,6 +366,11 @@ int qemuMonitorEmitAcpiOstInfo(qemuMonitorPtr mon, unsigned int source, unsigned int status); +int qemuMonitorEmitBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold, + unsigned long long excess); + int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorStopCPUs(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 3a29f1aa7..fc33e8635 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -89,6 +89,7 @@ static void qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon, virJSONValueP static void qemuMonitorJSONHandleMigrationStatus(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleMigrationPass(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -102,6 +103,7 @@ static qemuEventHandler eventHandlers[] = { { "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, }, { "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, }, + { "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold, }, { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, @@ -1065,6 +1067,30 @@ qemuMonitorJSONHandleAcpiOstInfo(qemuMonitorPtr mon, virJSONValuePtr data) } +static void +qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *nodename; + unsigned long long threshold; + unsigned long long excess; + + if (!(nodename = virJSONValueObjectGetString(data, "node-name"))) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "write-threshold", &threshold) < 0) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &excess) < 0) + goto error; + + qemuMonitorEmitBlockThreshold(mon, nodename, threshold, excess); + return; + + error: + VIR_WARN("malformed 'BLOCK_WRITE_THRESHOLD' event"); +} + + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml index b1b9e1571..8b72751e9 100644 --- a/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml @@ -182,6 +182,7 @@ <flag name='virtio-vga'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2004000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml index 2e23a93d6..215c3f110 100644 --- a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml @@ -188,6 +188,7 @@ <flag name='query-qmp-schema'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2005000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml b/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml index 0aed651e7..80c29d3ab 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml @@ -164,6 +164,7 @@ <flag name='query-qmp-schema'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml b/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml index 1041a12c1..2ef53173c 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml @@ -164,6 +164,7 @@ <flag name='query-qmp-schema'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml index 92e27810f..78e78ca96 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml @@ -159,6 +159,7 @@ <flag name='query-qmp-schema'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml index 1286f84df..8caf448b4 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml @@ -197,6 +197,7 @@ <flag name='query-qmp-schema'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml index af21017bc..6206bad9e 100644 --- a/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml @@ -127,6 +127,7 @@ <flag name='gluster.debug_level'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2007000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml index 0c0b423f3..fd13ae4f7 100644 --- a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml @@ -199,6 +199,7 @@ <flag name='gluster.debug_level'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2007000</version> <kvmVersion>0</kvmVersion> <package> (v2.7.0)</package> diff --git a/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml index d179a8413..b7ce2c6c0 100644 --- a/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml @@ -129,6 +129,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='query-cpu-model-expansion'/> + <flag name='block-write-threshold'/> <version>2007093</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml index d650f094a..b741e8fcc 100644 --- a/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml @@ -200,6 +200,7 @@ <flag name='gluster.debug_level'/> <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> + <flag name='block-write-threshold'/> <version>2008000</version> <kvmVersion>0</kvmVersion> <package> (v2.8.0)</package> diff --git a/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml index 334f8e74b..8043240e2 100644 --- a/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml @@ -202,6 +202,7 @@ <flag name='drive-iotune-group'/> <flag name='query-cpu-model-expansion'/> <flag name='virtio-net.host_mtu'/> + <flag name='block-write-threshold'/> <version>2008050</version> <kvmVersion>0</kvmVersion> <package> (v2.8.0-1961-g5b10b94bd5)</package> -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
The event is fired when a given block backend node (identified by the node name) experiences a write beyond the bound set via block-set-write-threshold QMP command. This wires up the monitor code to extract the data and allow us receiving the events and the capability. ---
+static void +qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *nodename; + unsigned long long threshold; + unsigned long long excess; + + if (!(nodename = virJSONValueObjectGetString(data, "node-name"))) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "write-threshold", &threshold) < 0) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &excess) < 0) + goto error; + + qemuMonitorEmitBlockThreshold(mon, nodename, threshold, excess);
Do we really want to emit the nodename as given by qemu? Or do we want to map the name into something that matches what is in the user XML? (Especially important since right now qemu is generating node names because we are not yet supplying them via blockdev-add commands, and exposing a randomly-generated name to the user seems fishy) The rest of the patch looks okay, though -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 03/22/2017 08:08 PM, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
The event is fired when a given block backend node (identified by the node name) experiences a write beyond the bound set via block-set-write-threshold QMP command. This wires up the monitor code to extract the data and allow us receiving the events and the capability. ---
+static void +qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *nodename; + unsigned long long threshold; + unsigned long long excess; + + if (!(nodename = virJSONValueObjectGetString(data, "node-name"))) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "write-threshold", &threshold) < 0) + goto error; + + if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &excess) < 0) + goto error; + + qemuMonitorEmitBlockThreshold(mon, nodename, threshold, excess);
Do we really want to emit the nodename as given by qemu? Or do we want to map the name into something that matches what is in the user XML? (Especially important since right now qemu is generating node names because we are not yet supplying them via blockdev-add commands, and exposing a randomly-generated name to the user seems fishy)
The rest of the patch looks okay, though
After looking at 9/23, I think I understand what's going on. This is not the user-visible event, but the internal layer from the qemu monitor back to the part of the qemu driver that generates user-visible events, and there is still another round of translation to happen. ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Looks up a disk and it's corresponding backing chain element by node name. --- src/qemu/qemu_domain.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 6 ++++++ 2 files changed, 49 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 07ce22417..95ea63615 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -8369,3 +8369,46 @@ qemuDomainNamespaceTeardownRNG(virQEMUDriverPtr driver, cleanup: return ret; } + + +/** + * qemuDomainDiskLookupByNodename: + * @def: domain definition to look for the disk + * @nodename: block backend node name to find + * @src: filled with the specific backing store element if provided + * @idx: index of @src in the backing chain, if provided + * + * Looks up the disk in the domain via @nodename and returns it's definition. + * Optionally fills @src and @idx if provided with the specific backing chain + * element which corresponds to the node name. + */ +virDomainDiskDefPtr +qemuDomainDiskLookupByNodename(virDomainDefPtr def, + const char *nodename, + virStorageSourcePtr *src, + unsigned int *idx) +{ + size_t i; + unsigned int srcindex; + virStorageSourcePtr tmp = NULL; + + if (!idx) + idx = &srcindex; + + if (src) + *src = NULL; + + *idx = 0; + + for (i = 0; i < def->ndisks; i++) { + if ((tmp = virStorageSourceFindByNodeName(def->disks[i]->src, + nodename, idx))) { + if (src) + *src = tmp; + + return def->disks[i]; + } + } + + return NULL; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index c646828e6..15eb72a5c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -852,4 +852,10 @@ int qemuDomainNamespaceSetupRNG(virQEMUDriverPtr driver, int qemuDomainNamespaceTeardownRNG(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng); + +virDomainDiskDefPtr qemuDomainDiskLookupByNodename(virDomainDefPtr def, + const char *nodename, + virStorageSourcePtr *src, + unsigned int *idx); + #endif /* __QEMU_DOMAIN_H__ */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Looks up a disk and it's corresponding backing chain element by node
s/it's/its/ (remember, "it's" is only appropriate if "it is" or "it has" can be used in its place)
name. --- src/qemu/qemu_domain.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 6 ++++++ 2 files changed, 49 insertions(+)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 07ce22417..95ea63615 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -8369,3 +8369,46 @@ qemuDomainNamespaceTeardownRNG(virQEMUDriverPtr driver, cleanup: return ret; } + + +/** + * qemuDomainDiskLookupByNodename: + * @def: domain definition to look for the disk + * @nodename: block backend node name to find + * @src: filled with the specific backing store element if provided + * @idx: index of @src in the backing chain, if provided + * + * Looks up the disk in the domain via @nodename and returns it's definition.
s/it's/its/
+ * Optionally fills @src and @idx if provided with the specific backing chain + * element which corresponds to the node name. + */
ACK with typos fixed -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The code is currently simple, but if we later add node names, it will be necessary to generate the names based on the node name. Add a helper so that there's a central point to fix once we add self-generated node names. --- src/qemu/qemu_domain.c | 22 ++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 95ea63615..0c633af33 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -8412,3 +8412,25 @@ qemuDomainDiskLookupByNodename(virDomainDefPtr def, return NULL; } + + +/** + * qemuDomainDiskBackingStoreGetName: + * + * Creates a name using the indexed syntax (vda[1])for the given backing store + * entry for a disk. + */ +char * +qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, + virStorageSourcePtr src ATTRIBUTE_UNUSED, + unsigned int idx) +{ + char *ret = NULL; + + if (idx) + ignore_value(virAsprintf(&ret, "%s[%d]", disk->dst, idx)); + else + ignore_value(VIR_STRDUP(ret, disk->dst)); + + return ret; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 15eb72a5c..a07d671a9 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -858,4 +858,8 @@ virDomainDiskDefPtr qemuDomainDiskLookupByNodename(virDomainDefPtr def, virStorageSourcePtr *src, unsigned int *idx); +char *qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, + virStorageSourcePtr src, + unsigned int idx); + #endif /* __QEMU_DOMAIN_H__ */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
The code is currently simple, but if we later add node names, it will be necessary to generate the names based on the node name. Add a helper so that there's a central point to fix once we add self-generated node names.
We'll get there soon enough - there's no convenient way to support encrypted qcow2 volumes backed by yet another encrypted volume without fully and persistently managing node names via blockdev-add (yes, we've been saying that for a long time, but qemu 2.9 will finally have blockdev-add fully supported...)
--- src/qemu/qemu_domain.c | 22 ++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 ++++ 2 files changed, 26 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Bind it to qemus BLOCK_WRITE_THRESHOLD event. Look up the disk by nodename and construct the string to return. --- src/qemu/qemu_process.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index b9c1847bb..d40deea10 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1441,6 +1441,45 @@ qemuProcessHandleAcpiOstInfo(qemuMonitorPtr mon ATTRIBUTE_UNUSED, static int +qemuProcessHandleBlockThreshold(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *nodename, + unsigned long long threshold, + unsigned long long excess, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + virObjectEventPtr event = NULL; + virDomainDiskDefPtr disk; + virStorageSourcePtr src; + unsigned int idx; + char *dev = NULL; + const char *path = NULL; + + virObjectLock(vm); + + VIR_DEBUG("BLOCK_WRITE_THRESHOLD event for block node '%s' in domain %p %s:" + "threshold '%llu' exceeded by '%llu'", + nodename, vm, vm->def->name, threshold, excess); + + if ((disk = qemuDomainDiskLookupByNodename(vm->def, nodename, &src, &idx))) { + if (virStorageSourceIsLocalStorage(src)) + path = src->path; + + if ((dev = qemuDomainDiskBackingStoreGetName(disk, src, idx))) { + event = virDomainEventBlockThresholdNewFromObj(vm, dev, path, + threshold, excess); + } + } + + virObjectUnlock(vm); + qemuDomainEventQueue(driver, event); + + return 0; +} + + +static int qemuProcessHandleNicRxFilterChanged(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm, const char *devAlias, @@ -1636,6 +1675,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainMigrationStatus = qemuProcessHandleMigrationStatus, .domainMigrationPass = qemuProcessHandleMigrationPass, .domainAcpiOstInfo = qemuProcessHandleAcpiOstInfo, + .domainBlockThreshold = qemuProcessHandleBlockThreshold, }; static void -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Bind it to qemus BLOCK_WRITE_THRESHOLD event. Look up the disk by
s/qemus/qemu's/
nodename and construct the string to return. --- src/qemu/qemu_process.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
static int +qemuProcessHandleBlockThreshold(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *nodename, + unsigned long long threshold, + unsigned long long excess, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + virObjectEventPtr event = NULL; + virDomainDiskDefPtr disk; + virStorageSourcePtr src; + unsigned int idx; + char *dev = NULL; + const char *path = NULL; + + virObjectLock(vm); + + VIR_DEBUG("BLOCK_WRITE_THRESHOLD event for block node '%s' in domain %p %s:" + "threshold '%llu' exceeded by '%llu'", + nodename, vm, vm->def->name, threshold, excess); + + if ((disk = qemuDomainDiskLookupByNodename(vm->def, nodename, &src, &idx))) { + if (virStorageSourceIsLocalStorage(src)) + path = src->path; + + if ((dev = qemuDomainDiskBackingStoreGetName(disk, src, idx))) { + event = virDomainEventBlockThresholdNewFromObj(vm, dev, path, + threshold, excess); + } + } + + virObjectUnlock(vm); + qemuDomainEventQueue(driver, event); + + return 0;
Does this leak dev? Okay, this looks like it fixes my concern on the earlier patch - you are indeed mapping qemu's node name into a proper XML-related name for the consumer of the event. ACK once you fix any leaks. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The new API can be used to configure the threshold when VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD should be fired. --- include/libvirt/libvirt-domain.h | 5 ++++ src/driver-hypervisor.h | 8 +++++++ src/libvirt-domain.c | 51 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 89 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 12f8ac21c..296c5664f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4656,4 +4656,9 @@ int virDomainSetVcpu(virDomainPtr domain, int state, unsigned int flags); +int virDomainSetBlockThreshold(virDomainPtr domain, + const char *dev, + unsigned long long threshold, + unsigned int flags); + #endif /* __VIR_LIBVIRT_DOMAIN_H__ */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index b81420aef..3053d7ae8 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1257,6 +1257,13 @@ typedef int int state, unsigned int flags); +typedef int +(*virDrvDomainSetBlockThreshold)(virDomainPtr domain, + const char *dev, + unsigned long long threshold, + unsigned int flags); + + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1496,6 +1503,7 @@ struct _virHypervisorDriver { virDrvDomainGetGuestVcpus domainGetGuestVcpus; virDrvDomainSetGuestVcpus domainSetGuestVcpus; virDrvDomainSetVcpu domainSetVcpu; + virDrvDomainSetBlockThreshold domainSetBlockThreshold; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index d8c88e06c..fd367bcb0 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11822,3 +11822,54 @@ virDomainSetVcpu(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainSetBlockThreshold: + * @domain: pointer to domain object + * @dev: string specifying the block device or backing chain element + * @threshold: threshold in bytes when to fire the event + * @flags: currently unused, callers should pass 0 + * + * Set the threshold level for delivering the + * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD if the device or backing chain element + * described by @dev is written beyond the set threshold level. The threshold + * level is unset once the event fired. The event may not be delivered at all if + * libvirtd was not running at the moment when the threshold was reached. + * + * This event allows to use thin-provisioned storage which needs management + * tools to grow it without the need for polling of the data. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int +virDomainSetBlockThreshold(virDomainPtr domain, + const char *dev, + unsigned long long threshold, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(domain, "dev='%s' threshold=%llu flags=%x", + NULLSTR(dev), threshold, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + virCheckReadOnlyGoto(domain->conn->flags, error); + + virCheckNonNullArgGoto(dev, error); + + if (domain->conn->driver->domainSetBlockThreshold) { + int ret; + ret = domain->conn->driver->domainSetBlockThreshold(domain, dev, + threshold, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 04ef58021..428cf2e19 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -755,6 +755,7 @@ LIBVIRT_3.0.0 { LIBVIRT_3.1.0 { global: + virDomainSetBlockThreshold; virDomainSetVcpu; } LIBVIRT_3.0.0; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index efa47beaf..baa5cbab3 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8436,6 +8436,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetGuestVcpus = remoteDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = remoteDomainSetGuestVcpus, /* 2.0.0 */ .domainSetVcpu = remoteDomainSetVcpu, /* 3.1.0 */ + .domainSetBlockThreshold = remoteDomainSetBlockThreshold, /* 3.1.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 39dd2b728..87b2bd365 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3402,6 +3402,14 @@ struct remote_secret_event_value_changed_msg { remote_nonnull_secret secret; }; +struct remote_domain_set_block_threshold_args { + remote_nonnull_domain dom; + remote_nonnull_string dev; + unsigned hyper threshold; + unsigned int flags; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6048,6 +6056,13 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385 + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385, + + /** + * @generate: both + * @acl: domain:write + */ + REMOTE_PROC_DOMAIN_SET_BLOCK_THRESHOLD = 386 + }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 67e43a4ac..a46fe37bf 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2840,6 +2840,12 @@ struct remote_secret_event_value_changed_msg { int callbackID; remote_nonnull_secret secret; }; +struct remote_domain_set_block_threshold_args { + remote_nonnull_domain dom; + remote_nonnull_string dev; + uint64_t threshold; + u_int flags; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3226,4 +3232,5 @@ enum remote_procedure { REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383, REMOTE_PROC_DOMAIN_SET_VCPU = 384, REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385, + REMOTE_PROC_DOMAIN_SET_BLOCK_THRESHOLD = 386, }; -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
The new API can be used to configure the threshold when VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD should be fired. ---
+++ b/src/libvirt-domain.c @@ -11822,3 +11822,54 @@ virDomainSetVcpu(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainSetBlockThreshold: + * @domain: pointer to domain object + * @dev: string specifying the block device or backing chain element + * @threshold: threshold in bytes when to fire the event + * @flags: currently unused, callers should pass 0 + * + * Set the threshold level for delivering the + * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD if the device or backing chain element + * described by @dev is written beyond the set threshold level. The threshold + * level is unset once the event fired. The event may not be delivered at all if
maybe s/may/might/
+ * libvirtd was not running at the moment when the threshold was reached.
We should document that the user can already poll the current write levels via querying block status information, to make it obvious that missing the event isn't necessarily fatal (when first [re-]connecting to libvirt, use the poll operation; from there, you can rely on the event to avoid further polling)
+ * + * This event allows to use thin-provisioned storage which needs management + * tools to grow it without the need for polling of the data. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int +virDomainSetBlockThreshold(virDomainPtr domain, + const char *dev, + unsigned long long threshold, + unsigned int flags)
Hmm. Because the threshold resets once the event fires, we don't have a handy way to say 'set a threshold 100M or 10% further out than the last limit). That would be a reason for using flags, although I don't think we need to worry about it for now. Does setting the threshold lower than the current high-water mark (as reported by polling block status information) immediately fire an event, or will an event never fire in that situation?
@@ -6048,6 +6056,13 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385 + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385, + + /** + * @generate: both + * @acl: domain:write
This doesn't modify the domain. Why does it need domain:write; wouldn't domain:read be sufficient (namely, the same privileges you already require for polling block status information to learn the current high water mark)? -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Mar 23, 2017 at 12:56:10 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
The new API can be used to configure the threshold when VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD should be fired. ---
[...]
+ * This event allows to use thin-provisioned storage which needs management + * tools to grow it without the need for polling of the data. + * + * Returns 0 if the operation has started, -1 on failure. + */ +int +virDomainSetBlockThreshold(virDomainPtr domain, + const char *dev, + unsigned long long threshold, + unsigned int flags)
Hmm. Because the threshold resets once the event fires, we don't have a handy way to say 'set a threshold 100M or 10% further out than the last limit). That would be a reason for using flags, although I don't think we need to worry about it for now.
Does setting the threshold lower than the current high-water mark (as reported by polling block status information) immediately fire an event, or will an event never fire in that situation?
It will fire once the hypervisor writes beyond the threshold. If you set it lower than the previous event, it will fire on the next write.
@@ -6048,6 +6056,13 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385 + REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385, + + /** + * @generate: both + * @acl: domain:write
This doesn't modify the domain. Why does it need domain:write; wouldn't domain:read be sufficient (namely, the same privileges you already require for polling block status information to learn the current high water mark)?
Since this api is setting the watermark level I don't feel it's right to allow it using read-only connection.

Add a simple wrapper which will allow to set the threshold for delivering the event. --- tools/virsh-domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 8 +++++++ 2 files changed, 72 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index ee702f3c4..36629523b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7097,6 +7097,64 @@ cmdSetvcpu(vshControl *ctl, const vshCmd *cmd) /* + * "domblkthreshold" command + */ +static const vshCmdInfo info_domblkthreshold[] = { + {.name = "help", + .data = N_("set the threshold for block-threshold event for a given block " + "device or it's backing chain element") + }, + {.name = "desc", + .data = N_("set threshold for ") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domblkthreshold[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL, + {.name = "dev", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("device to set threshold for") + }, + {.name = "threshold", + .type = VSH_OT_INT, + .flags = VSH_OFLAG_REQ, + .help = N_("threshold as a scaled number (by default bytes)") + }, + {.name = NULL} +}; + +static bool +cmdDomblkthreshold(vshControl *ctl, const vshCmd *cmd) +{ + unsigned long long threshold; + const char *dev = NULL; + virDomainPtr dom; + bool ret = false; + + if (vshCommandOptStringReq(ctl, cmd, "dev", &dev)) + return false; + + if (vshCommandOptScaledInt(ctl, cmd, "threshold", + &threshold, 1, ULLONG_MAX) < 0) + return false; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (virDomainSetBlockThreshold(dom, dev, threshold, 0) < 0) + goto cleanup; + + ret = true; + + cleanup: + virDomainFree(dom); + return ret; +} + + +/* * "iothreadinfo" command */ static const vshCmdInfo info_iothreadinfo[] = { @@ -14060,5 +14118,11 @@ const vshCmdDef domManagementCmds[] = { .info = info_setvcpu, .flags = 0 }, + {.name = "domblkthreshold", + .handler = cmdDomblkthreshold, + .opts = opts_domblkthreshold, + .info = info_domblkthreshold, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virsh.pod b/tools/virsh.pod index ee7904611..48be19234 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1274,6 +1274,14 @@ I<--bytes> with a scaled value allows to use finer granularity. A scaled value used without I<--bytes> will be rounded down to MiB/s. Note that the I<--bytes> may be unsupported by the hypervisor. + +=item B<domblkthreshold> I<domain> I<dev> I<threshold> + +Set the threshold value for delivering the block-threshold event. I<dev> +specifies the disk device target or backing chain element of given device using +the 'target[1]' syntax. I<threshold> is a scaled value of the offset. If the +block device should write beyond that offset the event will be delivered. + =item B<blockresize> I<domain> I<path> I<size> Resize a block device of domain while the domain is running, I<path> -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Add a simple wrapper which will allow to set the threshold for delivering the event. --- tools/virsh-domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 8 +++++++ 2 files changed, 72 insertions(+)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index ee702f3c4..36629523b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7097,6 +7097,64 @@ cmdSetvcpu(vshControl *ctl, const vshCmd *cmd)
/* + * "domblkthreshold" command + */ +static const vshCmdInfo info_domblkthreshold[] = { + {.name = "help", + .data = N_("set the threshold for block-threshold event for a given block " + "device or it's backing chain element") + }, + {.name = "desc", + .data = N_("set threshold for ")
Incomplete thought?
+ +=item B<domblkthreshold> I<domain> I<dev> I<threshold> + +Set the threshold value for delivering the block-threshold event. I<dev> +specifies the disk device target or backing chain element of given device using +the 'target[1]' syntax. I<threshold> is a scaled value of the offset. If the +block device should write beyond that offset the event will be delivered. +
Should virsh check that the event is actually available from the server (that is, that a registration for the event succeeds) before trying the threshold? Or are we not worried about that? At the lower level, it is reasonable to assume that any driver will either implement all or none of the threshold setting and event in the same release, so if the command succeeds, then you are right that the event should work. So, once you fix the "desc" text, ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Mar 23, 2017 at 14:34:04 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Add a simple wrapper which will allow to set the threshold for delivering the event. --- tools/virsh-domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 8 +++++++ 2 files changed, 72 insertions(+)
[...]
+=item B<domblkthreshold> I<domain> I<dev> I<threshold> + +Set the threshold value for delivering the block-threshold event. I<dev> +specifies the disk device target or backing chain element of given device using +the 'target[1]' syntax. I<threshold> is a scaled value of the offset. If the +block device should write beyond that offset the event will be delivered. +
Should virsh check that the event is actually available from the server (that is, that a registration for the event succeeds) before trying the threshold? Or are we not worried about that? At the lower level, it is reasonable to assume that any driver will either implement all or none of the threshold setting and event in the same release, so if the command succeeds, then you are right that the event should work.
Hypervisors which don't support the event don't have this API implemented. Additionally the qemu implementation checks the capability prior to returning success here. This would catch only the case where somebody would do a botched backport of this. Since it's not a good idea to backport APIs I don't think that situation will ever happen.

--- src/qemu/qemu_domain.c | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ 2 files changed, 40 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0c633af33..402b0730e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -8434,3 +8434,40 @@ qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, return ret; } + + +virStorageSourcePtr +qemuDomainGetStorageSourceByDevstr(const char *devstr, + virDomainDefPtr def) +{ + virDomainDiskDefPtr disk = NULL; + virStorageSourcePtr src = NULL; + char *target = NULL; + unsigned int idx; + size_t i; + + if (virStorageFileParseBackingStoreStr(devstr, &target, &idx) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("failed to parse block device '%s'"), devstr); + return NULL; + } + + for (i = 0; i < def->ndisks; i++) { + if (STREQ(target, def->disks[i]->dst)) { + disk = def->disks[i]; + break; + } + } + + if (!disk) { + virReportError(VIR_ERR_INVALID_ARG, + _("failed to find disk '%s"), target); + goto cleanup; + } + + src = virStorageFileChainLookup(disk->src, NULL, NULL, idx, NULL); + + cleanup: + VIR_FREE(target); + return src; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index a07d671a9..3a7afdf45 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -862,4 +862,7 @@ char *qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, virStorageSourcePtr src, unsigned int idx); +virStorageSourcePtr qemuDomainGetStorageSourceByDevstr(const char *devstr, + virDomainDefPtr def); + #endif /* __QEMU_DOMAIN_H__ */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
--- src/qemu/qemu_domain.c | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ 2 files changed, 40 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add code to call the appropriate monitor command and code to lookup the given disk backing chain member. --- src/qemu/qemu_driver.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 13 ++++++++ src/qemu/qemu_monitor.h | 5 ++++ src/qemu/qemu_monitor_json.c | 31 ++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++++ 5 files changed, 125 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 06bd442ee..d8e3ddf57 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20291,6 +20291,75 @@ qemuDomainSetVcpu(virDomainPtr dom, } +static int +qemuDomainSetBlockThreshold(virDomainPtr dom, + const char *dev, + unsigned long long threshold, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm = NULL; + virStorageSourcePtr src; + char *nodename = NULL; + int rc; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + priv = vm->privateData; + + if (virDomainSetBlockThresholdEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCK_WRITE_THRESHOLD)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("this qemu does not support setting device threshold")); + goto cleanup; + } + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not running")); + goto endjob; + } + + if (!(src = qemuDomainGetStorageSourceByDevstr(dev, vm->def))) + goto endjob; + + if (!src->nodebacking) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("threshold currently can't be set for block device '%s'"), + dev); + goto endjob; + } + + if (VIR_STRDUP(nodename, src->nodebacking) < 0) + goto endjob; + + qemuDomainObjEnterMonitor(driver, vm); + rc = qemuMonitorSetBlockThreshold(priv->mon, nodename, threshold); + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + goto endjob; + + ret = 0; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(nodename); + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20505,6 +20574,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetGuestVcpus = qemuDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = qemuDomainSetGuestVcpus, /* 2.0.0 */ .domainSetVcpu = qemuDomainSetVcpu, /* 3.1.0 */ + .domainSetBlockThreshold = qemuDomainSetBlockThreshold /* 3.2.0 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f43d79b98..e2af0ca02 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4171,3 +4171,16 @@ qemuMonitorQueryQMPSchema(qemuMonitorPtr mon) return qemuMonitorJSONQueryQMPSchema(mon); } + + +int +qemuMonitorSetBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold) +{ + VIR_DEBUG("mon=%p, node='%s', threshold=%llu", mon, nodename, threshold); + + QEMU_CHECK_MONITOR_JSON(mon); + + return qemuMonitorJSONSetBlockThreshold(mon, nodename, threshold); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 66dcd2468..61d89a9cf 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1072,4 +1072,9 @@ int qemuMonitorGetRTCTime(qemuMonitorPtr mon, virHashTablePtr qemuMonitorQueryQMPSchema(qemuMonitorPtr mon); +int qemuMonitorSetBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold); + + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index fc33e8635..6fb2af48a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -7487,3 +7487,34 @@ qemuMonitorJSONQueryQMPSchema(qemuMonitorPtr mon) return ret; } + + +int +qemuMonitorJSONSetBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold) +{ + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + int ret = -1; + + if (!(cmd = qemuMonitorJSONMakeCommand("block-set-write-threshold", + "s:node-name", nodename, + "U:write-threshold", threshold, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 59d9f098c..e0dd696d9 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -511,4 +511,10 @@ int qemuMonitorJSONGetHotpluggableCPUs(qemuMonitorPtr mon, virHashTablePtr qemuMonitorJSONQueryQMPSchema(qemuMonitorPtr mon) ATTRIBUTE_NONNULL(1); + +int qemuMonitorJSONSetBlockThreshold(qemuMonitorPtr mon, + const char *nodename, + unsigned long long threshold) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + #endif /* QEMU_MONITOR_JSON_H */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Add code to call the appropriate monitor command and code to lookup the given disk backing chain member. ---
+ + if (!src->nodebacking) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("threshold currently can't be set for block device '%s'"), + dev); + goto endjob; + }
So if I'm reading this right, at this point in the series, you always fail here because src->nodebacking is still not set, but that's what later patches address.
static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20505,6 +20574,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetGuestVcpus = qemuDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = qemuDomainSetGuestVcpus, /* 2.0.0 */ .domainSetVcpu = qemuDomainSetVcpu, /* 3.1.0 */ + .domainSetBlockThreshold = qemuDomainSetBlockThreshold /* 3.2.0 */
Earlier in 10/23: + .domainSetBlockThreshold = remoteDomainSetBlockThreshold, /* 3.1.0 */ You'll want those numbers to match ;) With that fixed (presumably the typo is in 10/23), ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

--- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml | 1 + 14 files changed, 15 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index c4695693a..918c20fa3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -360,6 +360,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "virtio-net.host_mtu", "spice-rendernode", "block-write-threshold", + "query-named-block-nodes", ); @@ -1517,6 +1518,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = { { "query-hotpluggable-cpus", QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS }, { "query-qmp-schema", QEMU_CAPS_QUERY_QMP_SCHEMA }, { "query-cpu-model-expansion", QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION}, + { "query-named-block-nodes", QEMU_CAPS_QUERY_NAMED_BLOCK_NODES} }; struct virQEMUCapsStringFlags virQEMUCapsMigration[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index eb72ad646..86a129a75 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -395,6 +395,7 @@ typedef enum { QEMU_CAPS_VIRTIO_NET_HOST_MTU, /* virtio-net-*.host_mtu */ QEMU_CAPS_SPICE_RENDERNODE, /* -spice rendernode */ QEMU_CAPS_BLOCK_WRITE_THRESHOLD, /* BLOCK_WRITE_THRESHOLD event */ + QEMU_CAPS_QUERY_NAMED_BLOCK_NODES, /* qmp query-named-block-nodes */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml index f3635d9a0..fd044c192 100644 --- a/tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml @@ -161,6 +161,7 @@ <flag name='drive-detect-zeroes'/> <flag name='display'/> <flag name='vhost-scsi'/> + <flag name='query-named-block-nodes'/> <version>2001001</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml index 8b72751e9..3423bec2a 100644 --- a/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml @@ -183,6 +183,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2004000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml index 215c3f110..d761935af 100644 --- a/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml @@ -189,6 +189,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2005000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml b/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml index 80c29d3ab..47616d8e8 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml @@ -165,6 +165,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml b/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml index 2ef53173c..6f8a7031a 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml @@ -165,6 +165,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml index 78e78ca96..8f0419993 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml @@ -160,6 +160,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml index 8caf448b4..c71a857b2 100644 --- a/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml @@ -198,6 +198,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2006000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml index 6206bad9e..2c2fb408f 100644 --- a/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml @@ -128,6 +128,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2007000</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml index fd13ae4f7..e3d7e7bf4 100644 --- a/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml @@ -200,6 +200,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2007000</version> <kvmVersion>0</kvmVersion> <package> (v2.7.0)</package> diff --git a/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml index b7ce2c6c0..256540ad4 100644 --- a/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml @@ -130,6 +130,7 @@ <flag name='drive-iotune-group'/> <flag name='query-cpu-model-expansion'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2007093</version> <kvmVersion>0</kvmVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml index b741e8fcc..04e4fcd09 100644 --- a/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml @@ -201,6 +201,7 @@ <flag name='vhost-scsi'/> <flag name='drive-iotune-group'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2008000</version> <kvmVersion>0</kvmVersion> <package> (v2.8.0)</package> diff --git a/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml index 8043240e2..aa4a06dcb 100644 --- a/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml @@ -203,6 +203,7 @@ <flag name='query-cpu-model-expansion'/> <flag name='virtio-net.host_mtu'/> <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> <version>2008050</version> <kvmVersion>0</kvmVersion> <package> (v2.8.0-1961-g5b10b94bd5)</package> -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
--- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0-gicv2.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0-gicv3.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml | 1 + tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml | 1 + 14 files changed, 15 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add monitor tooling for calling query-named-block-nodes. The monitor returns the data as the raw JSON array that is returned from the monitor. Unfortunately the logic to extract the node names for a complete backing chain will be so complex that I won't be able to extract any meaningful subset of the data in the monitor code. --- src/qemu/qemu_monitor.c | 11 +++++++++++ src/qemu/qemu_monitor.h | 1 + src/qemu/qemu_monitor_json.c | 26 ++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 +++ 4 files changed, 41 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index e2af0ca02..36b8b5ea4 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4184,3 +4184,14 @@ qemuMonitorSetBlockThreshold(qemuMonitorPtr mon, return qemuMonitorJSONSetBlockThreshold(mon, nodename, threshold); } + + +virJSONValuePtr +qemuMonitorQueryNamedBlockNodes(qemuMonitorPtr mon) +{ + VIR_DEBUG("mon=%p", mon); + + QEMU_CHECK_MONITOR_JSON_NULL(mon); + + return qemuMonitorJSONQueryNamedBlockNodes(mon); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 61d89a9cf..77e7aeeca 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1076,5 +1076,6 @@ int qemuMonitorSetBlockThreshold(qemuMonitorPtr mon, const char *nodename, unsigned long long threshold); +virJSONValuePtr qemuMonitorQueryNamedBlockNodes(qemuMonitorPtr mon); #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 6fb2af48a..141d56b3d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -7518,3 +7518,29 @@ qemuMonitorJSONSetBlockThreshold(qemuMonitorPtr mon, return ret; } + + +virJSONValuePtr +qemuMonitorJSONQueryNamedBlockNodes(qemuMonitorPtr mon) +{ + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr ret = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-named-block-nodes", NULL))) + return NULL; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + ret = virJSONValueObjectStealArray(reply, "return"); + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e0dd696d9..9c363a0a1 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -517,4 +517,7 @@ int qemuMonitorJSONSetBlockThreshold(qemuMonitorPtr mon, unsigned long long threshold) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +virJSONValuePtr qemuMonitorJSONQueryNamedBlockNodes(qemuMonitorPtr mon) + ATTRIBUTE_NONNULL(1); + #endif /* QEMU_MONITOR_JSON_H */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Add monitor tooling for calling query-named-block-nodes. The monitor returns the data as the raw JSON array that is returned from the monitor.
Unfortunately the logic to extract the node names for a complete backing chain will be so complex that I won't be able to extract any meaningful subset of the data in the monitor code.
Indeed. My recollection was that query-named-block-nodes is great for getting information if you already know a node name, but that you have to resort to other commands if you need to learn a node name (of course, it gets easier if libvirt actually assigns node names rather than relying on qemu - but that's what blockdev-add integration is all about).
--- src/qemu/qemu_monitor.c | 11 +++++++++++ src/qemu/qemu_monitor.h | 1 + src/qemu/qemu_monitor_json.c | 26 ++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 +++ 4 files changed, 41 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

qemu for some time already sets node names automatically for the block nodes. This patch adds code that attempts a best-effort detection of the node names for the backing chain from the output of 'query-named-block-nodes'. The only drawback is that the data provided by qemu needs to be matched by the filename as seen by qemu and thus if two disks share a single backing store file the detection won't work. This will allow us to use qemu commands such as 'block-set-write-threshold' which only accepts node names. In this patch only the detection code is added, it will be used later. --- src/Makefile.am | 1 + src/qemu/qemu_block.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 47 +++++++++ 3 files changed, 328 insertions(+) create mode 100644 src/qemu/qemu_block.c create mode 100644 src/qemu/qemu_block.h diff --git a/src/Makefile.am b/src/Makefile.am index f0d8efe50..b4ecd6366 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -823,6 +823,7 @@ VBOX_DRIVER_EXTRA_DIST = \ QEMU_DRIVER_SOURCES = \ qemu/qemu_agent.c qemu/qemu_agent.h \ qemu/qemu_alias.c qemu/qemu_alias.h \ + qemu/qemu_block.c qemu/qemu_block.h \ qemu/qemu_blockjob.c qemu/qemu_blockjob.h \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h \ qemu/qemu_command.c qemu/qemu_command.h \ diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c new file mode 100644 index 000000000..91c04ab7f --- /dev/null +++ b/src/qemu/qemu_block.c @@ -0,0 +1,280 @@ +/* + * qemu_block.c: helper functions for QEMU block subsystem + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "qemu_block.h" +#include "qemu_domain.h" + +#include "viralloc.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + + +static void +qemuBlockNodeNameBackingChainDataFree(qemuBlockNodeNameBackingChainDataPtr data) +{ + size_t i; + + if (!data) + return; + + for (i = 0; i < data->nelems; i++) + virJSONValueFree(data->elems[i]); + + VIR_FREE(data->nodeformat); + VIR_FREE(data->nodestorage); + VIR_FREE(data->nodebacking); + + VIR_FREE(data->qemufilename); + VIR_FREE(data->backingstore); + + VIR_FREE(data); +} + + +static void +qemuBlockNodeNameBackingChainDataHashEntryFree(void *opaque, + const void *name ATTRIBUTE_UNUSED) +{ + qemuBlockNodeNameBackingChainDataFree(opaque); +} + + +struct qemuBlockNodeNameGetBackingChainData { + virHashTablePtr table; + qemuBlockNodeNameBackingChainDataPtr *entries; + size_t nentries; +}; + + +static int +qemuBlockNodeNameDetectProcessByFilename(size_t pos ATTRIBUTE_UNUSED, + virJSONValuePtr item, + void *opaque) +{ + struct qemuBlockNodeNameGetBackingChainData *data = opaque; + qemuBlockNodeNameBackingChainDataPtr entry; + const char *file; + + if (!(file = virJSONValueObjectGetString(item, "file"))) + return 1; + + if (!(entry = virHashLookup(data->table, file))) { + if (VIR_ALLOC(entry) < 0) + return -1; + + if (VIR_APPEND_ELEMENT_COPY(data->entries, data->nentries, entry) < 0) { + VIR_FREE(entry); + return -1; + } + + if (VIR_STRDUP(entry->qemufilename, file) < 0) + return -1; + + if (virHashAddEntry(data->table, file, entry) < 0) + return -1; + } + + if (VIR_APPEND_ELEMENT(entry->elems, entry->nelems, item) < 0) + return -1; + + return 0; +} + + +static const char *qemuBlockDriversFormat[] = { + "qcow2", "raw", "qcow", "luks", "qed", "bochs", "cloop", "dmg", "parallels", + "vdi", "vhdx", "vmdk", "vpc", "vvfat", NULL}; +static const char *qemuBlockDriversStorage[] = { + "file", "iscsi", "nbd", "host_cdrom", "host_device", "ftp", "ftps", + "gluster", "http", "https", "nfs", "rbd", "sheepdog", "ssh", "tftp", NULL}; + + +static bool +qemuBlockDriverMatch(const char *drvname, + const char **drivers) +{ + while (*drivers) { + if (STREQ(drvname, *drivers)) + return true; + + drivers++; + } + + return false; +} + + +static int +qemuBlockNodeNameDetectProcessExtract(qemuBlockNodeNameBackingChainDataPtr data) +{ + const char *drv; + const char *nodename; + const char *backingstore; + size_t i; + + /* Since the only way to construct the backing chain is to look up the files + * by file name, if two disks share a backing image we can't know which node + * belongs to which backing chain. Refuse to detect such chains. */ + if (data->nelems > 2) + return 0; + + for (i = 0; i < data->nelems; i++) { + drv = virJSONValueObjectGetString(data->elems[i], "drv"); + nodename = virJSONValueObjectGetString(data->elems[i], "node-name"); + backingstore = virJSONValueObjectGetString(data->elems[i], "backing_file"); + + if (!drv || !nodename) + continue; + + if (qemuBlockDriverMatch(drv, qemuBlockDriversFormat)) { + if (data->nodeformat) + continue; + + if (VIR_STRDUP(data->nodeformat, nodename) < 0) + return -1; + + /* extract the backing store file name for the protocol layer */ + if (VIR_STRDUP(data->backingstore, backingstore) < 0) + return -1; + } else if (qemuBlockDriverMatch(drv, qemuBlockDriversStorage)) { + if (data->nodestorage) + continue; + + if (VIR_STRDUP(data->nodestorage, nodename) < 0) + return -1; + } + } + + return 0; +} + + +static int +qemuBlockNodeNameDetectProcessLinkBacking(qemuBlockNodeNameBackingChainDataPtr data, + virHashTablePtr table) +{ + qemuBlockNodeNameBackingChainDataPtr backing; + + if (!data->backingstore) + return 0; + + if (!(backing = virHashLookup(table, data->backingstore))) + return 0; + + if (VIR_STRDUP(data->nodebacking, backing->nodeformat) < 0) + return -1; + + return 0; +} + + +static void +qemuBlockNodeNameGetBackingChainDataClearLookup(qemuBlockNodeNameBackingChainDataPtr data) +{ + size_t i; + + for (i = 0; i < data->nelems; i++) + virJSONValueFree(data->elems[i]); + + VIR_FREE(data->elems); + data->nelems = 0; +} + + +/** + * qemuBlockNodeNameGetBackingChain: + * @json: JSON array of data returned from 'query-named-block-nodes' + * + * Tries to reconstruct the backing chain from @json to allow detection of + * node names that were auto-assigned by qemu. This is a best-effort operation + * and may not be successful. The returned hash table contains the entries as + * qemuBlockNodeNameBackingChainDataPtr accessible by the node name. The fields + * then can be used to recover the full backing chain. + * + * Returns a hash table on success and NULL on failure. + */ +virHashTablePtr +qemuBlockNodeNameGetBackingChain(virJSONValuePtr json) +{ + struct qemuBlockNodeNameGetBackingChainData data; + virHashTablePtr nodetable = NULL; + virHashTablePtr ret = NULL; + size_t i; + + memset(&data, 0, sizeof(data)); + + /* hash table keeps the entries accessible by the 'file' in qemu */ + if (!(data.table = virHashCreate(50, NULL))) + goto cleanup; + + /* first group the named entries by the 'file' field */ + if (virJSONValueArrayForeachSteal(json, + qemuBlockNodeNameDetectProcessByFilename, + &data) < 0) + goto cleanup; + + /* extract the node names for the format and storage layer */ + for (i = 0; i < data.nentries; i++) { + if (qemuBlockNodeNameDetectProcessExtract(data.entries[i]) < 0) + goto cleanup; + } + + /* extract the node name for the backing file */ + for (i = 0; i < data.nentries; i++) { + if (qemuBlockNodeNameDetectProcessLinkBacking(data.entries[i], + data.table) < 0) + goto cleanup; + } + + /* clear JSON data necessary only for the lookup procedure */ + for (i = 0; i < data.nentries; i++) + qemuBlockNodeNameGetBackingChainDataClearLookup(data.entries[i]); + + /* create hash table hashed by the format node name */ + if (!(nodetable = virHashCreate(50, + qemuBlockNodeNameBackingChainDataHashEntryFree))) + goto cleanup; + + /* fill the entries */ + for (i = 0; i < data.nentries; i++) { + if (!data.entries[i]->nodeformat) + continue; + + if (virHashAddEntry(nodetable, data.entries[i]->nodeformat, + data.entries[i]) < 0) + goto cleanup; + + /* hash table steals the entry and then frees it by itself */ + data.entries[i] = NULL; + } + + VIR_STEAL_PTR(ret, nodetable); + + cleanup: + virHashFree(data.table); + virHashFree(nodetable); + for (i = 0; i < data.nentries; i++) + qemuBlockNodeNameBackingChainDataFree(data.entries[i]); + + VIR_FREE(data.entries); + + return ret; +} diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h new file mode 100644 index 000000000..26f5ae062 --- /dev/null +++ b/src/qemu/qemu_block.h @@ -0,0 +1,47 @@ +/* + * qemu_block.h: helper functions for QEMU block subsystem + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __QEMU_BLOCK_H__ +# define __QEMU_BLOCK_H__ + +# include "internal.h" + +# include "qemu_conf.h" + +# include "virhash.h" +# include "virjson.h" + +typedef struct qemuBlockNodeNameBackingChainData qemuBlockNodeNameBackingChainData; +typedef qemuBlockNodeNameBackingChainData *qemuBlockNodeNameBackingChainDataPtr; +struct qemuBlockNodeNameBackingChainData { + char *qemufilename; /* name of the image from qemu */ + char *backingstore; + char *nodeformat; /* node name of the format layer */ + char *nodestorage; /* node name of the storage backing the format node */ + + char *nodebacking; /* node name of the backing file format layer */ + + /* data necessary for detection of the node names from qemu */ + virJSONValuePtr *elems; + size_t nelems; +}; + +virHashTablePtr +qemuBlockNodeNameGetBackingChain(virJSONValuePtr data); + +#endif /* __QEMU_BLOCK_H__ */ -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
qemu for some time already sets node names automatically for the block nodes. This patch adds code that attempts a best-effort detection of the node names for the backing chain from the output of 'query-named-block-nodes'. The only drawback is that the data provided by qemu needs to be matched by the filename as seen by qemu and thus if two disks share a single backing store file the detection won't work.
This will allow us to use qemu commands such as 'block-set-write-threshold' which only accepts node names.
In this patch only the detection code is added, it will be used later. ---
A bit hairy to follow, but looks like it works. The real test will be when I play with it after applying your entire series locally, to see what I can come up with. So for now, tentative ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The code is rather magic so a test case will help making sure that everything works well. The first case is a simple backing chain. --- .../qemumonitorjson-nodename-1.json | 268 +++++++++++++++++++++ .../qemumonitorjson-nodename-1.result | 15 ++ tests/qemumonitorjsontest.c | 116 +++++++++ 3 files changed, 399 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json b/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json new file mode 100644 index 000000000..fe2f32176 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json @@ -0,0 +1,268 @@ +[ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block567", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 9665380352, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "format": "file", + "actual-size": 9665384448, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block424", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block331", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block281", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 32968704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block118", + "backing_file_depth": 2, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545313", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 33030144, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "format": "file", + "actual-size": 32968704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block078", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545313", + "encryption_key_missing": false + } +] diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result b/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result new file mode 100644 index 000000000..e43f3d366 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result @@ -0,0 +1,15 @@ +filename : '/var/lib/libvirt/images/rhel7.3.1483545313' +format node : '#block118' +storage node: '#block078' +backingfile : '/var/lib/libvirt/images/rhel7.3.1483536402' +backing ptr : '#block331' + filename : '/var/lib/libvirt/images/rhel7.3.1483536402' + format node : '#block331' + storage node: '#block281' + backingfile : '/var/lib/libvirt/images/rhel7.3.qcow2' + backing ptr : '#block567' + filename : '/var/lib/libvirt/images/rhel7.3.qcow2' + format node : '#block567' + storage node: '#block424' + backingfile : '<null>' + backing ptr : '<null>' diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 402c87d45..fcc1540a8 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -23,6 +23,7 @@ #include "testutilsqemu.h" #include "qemumonitortestutils.h" #include "qemu/qemu_domain.h" +#include "qemu/qemu_block.h" #include "qemu/qemu_monitor_json.h" #include "virthread.h" #include "virerror.h" @@ -2659,6 +2660,109 @@ testQemuMonitorCPUInfo(const void *opaque) } +struct testBlockNodeNameDetectData { + const char *name; + const char *nodenames; +}; + + +static void +testBlockNodeNameDetectFormat(virBufferPtr buf, + const char *basenode, + virHashTablePtr nodedata) +{ + qemuBlockNodeNameBackingChainDataPtr entry = NULL; + const char *node = basenode; + + virBufferSetIndent(buf, 0); + + while (node) { + if (!(entry = virHashLookup(nodedata, node))) + break; + + virBufferAsprintf(buf, "filename : '%s'\n", entry->qemufilename); + virBufferAsprintf(buf, "format node : '%s'\n", + NULLSTR(entry->nodeformat)); + virBufferAsprintf(buf, "storage node: '%s'\n", + NULLSTR(entry->nodestorage)); + virBufferAsprintf(buf, "backingfile : '%s'\n", + NULLSTR(entry->backingstore)); + virBufferAsprintf(buf, "backing ptr : '%s'\n", + NULLSTR(entry->nodebacking)); + + virBufferAdjustIndent(buf, 2); + + node = entry->nodebacking; + } + + virBufferSetIndent(buf, 0); + virBufferAddLit(buf, "\n"); +} + + +static int +testBlockNodeNameDetect(const void *opaque) +{ + const struct testBlockNodeNameDetectData *data = opaque; + char *jsonFile = NULL; + char *jsonStr = NULL; + char *resultFile = NULL; + char *actual = NULL; + char **nodenames = NULL; + char **next; + virJSONValuePtr json = NULL; + virHashTablePtr nodedata = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int ret = -1; + + if (virAsprintf(&jsonFile, + "%s/qemumonitorjsondata/qemumonitorjson-nodename-%s.json", + abs_srcdir, data->name) < 0 || + virAsprintf(&resultFile, + "%s/qemumonitorjsondata/qemumonitorjson-nodename-%s.result", + abs_srcdir, data->name) < 0) + goto cleanup; + + if (!(nodenames = virStringSplit(data->nodenames, ",", 0))) + goto cleanup; + + if (virTestLoadFile(jsonFile, &jsonStr) < 0) + goto cleanup; + + if (!(json = virJSONValueFromString(jsonStr))) + goto cleanup; + + if (!(nodedata = qemuBlockNodeNameGetBackingChain(json))) + goto cleanup; + + for (next = nodenames; *next; next++) + testBlockNodeNameDetectFormat(&buf, *next, nodedata); + + virBufferTrim(&buf, "\n", -1); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (virTestCompareToFile(actual, resultFile) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(jsonFile); + VIR_FREE(resultFile); + VIR_FREE(jsonStr); + VIR_FREE(actual); + virHashFree(nodedata); + virStringListFree(nodenames); + virJSONValueFree(json); + + return ret; +} + + static int mymain(void) { @@ -2793,6 +2897,18 @@ mymain(void) DO_TEST_CPU_INFO("ppc64-hotplug-4", 24); DO_TEST_CPU_INFO("ppc64-no-threads", 16); +#define DO_TEST_BLOCK_NODE_DETECT(testname, testnodes) \ + do { \ + struct testBlockNodeNameDetectData testdata = {testname, testnodes}; \ + if (virTestRun("node-name-detect(" testname ")", \ + testBlockNodeNameDetect, &testdata) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_BLOCK_NODE_DETECT("1", "#block118"); + +#undef DO_TEST_BLOCK_NODE_DETECT + qemuTestDriverFree(&driver); return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
The code is rather magic so a test case will help making sure that everything works well. The first case is a simple backing chain. --- .../qemumonitorjson-nodename-1.json | 268 +++++++++++++++++++++ .../qemumonitorjson-nodename-1.result | 15 ++ tests/qemumonitorjsontest.c | 116 +++++++++ 3 files changed, 399 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result
+++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-1.result @@ -0,0 +1,15 @@ +filename : '/var/lib/libvirt/images/rhel7.3.1483545313' +format node : '#block118' +storage node: '#block078' +backingfile : '/var/lib/libvirt/images/rhel7.3.1483536402' +backing ptr : '#block331' + filename : '/var/lib/libvirt/images/rhel7.3.1483536402' + format node : '#block331' + storage node: '#block281' + backingfile : '/var/lib/libvirt/images/rhel7.3.qcow2' + backing ptr : '#block567' + filename : '/var/lib/libvirt/images/rhel7.3.qcow2' + format node : '#block567' + storage node: '#block424' + backingfile : '<null>' + backing ptr : '<null>'
Nice - and you're definitely right that having the test is essential to maintaining the code. ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

--- .../qemumonitorjson-nodename-2.json | 2270 ++++++++++++++++++++ .../qemumonitorjson-nodename-2.result | 60 + tests/qemumonitorjsontest.c | 1 + 3 files changed, 2331 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json b/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json new file mode 100644 index 000000000..452b9d695 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json @@ -0,0 +1,2270 @@ +[ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block2399", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 9665380352, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "format": "file", + "actual-size": 9665384448, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block2281", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block2157", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.qcow2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block2008", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1979", + "backing_file_depth": 2, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483536402", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545313", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 33161216, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "format": "file", + "actual-size": 33099776, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1814", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545313", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1799", + "backing_file_depth": 3, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483545313", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545901", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1690", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483545901", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1528", + "backing_file_depth": 4, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483545901", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483546244", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1427", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483546244", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1350", + "backing_file_depth": 5, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483546244", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483605920", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 19529728, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "format": "file", + "actual-size": 19472384, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1204", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483605920", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1119", + "backing_file_depth": 6, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483605920", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483605924", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 7798784, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "format": "file", + "actual-size": 7618560, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block1075", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483605924", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 159182365, + "name": "1483615264", + "date-sec": 1483615264, + "date-nsec": 325173000, + "vm-clock-sec": 8, + "id": "1", + "vm-state-size": 151985953 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 163266560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block998", + "backing_file_depth": 7, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483605924", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483615252", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 163446784, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "format": "file", + "actual-size": 163266560, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block843", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1483615252", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 159182365, + "name": "1483615264", + "date-sec": 1483615264, + "date-nsec": 325173000, + "vm-clock-sec": 8, + "id": "1", + "vm-state-size": 151985953 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 163266560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 4132864, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block717", + "backing_file_depth": 8, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1483615252", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071872", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 4194304, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "format": "file", + "actual-size": 4132864, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block612", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071872", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 159182365, + "name": "1483615264", + "date-sec": 1483615264, + "date-nsec": 325173000, + "vm-clock-sec": 8, + "id": "1", + "vm-state-size": 151985953 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 163266560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 4132864, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block560", + "backing_file_depth": 9, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1484071872", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071876", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block430", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071876", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 159182365, + "name": "1483615264", + "date-sec": 1483615264, + "date-nsec": 325173000, + "vm-clock-sec": 8, + "id": "1", + "vm-state-size": 151985953 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 163266560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 4132864, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071877", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 2560000, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block316", + "backing_file_depth": 10, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1484071876", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071877", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 2621440, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071877", + "format": "file", + "actual-size": 2560000, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block248", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071877", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 9665384448, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": true, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.qcow2", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 33099776, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483536402", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545313", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483545901", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 19472384, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483546244", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 945245467, + "name": "1483615244", + "date-sec": 1483615244, + "date-nsec": 439285000, + "vm-clock-sec": 4, + "id": "1", + "vm-state-size": 7226413 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 7618560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605920", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "snapshots": [ + { + "vm-clock-nsec": 159182365, + "name": "1483615264", + "date-sec": 1483615264, + "date-nsec": 325173000, + "vm-clock-sec": 8, + "id": "1", + "vm-state-size": 151985953 + } + ], + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 163266560, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483605924", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 4132864, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1483615252", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071872", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071877", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 2560000, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071876", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 9663676416, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071880", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 17440768, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071877", + "backing-filename": "/var/lib/libvirt/images/rhel7.3.1484071877", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block161", + "backing_file_depth": 11, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/rhel7.3.1484071877", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071880", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 17498112, + "filename": "/var/lib/libvirt/images/rhel7.3.1484071880", + "format": "file", + "actual-size": 17440768, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block013", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/rhel7.3.1484071880", + "encryption_key_missing": false + } +] diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result b/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result new file mode 100644 index 000000000..b6e1e2916 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result @@ -0,0 +1,60 @@ +filename : '/var/lib/libvirt/images/rhel7.3.1484071880' +format node : '#block161' +storage node: '#block013' +backingfile : '/var/lib/libvirt/images/rhel7.3.1484071877' +backing ptr : '#block316' + filename : '/var/lib/libvirt/images/rhel7.3.1484071877' + format node : '#block316' + storage node: '#block248' + backingfile : '/var/lib/libvirt/images/rhel7.3.1484071876' + backing ptr : '#block560' + filename : '/var/lib/libvirt/images/rhel7.3.1484071876' + format node : '#block560' + storage node: '#block430' + backingfile : '/var/lib/libvirt/images/rhel7.3.1484071872' + backing ptr : '#block717' + filename : '/var/lib/libvirt/images/rhel7.3.1484071872' + format node : '#block717' + storage node: '#block612' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483615252' + backing ptr : '#block998' + filename : '/var/lib/libvirt/images/rhel7.3.1483615252' + format node : '#block998' + storage node: '#block843' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483605924' + backing ptr : '#block1119' + filename : '/var/lib/libvirt/images/rhel7.3.1483605924' + format node : '#block1119' + storage node: '#block1075' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483605920' + backing ptr : '#block1350' + filename : '/var/lib/libvirt/images/rhel7.3.1483605920' + format node : '#block1350' + storage node: '#block1204' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483546244' + backing ptr : '#block1528' + filename : '/var/lib/libvirt/images/rhel7.3.1483546244' + format node : '#block1528' + storage node: '#block1427' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483545901' + backing ptr : '#block1799' + filename : '/var/lib/libvirt/images/rhel7.3.1483545901' + format node : '#block1799' + storage node: '#block1690' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483545313' + backing ptr : '#block1979' + filename : '/var/lib/libvirt/images/rhel7.3.1483545313' + format node : '#block1979' + storage node: '#block1814' + backingfile : '/var/lib/libvirt/images/rhel7.3.1483536402' + backing ptr : '#block2157' + filename : '/var/lib/libvirt/images/rhel7.3.1483536402' + format node : '#block2157' + storage node: '#block2008' + backingfile : '/var/lib/libvirt/images/rhel7.3.qcow2' + backing ptr : '#block2399' + filename : '/var/lib/libvirt/images/rhel7.3.qcow2' + format node : '#block2399' + storage node: '#block2281' + backingfile : '<null>' + backing ptr : '<null>' diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index fcc1540a8..fd27df184 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2906,6 +2906,7 @@ mymain(void) } while (0) DO_TEST_BLOCK_NODE_DETECT("1", "#block118"); + DO_TEST_BLOCK_NODE_DETECT("2", "#block161"); #undef DO_TEST_BLOCK_NODE_DETECT -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
--- .../qemumonitorjson-nodename-2.json | 2270 ++++++++++++++++++++ .../qemumonitorjson-nodename-2.result | 60 + tests/qemumonitorjsontest.c | 1 + 3 files changed, 2331 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result
Big, but gets the point made.
+++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-2.result @@ -0,0 +1,60 @@ +filename : '/var/lib/libvirt/images/rhel7.3.1484071880' +format node : '#block161' +storage node: '#block013' +backingfile : '/var/lib/libvirt/images/rhel7.3.1484071877' +backing ptr : '#block316' + filename : '/var/lib/libvirt/images/rhel7.3.1484071877'
+ filename : '/var/lib/libvirt/images/rhel7.3.qcow2'
12 or so levels of external snapshots. Makes for some noisy JSON from qemu. ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Since we have to match the images by filename a common backing image will break the detection process. Add a test case to see that the code correctly did not continue the detection process. --- .../qemumonitorjson-nodename-same-backing.json | 316 +++++++++++++++++++++ .../qemumonitorjson-nodename-same-backing.result | 11 + tests/qemumonitorjsontest.c | 1 + 3 files changed, 328 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json b/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json new file mode 100644 index 000000000..78954bb25 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json @@ -0,0 +1,316 @@ +[ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block729", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/base.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block628", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/b.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/base.qcow2", + "backing-filename": "/var/lib/libvirt/images/base.qcow2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block574", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/base.qcow2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/b.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/b.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block405", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/b.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block399", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/base.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block257", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/a.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/base.qcow2", + "backing-filename": "/var/lib/libvirt/images/base.qcow2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block170", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/base.qcow2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/a.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/a.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block057", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/a.qcow2", + "encryption_key_missing": false + } + ] diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result b/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result new file mode 100644 index 000000000..2c774be27 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result @@ -0,0 +1,11 @@ +filename : '/var/lib/libvirt/images/a.qcow2' +format node : '#block170' +storage node: '#block057' +backingfile : '/var/lib/libvirt/images/base.qcow2' +backing ptr : '<null>' + +filename : '/var/lib/libvirt/images/b.qcow2' +format node : '#block574' +storage node: '#block405' +backingfile : '/var/lib/libvirt/images/base.qcow2' +backing ptr : '<null>' diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index fd27df184..7e3e7db27 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2907,6 +2907,7 @@ mymain(void) DO_TEST_BLOCK_NODE_DETECT("1", "#block118"); DO_TEST_BLOCK_NODE_DETECT("2", "#block161"); + DO_TEST_BLOCK_NODE_DETECT("same-backing", "#block170,#block574"); #undef DO_TEST_BLOCK_NODE_DETECT -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Since we have to match the images by filename a common backing image will break the detection process. Add a test case to see that the code correctly did not continue the detection process.
Maybe qemu can be enhanced to add a new command that makes querying relationships easier (won't happen any sooner than qemu 2.10). Or maybe libvirt just learns to always invent its own node names via blockdev-add. But in the meantime, testing our limitations is the best we can do.
--- .../qemumonitorjson-nodename-same-backing.json | 316 +++++++++++++++++++++ .../qemumonitorjson-nodename-same-backing.result | 11 + tests/qemumonitorjsontest.c | 1 + 3 files changed, 328 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-same-backing.result
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Mar 23, 2017 at 17:19:40 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Since we have to match the images by filename a common backing image will break the detection process. Add a test case to see that the code correctly did not continue the detection process.
Maybe qemu can be enhanced to add a new command that makes querying relationships easier (won't happen any sooner than qemu 2.10). Or maybe libvirt just learns to always invent its own node names via blockdev-add. But in the meantime, testing our limitations is the best we can do.
We really want to switch to -blockdev/blockdev-add and do our own node names so such API would be of little use to libvirt.

oVirt uses relative names with directories in them. Test such configuration. Also tests a snapshot done with _REUSE_EXTERNAL and a relative backing file pre-specified in the qcow2 metadata. --- .../qemumonitorjson-nodename-relative.json | 554 +++++++++++++++++++++ .../qemumonitorjson-nodename-relative.result | 31 ++ tests/qemumonitorjsontest.c | 1 + 3 files changed, 586 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json b/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json new file mode 100644 index 000000000..1fee149ac --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json @@ -0,0 +1,554 @@ +[ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/relsnap.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/base.qcow2", + "backing-filename": "/var/lib/libvirt/images/base.qcow2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block1177", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/base.qcow2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/relsnap.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/relsnap.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block1027", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/relsnap.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/base.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block957", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/base.qcow2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block840", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/base.qcow2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img0", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block709", + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img0", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/img0", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block665", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img0", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img0", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img1", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img0", + "backing-filename": "/var/lib/libvirt/images/img0", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block514", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/img0", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img1", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/img1", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block481", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img1", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img0", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img1", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img0", + "backing-filename": "/var/lib/libvirt/images/img0", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img1", + "backing-filename": "/var/lib/libvirt/images/img1", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block357", + "backing_file_depth": 2, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/img1", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/img2", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block290", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img2", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "backing-image": { + "backing-image": { + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img0", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img1", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img0", + "backing-filename": "/var/lib/libvirt/images/img0", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img1", + "backing-filename": "/var/lib/libvirt/images/img1", + "dirty-flag": false + }, + "backing-filename-format": "qcow2", + "virtual-size": 10485760, + "filename": "/var/lib/libvirt/images/img3", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 200704, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "/var/lib/libvirt/images/img2", + "backing-filename": "/var/lib/libvirt/images/img2", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block153", + "backing_file_depth": 3, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "/var/lib/libvirt/images/img2", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img3", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "/var/lib/libvirt/images/img3", + "format": "file", + "actual-size": 200704, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block076", + "backing_file_depth": 0, + "drv": "file", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "/var/lib/libvirt/images/img3", + "encryption_key_missing": false + } + ] diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result b/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result new file mode 100644 index 000000000..2cd908786 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result @@ -0,0 +1,31 @@ +filename : '/var/lib/libvirt/images/img3' +format node : '#block153' +storage node: '#block076' +backingfile : '/var/lib/libvirt/images/img2' +backing ptr : '#block357' + filename : '/var/lib/libvirt/images/img2' + format node : '#block357' + storage node: '#block290' + backingfile : '/var/lib/libvirt/images/img1' + backing ptr : '#block514' + filename : '/var/lib/libvirt/images/img1' + format node : '#block514' + storage node: '#block481' + backingfile : '/var/lib/libvirt/images/img0' + backing ptr : '#block709' + filename : '/var/lib/libvirt/images/img0' + format node : '#block709' + storage node: '#block665' + backingfile : '<null>' + backing ptr : '<null>' + +filename : '/var/lib/libvirt/images/relsnap.qcow2' +format node : '#block1177' +storage node: '#block1027' +backingfile : '/var/lib/libvirt/images/base.qcow2' +backing ptr : '#block957' + filename : '/var/lib/libvirt/images/base.qcow2' + format node : '#block957' + storage node: '#block840' + backingfile : '<null>' + backing ptr : '<null>' diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 7e3e7db27..5b9f24d24 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2908,6 +2908,7 @@ mymain(void) DO_TEST_BLOCK_NODE_DETECT("1", "#block118"); DO_TEST_BLOCK_NODE_DETECT("2", "#block161"); DO_TEST_BLOCK_NODE_DETECT("same-backing", "#block170,#block574"); + DO_TEST_BLOCK_NODE_DETECT("relative", "#block153,#block1177"); #undef DO_TEST_BLOCK_NODE_DETECT -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
oVirt uses relative names with directories in them. Test such configuration. Also tests a snapshot done with _REUSE_EXTERNAL and a relative backing file pre-specified in the qcow2 metadata. --- .../qemumonitorjson-nodename-relative.json | 554 +++++++++++++++++++++ .../qemumonitorjson-nodename-relative.result | 31 ++ tests/qemumonitorjsontest.c | 1 + 3 files changed, 586 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-relative.result
Which shows that we've done enough path canonicalization to match XML filenames with whatever node-name spelling qemu might be using. ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

--- .../qemumonitorjson-nodename-gluster.json | 135 +++++++++++++++++++++ .../qemumonitorjson-nodename-gluster.result | 10 ++ tests/qemumonitorjsontest.c | 1 + 3 files changed, 146 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json b/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json new file mode 100644 index 000000000..7ea5c5d54 --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json @@ -0,0 +1,135 @@ +[ + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "backing-image": { + "virtual-size": 197120, + "filename": "gluster://gluster-host/vol0/img0", + "format": "raw", + "actual-size": 197120 + }, + "backing-filename-format": "raw", + "virtual-size": 197120, + "filename": "gluster://gluster-host/vol0/img1", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": 197120, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "full-backing-filename": "gluster://gluster-host/vol0/img0", + "backing-filename": "gluster://gluster-host/vol0/img0", + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block1008", + "backing_file_depth": 1, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "backing_file": "gluster://gluster-host/vol0/img0", + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": true, + "writeback": true + }, + "file": "gluster://gluster-host/vol0/img1", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "gluster://gluster-host/vol0/img1", + "format": "gluster", + "actual-size": 197120 + }, + "iops_wr": 0, + "ro": false, + "node-name": "#block981", + "backing_file_depth": 0, + "drv": "gluster", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": true, + "writeback": true + }, + "file": "gluster://gluster-host/vol0/img1", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "gluster://gluster-host/vol0/img0", + "format": "raw", + "actual-size": 197120 + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block349", + "backing_file_depth": 0, + "drv": "raw", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": true, + "writeback": true + }, + "file": "gluster://gluster-host/vol0/img0", + "encryption_key_missing": false + }, + { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 197120, + "filename": "gluster://gluster-host/vol0/img0", + "format": "gluster", + "actual-size": 197120 + }, + "iops_wr": 0, + "ro": true, + "node-name": "#block269", + "backing_file_depth": 0, + "drv": "gluster", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": true, + "writeback": true + }, + "file": "gluster://gluster-host/vol0/img0", + "encryption_key_missing": false + } + ] diff --git a/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result b/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result new file mode 100644 index 000000000..f38798d4b --- /dev/null +++ b/tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result @@ -0,0 +1,10 @@ +filename : 'gluster://gluster-host/vol0/img1' +format node : '#block1008' +storage node: '#block981' +backingfile : 'gluster://gluster-host/vol0/img0' +backing ptr : '#block349' + filename : 'gluster://gluster-host/vol0/img0' + format node : '#block349' + storage node: '#block269' + backingfile : '<null>' + backing ptr : '<null>' diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 5b9f24d24..4925a00b7 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2909,6 +2909,7 @@ mymain(void) DO_TEST_BLOCK_NODE_DETECT("2", "#block161"); DO_TEST_BLOCK_NODE_DETECT("same-backing", "#block170,#block574"); DO_TEST_BLOCK_NODE_DETECT("relative", "#block153,#block1177"); + DO_TEST_BLOCK_NODE_DETECT("gluster", "#block1008"); #undef DO_TEST_BLOCK_NODE_DETECT -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
--- .../qemumonitorjson-nodename-gluster.json | 135 +++++++++++++++++++++ .../qemumonitorjson-nodename-gluster.result | 10 ++ tests/qemumonitorjsontest.c | 1 + 3 files changed, 146 insertions(+) create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.json create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-nodename-gluster.result
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

To allow matching the node names gathered via 'query-named-block-nodes' we need to query and then use the top level nodes from 'query-block'. Add the data to the structure returned by qemuMonitorGetBlockInfo. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_monitor.c | 12 +++++++++++- src/qemu/qemu_monitor_json.c | 8 +++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 3a7afdf45..0cbea95fc 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -356,6 +356,7 @@ struct qemuDomainDiskInfo { bool tray_open; bool empty; int io_status; + char *nodename; }; typedef struct _qemuDomainHostdevPrivate qemuDomainHostdevPrivate; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 36b8b5ea4..d958f6390 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2148,6 +2148,16 @@ qemuMonitorBlockIOStatusToError(const char *status) } +static void +qemuDomainDiskInfoFree(void *value, const void *name ATTRIBUTE_UNUSED) +{ + struct qemuDomainDiskInfo *info = value; + + VIR_FREE(info->nodename); + VIR_FREE(info); +} + + virHashTablePtr qemuMonitorGetBlockInfo(qemuMonitorPtr mon) { @@ -2156,7 +2166,7 @@ qemuMonitorGetBlockInfo(qemuMonitorPtr mon) QEMU_CHECK_MONITOR_NULL(mon); - if (!(table = virHashCreate(32, virHashValueFree))) + if (!(table = virHashCreate(32, qemuDomainDiskInfoFree))) return NULL; if (mon->json) diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 141d56b3d..75af5cb93 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1868,9 +1868,11 @@ int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon, for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev; + virJSONValuePtr image; struct qemuDomainDiskInfo *info; const char *thisdev; const char *status; + const char *nodename; if (!(dev = qemuMonitorJSONGetBlockDev(devices, i))) goto cleanup; @@ -1907,8 +1909,12 @@ int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon, info->tray = true; /* presence of 'inserted' notifies that a medium is in the device */ - if (!virJSONValueObjectGetObject(dev, "inserted")) + if ((image = virJSONValueObjectGetObject(dev, "inserted"))) { + if ((nodename = virJSONValueObjectGetString(image, "node-name"))) + ignore_value(VIR_STRDUP(info->nodename, nodename)); + } else { info->empty = true; + } /* Missing io-status indicates no error */ if ((status = virJSONValueObjectGetString(dev, "io-status"))) { -- 2.12.0

On 03/15/2017 11:37 AM, Peter Krempa wrote:
To allow matching the node names gathered via 'query-named-block-nodes' we need to query and then use the top level nodes from 'query-block'. Add the data to the structure returned by qemuMonitorGetBlockInfo. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_monitor.c | 12 +++++++++++- src/qemu/qemu_monitor_json.c | 8 +++++++- 3 files changed, 19 insertions(+), 2 deletions(-)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Detect the node names when setting block threshold and when reconnecting or when they are cleared when a block job finishes. This operation will become a no-op once we fully support node names. --- src/qemu/qemu_block.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 4 ++ src/qemu/qemu_blockjob.c | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 4 ++ 5 files changed, 113 insertions(+) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 91c04ab7f..ebf11ceb6 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -278,3 +278,101 @@ qemuBlockNodeNameGetBackingChain(virJSONValuePtr json) return ret; } + + +static void +qemuBlockDiskClearDetectedNodes(virDomainDiskDefPtr disk) +{ + virStorageSourcePtr next = disk->src; + + while (next) { + VIR_FREE(next->nodeformat); + VIR_FREE(next->nodebacking); + + next = next->backingStore; + } +} + + +static int +qemuBlockDiskDetectNodes(virDomainDiskDefPtr disk, + const char *parentnode, + virHashTablePtr table) +{ + qemuBlockNodeNameBackingChainDataPtr entry = NULL; + virStorageSourcePtr src = disk->src; + + /* don't attempt the detection if the top level already has node names */ + if (!parentnode || src->nodeformat || src->nodebacking) + return 0; + + while (src && parentnode) { + if (!(entry = virHashLookup(table, parentnode))) + break; + + if (src->nodeformat || src->nodebacking) { + if (STRNEQ_NULLABLE(src->nodeformat, entry->nodeformat) || + STRNEQ_NULLABLE(src->nodebacking, entry->nodestorage)) + goto error; + + break; + } else { + if (VIR_STRDUP(src->nodeformat, entry->nodeformat) < 0 || + VIR_STRDUP(src->nodebacking, entry->nodestorage) < 0) + goto error; + } + + parentnode = entry->nodebacking; + src = src->backingStore; + } + + return 0; + + error: + qemuBlockDiskClearDetectedNodes(disk); + return -1; +} + + +int +qemuBlockNodeNamesDetect(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virHashTablePtr disktable = NULL; + virHashTablePtr nodenametable = NULL; + virJSONValuePtr data = NULL; + virDomainDiskDefPtr disk; + struct qemuDomainDiskInfo *info; + size_t i; + int ret = -1; + + qemuDomainObjEnterMonitor(driver, vm); + + disktable = qemuMonitorGetBlockInfo(qemuDomainGetMonitor(vm)); + data = qemuMonitorQueryNamedBlockNodes(qemuDomainGetMonitor(vm)); + + if (qemuDomainObjExitMonitor(driver, vm) < 0 || !data || !disktable) + goto cleanup; + + if (!(nodenametable = qemuBlockNodeNameGetBackingChain(data))) + goto cleanup; + + for (i = 0; i < vm->def->ndisks; i++) { + disk = vm->def->disks[i]; + + if (!(info = virHashLookup(disktable, disk->info.alias))) + continue; + + if (qemuBlockDiskDetectNodes(disk, info->nodename, nodenametable) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virJSONValueFree(data); + virHashFree(nodenametable); + virHashFree(disktable); + + return ret; +} diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index 26f5ae062..56f4a74dd 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -44,4 +44,8 @@ struct qemuBlockNodeNameBackingChainData { virHashTablePtr qemuBlockNodeNameGetBackingChain(virJSONValuePtr data); +int +qemuBlockNodeNamesDetect(virQEMUDriverPtr driver, + virDomainObjPtr vm); + #endif /* __QEMU_BLOCK_H__ */ diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 985fae1e9..0601e68da 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -24,6 +24,7 @@ #include "internal.h" #include "qemu_blockjob.h" +#include "qemu_block.h" #include "qemu_domain.h" #include "conf/domain_conf.h" @@ -166,6 +167,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true, true)); + ignore_value(qemuBlockNodeNamesDetect(driver, vm)); diskPriv->blockjob = false; break; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d8e3ddf57..22cf866cd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -46,6 +46,7 @@ #include "qemu_driver.h" #include "qemu_agent.h" #include "qemu_alias.h" +#include "qemu_block.h" #include "qemu_conf.h" #include "qemu_capabilities.h" #include "qemu_command.h" @@ -20333,6 +20334,10 @@ qemuDomainSetBlockThreshold(virDomainPtr dom, if (!(src = qemuDomainGetStorageSourceByDevstr(dev, vm->def))) goto endjob; + if (!src->nodebacking && + qemuBlockNodeNamesDetect(driver, vm) < 0) + goto endjob; + if (!src->nodebacking) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("threshold currently can't be set for block device '%s'"), diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d40deea10..d8626d410 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -35,6 +35,7 @@ #include "qemu_process.h" #include "qemu_processpriv.h" #include "qemu_alias.h" +#include "qemu_block.h" #include "qemu_domain.h" #include "qemu_domain_address.h" #include "qemu_cgroup.h" @@ -3474,6 +3475,9 @@ qemuProcessReconnect(void *opaque) if (qemuProcessRefreshDisks(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error; + if (qemuBlockNodeNamesDetect(driver, obj) < 0) + goto error; + if (qemuRefreshVirtioChannelState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error; -- 2.12.0

On Wed, Mar 15, 2017 at 17:37:35 +0100, Peter Krempa wrote:
Detect the node names when setting block threshold and when reconnecting or when they are cleared when a block job finishes. This operation will become a no-op once we fully support node names. --- src/qemu/qemu_block.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 4 ++ src/qemu/qemu_blockjob.c | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 4 ++ 5 files changed, 113 insertions(+)
I forgot to commit this diff prior to sending this series: diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index ebf11ceb6..e31907842 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -338,6 +338,7 @@ int qemuBlockNodeNamesDetect(virQEMUDriverPtr driver, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData; virHashTablePtr disktable = NULL; virHashTablePtr nodenametable = NULL; virJSONValuePtr data = NULL; @@ -346,6 +347,9 @@ qemuBlockNodeNamesDetect(virQEMUDriverPtr driver, size_t i; int ret = -1; + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_NAMED_BLOCK_NODES)) + return 0; + qemuDomainObjEnterMonitor(driver, vm); disktable = qemuMonitorGetBlockInfo(qemuDomainGetMonitor(vm));

On 03/15/2017 11:37 AM, Peter Krempa wrote:
Detect the node names when setting block threshold and when reconnecting or when they are cleared when a block job finishes. This operation will become a no-op once we fully support node names. --- src/qemu/qemu_block.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 4 ++ src/qemu/qemu_blockjob.c | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 4 ++ 5 files changed, 113 insertions(+)
+ +static int +qemuBlockDiskDetectNodes(virDomainDiskDefPtr disk, + const char *parentnode, + virHashTablePtr table) +{ + qemuBlockNodeNameBackingChainDataPtr entry = NULL; + virStorageSourcePtr src = disk->src; + + /* don't attempt the detection if the top level already has node names */ + if (!parentnode || src->nodeformat || src->nodebacking)
Should that be if (!parentnode && (src->nodeformat || src->nodebacking)) to match the comment? As written, it looks like it short-circuits every node that already has a node name, not just the parent node. Otherwise looks good. Now for me to apply the patches and see if I can trigger the event to happen... -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Fri, Mar 24, 2017 at 09:03:28 -0500, Eric Blake wrote:
On 03/15/2017 11:37 AM, Peter Krempa wrote:
Detect the node names when setting block threshold and when reconnecting or when they are cleared when a block job finishes. This operation will become a no-op once we fully support node names. --- src/qemu/qemu_block.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 4 ++ src/qemu/qemu_blockjob.c | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 4 ++ 5 files changed, 113 insertions(+)
+ +static int +qemuBlockDiskDetectNodes(virDomainDiskDefPtr disk, + const char *parentnode, + virHashTablePtr table) +{ + qemuBlockNodeNameBackingChainDataPtr entry = NULL; + virStorageSourcePtr src = disk->src; + + /* don't attempt the detection if the top level already has node names */ + if (!parentnode || src->nodeformat || src->nodebacking)
Should that be if (!parentnode && (src->nodeformat || src->nodebacking))
to match the comment? As written, it looks like it short-circuits every node that already has a node name, not just the parent node.
It's probably slightly misworded. If parentnode is NULL at this point we did not find the nodename of the top level, so detection doesn't make sense. The comment is relevant for the other two conditions which stop the detection if the parent of the disk already has a node name assigned/detected, so the logic should be good as is.

On Wed, Mar 15, 2017 at 17:37:12 +0100, Peter Krempa wrote:
This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html
This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this.
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
Michal's nvdimm series pushed recently caused a merge-conflict. You can fetch the changes after I've resoved the conflict and with the two hunks I forgot to commit before sending in my repo: git fetch git://pipo.sk/pipo/libvirt.git block-threshold-1 Also I forgot to mention that I'm working on the ability to report the currently set threshold via the bulk stats API. I'll post that separately once I finish that.

On Wed, Mar 15, 2017 at 17:37:12 +0100, Peter Krempa wrote:
This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html
This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this.
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
Peter Krempa (23): qemu: driver: Don't call qemuDomainDetermineDiskChain on block jobs util: buffer: Add API to set indentation level to a given value util: storage: Split out useful bits of virStorageFileParseChainIndex util: storage: Add variables for node names into virStorageSource lib: Introduce event for tracking disk backing file write threshold qemu: monitor: Add support for BLOCK_WRITE_THRESHOLD event qemu: domain: Add helper to lookup disk by node name qemu: domain: Add helper to generate indexed backing store names qemu: process: Wire up firing of the VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD event lib: Add API for setting the threshold size for VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD virsh: Implement 'domblkthreshold' command to call virDomainSetBlockThreshold qemu: domain: Add helper to look up disk soruce by the backing store string qemu: implement qemuDomainSetBlockThreshold qemu: capabilities: add capability for query-named-block-nodes qmp cmd qemu: monitor: Add monitor infrastructure for query-named-block-nodes qemu: block: Add code to allow detection of auto-allocated node names tests: qemumonitorjson: Add test case for node name detection code tests: qemumonitorjson: Add long backing chain test case for node name detection tests: qemumonitorjson: Add case for two disks sharing a backing image tests: qemumonitorjson: Add relative image names for node name detection tests: qemumonitorjson: Test node name detection on networked storage qemu: monitor: Extract the top level format node when querying disks qemu: block: Add code to detect node names when necessary
Ping? This feature is very sought after by oVirt so that they can start developing their code. I't be great if it would make this release. Thanks. Peter

On 03/22/2017 02:55 AM, Peter Krempa wrote:
On Wed, Mar 15, 2017 at 17:37:12 +0100, Peter Krempa wrote:
This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html
This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this.
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
Ping? This feature is very sought after by oVirt so that they can start developing their code. I't be great if it would make this release.
I'll be starting my review today -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Wed, Mar 15, 2017 at 17:37:12 +0100, Peter Krempa wrote:
This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html
This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this.
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
I've pushed the updated version after incorporating most of the review feedback to: git fetch git://pipo.sk/pipo/libvirt.git block-threshold-2

On 03/24/2017 09:55 AM, Peter Krempa wrote:
On Wed, Mar 15, 2017 at 17:37:12 +0100, Peter Krempa wrote:
This is another version of the stuff that I've posted here: https://www.redhat.com/archives/libvir-list/2017-February/msg01391.html which was partially based on the very old discussion at https://www.redhat.com/archives/libvir-list/2015-May/msg00580.html
This version fixes some of the review feedback that I've got and also fixes all the issues pointed out in the original cover letter since I managed to implement the node name detection in a way suitable for this.
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
I've pushed the updated version after incorporating most of the review feedback to:
git fetch git://pipo.sk/pipo/libvirt.git block-threshold-2
Here's the interdiff; still a couple of things to tweak:
diff --git w/src/libvirt-domain.c c/src/libvirt-domain.c index 815cbb1..ca63138 100644 --- w/src/libvirt-domain.c +++ c/src/libvirt-domain.c @@ -11838,8 +11838,12 @@ virDomainSetVcpu(virDomainPtr domain, * Set the threshold level for delivering the * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD if the device or backing chain element * described by @dev is written beyond the set threshold level. The threshold - * level is unset once the event fired. The event may not be delivered at all if - * libvirtd was not running at the moment when the threshold was reached. + * level is unset once the event fired. The event might not be delivered at all
maybe s/fired/fires/
+ * if libvirtd was not running at the moment when the threshold was reached. + * + * Hypervisors report the last written sector of an image in the bulk stats API
extra space
+ * (virConnectGetAllDomainStats/virDomainListGetStats) as + * "block.<num>.allocation" in the VIR_DOMAIN_STATS_BLOCK group.
Maybe also mention (thanks to the followup patch) that block.<num>.threshold can be inspected in bulk stats to learn the current threshold.
* * This event allows to use thin-provisioned storage which needs management * tools to grow it without the need for polling of the data. diff --git w/src/qemu/qemu_domain.c c/src/qemu/qemu_domain.c index 0469a1f..6985643 100644 --- w/src/qemu/qemu_domain.c +++ c/src/qemu/qemu_domain.c @@ -8521,7 +8521,7 @@ qemuDomainNamespaceTeardownRNG(virQEMUDriverPtr driver, * @src: filled with the specific backing store element if provided * @idx: index of @src in the backing chain, if provided * - * Looks up the disk in the domain via @nodename and returns it's definition. + * Looks up the disk in the domain via @nodename and returns its definition. * Optionally fills @src and @idx if provided with the specific backing chain * element which corresponds to the node name. */ diff --git w/src/qemu/qemu_process.c c/src/qemu/qemu_process.c index 8477710..53b4c5f 100644 --- w/src/qemu/qemu_process.c +++ c/src/qemu/qemu_process.c @@ -1470,6 +1470,7 @@ qemuProcessHandleBlockThreshold(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if ((dev = qemuDomainDiskBackingStoreGetName(disk, src, idx))) { event = virDomainEventBlockThresholdNewFromObj(vm, dev, path, threshold, excess); + VIR_FREE(dev); }
Oh nice - you caught something I missed.
}
diff --git w/src/remote/remote_driver.c c/src/remote/remote_driver.c index baa5cba..27bcc9b 100644 --- w/src/remote/remote_driver.c +++ c/src/remote/remote_driver.c @@ -8436,7 +8436,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetGuestVcpus = remoteDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = remoteDomainSetGuestVcpus, /* 2.0.0 */ .domainSetVcpu = remoteDomainSetVcpu, /* 3.1.0 */ - .domainSetBlockThreshold = remoteDomainSetBlockThreshold, /* 3.1.0 */ + .domainSetBlockThreshold = remoteDomainSetBlockThreshold, /* 3.2.0 */ };
static virNetworkDriver network_driver = { diff --git w/src/util/virbuffer.c c/src/util/virbuffer.c index 8f0f49d..80c8e28 100644 --- w/src/util/virbuffer.c +++ c/src/util/virbuffer.c @@ -90,7 +90,7 @@ virBufferAdjustIndent(virBufferPtr buf, int indent)
/** - * virBufferAdjustIndent: + * virBufferSetIndent: * @buf: the buffer * @indent: new indentation size. * diff --git w/tests/virbuftest.c c/tests/virbuftest.c index 34160e6..8ec6ce5 100644 --- w/tests/virbuftest.c +++ c/tests/virbuftest.c @@ -405,6 +405,34 @@ testBufEscapeN(const void *opaque)
static int +testBufSetIndent(const void *opaque ATTRIBUTE_UNUSED) +{
I might have just squashed in a test with the existing tests of AdjustIndent, but what you have is fine.
+ virBuffer buf = VIR_BUFFER_INITIALIZER; + char *actual; + int ret = -1; + + virBufferSetIndent(&buf, 11); + virBufferAddLit(&buf, "test\n"); + virBufferSetIndent(&buf, 2); + virBufferAddLit(&buf, "test2\n"); + + if (!(actual = virBufferContentAndReset(&buf))) + goto cleanup; + + if (STRNEQ(actual, " test\n test2\n")) { + VIR_TEST_DEBUG("testBufSetIndent: expected indent not set\n"); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(actual); + return ret; +} + + +static int mymain(void) { int ret = 0; @@ -422,6 +450,7 @@ mymain(void) DO_TEST("Auto-indentation", testBufAutoIndent, 0); DO_TEST("Trim", testBufTrim, 0); DO_TEST("AddBuffer", testBufAddBuffer, 0); + DO_TEST("set indent", testBufSetIndent, 0);
#define DO_TEST_ADD_STR(DATA, EXPECT) \ do { \ diff --git w/tools/virsh-domain.c c/tools/virsh-domain.c index 3662952..74d0a0e 100644 --- w/tools/virsh-domain.c +++ c/tools/virsh-domain.c @@ -7105,7 +7105,7 @@ static const vshCmdInfo info_domblkthreshold[] = { "device or it's backing chain element") }, {.name = "desc", - .data = N_("set threshold for ") + .data = N_("set threshold for block-threshold even for a block device")
s/even/event/
}, {.name = NULL} };
Once you make the right tweaks in the corresponding patches, I think that means you've earned an ACK to the series. My next email should be a summary of my testing... -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 03/24/2017 12:45 PM, Eric Blake wrote:
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
I've pushed the updated version after incorporating most of the review feedback to:
git fetch git://pipo.sk/pipo/libvirt.git block-threshold-2
Here's the interdiff; still a couple of things to tweak:
Once you make the right tweaks in the corresponding patches, I think that means you've earned an ACK to the series. My next email should be a summary of my testing...
First round of testing: new virsh, old libvirtd: # tools/virsh domblkthreshold mydom error: command 'domblkthreshold' requires <dev> option error: command 'domblkthreshold' requires --threshold option Weird that we aren't consistent in reporting the missing option name (<dev> vs. --threshold); but it appears to be based on the type the option is expecting, and not the fault of your series, so a fix (if any) would be for a followup, and doesn't block your series. # tools/virsh domblkthreshold mydom vdb 1m error: unknown procedure: 386 It might be nicer if we could teach the remote driver (on the client side) to augment unknown procedure errors to additionally include the procedure name it thought it was calling, but again not the fault of your series. # tools/virsh event --list ... metadata-change block-threshold Good - virsh displays the new event to register for. # tools/virsh event --domain mydom --event block-threshold error: internal error: unsupported event ID 24 Urggh - what's that "internal error:" doing in there? We should fix that. But again, I don't think it's the fault of your series, and can be done as a followup. Next round of testing: new virsh, new libvirtd, Fedora 25 fedora-virt-preview's qemu-kvm-2.9.0-0.1.rc1.fc25.x86_64: First, I validated that my domain is set up to spot watermark growth (it requires a qcow2 image), merely booting the guest is enough to see that writes start happening to the guest's view of the image, all as part of mounting the disk prior to even showing the login screen (I specifically am testing with vdb, with the guest OS installed in vda, so that the file system on vdb is less heavily used by the OS and therefore a bit more predictable in behavior as I reboot the guest): # tools/virsh start mydom --paused Domain mydom started # tools/virsh domstats mydom --block Domain: 'mydom' block.count=2 ... block.1.allocation=0 block.1.capacity=21474836480 block.1.physical=21480095744 # tools/virsh resume mydom Domain mydom resumed # sleep 10 # tools/virsh domstats mydom --block Domain: 'mydom' block.count=2 ... block.1.allocation=10878128640 block.1.capacity=21474836480 block.1.physical=21480095744 # tools/virsh shutdown mydom Domain mydom is being shutdown block.1.allocation does indeed grow (and I repeated this test a couple of times to validate that my particular guest OS has the same allocation upon reaching the login screen), so the next step is to see if I can register a threshold. I'm going to pick something near where the OS probes (note that I'm starting the domain paused so that I'm guaranteed my threshold won't fire until the guest is resumed): # tools/virsh domblkthreshold mydom vdb 10GB error: Operation not supported: this qemu does not support setting device threshold That's a bit misleading. In this case, we don't support setting device threshold because the domain is not running (there is no qemu process at all when it is shutdown). But at least you correctly failed to set a threshold in this state. # tools/virsh start mydom --paused Domain mydom started # tools/virsh domblkthreshold mydom vdb 9GB # tools/virsh domblkthreshold mydom vdb 10GB Nothing at all printed. But that's okay (I don't think we have to always be chatty on success); and at least we can register a new value even if an old value is already in place and has not yet fired. # tools/virsh domstats mydom --block Domain: 'mydom' block.count=2 ... block.1.allocation=0 block.1.capacity=21474836480 block.1.physical=21480095744 block.1.threshold.storage=10000000000 Yay - the new block.1.threshold.storage stat matches my most-recent registration! (Remember, this is just testing the current state of your series; if someone repeats these steps for validation later on, and you choose to tweak it to a shorter name for the final commit, then be prepared for a slight difference in output) # tools/virsh event --domain mydom --event block-threshold & [1] 11351 # tools/virsh resume mydom Domain mydom resumed event 'block-threshold' for domain mydom: dev: vdb(/path/to/vdb.qcow2) 10000000000 880225792 events received: 1 [1]+ Done tools/virsh event --domain mydom --event block-threshold # tools/virsh domstats mydom --block Domain: 'mydom' block.count=2 ... block.1.allocation=10880225792 block.1.capacity=21474836480 block.1.physical=21480095744 Would you look at that: block.1.threshold.storage disappeared now that the event fired; and the new block.1.allocation matches the sum of the threshold and the excess as reported in the event (meaning my OS did a single far-flung write beyond the 10GB mark to cause the event). I think we can declare success that the watermark event worked! So overall, it looks like you wired things up nicely, and I wasn't able to cause any major problems in my testing, even if we can improve some of the error message. Looks good to go, and you should be able to get it in before DV freezes in preparation for the release. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Fri, Mar 24, 2017 at 17:09:47 -0500, Eric Blake wrote:
On 03/24/2017 12:45 PM, Eric Blake wrote:
The event is useful for mgmt apps using thin-provisioned storage so that they don't have to poll for the disk filling all the time.
I've pushed the updated version after incorporating most of the review feedback to:
git fetch git://pipo.sk/pipo/libvirt.git block-threshold-2
Here's the interdiff; still a couple of things to tweak:
Once you make the right tweaks in the corresponding patches, I think that means you've earned an ACK to the series. My next email should be a summary of my testing...
Thanks. I've fixed the stuff you pointed out and I'll try to look into the few pre-existing problems you've pointed out. This series is now pushed. Peter
participants (2)
-
Eric Blake
-
Peter Krempa