[libvirt] [PATCHv2 0/5] Add agent lifecycle event

I'll post the promised channel state event later in a separate series. Peter Krempa (5): 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 +++++++++++++++ examples/object-events/event-test.c | 67 ++++++++++++++++++++++++++- include/libvirt/libvirt-domain.h | 41 +++++++++++++++++ src/conf/domain_event.c | 78 ++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 9 ++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 6 +++ src/qemu/qemu_monitor.c | 13 +++++- src/qemu/qemu_monitor.h | 6 +++ src/qemu/qemu_monitor_json.c | 51 ++++++++++++++------- src/qemu/qemu_monitor_text.c | 17 ++++--- src/qemu/qemu_process.c | 90 ++++++++++++++++++++++++++++++++++--- src/remote/remote_driver.c | 31 +++++++++++++ src/remote/remote_protocol.x | 16 ++++++- src/remote_protocol-structs | 7 +++ tests/qemumonitorjsontest.c | 41 ++++++++++++++--- tools/virsh-domain.c | 39 ++++++++++++++++ 17 files changed, 513 insertions(+), 37 deletions(-) -- 2.1.0

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 for all the character devices. The change to the testsuite makes sure that the data is parsed correctly. --- Notes: Version 2: - all devices are now stored in the hash - code is optimized according to the review - example input code format enhanced to contain new field - tests modified to pass src/qemu/qemu_monitor.c | 13 ++++++++++- src/qemu/qemu_monitor.h | 6 ++++++ src/qemu/qemu_monitor_json.c | 51 +++++++++++++++++++++++++++++--------------- src/qemu/qemu_monitor_text.c | 17 ++++++++++----- src/qemu/qemu_process.c | 8 +++---- tests/qemumonitorjsontest.c | 41 +++++++++++++++++++++++++++++------ 6 files changed, 103 insertions(+), 33 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..6e251b3 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3414,7 +3414,7 @@ qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, * * {"return": [ * {"filename": "stdio", "label": "monitor"}, - * {"filename": "pty:/dev/pts/6", "label": "serial0"}, + * {"filename": "pty:/dev/pts/6", "label": "serial0", "frontend-open": true}, * {"filename": "pty:/dev/pts/7", "label": "parallel0"} * ]} * @@ -3426,6 +3426,7 @@ qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, virJSONValuePtr data; int ret = -1; size_t i; + qemuMonitorChardevInfoPtr entry = NULL; if (!(data = virJSONValueObjectGet(reply, "return"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -3440,44 +3441,60 @@ 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 label")); 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) - goto cleanup; + if (VIR_ALLOC(entry) < 0) + goto cleanup; - if (virHashAddEntry(info, id, path) < 0) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to save chardev path '%s'"), path); - VIR_FREE(path); - goto cleanup; - } + if (STRPREFIX(type, "pty:") && + VIR_STRDUP(entry->ptyPath, type + strlen("pty:")) < 0) + goto cleanup; + + if (virJSONValueObjectGetBoolean(chardev, "frontend-open", &connected) == 0) { + if (connected) + entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + } + + if (virHashAddEntry(info, alias, entry) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("failed to add chardev '%s' info"), alias); + goto cleanup; } + + entry = NULL; } ret = 0; cleanup: + if (entry) { + VIR_FREE(entry->ptyPath); + VIR_FREE(entry); + } + 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 8655b5c..41fe0d1 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1919,7 +1919,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" : "", @@ -1930,8 +1930,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 @@ -1948,7 +1948,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..5bfcd20 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,10 @@ testQemuMonitorJSONqemuMonitorJSONGetChardevInfo(const void *data) qemuMonitorTestPtr test = qemuMonitorTestNewSimple(true, xmlopt); int ret = -1; virHashTablePtr info = NULL, expectedInfo = NULL; + qemuMonitorChardevInfo info0 = { NULL, VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT }; + 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 +1800,10 @@ 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, "charmonitor", &info0) < 0 || + virHashAddEntry(expectedInfo, "charserial2", &info3) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "Unable to create expectedInfo hash table"); goto cleanup; @@ -1791,7 +1814,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 +1824,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 +1839,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 Fri, Nov 21, 2014 at 16:29:51 +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 for all the character devices.
The change to the testsuite makes sure that the data is parsed correctly. ---
Notes: Version 2: - all devices are now stored in the hash - code is optimized according to the review - example input code format enhanced to contain new field - tests modified to pass
src/qemu/qemu_monitor.c | 13 ++++++++++- src/qemu/qemu_monitor.h | 6 ++++++ src/qemu/qemu_monitor_json.c | 51 +++++++++++++++++++++++++++++--------------- src/qemu/qemu_monitor_text.c | 17 ++++++++++----- src/qemu/qemu_process.c | 8 +++---- tests/qemumonitorjsontest.c | 41 +++++++++++++++++++++++++++++------ 6 files changed, 103 insertions(+), 33 deletions(-)
ACK Jirka

Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. --- Notes: Version 2: - tweak style of catching errors - adapt to changes in previous patch 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 41fe0d1..c40ca04 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2069,6 +2069,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)) || + !entry->state) + 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, @@ -2111,8 +2166,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); @@ -3595,6 +3656,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 Fri, Nov 21, 2014 at 16:29:52 +0100, Peter Krempa wrote:
Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. ---
Notes: Version 2: - tweak style of catching errors - adapt to changes in previous patch
src/qemu/qemu_process.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
ACK Jirka

