[libvirt] [PATCH 00/11] Introduce guest agent lifecycle event and impl for qemu

Peter Krempa (11): qemu: process: report useful error if alias formatting fails conf: Annotate source enums for character device struct members test: xml2xml: Print full filenames if xml2xml test fails qemu: monitor: Rename and improve qemuMonitorGetPtyPaths conf: Add channel state for virtio channels to the XML qemu: Add handling for VSERPORT_CHANGE event qemu: chardev: Extract more information about character devices qemu: process: Refresh virtio channel guest state when connecting to mon event: Add guest agent lifecycle event examples: Add support for the guest agent lifecycle event qemu: Emit the guest agent lifecycle event daemon/remote.c | 36 +++++ docs/formatdomain.html.in | 9 +- docs/schemas/domaincommon.rng | 3 + examples/object-events/event-test.c | 43 +++++- include/libvirt/libvirt-domain.h | 28 ++++ src/conf/domain_conf.c | 35 ++++- src/conf/domain_conf.h | 19 ++- src/conf/domain_event.c | 78 ++++++++++ src/conf/domain_event.h | 9 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 62 ++++++++ src/qemu/qemu_monitor.c | 56 ++++++-- src/qemu/qemu_monitor.h | 20 ++- src/qemu/qemu_monitor_json.c | 84 ++++++++--- src/qemu/qemu_monitor_json.h | 4 +- src/qemu/qemu_monitor_text.c | 21 ++- src/qemu/qemu_monitor_text.h | 4 +- src/qemu/qemu_process.c | 157 ++++++++++++++++++--- src/remote/remote_driver.c | 31 ++++ src/remote/remote_protocol.x | 16 ++- src/remote_protocol-structs | 7 + tests/qemumonitorjsontest.c | 59 +++++--- .../qemuxml2argv-channel-virtio-state.args | 17 +++ .../qemuxml2argv-channel-virtio-state.xml | 42 ++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-channel-virtio-state-active.xml | 43 ++++++ ...emuxml2xmlout-channel-virtio-state-inactive.xml | 42 ++++++ tests/qemuxml2xmltest.c | 3 +- tests/testutils.c | 35 ++++- tests/testutils.h | 5 + tools/virsh-domain.c | 40 ++++++ 32 files changed, 922 insertions(+), 91 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml -- 2.1.0

