job->mask = QEMU_JOB_DEFAULT_MASK;
job->dump_memory_only = false;
job->abortJob = false;
+ job->spiceMigrated = false;
VIR_FREE(job->current);
}
qemuDomainJobInfoPtr current; /* async job progress data */
qemuDomainJobInfoPtr completed; /* statistics data of a recently completed job */
bool abortJob; /* abort of the job requested */
+ bool spiceMigrated; /* spice migration completed */
};
typedef void (*qemuDomainCleanupCallback)(virQEMUDriverPtr driver,
}
static int
-qemuMigrationWaitForSpice(virQEMUDriverPtr driver,
- virDomainObjPtr vm)
+qemuMigrationWaitForSpice(virDomainObjPtr vm)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
bool wait_for_spice = false;
- bool spice_migrated = false;
size_t i = 0;
- int rc;
- if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SEAMLESS_MIGRATION)) {
- for (i = 0; i < vm->def->ngraphics; i++) {
- if (vm->def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
- wait_for_spice = true;
- break;
- }
+ if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SEAMLESS_MIGRATION))
+ return 0;
+
+ for (i = 0; i < vm->def->ngraphics; i++) {
+ if (vm->def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+ wait_for_spice = true;
+ break;
}
}
if (!wait_for_spice)
return 0;
- while (!spice_migrated) {
- /* Poll every 50ms for progress & to allow cancellation */
- struct timespec ts = { .tv_sec = 0, .tv_nsec = 50 * 1000 * 1000ull };
-
- if (qemuDomainObjEnterMonitorAsync(driver, vm,
- QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
- return -1;
-
- rc = qemuMonitorGetSpiceMigrationStatus(priv->mon, &spice_migrated);
- if (qemuDomainObjExitMonitor(driver, vm) < 0)
- return -1;
- if (rc < 0)
+ while (!priv->job.spiceMigrated && !priv->job.abortJob) {
+ if (virDomainObjWait(vm) < 0)
return -1;
- virObjectUnlock(vm);
- nanosleep(&ts, NULL);
- virObjectLock(vm);
}
-
return 0;
}
if (retcode == 0) {
/* If guest uses SPICE and supports seamless migration we have to hold
* up domain shutdown until SPICE server transfers its data */
- qemuMigrationWaitForSpice(driver, vm);
+ qemuMigrationWaitForSpice(vm);
qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_MIGRATED,
VIR_QEMU_PROCESS_STOP_MIGRATED);
}
-int
-qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
- bool *spice_migrated)
-{
- QEMU_CHECK_MONITOR_JSON(mon);
-
- return qemuMonitorJSONGetSpiceMigrationStatus(mon, spice_migrated);
-}
-
-
int
qemuMonitorMigrateToFd(qemuMonitorPtr mon,
unsigned int flags,
int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon,
qemuMonitorMigrationStatusPtr status);
-int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
- bool *spice_migrated);
typedef enum {
QEMU_MONITOR_MIGRATION_CAPS_XBZRLE,
}
-static int
-qemuMonitorJSONSpiceGetMigrationStatusReply(virJSONValuePtr reply,
- bool *spice_migrated)
-{
- virJSONValuePtr ret;
-
- if (!(ret = virJSONValueObjectGet(reply, "return"))) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("query-spice reply was missing return data"));
- return -1;
- }
-
- if (virJSONValueObjectGetBoolean(ret, "migrated", spice_migrated) < 0) {
- /* Deliberately don't report error here as we are
- * probably dealing with older qemu which doesn't
- * report this yet. Pretend spice is migrated. */
- *spice_migrated = true;
- }
-
- return 0;
-}
-
-
-int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon,
- bool *spice_migrated)
-{
- int ret;
- virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-spice",
- NULL);
- virJSONValuePtr reply = NULL;
-
- if (!cmd)
- return -1;
-
- ret = qemuMonitorJSONCommand(mon, cmd, &reply);
-
- if (ret == 0)
- ret = qemuMonitorJSONCheckError(cmd, reply);
-
- if (ret == 0)
- ret = qemuMonitorJSONSpiceGetMigrationStatusReply(reply,
- spice_migrated);
-
- virJSONValueFree(cmd);
- virJSONValueFree(reply);
- return ret;
-}
-
-
int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
unsigned int flags,
const char *uri)
}
+static int
+qemuProcessHandleSpiceMigrated(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ qemuDomainObjPrivatePtr priv;
+
+ virObjectLock(vm);
+
+ VIR_DEBUG("Spice migration completed for domain %p %s",
+ vm, vm->def->name);
+
+ priv = vm->privateData;
+ if (priv->job.asyncJob != QEMU_ASYNC_JOB_MIGRATION_OUT) {
+ VIR_DEBUG("got SPICE_MIGRATE_COMPLETED event without a migration job");
+ goto cleanup;
+ }
+
+ priv->job.spiceMigrated = true;
+ virDomainObjSignal(vm);
+
+ cleanup:
+ virObjectUnlock(vm);
+ return 0;
+}
+
+
static qemuMonitorCallbacks monitorCallbacks = {
.eofNotify = qemuProcessHandleMonitorEOF,
.errorNotify = qemuProcessHandleMonitorError,
.domainDeviceDeleted = qemuProcessHandleDeviceDeleted,
.domainNicRxFilterChanged = qemuProcessHandleNicRxFilterChanged,
.domainSerialChange = qemuProcessHandleSerialChanged,
+ .domainSpiceMigrated = qemuProcessHandleSpiceMigrated,
};
static int
return ret;
}
-static int
-testQemuMonitorJSONqemuMonitorJSONGetSpiceMigrationStatus(const void *data)
-{
- virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
- qemuMonitorTestPtr test = qemuMonitorTestNewSimple(true, xmlopt);
- int ret = -1;
- bool spiceMigrated;
-
- if (!test)
- return -1;
-
- if (qemuMonitorTestAddItem(test, "query-spice",
- "{"
- " \"return\": {"
- " \"migrated\": true,"
- " \"enabled\": false,"
- " \"mouse-mode\": \"client\""
- " },"
- " \"id\": \"libvirt-14\""
- "}") < 0)
- goto cleanup;
-
- if (qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorTestGetMonitor(test),
- &spiceMigrated) < 0)
- goto cleanup;
-
- if (!spiceMigrated) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "Invalid spice migration status: %d, expecting 1",
- spiceMigrated);
- goto cleanup;
- }
-
- ret = 0;
- cleanup:
- qemuMonitorTestFree(test);
- return ret;
-}
-
static int
testHashEqualChardevInfo(const void *value1, const void *value2)
{
DO_TEST(qemuMonitorJSONGetBlockStatsInfo);
DO_TEST(qemuMonitorJSONGetMigrationCacheSize);
DO_TEST(qemuMonitorJSONGetMigrationStatus);
- DO_TEST(qemuMonitorJSONGetSpiceMigrationStatus);
DO_TEST(qemuMonitorJSONGetChardevInfo);
DO_TEST(qemuMonitorJSONSetBlockIoThrottle);
DO_TEST(qemuMonitorJSONGetTargetArch);