On 11/21/14 17:09, Jiri Denemark wrote:
On Fri, Nov 21, 2014 at 16:29:52 +0100, Peter Krempa wrote:
Use data provided by "query-chardev" to refresh the guest frontend state of virtio channels. ---
Notes: Version 2: - tweak style of catching errors - adapt to changes in previous patch
src/qemu/qemu_process.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
ACK
Patches 1 and 2 are now pushed. Thanks. Peter

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. --- Notes: Version 2: - add more reasons - tweak enum and enum member names daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 41 +++++++++++++++++++++ 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 | 39 ++++++++++++++++++++ 9 files changed, 258 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..47e13a6 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3332,6 +3332,46 @@ typedef void (*virConnectDomainEventTunableCallback)(virConnectPtr conn, void *opaque); +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_CONNECTED = 1, /* agent connected */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_DISCONNECTED = 2, /* agent disconnected */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST +# endif +} virConnectDomainEventAgentLifecycleState; + +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_UNKNOWN = 0, /* unknown state change reason */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED = 1, /* state changed due to domain boot */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL = 2, /* channel state changed */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST +# endif +} virConnectDomainEventAgentLifecycleReason; + +/** + * 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; one of virConnectDomainEventAgentLifecycleReason + * @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 +3407,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 daf4dd7..f58be3e 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 0572275..c0d404d 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11672,6 +11672,43 @@ vshEventTunablePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); } +VIR_ENUM_DECL(vshEventAgentLifecycleState) +VIR_ENUM_IMPL(vshEventAgentLifecycleState, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST, + N_("unknown"), + N_("connected"), + N_("disconnected")) + +VIR_ENUM_DECL(vshEventAgentLifecycleReason) +VIR_ENUM_IMPL(vshEventAgentLifecycleReason, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST, + N_("unknown"), + N_("domain booted"), + N_("channel event")) + +#define UNKNOWNSTR(str) (str ? str : N_("unsupported value")) +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: '%s'\n"), + virDomainGetName(dom), + UNKNOWNSTR(vshEventAgentLifecycleStateTypeToString(state)), + UNKNOWNSTR(vshEventAgentLifecycleReasonTypeToString(reason))); + + (*data->count)++; + if (!data->loop) + vshEventDone(data->ctl); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), }, @@ -11707,6 +11744,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 Fri, Nov 21, 2014 at 16:29:53 +0100, 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. ---
Notes: Version 2: - add more reasons - tweak enum and enum member names
daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 41 +++++++++++++++++++++ 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 | 39 ++++++++++++++++++++ 9 files changed, 258 insertions(+), 1 deletion(-)
ACK, although I think we should try to generate more event handling code. Jirka

On Mon, Nov 24, 2014 at 11:33:56 +0100, Jiri Denemark wrote:
On Fri, Nov 21, 2014 at 16:29:53 +0100, 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. ---
Notes: Version 2: - add more reasons - tweak enum and enum member names
daemon/remote.c | 36 +++++++++++++++++++ include/libvirt/libvirt-domain.h | 41 +++++++++++++++++++++ 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 | 39 ++++++++++++++++++++ 9 files changed, 258 insertions(+), 1 deletion(-)
ACK, although I think we should try to generate more event handling code.
Actually, after seeing 5/5, I think VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED reason should be renamed as VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED because it's really emitted when a domain starts rather than with the guest OS inside boots. Jirka