When retrieving the paths for PTY devices the alias gets formatted into a static string. If it doesn't fit we wouldn't report an error. --- src/qemu/qemu_process.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 7518138..e58a46d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1878,8 +1878,12 @@ qemuProcessLookupPTYs(virDomainDefPtr def, if (snprintf(id, sizeof(id), "%s%s", chardevfmt ? "char" : "", - chr->info.alias) >= sizeof(id)) + chr->info.alias) >= sizeof(id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to format device alias " + "for PTY retrieval")); return -1; + } path = (const char *) virHashLookup(paths, id); if (path == NULL) { -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:14 +0100, Peter Krempa wrote:
When retrieving the paths for PTY devices the alias gets formatted into a static string. If it doesn't fit we wouldn't report an error. --- src/qemu/qemu_process.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 7518138..e58a46d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1878,8 +1878,12 @@ qemuProcessLookupPTYs(virDomainDefPtr def,
if (snprintf(id, sizeof(id), "%s%s", chardevfmt ? "char" : "", - chr->info.alias) >= sizeof(id)) + chr->info.alias) >= sizeof(id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to format device alias " + "for PTY retrieval")); return -1; + }
path = (const char *) virHashLookup(paths, id); if (path == NULL) {
ACK although I thought we had some neat wrapper around snprintf, or is my memory playing games with me? Jirka

Add a comment to track which values may be present in certain members of a struct _virDomainChrDef. --- src/conf/domain_conf.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 530a3ca..574d3ea 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1100,10 +1100,13 @@ struct _virDomainChrSourceDef { /* A complete character device, both host and domain views. */ struct _virDomainChrDef { - int deviceType; + int deviceType; /* enum virDomainChrDeviceType */ bool targetTypeAttr; - int targetType; + int targetType; /* enum virDomainChrConsoleTargetType || + enum virDomainChrChannelTargetType || + enum virDomainChrSerialTargetType according to deviceType */ + union { int port; /* parallel, serial, console */ virSocketAddrPtr addr; /* guestfwd */ -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:15 +0100, Peter Krempa wrote:
Add a comment to track which values may be present in certain members of a struct _virDomainChrDef. --- src/conf/domain_conf.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 530a3ca..574d3ea 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1100,10 +1100,13 @@ struct _virDomainChrSourceDef {
/* A complete character device, both host and domain views. */ struct _virDomainChrDef { - int deviceType; + int deviceType; /* enum virDomainChrDeviceType */
bool targetTypeAttr; - int targetType; + int targetType; /* enum virDomainChrConsoleTargetType || + enum virDomainChrChannelTargetType || + enum virDomainChrSerialTargetType according to deviceType */ + union { int port; /* parallel, serial, console */ virSocketAddrPtr addr; /* guestfwd */
ACK Jirka

To simplify looking for a problem instrument the XML comparator function with possibility to print the filename of the failed/expected XML output. This is necessary as the VIR_TEST_DIFFERENT macro possibly tests two XML files for the inactive/active state and the resulting error may not be obvious. --- tests/qemuxml2xmltest.c | 2 +- tests/testutils.c | 35 ++++++++++++++++++++++++++++++----- tests/testutils.h | 5 +++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index ca11e90..a5dccd5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -50,7 +50,7 @@ testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live) goto fail; if (STRNEQ(outXmlData, actual)) { - virtTestDifference(stderr, outXmlData, actual); + virtTestDifferenceFull(stderr, outXmlData, outxml, actual, inxml); goto fail; } diff --git a/tests/testutils.c b/tests/testutils.c index 9d6980f..9a79f98 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -450,14 +450,19 @@ virtTestCaptureProgramOutput(const char *const argv[] ATTRIBUTE_UNUSED, /** * @param stream: output stream write to differences to * @param expect: expected output text + * @param expectName: name designator of the expected text * @param actual: actual output text + * @param actualName: name designator of the actual text * - * Display expected and actual output text, trimmed to - * first and last characters at which differences occur + * Display expected and actual output text, trimmed to first and last + * characters at which differences occur. Displays names of the text strings if + * non-NULL. */ -int virtTestDifference(FILE *stream, - const char *expect, - const char *actual) +int virtTestDifferenceFull(FILE *stream, + const char *expect, + const char *expectName, + const char *actual, + const char *actualName) { const char *expectStart; const char *expectEnd; @@ -495,11 +500,15 @@ int virtTestDifference(FILE *stream, } /* Show the trimmed differences */ + if (expectName) + fprintf(stream, "\nIn '%s':", expectName); fprintf(stream, "\nOffset %d\nExpect [", (int) (expectStart - expect)); if ((expectEnd - expectStart + 1) && fwrite(expectStart, (expectEnd-expectStart+1), 1, stream) != 1) return -1; fprintf(stream, "]\n"); + if (actualName) + fprintf(stream, "In '%s':\n", actualName); fprintf(stream, "Actual ["); if ((actualEnd - actualStart + 1) && fwrite(actualStart, (actualEnd-actualStart+1), 1, stream) != 1) @@ -520,6 +529,22 @@ int virtTestDifference(FILE *stream, * Display expected and actual output text, trimmed to * first and last characters at which differences occur */ +int virtTestDifference(FILE *stream, + const char *expect, + const char *actual) +{ + return virtTestDifferenceFull(stream, expect, NULL, actual, NULL); +} + + +/** + * @param stream: output stream write to differences to + * @param expect: expected output text + * @param actual: actual output text + * + * Display expected and actual output text, trimmed to + * first and last characters at which differences occur + */ int virtTestDifferenceBin(FILE *stream, const char *expect, const char *actual, diff --git a/tests/testutils.h b/tests/testutils.h index ad28ea7..d78818d 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -64,6 +64,11 @@ void virtTestClearCommandPath(char *cmdset); int virtTestDifference(FILE *stream, const char *expect, const char *actual); +int virtTestDifferenceFull(FILE *stream, + const char *expect, + const char *expectName, + const char *actual, + const char *actualName); int virtTestDifferenceBin(FILE *stream, const char *expect, const char *actual, -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:16 +0100, Peter Krempa wrote:
To simplify looking for a problem instrument the XML comparator function with possibility to print the filename of the failed/expected XML output.
This is necessary as the VIR_TEST_DIFFERENT macro possibly tests two XML files for the inactive/active state and the resulting error may not be obvious. --- tests/qemuxml2xmltest.c | 2 +- tests/testutils.c | 35 ++++++++++++++++++++++++++++++----- tests/testutils.h | 5 +++++ 3 files changed, 36 insertions(+), 6 deletions(-)
ACK Jirka

To unify future additions that require information from "query-chardev" rename qemuMonitorGetPtyPaths and friends to qemuMonitorGetChardevInfo and move the allocation of the returned hash into the top level function. --- src/qemu/qemu_monitor.c | 31 +++++++++++++++++++++++-------- src/qemu/qemu_monitor.h | 4 ++-- src/qemu/qemu_monitor_json.c | 15 +++++++++------ src/qemu/qemu_monitor_json.h | 4 ++-- src/qemu/qemu_monitor_text.c | 6 +++--- src/qemu/qemu_monitor_text.h | 4 ++-- src/qemu/qemu_process.c | 28 ++++++++++++---------------- tests/qemumonitorjsontest.c | 26 +++++++++++++------------- 8 files changed, 66 insertions(+), 52 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 330fd76..60440a7 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2968,24 +2968,39 @@ qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, } -int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths) +int +qemuMonitorGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr *retinfo) { int ret; - VIR_DEBUG("mon=%p", - mon); + virHashTablePtr info = NULL; + + VIR_DEBUG("mon=%p retinfo=%p", mon, retinfo); if (!mon) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("monitor must not be NULL")); - return -1; + goto error; } + if (!(info = virHashCreate(10, virHashValueFree))) + goto error; + if (mon->json) - ret = qemuMonitorJSONGetPtyPaths(mon, paths); + ret = qemuMonitorJSONGetChardevInfo(mon, info); else - ret = qemuMonitorTextGetPtyPaths(mon, paths); - return ret; + ret = qemuMonitorTextGetChardevInfo(mon, info); + + if (ret < 0) + goto error; + + *retinfo = info; + return 0; + + error: + virHashFree(info); + *retinfo = NULL; + return -1; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 24c36dd..54d4476 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -639,8 +639,8 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, virNetDevRxFilterPtr *filter); -int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths); +int qemuMonitorGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr *retinfo); int qemuMonitorAttachPCIDiskController(qemuMonitorPtr mon, const char *bus, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 7bc5f6e..394617c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3396,8 +3396,9 @@ qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, * ]} * */ -static int qemuMonitorJSONExtractPtyPaths(virJSONValuePtr reply, - virHashTablePtr paths) +static int +qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, + virHashTablePtr info) { virJSONValuePtr data; int ret = -1; @@ -3442,7 +3443,7 @@ static int qemuMonitorJSONExtractPtyPaths(virJSONValuePtr reply, if (VIR_STRDUP(path, type + strlen("pty:")) < 0) goto cleanup; - if (virHashAddEntry(paths, id, path) < 0) { + if (virHashAddEntry(info, id, path) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("failed to save chardev path '%s'"), path); VIR_FREE(path); @@ -3457,8 +3458,10 @@ static int qemuMonitorJSONExtractPtyPaths(virJSONValuePtr reply, return ret; } -int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths) + +int +qemuMonitorJSONGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr info) { int ret; @@ -3475,7 +3478,7 @@ int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, ret = qemuMonitorJSONCheckError(cmd, reply); if (ret == 0) - ret = qemuMonitorJSONExtractPtyPaths(reply, paths); + ret = qemuMonitorJSONExtractChardevInfo(reply, info); virJSONValueFree(cmd); virJSONValueFree(reply); diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index a966f97..ae20fb1 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,8 +214,8 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, virNetDevRxFilterPtr *filter); -int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths); +int qemuMonitorJSONGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr info); int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon, const char *bus, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index e40c676..b099a32 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2170,8 +2170,8 @@ int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon, * '/dev/pty/7'. The hash will contain only a single value. */ -int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths) +int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr info) { char *reply = NULL; int ret = -1; @@ -2222,7 +2222,7 @@ int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon, if (VIR_STRDUP(path, needle + strlen(NEEDLE)) < 0) goto cleanup; - if (virHashAddEntry(paths, id, path) < 0) { + if (virHashAddEntry(info, id, path) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("failed to save chardev path '%s'"), path); diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 49d4b88..f118a30 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -179,8 +179,8 @@ int qemuMonitorTextAddNetdev(qemuMonitorPtr mon, int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon, const char *alias); -int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon, - virHashTablePtr paths); +int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, + virHashTablePtr info); int qemuMonitorTextAttachPCIDiskController(qemuMonitorPtr mon, const char *bus, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e58a46d..bf5239a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1864,7 +1864,7 @@ qemuProcessLookupPTYs(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainChrDefPtr *devices, int count, - virHashTablePtr paths) + virHashTablePtr info) { size_t i; @@ -1885,7 +1885,7 @@ qemuProcessLookupPTYs(virDomainDefPtr def, return -1; } - path = (const char *) virHashLookup(paths, id); + path = (const char *) virHashLookup(info, id); if (path == NULL) { if (chr->source.data.file.path == NULL) { /* neither the log output nor 'info chardev' had a @@ -1914,23 +1914,23 @@ qemuProcessLookupPTYs(virDomainDefPtr def, static int qemuProcessFindCharDevicePTYsMonitor(virDomainObjPtr vm, virQEMUCapsPtr qemuCaps, - virHashTablePtr paths) + virHashTablePtr info) { size_t i = 0; if (qemuProcessLookupPTYs(vm->def, qemuCaps, vm->def->serials, vm->def->nserials, - paths) < 0) + info) < 0) return -1; if (qemuProcessLookupPTYs(vm->def, qemuCaps, vm->def->parallels, vm->def->nparallels, - paths) < 0) + info) < 0) return -1; if (qemuProcessLookupPTYs(vm->def, qemuCaps, vm->def->channels, vm->def->nchannels, - paths) < 0) + info) < 0) return -1; /* For historical reasons, console[0] can be just an alias * for serial[0]. That's why we need to update it as well. */ @@ -1950,7 +1950,7 @@ qemuProcessFindCharDevicePTYsMonitor(virDomainObjPtr vm, if (qemuProcessLookupPTYs(vm->def, qemuCaps, vm->def->consoles + i, vm->def->nconsoles - i, - paths) < 0) + info) < 0) return -1; return 0; @@ -2034,7 +2034,7 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, size_t buf_size = 4096; /* Plenty of space to get startup greeting */ int logfd = -1; int ret = -1; - virHashTablePtr paths = NULL; + virHashTablePtr info = NULL; qemuDomainObjPrivatePtr priv; if (pos != -1 && @@ -2059,22 +2059,18 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, * reliable if it's available. * Note that the monitor itself can be on a pty, so we still need to try the * log output method. */ - paths = virHashCreate(0, virHashValueFree); - if (paths == NULL) - goto cleanup; - priv = vm->privateData; if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) goto cleanup; - ret = qemuMonitorGetPtyPaths(priv->mon, paths); + ret = qemuMonitorGetChardevInfo(priv->mon, &info); qemuDomainObjExitMonitor(driver, vm); - VIR_DEBUG("qemuMonitorGetPtyPaths returned %i", ret); + VIR_DEBUG("qemuMonitorGetChardevInfo returned %i", ret); if (ret == 0) - ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, paths); + ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, info); cleanup: - virHashFree(paths); + virHashFree(info); if (pos != -1 && kill(vm->pid, 0) == -1 && errno == ESRCH) { int len; diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 196901c..acbb414 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1765,24 +1765,24 @@ testHashEqualString(const void *value1, const void *value2) } static int -testQemuMonitorJSONqemuMonitorJSONGetPtyPaths(const void *data) +testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) { virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; qemuMonitorTestPtr test = qemuMonitorTestNewSimple(true, xmlopt); int ret = -1; - virHashTablePtr paths = NULL, expectedPaths = NULL; + virHashTablePtr info = NULL, expectedInfo = NULL; if (!test) return -1; - if (!(paths = virHashCreate(32, (virHashDataFree) free)) || - !(expectedPaths = virHashCreate(32, NULL))) + if (!(info = virHashCreate(32, (virHashDataFree) free)) || + !(expectedInfo = virHashCreate(32, NULL))) goto cleanup; - if (virHashAddEntry(expectedPaths, "charserial1", (void *) "/dev/pts/21") < 0 || - virHashAddEntry(expectedPaths, "charserial0", (void *) "/dev/pts/20") < 0) { + if (virHashAddEntry(expectedInfo, "charserial1", (void *) "/dev/pts/21") < 0 || + virHashAddEntry(expectedInfo, "charserial0", (void *) "/dev/pts/20") < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - "Unable to create expectedPaths hash table"); + "Unable to create expectedInfo hash table"); goto cleanup; } @@ -1806,11 +1806,11 @@ testQemuMonitorJSONqemuMonitorJSONGetPtyPaths(const void *data) "}") < 0) goto cleanup; - if (qemuMonitorJSONGetPtyPaths(qemuMonitorTestGetMonitor(test), - paths) < 0) + if (qemuMonitorJSONGetChardevInfo(qemuMonitorTestGetMonitor(test), + info) < 0) goto cleanup; - if (!virHashEqual(paths, expectedPaths, testHashEqualString)) { + if (!virHashEqual(info, expectedInfo, testHashEqualString)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Hashtable is different to the expected one"); goto cleanup; @@ -1818,8 +1818,8 @@ testQemuMonitorJSONqemuMonitorJSONGetPtyPaths(const void *data) ret = 0; cleanup: - virHashFree(paths); - virHashFree(expectedPaths); + virHashFree(info); + virHashFree(expectedInfo); qemuMonitorTestFree(test); return ret; } @@ -2387,7 +2387,7 @@ mymain(void) DO_TEST(qemuMonitorJSONGetMigrationCacheSize); DO_TEST(qemuMonitorJSONGetMigrationStatus); DO_TEST(qemuMonitorJSONGetSpiceMigrationStatus); - DO_TEST(qemuMonitorJSONGetPtyPaths); + DO_TEST(qemuMonitorJSONGetChardevInfo); DO_TEST(qemuMonitorJSONSetBlockIoThrottle); DO_TEST(qemuMonitorJSONGetTargetArch); DO_TEST(qemuMonitorJSONGetMigrationCapability); -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:17 +0100, Peter Krempa wrote:
To unify future additions that require information from "query-chardev" rename qemuMonitorGetPtyPaths and friends to qemuMonitorGetChardevInfo and move the allocation of the returned hash into the top level function. --- src/qemu/qemu_monitor.c | 31 +++++++++++++++++++++++-------- src/qemu/qemu_monitor.h | 4 ++-- src/qemu/qemu_monitor_json.c | 15 +++++++++------ src/qemu/qemu_monitor_json.h | 4 ++-- src/qemu/qemu_monitor_text.c | 6 +++--- src/qemu/qemu_monitor_text.h | 4 ++-- src/qemu/qemu_process.c | 28 ++++++++++++---------------- tests/qemumonitorjsontest.c | 26 +++++++++++++------------- 8 files changed, 66 insertions(+), 52 deletions(-)
ACK Jirka

To track state of virtio channels this patch adds a new output-only attribute called 'state' to the <target> element of virtio channels. This will be later populated with the guest state of the channel. --- docs/formatdomain.html.in | 9 ++++- docs/schemas/domaincommon.rng | 3 ++ src/conf/domain_conf.c | 35 ++++++++++++++++-- src/conf/domain_conf.h | 12 ++++++ .../qemuxml2argv-channel-virtio-state.args | 17 +++++++++ .../qemuxml2argv-channel-virtio-state.xml | 42 +++++++++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-channel-virtio-state-active.xml | 43 ++++++++++++++++++++++ ...emuxml2xmlout-channel-virtio-state-inactive.xml | 42 +++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 10 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9364eb5..229783d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4911,7 +4911,7 @@ qemu-kvm -net nic,model=? /dev/null </channel> <channel type='unix'> <source mode='bind' path='/var/lib/libvirt/qemu/f16x86_64.agent'/> - <target type='virtio' name='org.qemu.guest_agent.0'/> + <target type='virtio' name='org.qemu.guest_agent.0' status='connected'/> </channel> <channel type='spicevmc'> <target type='virtio' name='com.redhat.spice.0'/> @@ -4950,7 +4950,12 @@ qemu-kvm -net nic,model=? /dev/null This is very useful in case of a qemu guest agent, where users don't usually care about the source path since it's libvirt who talks to the guest agent. In case users want to utilize this feature, they should - leave <code><source></code> element out. + leave <code><source></code> element out. <span class="since">Since + 1.2.11</span>the active XML for a virtio channel may contain an optional + <code>state</code> attribute that reflects whether a process in the + guest is active on the channel. This is an output-only attribute. + Possible values for the <code>state</code> attribute are + <code>connected</code> and <code>disconnected</code>. </dd> <dt><code>spicevmc</code></dt> <dd>Paravirtualized SPICE channel. The domain must also have a diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6863ec6..9d23f3b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3377,6 +3377,9 @@ <optional> <attribute name="name"/> </optional> + <optional> + <attribute name="state"/> + </optional> </element> </define> <define name="channel"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5f4b9f6..f1c07b1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -410,6 +410,11 @@ VIR_ENUM_IMPL(virDomainNetInterfaceLinkState, VIR_DOMAIN_NET_INTERFACE_LINK_STAT "up", "down") +VIR_ENUM_IMPL(virDomainChrDeviceState, VIR_DOMAIN_CHR_DEVICE_STATE_LAST, + "default", + "connected", + "disconnected"); + VIR_ENUM_IMPL(virDomainChrSerialTarget, VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST, "isa-serial", @@ -7857,13 +7862,15 @@ virDomainChrTargetTypeFromString(virDomainChrDefPtr def, static int virDomainChrDefParseTargetXML(virDomainChrDefPtr def, - xmlNodePtr cur) + xmlNodePtr cur, + unsigned int flags) { int ret = -1; unsigned int port; char *targetType = virXMLPropString(cur, "type"); char *addrStr = NULL; char *portStr = NULL; + char *stateStr = NULL; if ((def->targetType = virDomainChrTargetTypeFromString(def, def->deviceType, @@ -7920,6 +7927,20 @@ virDomainChrDefParseTargetXML(virDomainChrDefPtr def, case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: def->target.name = virXMLPropString(cur, "name"); + + if (!(flags & VIR_DOMAIN_XML_INACTIVE) && + (stateStr = virXMLPropString(cur, "state"))) { + int tmp; + + if ((tmp = virDomainChrDeviceStateTypeFromString(stateStr)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid channel state value '%s'"), + stateStr); + goto error; + } + + def->state = tmp; + } break; } break; @@ -7948,6 +7969,7 @@ virDomainChrDefParseTargetXML(virDomainChrDefPtr def, VIR_FREE(targetType); VIR_FREE(addrStr); VIR_FREE(portStr); + VIR_FREE(stateStr); return ret; } @@ -8323,7 +8345,7 @@ virDomainChrDefParseXML(xmlXPathContextPtr ctxt, if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "target")) { seenTarget = true; - if (virDomainChrDefParseTargetXML(def, cur) < 0) + if (virDomainChrDefParseTargetXML(def, cur, flags) < 0) goto error; } } @@ -17520,13 +17542,18 @@ virDomainChrDefFormat(virBufferPtr buf, break; } - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: { + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: if (def->target.name) virBufferEscapeString(buf, " name='%s'", def->target.name); + + if (def->state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT && + !(flags & VIR_DOMAIN_XML_INACTIVE)) { + virBufferAsprintf(buf, " state='%s'", + virDomainChrDeviceStateTypeToString(def->state)); + } break; } - } virBufferAddLit(buf, "/>\n"); break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 574d3ea..f1d4305 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -991,6 +991,16 @@ struct _virDomainNetDef { # define VIR_NET_GENERATED_PREFIX "vnet" typedef enum { + VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT = 0, + VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED, + VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED, + + VIR_DOMAIN_CHR_DEVICE_STATE_LAST +} virDomainChrDeviceState; + +VIR_ENUM_DECL(virDomainChrDeviceState) + +typedef enum { VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL = 0, VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL, VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE, @@ -1113,6 +1123,8 @@ struct _virDomainChrDef { char *name; /* virtio */ } target; + virDomainChrDeviceState state; + virDomainChrSourceDef source; virDomainDeviceInfo info; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args new file mode 100644 index 0000000..62bf14d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args @@ -0,0 +1,17 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \ +/usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi -boot c \ +-device virtio-serial-pci,id=virtio-serial1,bus=pci.0,addr=0xa \ +-usb -hda /dev/HostVG/QEMUGuest1 \ +-chardev pty,id=charchannel0 \ +-device virtserialport,bus=virtio-serial1.0,nr=3,chardev=charchannel0,\ +id=channel0,name=org.linux-kvm.port.foo \ +-chardev pty,id=charchannel1 \ +-device virtserialport,bus=virtio-serial1.0,nr=4,chardev=charchannel1,\ +id=channel1,name=org.linux-kvm.port.foo1 \ +-chardev pty,id=charchannel2 \ +-device virtserialport,bus=virtio-serial1.0,nr=5,chardev=charchannel2,\ +id=channel2,name=org.linux-kvm.port.foo2 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml new file mode 100644 index 0000000..044b369 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml @@ -0,0 +1,42 @@ +<domain type='qemu' id='2'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='virtio-serial' index='1'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo' state='connected'/> + <address type='virtio-serial' controller='1' bus='0' port='3'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo1' state='disconnected'/> + <address type='virtio-serial' controller='1' bus='0' port='4'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo2'/> + <address type='virtio-serial' controller='1' bus='0' port='5'/> + </channel> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index b2e60e8..eb02b38 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1070,6 +1070,8 @@ mymain(void) QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG); DO_TEST("channel-virtio", QEMU_CAPS_DEVICE, QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); + DO_TEST("channel-virtio-state", + QEMU_CAPS_DEVICE, QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("channel-virtio-auto", QEMU_CAPS_DEVICE, QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("console-virtio", diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml new file mode 100644 index 0000000..4f050fc --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml @@ -0,0 +1,43 @@ +<domain type='qemu' id='2'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <backingStore/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='virtio-serial' index='1'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo' state='connected'/> + <address type='virtio-serial' controller='1' bus='0' port='3'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo1' state='disconnected'/> + <address type='virtio-serial' controller='1' bus='0' port='4'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo2'/> + <address type='virtio-serial' controller='1' bus='0' port='5'/> + </channel> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml new file mode 100644 index 0000000..5027a1e --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='virtio-serial' index='1'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo'/> + <address type='virtio-serial' controller='1' bus='0' port='3'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo1'/> + <address type='virtio-serial' controller='1' bus='0' port='4'/> + </channel> + <channel type='pty'> + <target type='virtio' name='org.linux-kvm.port.foo2'/> + <address type='virtio-serial' controller='1' bus='0' port='5'/> + </channel> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index a5dccd5..e1ec514 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -288,6 +288,7 @@ mymain(void) DO_TEST("console-virtio-many"); DO_TEST("channel-guestfwd"); DO_TEST("channel-virtio"); + DO_TEST_DIFFERENT("channel-virtio-state"); DO_TEST("hostdev-usb-address"); DO_TEST("hostdev-pci-address"); -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:18 +0100, Peter Krempa wrote:
To track state of virtio channels this patch adds a new output-only attribute called 'state' to the <target> element of virtio channels.
This will be later populated with the guest state of the channel. --- docs/formatdomain.html.in | 9 ++++- docs/schemas/domaincommon.rng | 3 ++ src/conf/domain_conf.c | 35 ++++++++++++++++-- src/conf/domain_conf.h | 12 ++++++ .../qemuxml2argv-channel-virtio-state.args | 17 +++++++++ .../qemuxml2argv-channel-virtio-state.xml | 42 +++++++++++++++++++++ tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-channel-virtio-state-active.xml | 43 ++++++++++++++++++++++ ...emuxml2xmlout-channel-virtio-state-inactive.xml | 42 +++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 10 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-state.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-active.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-state-inactive.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9364eb5..229783d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4911,7 +4911,7 @@ qemu-kvm -net nic,model=? /dev/null </channel> <channel type='unix'> <source mode='bind' path='/var/lib/libvirt/qemu/f16x86_64.agent'/> - <target type='virtio' name='org.qemu.guest_agent.0'/> + <target type='virtio' name='org.qemu.guest_agent.0' status='connected'/>
s/status/state/
</channel> <channel type='spicevmc'> <target type='virtio' name='com.redhat.spice.0'/> @@ -4950,7 +4950,12 @@ qemu-kvm -net nic,model=? /dev/null This is very useful in case of a qemu guest agent, where users don't usually care about the source path since it's libvirt who talks to the guest agent. In case users want to utilize this feature, they should - leave <code><source></code> element out. + leave <code><source></code> element out. <span class="since">Since + 1.2.11</span>the active XML for a virtio channel may contain an optional
s/the/ the/
+ <code>state</code> attribute that reflects whether a process in the + guest is active on the channel. This is an output-only attribute. + Possible values for the <code>state</code> attribute are + <code>connected</code> and <code>disconnected</code>. </dd> <dt><code>spicevmc</code></dt> <dd>Paravirtualized SPICE channel. The domain must also have a diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6863ec6..9d23f3b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3377,6 +3377,9 @@ <optional> <attribute name="name"/> </optional> + <optional> + <attribute name="state"/>
I think you can be more specific and use <attribute name="state"> <choice> <value>connected</value> <value>disconnected</value> </choice> </attribute>
+ </optional> </element> </define> <define name="channel"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5f4b9f6..f1c07b1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -410,6 +410,11 @@ VIR_ENUM_IMPL(virDomainNetInterfaceLinkState, VIR_DOMAIN_NET_INTERFACE_LINK_STAT "up", "down")
+VIR_ENUM_IMPL(virDomainChrDeviceState, VIR_DOMAIN_CHR_DEVICE_STATE_LAST, + "default", + "connected", + "disconnected"); + VIR_ENUM_IMPL(virDomainChrSerialTarget, VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST, "isa-serial", @@ -7857,13 +7862,15 @@ virDomainChrTargetTypeFromString(virDomainChrDefPtr def,
static int virDomainChrDefParseTargetXML(virDomainChrDefPtr def, - xmlNodePtr cur) + xmlNodePtr cur, + unsigned int flags) { int ret = -1; unsigned int port; char *targetType = virXMLPropString(cur, "type"); char *addrStr = NULL; char *portStr = NULL; + char *stateStr = NULL;
if ((def->targetType = virDomainChrTargetTypeFromString(def, def->deviceType, @@ -7920,6 +7927,20 @@ virDomainChrDefParseTargetXML(virDomainChrDefPtr def,
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: def->target.name = virXMLPropString(cur, "name"); + + if (!(flags & VIR_DOMAIN_XML_INACTIVE) && + (stateStr = virXMLPropString(cur, "state"))) { + int tmp; + + if ((tmp = virDomainChrDeviceStateTypeFromString(stateStr)) < 0) {
You should probably change the check to tmp <= 0 so that you don't allow the XML to contain state='default'.
+ virReportError(VIR_ERR_XML_ERROR, + _("invalid channel state value '%s'"), + stateStr); + goto error; + } + + def->state = tmp; + } break; } break; ...
ACK, everything else looks OK. Jirka

New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element. This patch implements the monitor callbacks and necessary handlers to update the state in the definition. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++++++++ src/qemu/qemu_monitor.h | 10 ++++++++ src/qemu/qemu_monitor_json.c | 23 ++++++++++++++++++ src/qemu/qemu_process.c | 44 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index ad45a66..e4ea4ce 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -196,6 +196,7 @@ typedef enum { QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, + QEMU_PROCESS_EVENT_SERIAL_CHANGED, QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a84fd47..31bf6bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4334,6 +4334,60 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, } +static void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virDomainDeviceDef dev; + + if (connected) + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type != VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) + goto endjob; + + dev.data.chr->state = newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + VIR_WARN("unable to save domain status after removing device %s", + devAlias); + + endjob: + /* We had an extra reference to vm before starting a new job so ending the + * job is guaranteed not to remove the last reference. + */ + ignore_value(qemuDomainObjEndJob(driver, vm)); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); + +} + + static void qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent = data; @@ -4357,6 +4411,9 @@ static void qemuProcessEventHandler(void *data, void *opaque) case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED: processNicRxFilterChangedEvent(driver, vm, processEvent->data); break; + case QEMU_PROCESS_EVENT_SERIAL_CHANGED: + processSerialChangedEvent(driver, vm, processEvent->data, + processEvent->action); case QEMU_PROCESS_EVENT_LAST: break; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 60440a7..926619f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1400,6 +1400,20 @@ qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, } +int +qemuMonitorEmitSerialChange(qemuMonitorPtr mon, + const char *devAlias, + bool connected) +{ + int ret = -1; + VIR_DEBUG("mon=%p, devAlias='%s', connected=%d", mon, devAlias, connected); + + QEMU_MONITOR_CALLBACK(mon, ret, domainSerialChange, mon->vm, devAlias, connected); + + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 54d4476..3078be0 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -176,6 +176,12 @@ typedef int (*qemuMonitorDomainNicRxFilterChangedCallback)(qemuMonitorPtr mon, const char *devAlias, void *opaque); +typedef int (*qemuMonitorDomainSerialChangeCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *devAlias, + bool connected, + void *opaque); + typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; struct _qemuMonitorCallbacks { @@ -202,6 +208,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainGuestPanicCallback domainGuestPanic; qemuMonitorDomainDeviceDeletedCallback domainDeviceDeleted; qemuMonitorDomainNicRxFilterChangedCallback domainNicRxFilterChanged; + qemuMonitorDomainSerialChangeCallback domainSerialChange; }; char *qemuMonitorEscapeArg(const char *in); @@ -292,6 +299,9 @@ int qemuMonitorEmitDeviceDeleted(qemuMonitorPtr mon, const char *devAlias); int qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, const char *devAlias); +int qemuMonitorEmitSerialChange(qemuMonitorPtr mon, + const char *devAlias, + bool connected); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 394617c..9c8a6fb 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -82,6 +82,7 @@ static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValueP static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleSerialChange(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -112,6 +113,7 @@ static qemuEventHandler eventHandlers[] = { { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, + { "VSERPORT_CHANGE", qemuMonitorJSONHandleSerialChange, }, { "WAKEUP", qemuMonitorJSONHandlePMWakeup, }, { "WATCHDOG", qemuMonitorJSONHandleWatchdog, }, /* We use bsearch, so keep this list sorted. */ @@ -895,6 +897,27 @@ qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data } +static void +qemuMonitorJSONHandleSerialChange(qemuMonitorPtr mon, + virJSONValuePtr data) +{ + const char *name; + bool connected; + + if (!(name = virJSONValueObjectGetString(data, "id"))) { + VIR_WARN("missing device alias in VSERPORT_CHANGE event"); + return; + } + + if (virJSONValueObjectGetBoolean(data, "open", &connected) < 0) { + VIR_WARN("missing port state for '%s' in VSERPORT_CHANGE event", name); + return; + } + + qemuMonitorEmitSerialChange(mon, name, connected); +} + + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index bf5239a..24c7383 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1539,6 +1539,49 @@ qemuProcessHandleNicRxFilterChanged(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuProcessHandleSerialChanged(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *devAlias, + bool connected, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + struct qemuProcessEvent *processEvent = NULL; + char *data; + + virObjectLock(vm); + + VIR_DEBUG("Serial port %s state changed to '%d' in domain %p %s", + devAlias, connected, vm, vm->def->name); + + if (VIR_ALLOC(processEvent) < 0) + goto error; + + processEvent->eventType = QEMU_PROCESS_EVENT_SERIAL_CHANGED; + if (VIR_STRDUP(data, devAlias) < 0) + goto error; + processEvent->data = data; + processEvent->action = connected; + processEvent->vm = vm; + + virObjectRef(vm); + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + ignore_value(virObjectUnref(vm)); + goto error; + } + + cleanup: + virObjectUnlock(vm); + return 0; + error: + if (processEvent) + VIR_FREE(processEvent->data); + VIR_FREE(processEvent); + goto cleanup; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuProcessHandleMonitorEOF, .errorNotify = qemuProcessHandleMonitorError, @@ -1561,6 +1604,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainGuestPanic = qemuProcessHandleGuestPanic, .domainDeviceDeleted = qemuProcessHandleDeviceDeleted, .domainNicRxFilterChanged = qemuProcessHandleNicRxFilterChanged, + .domainSerialChange = qemuProcessHandleSerialChanged, }; static int -- 2.1.0

On 2014/11/19 18:23, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
This patch implements the monitor callbacks and necessary handlers to update the state in the definition. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++++++++ src/qemu/qemu_monitor.h | 10 ++++++++ src/qemu/qemu_monitor_json.c | 23 ++++++++++++++++++ src/qemu/qemu_process.c | 44 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+)
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index ad45a66..e4ea4ce 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -196,6 +196,7 @@ typedef enum { QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, + QEMU_PROCESS_EVENT_SERIAL_CHANGED,
QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a84fd47..31bf6bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4334,6 +4334,60 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, }
+static void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virDomainDeviceDef dev; + + if (connected) + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type != VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) + goto endjob; + + dev.data.chr->state = newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + VIR_WARN("unable to save domain status after removing device %s", + devAlias); +
Hi, Peter IIUC, QEMU emitted the event and libvirt saved the state for the next time being queryed. 'the output-only XML element' and 'SaveStatus' means the state is not saved persistently. In case of libvirtd being restarted after state is saved, we'll lose it. Could we handle this case?

On 11/20/14 08:44, Wang Rui wrote:
On 2014/11/19 18:23, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
This patch implements the monitor callbacks and necessary handlers to update the state in the definition. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++++++++ src/qemu/qemu_monitor.h | 10 ++++++++ src/qemu/qemu_monitor_json.c | 23 ++++++++++++++++++ src/qemu/qemu_process.c | 44 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+)
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index ad45a66..e4ea4ce 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -196,6 +196,7 @@ typedef enum { QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, + QEMU_PROCESS_EVENT_SERIAL_CHANGED,
QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a84fd47..31bf6bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4334,6 +4334,60 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, }
+static void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virDomainDeviceDef dev; + + if (connected) + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type != VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) + goto endjob; + + dev.data.chr->state = newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + VIR_WARN("unable to save domain status after removing device %s", + devAlias); +
Hi, Peter IIUC, QEMU emitted the event and libvirt saved the state for the next time being queryed. 'the output-only XML element' and 'SaveStatus' means the state is not saved persistently.
This saves the state of the port into the status XML and
In case of libvirtd being restarted after state is saved, we'll lose it. Could we handle this case?
the status XML is the piece that is reloaded on libvirtd restart for running VMs. For inactive VMs this doesn't make sense to report.
Peter

On 2014/11/20 15:47, Peter Krempa wrote:
the status XML is the piece that is reloaded on libvirtd restart for running VMs. For inactive VMs this doesn't make sense to report.
Thanks. I missed status XML in /var/run/libvirt/.

On 11/20/2014 12:47 AM, Peter Krempa wrote:
On 11/20/14 08:44, Wang Rui wrote:
On 2014/11/19 18:23, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
Hi, Peter IIUC, QEMU emitted the event and libvirt saved the state for the next time being queryed. 'the output-only XML element' and 'SaveStatus' means the state is not saved persistently.
This saves the state of the port into the status XML and
In case of libvirtd being restarted after state is saved, we'll lose it. Could we handle this case?
the status XML is the piece that is reloaded on libvirtd restart for running VMs. For inactive VMs this doesn't make sense to report.
When reconnecting to the QMP monitor after a libvirtd restart, we need to query for whether the agent state has changed in the time that libvirt was not receiving events. At the same time as VSERPORT_CHANGE was added as an event, Laszlo also enhanced the 'query-chardev' command to also poll the current connectedness of the agent channel (qemu commit 32a97ea). This series needs to call that command on reconnect instead of trusting that the state saved prior to libvirtd restart is still valid. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 11/20/14 14:11, Eric Blake wrote:
On 11/20/2014 12:47 AM, Peter Krempa wrote:
On 11/20/14 08:44, Wang Rui wrote:
On 2014/11/19 18:23, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
Hi, Peter IIUC, QEMU emitted the event and libvirt saved the state for the next time being queryed. 'the output-only XML element' and 'SaveStatus' means the state is not saved persistently.
This saves the state of the port into the status XML and
In case of libvirtd being restarted after state is saved, we'll lose it. Could we handle this case?
the status XML is the piece that is reloaded on libvirtd restart for running VMs. For inactive VMs this doesn't make sense to report.
When reconnecting to the QMP monitor after a libvirtd restart, we need to query for whether the agent state has changed in the time that libvirt was not receiving events. At the same time as VSERPORT_CHANGE was added as an event, Laszlo also enhanced the 'query-chardev' command to also poll the current connectedness of the agent channel (qemu commit 32a97ea). This series needs to call that command on reconnect instead of trusting that the state saved prior to libvirtd restart is still valid.
See patch 8/11. Peter

On Wed, Nov 19, 2014 at 11:23:19 +0100, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
This patch implements the monitor callbacks and necessary handlers to update the state in the definition. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++++++++ src/qemu/qemu_monitor.h | 10 ++++++++ src/qemu/qemu_monitor_json.c | 23 ++++++++++++++++++ src/qemu/qemu_process.c | 44 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+)
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index ad45a66..e4ea4ce 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -196,6 +196,7 @@ typedef enum { QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, + QEMU_PROCESS_EVENT_SERIAL_CHANGED,
QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a84fd47..31bf6bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4334,6 +4334,60 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, }
+static void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virDomainDeviceDef dev; + + if (connected) + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type != VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) + goto endjob; + + dev.data.chr->state = newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + VIR_WARN("unable to save domain status after removing device %s",
Looks like a copy&paste error :-) ACK with the warning fixed. Jirka

On 11/20/14 21:14, Jiri Denemark wrote:
On Wed, Nov 19, 2014 at 11:23:19 +0100, Peter Krempa wrote:
New qemu added a new event that is emitted when a virtio serial channel is opened in the guest OS. This allows us to update the state of the port in the output-only XML element.
This patch implements the monitor callbacks and necessary handlers to update the state in the definition. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 +++++++++++ src/qemu/qemu_monitor.h | 10 ++++++++ src/qemu/qemu_monitor_json.c | 23 ++++++++++++++++++ src/qemu/qemu_process.c | 44 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+)
I've pushed patches 1-6 with the requested changes. I'll repost the rest later. Thanks. Peter

Improve the monitor function to also retrieve the guest state of character device (if provided) so that we can refresh the state of virtio-serial channels and perhaps react to changes in the state in future patches. This patch changes the returned data from qemuMonitorGetChardevInfo to return a structure containing the pty path and the state if the info is relevant. The change to the testsuite makes sure that the data is parsed correctly. --- src/qemu/qemu_monitor.c | 13 +++++++++++- src/qemu/qemu_monitor.h | 6 ++++++ src/qemu/qemu_monitor_json.c | 48 +++++++++++++++++++++++++++++++++----------- src/qemu/qemu_monitor_text.c | 17 +++++++++++----- src/qemu/qemu_process.c | 8 ++++---- tests/qemumonitorjsontest.c | 39 +++++++++++++++++++++++++++++------ 6 files changed, 103 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 926619f..c9c84f9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2982,6 +2982,17 @@ qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, } +static void +qemuMonitorChardevInfoFree(void *data, + const void *name ATTRIBUTE_UNUSED) +{ + qemuMonitorChardevInfoPtr info = data; + + VIR_FREE(info->ptyPath); + VIR_FREE(info); +} + + int qemuMonitorGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr *retinfo) @@ -2997,7 +3008,7 @@ qemuMonitorGetChardevInfo(qemuMonitorPtr mon, goto error; } - if (!(info = virHashCreate(10, virHashValueFree))) + if (!(info = virHashCreate(10, qemuMonitorChardevInfoFree))) goto error; if (mon->json) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3078be0..21533a4 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -649,6 +649,12 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, virNetDevRxFilterPtr *filter); +typedef struct _qemuMonitorChardevInfo qemuMonitorChardevInfo; +typedef qemuMonitorChardevInfo *qemuMonitorChardevInfoPtr; +struct _qemuMonitorChardevInfo { + char *ptyPath; + virDomainChrDeviceState state; +}; int qemuMonitorGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr *retinfo); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9c8a6fb..5429382 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3426,6 +3426,9 @@ qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, virJSONValuePtr data; int ret = -1; size_t i; + char *ptyPath = NULL; + virDomainChrDeviceState state; + qemuMonitorChardevInfoPtr entry = NULL; if (!(data = virJSONValueObjectGet(reply, "return"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -3440,44 +3443,65 @@ qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, } for (i = 0; i < virJSONValueArraySize(data); i++) { - virJSONValuePtr entry = virJSONValueArrayGet(data, i); + virJSONValuePtr chardev = virJSONValueArrayGet(data, i); const char *type; - const char *id; - if (!entry) { + const char *alias; + bool connected; + + if (!chardev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing array element")); goto cleanup; } - if (!(type = virJSONValueObjectGetString(entry, "filename"))) { + if (!(alias = virJSONValueObjectGetString(chardev, "label"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("character device information was missing filename")); + _("character device information was missing alias")); goto cleanup; } - if (!(id = virJSONValueObjectGetString(entry, "label"))) { + if (!(type = virJSONValueObjectGetString(chardev, "filename"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing filename")); goto cleanup; } - if (STRPREFIX(type, "pty:")) { - char *path; - if (VIR_STRDUP(path, type + strlen("pty:")) < 0) + if (STRPREFIX(type, "pty:") && + VIR_STRDUP(ptyPath, type + strlen("pty:")) < 0) + goto cleanup; + + if (virJSONValueObjectGetBoolean(chardev, "frontend-open", &connected) == 0) { + if (connected) + state = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + state = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + } + + if (ptyPath || state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT) { + if (VIR_ALLOC(entry) < 0) goto cleanup; - if (virHashAddEntry(info, id, path) < 0) { + entry->ptyPath = ptyPath; + entry->state = state; + + if (virHashAddEntry(info, alias, entry) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to save chardev path '%s'"), path); - VIR_FREE(path); + _("failed to add chardev '%s' info"), alias); goto cleanup; } + + entry = NULL; + ptyPath = NULL; + state = VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT; } } ret = 0; cleanup: + VIR_FREE(entry); + VIR_FREE(ptyPath); + return ret; } diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index b099a32..70aeaca 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2174,6 +2174,7 @@ int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr info) { char *reply = NULL; + qemuMonitorChardevInfoPtr entry = NULL; int ret = -1; if (qemuMonitorHMPCommand(mon, "info chardev", &reply) < 0) @@ -2218,17 +2219,22 @@ int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, /* Path is everything after needle to the end of the line */ *eol = '\0'; - char *path; - if (VIR_STRDUP(path, needle + strlen(NEEDLE)) < 0) + + if (VIR_ALLOC(entry) < 0) + goto cleanup; + + if (VIR_STRDUP(entry->ptyPath, needle + strlen(NEEDLE)) < 0) goto cleanup; - if (virHashAddEntry(info, id, path) < 0) { + if (virHashAddEntry(info, id, entry) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("failed to save chardev path '%s'"), - path); - VIR_FREE(path); + entry->ptyPath); + VIR_FREE(entry->ptyPath); goto cleanup; } + + entry = NULL; #undef NEEDLE } @@ -2236,6 +2242,7 @@ int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, cleanup: VIR_FREE(reply); + VIR_FREE(entry); return ret; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 24c7383..f19963c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1918,7 +1918,7 @@ qemuProcessLookupPTYs(virDomainDefPtr def, if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { char id[32]; - const char *path; + qemuMonitorChardevInfoPtr entry; if (snprintf(id, sizeof(id), "%s%s", chardevfmt ? "char" : "", @@ -1929,8 +1929,8 @@ qemuProcessLookupPTYs(virDomainDefPtr def, return -1; } - path = (const char *) virHashLookup(info, id); - if (path == NULL) { + entry = virHashLookup(info, id); + if (!entry || !entry->ptyPath) { if (chr->source.data.file.path == NULL) { /* neither the log output nor 'info chardev' had a * pty path for this chardev, report an error @@ -1947,7 +1947,7 @@ qemuProcessLookupPTYs(virDomainDefPtr def, } VIR_FREE(chr->source.data.file.path); - if (VIR_STRDUP(chr->source.data.file.path, path) < 0) + if (VIR_STRDUP(chr->source.data.file.path, entry->ptyPath) < 0) return -1; } } diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index acbb414..0dab084 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1759,11 +1759,28 @@ testQemuMonitorJSONqemuMonitorJSONGetSpiceMigrationStatus(const void *data) } static int -testHashEqualString(const void *value1, const void *value2) +testHashEqualChardevInfo(const void *value1, const void *value2) { - return strcmp(value1, value2); + const qemuMonitorChardevInfo *info1 = value1; + const qemuMonitorChardevInfo *info2 = value2; + + if (info1->state != info2->state) + goto error; + + if (STRNEQ_NULLABLE(info1->ptyPath, info2->ptyPath)) + goto error; + + return 0; + + error: + fprintf(stderr, "\n" + "info1->state: %d info2->state: %d\n" + "info1->ptyPath: %s info2->ptyPath: %s\n", + info1->state, info2->state, info1->ptyPath, info2->ptyPath); + return -1; } + static int testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) { @@ -1771,6 +1788,9 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) qemuMonitorTestPtr test = qemuMonitorTestNewSimple(true, xmlopt); int ret = -1; virHashTablePtr info = NULL, expectedInfo = NULL; + qemuMonitorChardevInfo info1 = { (char *) "/dev/pts/21", VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED }; + qemuMonitorChardevInfo info2 = { (char *) "/dev/pts/20", VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT }; + qemuMonitorChardevInfo info3 = { NULL, VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED }; if (!test) return -1; @@ -1779,8 +1799,9 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) !(expectedInfo = virHashCreate(32, NULL))) goto cleanup; - if (virHashAddEntry(expectedInfo, "charserial1", (void *) "/dev/pts/21") < 0 || - virHashAddEntry(expectedInfo, "charserial0", (void *) "/dev/pts/20") < 0) { + if (virHashAddEntry(expectedInfo, "charserial1", &info1) < 0 || + virHashAddEntry(expectedInfo, "charserial0", &info2) < 0 || + virHashAddEntry(expectedInfo, "charserial2", &info3) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedInfo hash table"); goto cleanup; @@ -1791,7 +1812,8 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) " \"return\": [" " {" " \"filename\": \"pty:/dev/pts/21\"," - " \"label\": \"charserial1\"" + " \"label\": \"charserial1\"," + " \"frontend-open\": true" " }," " {" " \"filename\": \"pty:/dev/pts/20\"," @@ -1800,6 +1822,11 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) " {" " \"filename\": \"unix:/var/lib/libvirt/qemu/gentoo.monitor,server\"," " \"label\": \"charmonitor\"" + " }," + " {" + " \"filename\": \"unix:/path/to/socket,server\"," + " \"label\": \"charserial2\"," + " \"frontend-open\": false" " }" " ]," " \"id\": \"libvirt-15\"" @@ -1810,7 +1837,7 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) info) < 0) goto cleanup; - if (!virHashEqual(info, expectedInfo, testHashEqualString)) { + if (!virHashEqual(info, expectedInfo, testHashEqualChardevInfo)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Hashtable is different to the expected one"); goto cleanup; -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:20 +0100, Peter Krempa wrote:
Improve the monitor function to also retrieve the guest state of character device (if provided) so that we can refresh the state of virtio-serial channels and perhaps react to changes in the state in future patches.
This patch changes the returned data from qemuMonitorGetChardevInfo to return a structure containing the pty path and the state if the info is relevant.
The change to the testsuite makes sure that the data is parsed correctly. --- src/qemu/qemu_monitor.c | 13 +++++++++++- src/qemu/qemu_monitor.h | 6 ++++++ src/qemu/qemu_monitor_json.c | 48 +++++++++++++++++++++++++++++++++----------- src/qemu/qemu_monitor_text.c | 17 +++++++++++----- src/qemu/qemu_process.c | 8 ++++---- tests/qemumonitorjsontest.c | 39 +++++++++++++++++++++++++++++------ 6 files changed, 103 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 926619f..c9c84f9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2982,6 +2982,17 @@ qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, }
+static void +qemuMonitorChardevInfoFree(void *data, + const void *name ATTRIBUTE_UNUSED) +{ + qemuMonitorChardevInfoPtr info = data; + + VIR_FREE(info->ptyPath); + VIR_FREE(info); +} + + int qemuMonitorGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr *retinfo) @@ -2997,7 +3008,7 @@ qemuMonitorGetChardevInfo(qemuMonitorPtr mon, goto error; }
- if (!(info = virHashCreate(10, virHashValueFree))) + if (!(info = virHashCreate(10, qemuMonitorChardevInfoFree))) goto error;
if (mon->json) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3078be0..21533a4 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -649,6 +649,12 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, virNetDevRxFilterPtr *filter);
+typedef struct _qemuMonitorChardevInfo qemuMonitorChardevInfo; +typedef qemuMonitorChardevInfo *qemuMonitorChardevInfoPtr; +struct _qemuMonitorChardevInfo { + char *ptyPath; + virDomainChrDeviceState state; +}; int qemuMonitorGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr *retinfo);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9c8a6fb..5429382 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3426,6 +3426,9 @@ qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, virJSONValuePtr data; int ret = -1; size_t i; + char *ptyPath = NULL; + virDomainChrDeviceState state; + qemuMonitorChardevInfoPtr entry = NULL;
if (!(data = virJSONValueObjectGet(reply, "return"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -3440,44 +3443,65 @@ qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, }
for (i = 0; i < virJSONValueArraySize(data); i++) { - virJSONValuePtr entry = virJSONValueArrayGet(data, i); + virJSONValuePtr chardev = virJSONValueArrayGet(data, i); const char *type; - const char *id; - if (!entry) { + const char *alias; + bool connected; + + if (!chardev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing array element")); goto cleanup; }
- if (!(type = virJSONValueObjectGetString(entry, "filename"))) { + if (!(alias = virJSONValueObjectGetString(chardev, "label"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("character device information was missing filename")); + _("character device information was missing alias"));
Shouldn't we report that device information was missing label rather than alias as we are referring to a field in the QMP event?
goto cleanup; }
- if (!(id = virJSONValueObjectGetString(entry, "label"))) { + if (!(type = virJSONValueObjectGetString(chardev, "filename"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing filename")); goto cleanup; }
- if (STRPREFIX(type, "pty:")) { - char *path; - if (VIR_STRDUP(path, type + strlen("pty:")) < 0) + if (STRPREFIX(type, "pty:") && + VIR_STRDUP(ptyPath, type + strlen("pty:")) < 0) + goto cleanup; + + if (virJSONValueObjectGetBoolean(chardev, "frontend-open", &connected) == 0) { + if (connected) + state = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + state = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + } + + if (ptyPath || state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT) {
It took me a while to understand what you want to do here :-) So you want store only "pty:" char devices or char devices with "frontend-open" field, right? Any reason for this filtering (apart from the fact we don't care about other devices now)? I guess storing all devices would work too and the code would be a bit simpler. Or am I wrong?
+ if (VIR_ALLOC(entry) < 0) goto cleanup;
- if (virHashAddEntry(info, id, path) < 0) { + entry->ptyPath = ptyPath; + entry->state = state; + + if (virHashAddEntry(info, alias, entry) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to save chardev path '%s'"), path); - VIR_FREE(path); + _("failed to add chardev '%s' info"), alias); goto cleanup; } + + entry = NULL; + ptyPath = NULL; + state = VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT;
Ah, you managed to confuse me again :-) I'd prefer if you moved the "virDomainChrDeviceState state;" declaration inside the for loop and initialized state to VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT there. However, if you follow my suggestion to remove the condition above, you can avoid state variable completely and rather assign to entry->state directly (after moving the allocation of entry before checking for frontend-open, of course).
} }
ret = 0;
cleanup: + VIR_FREE(entry); + VIR_FREE(ptyPath); + return ret; }
... Jirka

Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. --- src/qemu/qemu_process.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f19963c..3c3ff66 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2068,6 +2068,61 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm, static int +qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, + virHashTablePtr info) +{ + size_t i; + qemuMonitorChardevInfoPtr entry; + char id[32]; + + for (i = 0; i < vm->def->nchannels; i++) { + virDomainChrDefPtr chr = vm->def->channels[i]; + if (chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { + if (snprintf(id, sizeof(id), "char%s", + chr->info.alias) >= sizeof(id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to format device alias " + "for PTY retrieval")); + return -1; + } + + /* port state not reported */ + if (!(entry = virHashLookup(info, id))) + continue; + + chr->state = entry->state; + } + } + + return 0; +} + + +static int +qemuProcessReconnectRefreshChannelVirtioState(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virHashTablePtr info = NULL; + int ret = -1; + + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorGetChardevInfo(priv->mon, &info); + qemuDomainObjExitMonitor(driver, vm); + + if (ret < 0) + goto cleanup; + + ret = qemuProcessRefreshChannelVirtioState(vm, info); + + cleanup: + virHashFree(info); + return ret; + +} + + +static int qemuProcessWaitForMonitor(virQEMUDriverPtr driver, virDomainObjPtr vm, int asyncJob, @@ -2110,8 +2165,14 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, qemuDomainObjExitMonitor(driver, vm); VIR_DEBUG("qemuMonitorGetChardevInfo returned %i", ret); - if (ret == 0) - ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, info); + if (ret == 0) { + if ((ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, + info)) != 0) + goto cleanup; + + if ((ret = qemuProcessRefreshChannelVirtioState(vm, info)) != 0) + goto cleanup; + } cleanup: virHashFree(info); @@ -3594,6 +3655,9 @@ qemuProcessReconnect(void *opaque) if (qemuDomainCheckEjectableMedia(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error; + if (qemuProcessReconnectRefreshChannelVirtioState(driver, obj) < 0) + goto error; + if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0) goto error; -- 2.1.0

On Wed, Nov 19, 2014 at 11:23:21 +0100, Peter Krempa wrote:
Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. --- src/qemu/qemu_process.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f19963c..3c3ff66 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2068,6 +2068,61 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm,
static int +qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, + virHashTablePtr info) +{ + size_t i; + qemuMonitorChardevInfoPtr entry; + char id[32]; + + for (i = 0; i < vm->def->nchannels; i++) { + virDomainChrDefPtr chr = vm->def->channels[i]; + if (chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { + if (snprintf(id, sizeof(id), "char%s", + chr->info.alias) >= sizeof(id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to format device alias " + "for PTY retrieval")); + return -1; + } + + /* port state not reported */ + if (!(entry = virHashLookup(info, id))) + continue; + + chr->state = entry->state; + } + } + + return 0; +} + + +static int +qemuProcessReconnectRefreshChannelVirtioState(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virHashTablePtr info = NULL; + int ret = -1; + + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorGetChardevInfo(priv->mon, &info); + qemuDomainObjExitMonitor(driver, vm); + + if (ret < 0) + goto cleanup; + + ret = qemuProcessRefreshChannelVirtioState(vm, info); + + cleanup: + virHashFree(info); + return ret; +
Extra empty line.
+} + + +static int qemuProcessWaitForMonitor(virQEMUDriverPtr driver, virDomainObjPtr vm, int asyncJob, @@ -2110,8 +2165,14 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, qemuDomainObjExitMonitor(driver, vm);
VIR_DEBUG("qemuMonitorGetChardevInfo returned %i", ret); - if (ret == 0) - ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, info); + if (ret == 0) { + if ((ret = qemuProcessFindCharDevicePTYsMonitor(vm, qemuCaps, + info)) != 0)
We usually check for errors with ... < 0
+ goto cleanup; + + if ((ret = qemuProcessRefreshChannelVirtioState(vm, info)) != 0)
Same here.
+ goto cleanup; + }
cleanup: virHashFree(info); @@ -3594,6 +3655,9 @@ qemuProcessReconnect(void *opaque) if (qemuDomainCheckEjectableMedia(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error;
+ if (qemuProcessReconnectRefreshChannelVirtioState(driver, obj) < 0) + goto error; + if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0) goto error;
ACK Jirka

On Thu, Nov 20, 2014 at 22:22:56 +0100, Jiri Denemark wrote:
On Wed, Nov 19, 2014 at 11:23:21 +0100, Peter Krempa wrote:
Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. --- src/qemu/qemu_process.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f19963c..3c3ff66 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2068,6 +2068,61 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm,
static int +qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, + virHashTablePtr info) +{ + size_t i; + qemuMonitorChardevInfoPtr entry; + char id[32]; + + for (i = 0; i < vm->def->nchannels; i++) { + virDomainChrDefPtr chr = vm->def->channels[i]; + if (chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { + if (snprintf(id, sizeof(id), "char%s", + chr->info.alias) >= sizeof(id)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to format device alias " + "for PTY retrieval")); + return -1; + } + + /* port state not reported */ + if (!(entry = virHashLookup(info, id))) + continue;
But of course, this should be changed to if (!(entry = virHashLookup(info, id)) || !entry->state) in case you decide to store all devices in the hash as I suggested in my review to 7/11. Jirka

As qemu is now able to notify us about change of the channel state used for communication with the guest agent we now can more precisely track the state of the guest agent. To allow notifying management apps this patch implements a new event that will be triggered on changes of the guest agent state. --- daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 28 +++++++++++++++ src/conf/domain_event.c | 78 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 9 +++++ src/libvirt_private.syms | 2 ++ src/remote/remote_driver.c | 31 ++++++++++++++++ src/remote/remote_protocol.x | 16 ++++++++- src/remote_protocol-structs | 7 ++++ tools/virsh-domain.c | 40 +++++++++++++++++++++ 9 files changed, 246 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1d7082e..18f493d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1008,6 +1008,41 @@ remoteRelayDomainEventTunable(virConnectPtr conn, } +static int +remoteRelayDomainEventAgentLifecycle(virConnectPtr conn, + virDomainPtr dom, + int state, + int reason, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_agent_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain agent lifecycle event %s %d, callback %d, " + " state %d, reason %d", + dom->name, dom->id, callback->callbackID, state, reason); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + data.state = state; + data.reason = reason; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg, + &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -1027,6 +1062,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemoved), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob2), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTunable), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventAgentLifecycle), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 1fac2a3..fcb833e 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3332,6 +3332,33 @@ typedef void (*virConnectDomainEventTunableCallback)(virConnectPtr conn, void *opaque); +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_CONNECTED = 1, /* agent connected */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_DISCONNECTED = 2, /* agent disconnected */ +} virConnectDomainEventAgentLifecycleState; + +/** + * virConnectDomainEventAgentLifecycleCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @state: new state of the guest agent, one of virConnectDomainEventAgentLifecycleState + * @reason: reason for state change; currently only 0 is passed denoting change + * in the guest agent socket state + * @opaque: application specified data + * + * This callback occurs when libvirt detects a change in the state of a guest + * agent. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, + virDomainPtr dom, + int state, + int reason, + void *opaque); + + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -3367,6 +3394,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED = 15, /* virConnectDomainEventDeviceRemovedCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 = 16, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_TUNABLE = 17, /* virConnectDomainEventTunableCallback */ + VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE = 18,/* virConnectDomainEventAgentLifecycleCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 3504b34..d1042bf 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -54,6 +54,7 @@ static virClassPtr virDomainEventDeviceRemovedClass; static virClassPtr virDomainEventPMClass; static virClassPtr virDomainQemuMonitorEventClass; static virClassPtr virDomainEventTunableClass; +static virClassPtr virDomainEventAgentLifecycleClass; static void virDomainEventDispose(void *obj); @@ -70,6 +71,7 @@ static void virDomainEventDeviceRemovedDispose(void *obj); static void virDomainEventPMDispose(void *obj); static void virDomainQemuMonitorEventDispose(void *obj); static void virDomainEventTunableDispose(void *obj); +static void virDomainEventAgentLifecycleDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -215,6 +217,15 @@ struct _virDomainEventTunable { typedef struct _virDomainEventTunable virDomainEventTunable; typedef virDomainEventTunable *virDomainEventTunablePtr; +struct _virDomainEventAgentLifecycle { + virDomainEvent parent; + + int state; + int reason; +}; +typedef struct _virDomainEventAgentLifecycle virDomainEventAgentLifecycle; +typedef virDomainEventAgentLifecycle *virDomainEventAgentLifecyclePtr; + static int virDomainEventsOnceInit(void) @@ -303,6 +314,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventTunable), virDomainEventTunableDispose))) return -1; + if (!(virDomainEventAgentLifecycleClass = + virClassNew(virDomainEventClass, + "virDomainEventAgentLifecycle", + sizeof(virDomainEventAgentLifecycle), + virDomainEventAgentLifecycleDispose))) + return -1; return 0; } @@ -447,6 +464,13 @@ virDomainEventTunableDispose(void *obj) virTypedParamsFree(event->params, event->nparams); } +static void +virDomainEventAgentLifecycleDispose(void *obj) +{ + virDomainEventAgentLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); +}; + static void * virDomainEventNew(virClassPtr klass, @@ -1202,6 +1226,49 @@ virDomainEventDeviceRemovedNewFromDom(virDomainPtr dom, devAlias); } + +static virObjectEventPtr +virDomainEventAgentLifecycleNew(int id, + const char *name, + const unsigned char *uuid, + int state, + int reason) +{ + virDomainEventAgentLifecyclePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventAgentLifecycleClass, + VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, + id, name, uuid))) + return NULL; + + ev->state = state; + ev->reason = reason; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromObj(virDomainObjPtr obj, + int state, + int reason) +{ + return virDomainEventAgentLifecycleNew(obj->def->id, obj->def->name, + obj->def->uuid, state, reason); +} + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, + int state, + int reason) +{ + return virDomainEventAgentLifecycleNew(dom->id, dom->name, dom->uuid, + state, reason); +} + + /* This function consumes the params so caller don't have to care about * freeing it even if error occurs. The reason is to not have to do deep * copy of params. @@ -1459,6 +1526,17 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE: + { + virDomainEventAgentLifecyclePtr agentLifecycleEvent; + agentLifecycleEvent = (virDomainEventAgentLifecyclePtr)event; + ((virConnectDomainEventAgentLifecycleCallback)cb)(conn, dom, + agentLifecycleEvent->state, + agentLifecycleEvent->reason, + 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 dc0109c..534ff9e 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -193,6 +193,15 @@ virDomainEventTunableNewFromDom(virDomainPtr dom, virTypedParameterPtr params, int nparams); +virObjectEventPtr +virDomainEventAgentLifecycleNewFromObj(virDomainObjPtr obj, + int state, + int reason); + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, + int state, + int reason); int virDomainEventStateRegister(virConnectPtr conn, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0864618..a9b63c9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -437,6 +437,8 @@ virDomainXMLOptionNew; # conf/domain_event.h +virDomainEventAgentLifecycleNewFromDom; +virDomainEventAgentLifecycleNewFromObj; virDomainEventBalloonChangeNewFromDom; virDomainEventBalloonChangeNewFromObj; virDomainEventBlockJob2NewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 04e5360..88f8743 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -335,6 +335,11 @@ remoteDomainBuildEventCallbackTunable(virNetClientProgramPtr prog, void *evdata, void *opaque); static void +remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); @@ -489,6 +494,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackTunable, sizeof(remote_domain_event_callback_tunable_msg), (xdrproc_t)xdr_remote_domain_event_callback_tunable_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE, + remoteDomainBuildEventCallbackAgentLifecycle, + sizeof(remote_domain_event_callback_agent_lifecycle_msg), + (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg }, }; @@ -5483,6 +5492,28 @@ remoteDomainBuildEventCallbackTunable(virNetClientProgramPtr prog ATTRIBUTE_UNUS static void +remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_agent_lifecycle_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventAgentLifecycleNewFromDom(dom, msg->state, + msg->reason); + + virDomainFree(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + +static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ebf4530..85c95f5 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3108,6 +3108,14 @@ struct remote_connect_get_all_domain_stats_args { unsigned int flags; }; +struct remote_domain_event_callback_agent_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + int state; + int reason; +}; + struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record retStats<REMOTE_DOMAIN_LIST_MAX>; }; @@ -5506,5 +5514,11 @@ enum remote_procedure { * @generate: none * @acl: connect:write */ - REMOTE_PROC_NODE_ALLOC_PAGES = 347 + REMOTE_PROC_NODE_ALLOC_PAGES = 347, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE = 348 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 362baf9..9769b2a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2573,6 +2573,12 @@ struct remote_connect_get_all_domain_stats_args { u_int stats; u_int flags; }; +struct remote_domain_event_callback_agent_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + int state; + int reason; +}; struct remote_connect_get_all_domain_stats_ret { struct { u_int retStats_len; @@ -2927,4 +2933,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_BLOCK_COPY = 345, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TUNABLE = 346, REMOTE_PROC_NODE_ALLOC_PAGES = 347, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE = 348, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index a7e9151..3a41cfc 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11606,6 +11606,44 @@ vshEventTunablePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); } +static const char * +vshEventAgentLifecycleStateToString(int event) +{ + const char *ret = ""; + + switch ((virConnectDomainEventAgentLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_DISCONNECTED: + ret = "disconnected"; + break; + + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_CONNECTED: + ret = "connected"; + break; + } + + return ret; +} + +static void +vshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int state, + int reason, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && *data->count) + return; + vshPrint(data->ctl, + _("event 'agent-lifecycle' for domain %s: state: '%s' reason: %d\n"), + virDomainGetName(dom), vshEventAgentLifecycleStateToString(state), + reason); + (*data->count)++; + if (!data->loop) + vshEventDone(data->ctl); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), }, @@ -11641,6 +11679,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(vshEventBlockJobPrint), }, { "tunable", VIR_DOMAIN_EVENT_CALLBACK(vshEventTunablePrint), }, + { "agent-lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(vshEventAgentLifecyclePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 2.1.0

On 11/19/2014 03:23 AM, Peter Krempa wrote:
As qemu is now able to notify us about change of the channel state used for communication with the guest agent we now can more precisely track the state of the guest agent.
To allow notifying management apps this patch implements a new event that will be triggered on changes of the guest agent state. --- daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 28 +++++++++++++++ src/conf/domain_event.c | 78 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 9 +++++ src/libvirt_private.syms | 2 ++ src/remote/remote_driver.c | 31 ++++++++++++++++ src/remote/remote_protocol.x | 16 ++++++++- src/remote_protocol-structs | 7 ++++ tools/virsh-domain.c | 40 +++++++++++++++++++++ 9 files changed, 246 insertions(+), 1 deletion(-)
+++ b/include/libvirt/libvirt-domain.h @@ -3332,6 +3332,33 @@ typedef void (*virConnectDomainEventTunableCallback)(virConnectPtr conn, void *opaque);
+typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_CONNECTED = 1, /* agent connected */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_DISCONNECTED = 2, /* agent disconnected */ +} virConnectDomainEventAgentLifecycleState;
Do we want to explicitly reserve 0 for unknown state? Do we want to provide a _LAST flag so that additions of future states can be detected by the increase in the value of _LAST? Everything else looks good. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 11/20/14 19:54, Eric Blake wrote:
On 11/19/2014 03:23 AM, Peter Krempa wrote:
As qemu is now able to notify us about change of the channel state used for communication with the guest agent we now can more precisely track the state of the guest agent.
To allow notifying management apps this patch implements a new event that will be triggered on changes of the guest agent state. --- daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 28 +++++++++++++++ src/conf/domain_event.c | 78 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 9 +++++ src/libvirt_private.syms | 2 ++ src/remote/remote_driver.c | 31 ++++++++++++++++ src/remote/remote_protocol.x | 16 ++++++++- src/remote_protocol-structs | 7 ++++ tools/virsh-domain.c | 40 +++++++++++++++++++++ 9 files changed, 246 insertions(+), 1 deletion(-)
+++ b/include/libvirt/libvirt-domain.h @@ -3332,6 +3332,33 @@ typedef void (*virConnectDomainEventTunableCallback)(virConnectPtr conn, void *opaque);
+typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_CONNECTED = 1, /* agent connected */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_DISCONNECTED = 2, /* agent disconnected */ +} virConnectDomainEventAgentLifecycleState;
Do we want to explicitly reserve 0 for unknown state? Do we want to provide a _LAST flag so that additions of future states can be detected by the increase in the value of _LAST?
Hmm, the 0 value also might be treated as VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_PORT_STATUS or similar denoting that the reason is that the socket state changed. This might then change if we detect that the agent sent garbage or other stuff. Providing an enum for this may be a way then. Peter
Everything else looks good.

On 11/19/2014 03:23 AM, Peter Krempa wrote:
As qemu is now able to notify us about change of the channel state used for communication with the guest agent we now can more precisely track the state of the guest agent.
To allow notifying management apps this patch implements a new event that will be triggered on changes of the guest agent state. ---
Actually, after seeing patch 11, I think this event is too narrow. You are only reporting if the guest agent on org.qemu.guest_agent.0 changes state. But a user can provide more than one channel, and the VSERPORT_CHANGE event works for every channel. I think we need to broaden the definition of the event to allow the event on EVERY virtio channel, and include the name of _which_ channel had the event, so that someone using any other channel can also tell if their custom code in the guest is properly connecting to that channel.
+/** + * virConnectDomainEventAgentLifecycleCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @state: new state of the guest agent, one of virConnectDomainEventAgentLifecycleState + * @reason: reason for state change; currently only 0 is passed denoting change + * in the guest agent socket state + * @opaque: application specified data + * + * This callback occurs when libvirt detects a change in the state of a guest + * agent. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, + virDomainPtr dom, + int state, + int reason, + void *opaque);
Thus, I think this signature needs an additional const char *channel name, which provides the name of which agent channel is changing.
+++ b/src/remote/remote_protocol.x @@ -3108,6 +3108,14 @@ struct remote_connect_get_all_domain_stats_args { unsigned int flags; };
+struct remote_domain_event_callback_agent_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + int state; + int reason; +};
Similarly, you'll need a remote_nonnull_string as the channel name as part of this RPC call. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add code to support the event in the object-event example. --- examples/object-events/event-test.c | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 8987ee5..661f1ed 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -244,6 +244,24 @@ networkEventToString(int event) return ret; } +static const char * +guestAgentLifecycleEventStateToString(int event) +{ + const char *ret = ""; + + switch ((virConnectDomainEventAgentLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_DISCONNECTED: + ret = "Disconnected"; + break; + + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_CONNECTED: + ret = "Connected"; + break; + } + + return ret; +} + static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int event, @@ -509,6 +527,20 @@ myDomainEventTunableCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +myDomainEventAgentLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int state, + int reason, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) guest agent state changed: %s reason: %d\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), + guestAgentLifecycleEventStateToString(state), reason); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -551,6 +583,7 @@ int main(int argc, char **argv) int callback15ret = -1; int callback16ret = -1; int callback17ret = -1; + int callback18ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof(action_stop)); @@ -674,6 +707,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_TUNABLE, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventTunableCallback), strdup("tunable"), myFreeFunc); + callback18ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventAgentLifecycleCallback), + strdup("guest agent lifecycle"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && @@ -690,7 +728,8 @@ int main(int argc, char **argv) (callback14ret != -1) && (callback15ret != -1) && (callback16ret != -1) && - (callback17ret != -1)) { + (callback17ret != -1) && + (callback18ret != -1)) { if (virConnectSetKeepAlive(dconn, 5, 3) < 0) { virErrorPtr err = virGetLastError(); fprintf(stderr, "Failed to start keepalive protocol: %s\n", @@ -723,6 +762,8 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback15ret); virConnectNetworkEventDeregisterAny(dconn, callback16ret); virConnectDomainEventDeregisterAny(dconn, callback17ret); + virConnectDomainEventDeregisterAny(dconn, callback18ret); + if (callback8ret != -1) virConnectDomainEventDeregisterAny(dconn, callback8ret); } -- 2.1.0

On 11/19/2014 03:23 AM, Peter Krempa wrote:
Add code to support the event in the object-event example. --- examples/object-events/event-test.c | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add code to emit the event on change of the channel state and reconnect to the qemu process. --- src/qemu/qemu_driver.c | 5 +++++ src/qemu/qemu_process.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 31bf6bb..c489d2f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4342,6 +4342,7 @@ processSerialChangedEvent(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainChrDeviceState newstate; + virObjectEventPtr event = NULL; virDomainDeviceDef dev; if (connected) @@ -4369,6 +4370,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) goto endjob; + if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0") && + (event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, 0))) + qemuDomainEventQueue(driver, event); + dev.data.chr->state = newstate; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3c3ff66..15dabe3 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2068,11 +2068,13 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm, static int -qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, +qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, + virDomainObjPtr vm, virHashTablePtr info) { size_t i; qemuMonitorChardevInfoPtr entry; + virObjectEventPtr event = NULL; char id[32]; for (i = 0; i < vm->def->nchannels; i++) { @@ -2090,6 +2092,11 @@ qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, if (!(entry = virHashLookup(info, id))) continue; + if (entry->state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT && + STREQ_NULLABLE(chr->target.name, "org.qemu.guest_agent.0") && + (event = virDomainEventAgentLifecycleNewFromObj(vm, entry->state, 0))) + qemuDomainEventQueue(driver, event); + chr->state = entry->state; } } @@ -2113,7 +2120,7 @@ qemuProcessReconnectRefreshChannelVirtioState(virQEMUDriverPtr driver, if (ret < 0) goto cleanup; - ret = qemuProcessRefreshChannelVirtioState(vm, info); + ret = qemuProcessRefreshChannelVirtioState(driver, vm, info); cleanup: virHashFree(info); @@ -2170,7 +2177,7 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, info)) != 0) goto cleanup; - if ((ret = qemuProcessRefreshChannelVirtioState(vm, info)) != 0) + if ((ret = qemuProcessRefreshChannelVirtioState(driver, vm, info)) != 0) goto cleanup; } -- 2.1.0

On 11/19/2014 03:23 AM, Peter Krempa wrote:
Add code to emit the event on change of the channel state and reconnect to the qemu process. --- src/qemu/qemu_driver.c | 5 +++++ src/qemu/qemu_process.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-)
@@ -4369,6 +4370,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) goto endjob;
+ if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0") && + (event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, 0))) + qemuDomainEventQueue(driver, event); +
See my comments on 9/11 - I think that filtering this event to just the org.qemu.guest_agent.0 channel is wrong. That may be the only channel that libvirt specifically cares about for tracking whether we can send guest agent commands, but it is not the only channel that management may care about. In fact, naming it virDomainEventAgentLifecycle* may be misleading; isn't it really about virDomainEventChannelLifecycle (where guest-agent is just one use of a channel)? -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 11/20/14 20:01, Eric Blake wrote:
On 11/19/2014 03:23 AM, Peter Krempa wrote:
Add code to emit the event on change of the channel state and reconnect to the qemu process. --- src/qemu/qemu_driver.c | 5 +++++ src/qemu/qemu_process.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-)
@@ -4369,6 +4370,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) goto endjob;
+ if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0") && + (event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, 0))) + qemuDomainEventQueue(driver, event); +
See my comments on 9/11 - I think that filtering this event to just the org.qemu.guest_agent.0 channel is wrong. That may be the only channel that libvirt specifically cares about for tracking whether we can send guest agent commands, but it is not the only channel that management may care about. In fact, naming it virDomainEventAgentLifecycle* may be misleading; isn't it really about virDomainEventChannelLifecycle (where guest-agent is just one use of a channel)?
I specifically wanted to shoot for agent events as they may be used also in a different way than just connect/disconnect. If we want to report state of channels too I have a partially finished patch that allows to do that. The callback prototype then needs to be different as we need to pass also the channel name and possibly other data to identify the channel (and/or serial port? ) Peter

On 11/20/2014 12:03 PM, Peter Krempa wrote:
@@ -4369,6 +4370,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) goto endjob;
+ if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0") && + (event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, 0))) + qemuDomainEventQueue(driver, event); +
See my comments on 9/11 - I think that filtering this event to just the org.qemu.guest_agent.0 channel is wrong. That may be the only channel that libvirt specifically cares about for tracking whether we can send guest agent commands, but it is not the only channel that management may care about. In fact, naming it virDomainEventAgentLifecycle* may be misleading; isn't it really about virDomainEventChannelLifecycle (where guest-agent is just one use of a channel)?
I specifically wanted to shoot for agent events as they may be used also in a different way than just connect/disconnect. If we want to report state of channels too I have a partially finished patch that allows to do that.
Ooh, you have a point - we might be able to report multiple states, such as: no agent connected (reasons include just booted, agent disconnect, ...) agent connected and idle (reasons include agent connect, last agent command completed, ...) agent busy (reasons include other libvirt API, qemu-agent-command backdoor API, ...) On the other hand, do we need to trigger an event for every state change like that? And virDomainEventRegisterAny() doesn't really provide a nice way for callers to filter which events they want (they have to filter on every callback for the events they are actually interested in). It's better to start with a small set of events, and add more if there is a good use case.
The callback prototype then needs to be different as we need to pass also the channel name and possibly other data to identify the channel (and/or serial port? )
Yeah, I mentioned that in one of my replies to 9/11; I think the channel name is sufficient. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (4)
-
Eric Blake
-
Jiri Denemark
-
Peter Krempa
-
Wang Rui