On 08/10/2017 04:02 PM, Martin Kletzander wrote: > On Thu, Aug 03, 2017 at 09:36:27AM +0200, Michal Privoznik wrote: >> https://bugzilla.redhat.com/show_bug.cgi?id=1476866 >> >> For some reason, we completely ignore <on_reboot/> setting for >> domains. The implementation is simply not there. It never was. >> However, things are slightly more complicated. QEMU sends us two >> RESET events on domain reboot. Fortunately, the event contains >> this 'guest' field telling us who initiated the reboot. And since >> we don't want to destroy the domain if the reset is initiated by >> a user, we have to ignore those events. Whatever, just look at >> the code. >> >> Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> >> --- >> src/qemu/qemu_domain.h | 1 + >> src/qemu/qemu_monitor.c | 4 ++-- >> src/qemu/qemu_monitor.h | 3 ++- >> src/qemu/qemu_monitor_json.c | 8 +++++++- >> src/qemu/qemu_process.c | 34 ++++++++++++++++++++++++++++++---- >> 5 files changed, 42 insertions(+), 8 deletions(-) >> >> diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h >> index 4c9050aff..d865e67c7 100644 >> --- a/src/qemu/qemu_domain.h >> +++ b/src/qemu/qemu_domain.h >> @@ -233,6 +233,7 @@ struct _qemuDomainObjPrivate { >> bool agentError; >> >> bool gotShutdown; >> + bool gotReset; >> bool beingDestroyed; >> char *pidfile; >> >> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c >> index 19082d8bf..8f81a2b28 100644 >> --- a/src/qemu/qemu_monitor.c >> +++ b/src/qemu/qemu_monitor.c >> @@ -1344,12 +1344,12 @@ qemuMonitorEmitShutdown(qemuMonitorPtr mon, >> virTristateBool guest) >> >> >> int >> -qemuMonitorEmitReset(qemuMonitorPtr mon) >> +qemuMonitorEmitReset(qemuMonitorPtr mon, virTristateBool guest) >> { >> int ret = -1; >> VIR_DEBUG("mon=%p", mon); >> >> - QEMU_MONITOR_CALLBACK(mon, ret, domainReset, mon->vm); >> + QEMU_MONITOR_CALLBACK(mon, ret, domainReset, mon->vm, guest); >> return ret; >> } >> >> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h >> index 31f7e97ba..8c33f6783 100644 >> --- a/src/qemu/qemu_monitor.h >> +++ b/src/qemu/qemu_monitor.h >> @@ -134,6 +134,7 @@ typedef int >> (*qemuMonitorDomainShutdownCallback)(qemuMonitorPtr mon, >> void *opaque); >> typedef int (*qemuMonitorDomainResetCallback)(qemuMonitorPtr mon, >> virDomainObjPtr vm, >> + virTristateBool guest, >> void *opaque); >> typedef int (*qemuMonitorDomainPowerdownCallback)(qemuMonitorPtr mon, >> virDomainObjPtr vm, >> @@ -346,7 +347,7 @@ int qemuMonitorEmitEvent(qemuMonitorPtr mon, const >> char *event, >> long long seconds, unsigned int micros, >> const char *details); >> int qemuMonitorEmitShutdown(qemuMonitorPtr mon, virTristateBool guest); >> -int qemuMonitorEmitReset(qemuMonitorPtr mon); >> +int qemuMonitorEmitReset(qemuMonitorPtr mon, virTristateBool guest); >> int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); >> int qemuMonitorEmitStop(qemuMonitorPtr mon); >> int qemuMonitorEmitResume(qemuMonitorPtr mon); >> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c >> index b8a68154a..8a1501ced 100644 >> --- a/src/qemu/qemu_monitor_json.c >> +++ b/src/qemu/qemu_monitor_json.c >> @@ -536,7 +536,13 @@ static void >> qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr da >> >> static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, >> virJSONValuePtr data ATTRIBUTE_UNUSED) >> { >> - qemuMonitorEmitReset(mon); >> + bool guest = false; >> + virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT; >> + >> + if (data && virJSONValueObjectGetBoolean(data, "guest", &guest) >> == 0) >> + guest_initiated = guest ? VIR_TRISTATE_BOOL_YES : >> VIR_TRISTATE_BOOL_NO; >> + >> + qemuMonitorEmitReset(mon, guest_initiated); >> } >> >> static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, >> virJSONValuePtr data ATTRIBUTE_UNUSED) >> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c >> index 0aecce3b1..889efc7f0 100644 >> --- a/src/qemu/qemu_process.c >> +++ b/src/qemu/qemu_process.c >> @@ -478,27 +478,51 @@ >> qemuProcessFindVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, >> static int >> qemuProcessHandleReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED, >> virDomainObjPtr vm, >> + virTristateBool guest_initiated, >> void *opaque) >> { >> virQEMUDriverPtr driver = opaque; >> - virObjectEventPtr event; >> + virObjectEventPtr event = NULL; >> qemuDomainObjPrivatePtr priv; >> virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); >> + bool callOnReboot = false; >> >> virObjectLock(vm); >> >> + priv = vm->privateData; >> + >> + /* This is a bit tricky. When a guest does 'reboot' we receive >> RESET event >> + * twice, both times it's guest initiated. However, if users call >> 'virsh >> + * reset' we still receive two events but the first one is >> guest_initiated >> + * = no, the second one is guest_initiated = yes. Therefore, to >> avoid >> + * executing onReboot action in the latter case we need this >> complicated >> + * construction. */ > > I think there is a bug in qemu if calling reset gets us one > guest-initiated reset. You are not guaranteed to get two events anyway, > I believe. I think it's Seabios that issues the second reset. Therefore I don't think it's a bug. But the truth is I completely forgot about UEFI guests. > > Anyway, let's say you're right (for now), I think the following logic is > flawed a bit. > >> + if (guest_initiated == VIR_TRISTATE_BOOL_NO) { >> + VIR_DEBUG("Ignoring not guest initiated RESET event from >> domain %s", >> + vm->def->name); >> + priv->gotReset = true; >> + } else if (priv->gotReset && guest_initiated == >> VIR_TRISTATE_BOOL_YES) { >> + VIR_DEBUG("Ignoring second RESET event from domain %s", >> + vm->def->name); >> + priv->gotReset = false; >> + } else { >> + callOnReboot = true; > > This will be set either if guest_initiated == VIR_TRISTATE_BOOL_ABSENT > (keep in mind this will always be the case with older QEMUs) or > priv->gotReset == false && guest_initiated == VIR_TRISTATE_BOOL_YES. > > If we walk through your examples (reboot => guest_initiated = [yes, > yes], reset => guest_initiated = [no, yes]), then: > > reboot: > - first event (guest_initiated = yes) => callOnReboot = true; > - second event (guest_initiated = yes) => callOnReboot = true; > ( because priv->gotReset is still false ) > > reset: > - first event (guest_initiated = no) => priv->gotReset = true; > - second event (guest_initiated = yes) => priv->gotReset = false; > > So basically in the first scenario you only set the bool to true and in > the second one nothing is set ... True, 'reboot' ran from within the guest sets callOnReboot; 'virsh reset' does not. > >> + } >> + >> event = virDomainEventRebootNewFromObj(vm); >> - priv = vm->privateData; >> if (priv->agent) >> qemuAgentNotifyEvent(priv->agent, QEMU_AGENT_EVENT_RESET); >> >> if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, >> driver->caps) < 0) >> VIR_WARN("Failed to save status on vm %s", vm->def->name); >> >> + if (callOnReboot && >> + guest_initiated == VIR_TRISTATE_BOOL_YES && > > ... so either this will be never executed or I missed something. Therefore, just 'reboot' has an option to fire the action. But since I completely forgot about UEFI, maybe we don't want this logic after all. I mean, how can we safely assume that whatever BIOS guest uses is going to issue the reset event? We can not! So this logic *is* flawed after all but for a different reason. So I guess the only thing we can do here is: a) throw this logic away, b) call whatever action configured > >> + vm->def->onReboot == VIR_DOMAIN_LIFECYCLE_DESTROY) >> + qemuProcessShutdownOrReboot(driver, vm); >> + > > You should also check VIR_DOMAIN_LIFECYCLE_PRESERVE according to the > documentation: Shoot! You're right. > > ... The preserve action for an on_reboot event is treated as a destroy ... > >> virObjectUnlock(vm); >> - >> qemuDomainEventQueue(driver, event); >> - > > Spurious whitespace changes, feel free to keep them if was intended. Yeah, I'd like to keep them in. It's unnecessary to have them in a separate patch since they are trivial and I'm touching the area anyway. Michal -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list