Add code to support the event in the object-event example. --- Notes: Version 2: - add support for reasons examples/object-events/event-test.c | 67 ++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 8987ee5..d90afe3 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -244,6 +244,47 @@ networkEventToString(int event) return ret; } +static const char * +guestAgentLifecycleEventStateToString(int event) +{ + const char *ret = ""; + + switch ((virConnectDomainEventAgentLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_DISCONNECTED: + ret = "Disconnected"; + break; + + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_CONNECTED: + ret = "Connected"; + break; + } + + return ret; +} + +static const char * +guestAgentLifecycleEventReasonToString(int event) +{ + const char *ret = ""; + + switch ((virConnectDomainEventAgentLifecycleReason) event) { + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_UNKNOWN: + ret = "Unknown"; + break; + + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED: + ret = "Domain booted"; + break; + + case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL: + ret = "Channel event"; + break; + } + + return ret; +} + + static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int event, @@ -509,6 +550,21 @@ 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: %s\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), + guestAgentLifecycleEventStateToString(state), + guestAgentLifecycleEventReasonToString(reason)); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -551,6 +607,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 +731,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 +752,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 +786,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 Fri, Nov 21, 2014 at 16:29:54 +0100, Peter Krempa wrote:
Add code to support the event in the object-event example.
Please, provide a separate patch for event example in libvirt-python. ACK Jirka

Add code to emit the event on change of the channel state and reconnect to the qemu process. --- Notes: Version 2: - emit more reasons src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_process.c | 22 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5936d6f..70c781a 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,11 @@ 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, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL))) + 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 c40ca04..f8a176c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2069,13 +2069,20 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm, static int -qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, - virHashTablePtr info) +qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virHashTablePtr info, + int booted) { size_t i; + int agentReason = VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL; qemuMonitorChardevInfoPtr entry; + virObjectEventPtr event = NULL; char id[32]; + if (booted) + agentReason = VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED; + for (i = 0; i < vm->def->nchannels; i++) { virDomainChrDefPtr chr = vm->def->channels[i]; if (chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { @@ -2092,6 +2099,12 @@ qemuProcessRefreshChannelVirtioState(virDomainObjPtr vm, !entry->state) 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, + agentReason))) + qemuDomainEventQueue(driver, event); + chr->state = entry->state; } } @@ -2115,7 +2128,7 @@ qemuProcessReconnectRefreshChannelVirtioState(virQEMUDriverPtr driver, if (ret < 0) goto cleanup; - ret = qemuProcessRefreshChannelVirtioState(vm, info); + ret = qemuProcessRefreshChannelVirtioState(driver, vm, info, false); cleanup: virHashFree(info); @@ -2171,7 +2184,8 @@ qemuProcessWaitForMonitor(virQEMUDriverPtr driver, info)) < 0) goto cleanup; - if ((ret = qemuProcessRefreshChannelVirtioState(vm, info)) < 0) + if ((ret = qemuProcessRefreshChannelVirtioState(driver, vm, info, + true)) < 0) goto cleanup; } -- 2.1.0

On Fri, Nov 21, 2014 at 16:29:55 +0100, Peter Krempa wrote:
Add code to emit the event on change of the channel state and reconnect to the qemu process. ---
Notes: Version 2: - emit more reasons
src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_process.c | 22 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-)
ACK with the suggested change to VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED reason (see 3/5). Jirka

On 11/24/14 13:35, Jiri Denemark wrote:
On Fri, Nov 21, 2014 at 16:29:55 +0100, Peter Krempa wrote:
Add code to emit the event on change of the channel state and reconnect to the qemu process. ---
Notes: Version 2: - emit more reasons
src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_process.c | 22 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-)
ACK with the suggested change to VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_BOOTED reason (see 3/5).
Jirka
I've changed the reason to DOMAIN_STARTED and pushed the series. Thanks. Peter
participants (2)
-
Jiri Denemark
-
Peter Krempa