}
+static int
+qemuMigrationSetCompression(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ enum qemuDomainAsyncJob job)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int ret;
+
+ if (qemuDomainObjEnterMonitorAsync(driver, vm, job) < 0)
+ return -1;
+
+ ret = qemuMonitorGetMigrationCapability(
+ priv->mon,
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE);
+
+ if (ret < 0) {
+ goto cleanup;
+ } else if (ret == 0) {
+ if (job == QEMU_ASYNC_JOB_MIGRATION_IN) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("Compressed migration is not supported by "
+ "target QEMU binary"));
+ } else {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("Compressed migration is not supported by "
+ "source QEMU binary"));
+ }
+ ret = -1;
+ goto cleanup;
+ }
+
+ ret = qemuMonitorSetMigrationCapability(
+ priv->mon,
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE);
+
+cleanup:
+ qemuDomainObjExitMonitor(driver, vm);
+ return ret;
+}
+
+
static int
qemuMigrationUpdateJobStatus(virQEMUDriverPtr driver,
virDomainObjPtr vm,
if (virFDStreamOpen(st, dataFD[1]) < 0) {
virReportSystemError(errno, "%s",
_("cannot pass pipe for tunnelled migration"));
- virDomainAuditStart(vm, "migrated", false);
- qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
- goto endjob;
+ goto stop;
}
dataFD[1] = -1; /* 'st' owns the FD now & will close it */
}
+ if (flags & VIR_MIGRATE_COMPRESSED &&
+ qemuMigrationSetCompression(driver, vm,
+ QEMU_ASYNC_JOB_MIGRATION_IN) < 0)
+ goto stop;
+
if (mig->lockState) {
VIR_DEBUG("Received lockstate %s", mig->lockState);
VIR_FREE(priv->lockState);
virObjectUnref(caps);
return ret;
+stop:
+ virDomainAuditStart(vm, "migrated", false);
+ qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, 0);
+
endjob:
if (!qemuMigrationJobFinish(driver, vm)) {
vm = NULL;
goto cleanup;
}
+ if (flags & VIR_MIGRATE_COMPRESSED &&
+ qemuMigrationSetCompression(driver, vm,
+ QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
+ goto cleanup;
+
if (qemuDomainObjEnterMonitorAsync(driver, vm,
QEMU_ASYNC_JOB_MIGRATION_OUT) < 0)
goto cleanup;
VIR_MIGRATE_NON_SHARED_INC | \
VIR_MIGRATE_CHANGE_PROTECTION | \
VIR_MIGRATE_UNSAFE | \
- VIR_MIGRATE_OFFLINE)
+ VIR_MIGRATE_OFFLINE | \
+ VIR_MIGRATE_COMPRESSED)
enum qemuMigrationJobPhase {
QEMU_MIGRATION_PHASE_NONE = 0,
QEMU_MONITOR_MIGRATION_STATUS_LAST,
"inactive", "active", "completed", "failed", "cancelled")
+VIR_ENUM_IMPL(qemuMonitorMigrationCaps,
+ QEMU_MONITOR_MIGRATION_CAPS_LAST,
+ "xbzrle")
+
VIR_ENUM_IMPL(qemuMonitorVMStatus,
QEMU_MONITOR_VM_STATUS_LAST,
"debug", "inmigrate", "internal-error", "io-error", "paused",
return qemuMonitorJSONGetTargetArch(mon);
}
+
+
+/**
+ * Returns 1 if @capability is supported, 0 if it's not, or -1 on error.
+ */
+int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ VIR_DEBUG("mon=%p capability=%d", mon, capability);
+
+ if (!mon) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ /* No capability is supported without JSON monitor */
+ if (!mon->json)
+ return 0;
+
+ return qemuMonitorJSONGetMigrationCapability(mon, capability);
+}
+
+int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ VIR_DEBUG("mon=%p capability=%d", mon, capability);
+
+ if (!mon) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ if (!mon->json) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("JSON monitor is required"));
+ return -1;
+ }
+
+ return qemuMonitorJSONSetMigrationCapability(mon, capability);
+}
int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
bool *spice_migrated);
+typedef enum {
+ QEMU_MONITOR_MIGRATION_CAPS_XBZRLE,
+
+ QEMU_MONITOR_MIGRATION_CAPS_LAST
+} qemuMonitorMigrationCaps;
+
+VIR_ENUM_DECL(qemuMonitorMigrationCaps);
+
+int qemuMonitorGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+int qemuMonitorSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+
typedef enum {
QEMU_MONITOR_MIGRATE_BACKGROUND = 1 << 0,
QEMU_MONITOR_MIGRATE_NON_SHARED_DISK = 1 << 1, /* migration with non-shared storage with full disk copy */
virJSONValueFree(reply);
return ret;
}
+
+
+int
+qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ int ret;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr caps;
+ int i;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities",
+ NULL)))
+ return -1;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0) {
+ if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
+ goto cleanup;
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+ }
+
+ if (ret < 0)
+ goto cleanup;
+
+ ret = -1;
+
+ caps = virJSONValueObjectGet(reply, "return");
+ if (!caps || caps->type != VIR_JSON_TYPE_ARRAY) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing migration capabilities"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < virJSONValueArraySize(caps); i++) {
+ virJSONValuePtr cap = virJSONValueArrayGet(caps, i);
+ const char *name;
+
+ if (!cap || cap->type != VIR_JSON_TYPE_OBJECT) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing entry in migration capabilities list"));
+ goto cleanup;
+ }
+
+ if (!(name = virJSONValueObjectGetString(cap, "capability"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing migration capability name"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorMigrationCapsTypeFromString(name) == capability) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
+int
+qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability)
+{
+ int ret = -1;
+
+ virJSONValuePtr cmd = NULL;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr cap = NULL;
+ virJSONValuePtr caps;
+
+ if (!(caps = virJSONValueNewArray()))
+ goto cleanup;
+
+ if (!(cap = virJSONValueNewObject()))
+ goto no_memory;
+
+ if (virJSONValueObjectAppendString(
+ cap, "capability",
+ qemuMonitorMigrationCapsTypeToString(capability)) < 0)
+ goto no_memory;
+
+ if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0)
+ goto no_memory;
+
+ if (virJSONValueArrayAppend(caps, cap) < 0)
+ goto no_memory;
+
+ cap = NULL;
+
+ cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities",
+ "a:capabilities", caps,
+ NULL);
+ if (!cmd)
+ goto cleanup;
+
+ caps = NULL;
+
+ if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
+ goto cleanup;
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+cleanup:
+ virJSONValueFree(caps);
+ virJSONValueFree(cap);
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+
+no_memory:
+ virReportOOMError();
+ goto cleanup;
+}
unsigned long long *remaining,
unsigned long long *total);
+int qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+int qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
+ qemuMonitorMigrationCaps capability);
+
int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
unsigned int flags,
const char *uri);