mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 11:35:19 +00:00
qemu: Remember incoming migration errors
If QEMU fails during incoming migration, the domain disappears including a possibly useful error message read from QEMU log file. Let's remember the error in virQEMUDriver so that Finish can report more than just "no such domain". Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
This commit is contained in:
parent
9d0a2af6c2
commit
e68f395fcb
@ -252,6 +252,9 @@ struct _virQEMUDriver {
|
||||
|
||||
/* Immutable pointer, self-clocking APIs */
|
||||
virCloseCallbacksPtr closeCallbacks;
|
||||
|
||||
/* Immutable pointer, self-locking APIs */
|
||||
virHashAtomicPtr migrationErrors;
|
||||
};
|
||||
|
||||
typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef;
|
||||
|
@ -775,6 +775,9 @@ qemuStateInitialize(bool privileged,
|
||||
if (!(qemu_driver->sharedDevices = virHashCreate(30, qemuSharedDeviceEntryFree)))
|
||||
goto error;
|
||||
|
||||
if (qemuMigrationErrorInit(qemu_driver) < 0)
|
||||
goto error;
|
||||
|
||||
if (privileged) {
|
||||
char *channeldir;
|
||||
|
||||
@ -1091,6 +1094,7 @@ qemuStateCleanup(void)
|
||||
virObjectUnref(qemu_driver->remotePorts);
|
||||
virObjectUnref(qemu_driver->webSocketPorts);
|
||||
virObjectUnref(qemu_driver->migrationPorts);
|
||||
virObjectUnref(qemu_driver->migrationErrors);
|
||||
|
||||
virObjectUnref(qemu_driver->xmlopt);
|
||||
|
||||
@ -12117,6 +12121,7 @@ qemuDomainMigrateFinish2(virConnectPtr dconn,
|
||||
if (!vm) {
|
||||
virReportError(VIR_ERR_NO_DOMAIN,
|
||||
_("no domain with matching name '%s'"), dname);
|
||||
qemuMigrationErrorReport(driver, dname);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -12566,11 +12571,16 @@ qemuDomainMigrateFinish3(virConnectPtr dconn,
|
||||
|
||||
virCheckFlags(QEMU_MIGRATION_FLAGS, NULL);
|
||||
|
||||
if (!dname ||
|
||||
!(vm = virDomainObjListFindByName(driver->domains, dname))) {
|
||||
if (!dname) {
|
||||
virReportError(VIR_ERR_NO_DOMAIN, "%s", _("missing domain name"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vm = virDomainObjListFindByName(driver->domains, dname);
|
||||
if (!vm) {
|
||||
virReportError(VIR_ERR_NO_DOMAIN,
|
||||
_("no domain with matching name '%s'"),
|
||||
NULLSTR(dname));
|
||||
_("no domain with matching name '%s'"), dname);
|
||||
qemuMigrationErrorReport(driver, dname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -12609,11 +12619,16 @@ qemuDomainMigrateFinish3Params(virConnectPtr dconn,
|
||||
&dname) < 0)
|
||||
return NULL;
|
||||
|
||||
if (!dname ||
|
||||
!(vm = virDomainObjListFindByName(driver->domains, dname))) {
|
||||
if (!dname) {
|
||||
virReportError(VIR_ERR_NO_DOMAIN, "%s", _("missing domain name"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vm = virDomainObjListFindByName(driver->domains, dname);
|
||||
if (!vm) {
|
||||
virReportError(VIR_ERR_NO_DOMAIN,
|
||||
_("no domain with matching name '%s'"),
|
||||
NULLSTR(dname));
|
||||
_("no domain with matching name '%s'"), dname);
|
||||
qemuMigrationErrorReport(driver, dname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -5543,8 +5543,10 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
|
||||
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
|
||||
goto cleanup;
|
||||
|
||||
if (!qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_IN))
|
||||
if (!qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_IN)) {
|
||||
qemuMigrationErrorReport(driver, vm->def->name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qemuMigrationJobStartPhase(driver, vm,
|
||||
v3proto ? QEMU_MIGRATION_PHASE_FINISH3
|
||||
@ -5570,6 +5572,7 @@ qemuMigrationFinish(virQEMUDriverPtr driver,
|
||||
if (!virDomainObjIsActive(vm) && !(flags & VIR_MIGRATE_OFFLINE)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("guest unexpectedly quit"));
|
||||
qemuMigrationErrorReport(driver, vm->def->name);
|
||||
goto endjob;
|
||||
}
|
||||
|
||||
@ -6094,3 +6097,57 @@ qemuMigrationJobFinish(virQEMUDriverPtr driver, virDomainObjPtr vm)
|
||||
{
|
||||
qemuDomainObjEndAsyncJob(driver, vm);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qemuMigrationErrorFree(void *data,
|
||||
const void *name ATTRIBUTE_UNUSED)
|
||||
{
|
||||
virErrorPtr err = data;
|
||||
virFreeError(err);
|
||||
}
|
||||
|
||||
int
|
||||
qemuMigrationErrorInit(virQEMUDriverPtr driver)
|
||||
{
|
||||
driver->migrationErrors = virHashAtomicNew(64, qemuMigrationErrorFree);
|
||||
if (driver->migrationErrors)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function consumes @err; the caller should consider the @err pointer
|
||||
* invalid after calling this function.
|
||||
*/
|
||||
void
|
||||
qemuMigrationErrorSave(virQEMUDriverPtr driver,
|
||||
const char *name,
|
||||
virErrorPtr err)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
VIR_DEBUG("Saving incoming migration error for domain %s: %s",
|
||||
name, err->message);
|
||||
if (virHashAtomicUpdate(driver->migrationErrors, name, err) < 0) {
|
||||
VIR_WARN("Failed to save migration error for domain '%s'", name);
|
||||
virFreeError(err);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qemuMigrationErrorReport(virQEMUDriverPtr driver,
|
||||
const char *name)
|
||||
{
|
||||
virErrorPtr err;
|
||||
|
||||
if (!(err = virHashAtomicSteal(driver->migrationErrors, name)))
|
||||
return;
|
||||
|
||||
VIR_DEBUG("Restoring saved incoming migration error for domain %s: %s",
|
||||
name, err->message);
|
||||
virSetError(err);
|
||||
virFreeError(err);
|
||||
}
|
||||
|
@ -193,4 +193,11 @@ int qemuMigrationFetchJobStatus(virQEMUDriverPtr driver,
|
||||
qemuDomainAsyncJob asyncJob,
|
||||
qemuDomainJobInfoPtr jobInfo);
|
||||
|
||||
int qemuMigrationErrorInit(virQEMUDriverPtr driver);
|
||||
void qemuMigrationErrorSave(virQEMUDriverPtr driver,
|
||||
const char *name,
|
||||
virErrorPtr err);
|
||||
void qemuMigrationErrorReport(virQEMUDriverPtr driver,
|
||||
const char *name);
|
||||
|
||||
#endif /* __QEMU_MIGRATION_H__ */
|
||||
|
@ -1057,6 +1057,20 @@ qemuMonitorSend(qemuMonitorPtr mon,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function returns a new virError object; the caller is responsible
|
||||
* for freeing it.
|
||||
*/
|
||||
virErrorPtr
|
||||
qemuMonitorLastError(qemuMonitorPtr mon)
|
||||
{
|
||||
if (mon->lastError.code == VIR_ERR_OK)
|
||||
return NULL;
|
||||
|
||||
return virErrorCopyNew(&mon->lastError);
|
||||
}
|
||||
|
||||
|
||||
virJSONValuePtr
|
||||
qemuMonitorGetOptions(qemuMonitorPtr mon)
|
||||
{
|
||||
|
@ -240,6 +240,8 @@ qemuMonitorPtr qemuMonitorOpenFD(virDomainObjPtr vm,
|
||||
|
||||
void qemuMonitorClose(qemuMonitorPtr mon);
|
||||
|
||||
virErrorPtr qemuMonitorLastError(qemuMonitorPtr mon);
|
||||
|
||||
int qemuMonitorSetCapabilities(qemuMonitorPtr mon);
|
||||
|
||||
int qemuMonitorSetLink(qemuMonitorPtr mon,
|
||||
|
@ -310,6 +310,10 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
||||
auditReason = "failed";
|
||||
}
|
||||
|
||||
if (qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_IN))
|
||||
qemuMigrationErrorSave(driver, vm->def->name,
|
||||
qemuMonitorLastError(priv->mon));
|
||||
|
||||
event = virDomainEventLifecycleNewFromObj(vm,
|
||||
VIR_DOMAIN_EVENT_STOPPED,
|
||||
eventReason);
|
||||
|
Loading…
Reference in New Issue
Block a user