When libvirtd is restarted during migration, we properly cancel the
ongoing migration (unless it managed to almost finished before the
restart). But if we were also migrating storage using NBD, we would
completely forget about the running disk mirrors.
Signed-off-by: Jiri Denemark <jdenemar(a)redhat.com>
---
src/qemu/qemu_domain.c | 45 +++++++++++++++++++++++++++++++++-
src/qemu/qemu_migration.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_migration.h | 3 +++
src/qemu/qemu_process.c | 8 +------
4 files changed, 109 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 8724cf0..35cfe20 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -592,7 +592,27 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf,
qemuDomainAsyncJobPhaseToString(
priv->job.asyncJob, priv->job.phase));
}
- virBufferAddLit(buf, "/>\n");
+ if (priv->job.asyncJob != QEMU_ASYNC_JOB_MIGRATION_OUT) {
+ virBufferAddLit(buf, "/>\n");
+ } else {
+ size_t i;
+ virDomainDiskDefPtr disk;
+ qemuDomainDiskPrivatePtr diskPriv;
+
+ virBufferAddLit(buf, ">\n");
+ virBufferAdjustIndent(buf, 2);
+
+ for (i = 0; i < vm->def->ndisks; i++) {
+ disk = vm->def->disks[i];
+ diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+ virBufferAsprintf(buf, "<disk dev='%s'
migrating='%s'/>\n",
+ disk->dst,
+ diskPriv->migrating ? "yes" :
"no");
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</job>\n");
+ }
}
priv->job.active = job;
@@ -750,6 +770,29 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
}
}
+ if ((n = virXPathNodeSet("./job[1]/disk[@migrating='yes']",
+ ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to parse list of disks marked for
migration"));
+ goto error;
+ }
+ if (n > 0) {
+ if (priv->job.asyncJob != QEMU_ASYNC_JOB_MIGRATION_OUT) {
+ VIR_WARN("Found disks marked for migration but we were not "
+ "migrating");
+ n = 0;
+ }
+ for (i = 0; i < n; i++) {
+ char *dst = virXMLPropString(nodes[i], "dev");
+ virDomainDiskDefPtr disk;
+
+ if (dst && (disk = virDomainDiskByName(vm->def, dst, false)))
+ QEMU_DOMAIN_DISK_PRIVATE(disk)->migrating = true;
+ VIR_FREE(dst);
+ }
+ }
+ VIR_FREE(nodes);
+
priv->fakeReboot = virXPathBoolean("boolean(./fakereboot)", ctxt) == 1;
if ((n = virXPathNodeSet("./devices/device", ctxt, &nodes)) < 0) {
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index cd08c88..1db6979 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1985,6 +1985,7 @@ qemuMigrationDriveMirror(virQEMUDriverPtr driver,
char *hoststr = NULL;
unsigned int mirror_flags = VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
int rv;
+ virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
VIR_DEBUG("Starting drive mirrors for domain %s", vm->def->name);
@@ -2034,6 +2035,11 @@ qemuMigrationDriveMirror(virQEMUDriverPtr driver,
goto cleanup;
}
diskPriv->migrating = true;
+
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) {
+ VIR_WARN("Failed to save status on vm %s", vm->def->name);
+ goto cleanup;
+ }
}
while ((rv = qemuMigrationDriveMirrorReady(driver, vm)) != 1) {
@@ -2061,6 +2067,7 @@ qemuMigrationDriveMirror(virQEMUDriverPtr driver,
ret = 0;
cleanup:
+ virObjectUnref(cfg);
VIR_FREE(diskAlias);
VIR_FREE(nbd_dest);
VIR_FREE(hoststr);
@@ -5682,6 +5689,60 @@ qemuMigrationToFile(virQEMUDriverPtr driver, virDomainObjPtr vm,
return ret;
}
+
+int
+qemuMigrationCancel(virQEMUDriverPtr driver,
+ virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ size_t i;
+
+ VIR_DEBUG("Canceling unfinished outgoing migration of domain %s",
+ vm->def->name);
+
+ qemuDomainObjEnterMonitor(driver, vm);
+ ignore_value(qemuMonitorMigrateCancel(priv->mon));
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ return -1;
+
+ for (i = 0; i < vm->def->ndisks; i++) {
+ virDomainDiskDefPtr disk = vm->def->disks[i];
+ qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+ virDomainBlockJobInfo info;
+ char *diskAlias;
+ int rc;
+
+ if (!diskPriv->migrating)
+ continue;
+
+ if (virAsprintf(&diskAlias, QEMU_DRIVE_HOST_PREFIX "%s",
+ disk->info.alias) < 0)
+ return -1;
+
+ VIR_DEBUG("Checking drive mirror state for disk %s (%s)",
+ disk->dst, diskAlias);
+
+ qemuBlockJobSyncBegin(disk);
+ qemuDomainObjEnterMonitor(driver, vm);
+ rc = qemuMonitorBlockJobInfo(priv->mon, diskAlias, &info, NULL);
+ VIR_FREE(diskAlias);
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ return -1;
+
+ if (rc) {
+ VIR_DEBUG("Drive mirror on disk %s is still running",
disk->dst);
+ } else {
+ VIR_DEBUG("Drive mirror on disk %s is gone", disk->dst);
+ qemuBlockJobSyncEnd(driver, vm, disk);
+ diskPriv->migrating = false;
+ }
+ }
+
+ qemuMigrationCancelDriveMirror(driver, vm, false, QEMU_ASYNC_JOB_NONE);
+ return 0;
+}
+
+
int
qemuMigrationJobStart(virQEMUDriverPtr driver,
virDomainObjPtr vm,
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 1726455..e47bde5 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -177,4 +177,7 @@ int qemuMigrationToFile(virQEMUDriverPtr driver, virDomainObjPtr vm,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5)
ATTRIBUTE_RETURN_CHECK;
+int qemuMigrationCancel(virQEMUDriverPtr driver,
+ virDomainObjPtr vm);
+
#endif /* __QEMU_MIGRATION_H__ */
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index b66502c..e4afcf9 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3354,8 +3354,6 @@ qemuProcessRecoverMigration(virQEMUDriverPtr driver,
virDomainState state,
int reason)
{
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
if (job == QEMU_ASYNC_JOB_MIGRATION_IN) {
switch (phase) {
case QEMU_MIGRATION_PHASE_NONE:
@@ -3409,11 +3407,7 @@ qemuProcessRecoverMigration(virQEMUDriverPtr driver,
case QEMU_MIGRATION_PHASE_PERFORM3:
/* migration is still in progress, let's cancel it and resume the
* domain */
- VIR_DEBUG("Canceling unfinished outgoing migration of domain %s",
- vm->def->name);
- qemuDomainObjEnterMonitor(driver, vm);
- ignore_value(qemuMonitorMigrateCancel(priv->mon));
- if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ if (qemuMigrationCancel(driver, vm) < 0)
return -1;
/* resume the domain but only if it was paused as a result of
* migration */
--
2.4.1