This patch adds initial migration support to the libxl driver,
using the VIR_DRV_FEATURE_MIGRATION_PARAMS family of migration
functions.
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
V3 here
https://www.redhat.com/archives/libvir-list/2014-April/msg00968.html
In V4:
- Code cleanup
- Improved error handling
Although this patch has been floating around for a long time, not sure
if it is 1.2.4 material since it brings a new feature to the libxl
driver.
po/POTFILES.in | 1 +
src/Makefile.am | 3 +-
src/libxl/libxl_conf.h | 6 +
src/libxl/libxl_domain.h | 1 +
src/libxl/libxl_driver.c | 224 ++++++++++
src/libxl/libxl_migration.c | 978 ++++++++++++++++++++++++++++++++++++++++++++
src/libxl/libxl_migration.h | 78 ++++
7 files changed, 1290 insertions(+), 1 deletion(-)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e35eb82..a72dc1e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -73,6 +73,7 @@ src/lxc/lxc_process.c
src/libxl/libxl_domain.c
src/libxl/libxl_driver.c
src/libxl/libxl_conf.c
+src/libxl/libxl_migration.c
src/network/bridge_driver.c
src/network/bridge_driver_linux.c
src/node_device/node_device_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index e9dc9e0..0dbda7f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -706,7 +706,8 @@ XENAPI_DRIVER_SOURCES = \
LIBXL_DRIVER_SOURCES = \
libxl/libxl_conf.c libxl/libxl_conf.h \
libxl/libxl_domain.c libxl/libxl_domain.h \
- libxl/libxl_driver.c libxl/libxl_driver.h
+ libxl/libxl_driver.c libxl/libxl_driver.h \
+ libxl/libxl_migration.c libxl/libxl_migration.h
UML_DRIVER_SOURCES = \
uml/uml_conf.c uml/uml_conf.h \
diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h
index 24e1720..b798567 100644
--- a/src/libxl/libxl_conf.h
+++ b/src/libxl/libxl_conf.h
@@ -43,6 +43,9 @@
# define LIBXL_VNC_PORT_MIN 5900
# define LIBXL_VNC_PORT_MAX 65535
+# define LIBXL_MIGRATION_PORT_MIN 49152
+# define LIBXL_MIGRATION_PORT_MAX 49216
+
# define LIBXL_CONFIG_DIR SYSCONFDIR "/libvirt/libxl"
# define LIBXL_AUTOSTART_DIR LIBXL_CONFIG_DIR "/autostart"
# define LIBXL_STATE_DIR LOCALSTATEDIR "/run/libvirt/libxl"
@@ -115,6 +118,9 @@ struct _libxlDriverPrivate {
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr reservedVNCPorts;
+ /* Immutable pointer, self-locking APIs */
+ virPortAllocatorPtr migrationPorts;
+
/* Immutable pointer, lockless APIs*/
virSysinfoDefPtr hostsysinfo;
};
diff --git a/src/libxl/libxl_domain.h b/src/libxl/libxl_domain.h
index 979ce2a..9d48049 100644
--- a/src/libxl/libxl_domain.h
+++ b/src/libxl/libxl_domain.h
@@ -69,6 +69,7 @@ struct _libxlDomainObjPrivate {
virChrdevsPtr devs;
libxl_evgen_domain_death *deathW;
libxlDriverPrivatePtr driver;
+ unsigned short migrationPort;
struct libxlDomainJobObj job;
};
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index e5ed0f2..2359f71 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -45,6 +45,7 @@
#include "libxl_domain.h"
#include "libxl_driver.h"
#include "libxl_conf.h"
+#include "libxl_migration.h"
#include "xen_xm.h"
#include "xen_sxpr.h"
#include "virtypedparam.h"
@@ -209,6 +210,7 @@ libxlStateCleanup(void)
virObjectUnref(libxl_driver->xmlopt);
virObjectUnref(libxl_driver->domains);
virObjectUnref(libxl_driver->reservedVNCPorts);
+ virObjectUnref(libxl_driver->migrationPorts);
virObjectEventStateFree(libxl_driver->domainEventState);
virSysinfoDefFree(libxl_driver->hostsysinfo);
@@ -296,6 +298,13 @@ libxlStateInitialize(bool privileged,
LIBXL_VNC_PORT_MAX)))
goto error;
+ /* Allocate bitmap for migration port reservation */
+ if (!(libxl_driver->migrationPorts =
+ virPortAllocatorNew(_("migration"),
+ LIBXL_MIGRATION_PORT_MIN,
+ LIBXL_MIGRATION_PORT_MAX)))
+ goto error;
+
if (!(libxl_driver->domains = virDomainObjListNew()))
goto error;
@@ -4126,6 +4135,7 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature)
switch (feature) {
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
+ case VIR_DRV_FEATURE_MIGRATION_PARAMS:
return 1;
default:
return 0;
@@ -4304,6 +4314,215 @@ libxlNodeDeviceReset(virNodeDevicePtr dev)
return ret;
}
+static char *
+libxlDomainMigrateBegin3Params(virDomainPtr domain,
+ virTypedParameterPtr params,
+ int nparams,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ const char *xmlin = NULL;
+ virDomainObjPtr vm = NULL;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
+ if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
+ return NULL;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_XML,
+ &xmlin) < 0)
+ return NULL;
+
+ if (!(vm = libxlDomObjFromDomain(domain)))
+ return NULL;
+
+ if (virDomainMigrateBegin3ParamsEnsureACL(domain->conn, vm->def) < 0) {
+ virObjectUnlock(vm);
+ return NULL;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ virObjectUnlock(vm);
+ return NULL;
+ }
+
+ return libxlDomainMigrationBegin(domain->conn, vm, xmlin);
+}
+
+static int
+libxlDomainMigratePrepare3Params(virConnectPtr dconn,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ char **uri_out,
+ unsigned int flags)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ virDomainDefPtr def = NULL;
+ const char *dom_xml = NULL;
+ const char *dname = NULL;
+ const char *uri_in = NULL;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+ if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
+ goto error;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_XML,
+ &dom_xml) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME,
+ &dname) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_URI,
+ &uri_in) < 0)
+
+ goto error;
+
+ if (!(def = libxlDomainMigrationPrepareDef(driver, dom_xml, dname)))
+ goto error;
+
+ if (virDomainMigratePrepare3ParamsEnsureACL(dconn, def) < 0)
+ goto error;
+
+ if (libxlDomainMigrationPrepare(dconn, def, uri_in, uri_out) < 0)
+ goto error;
+
+ return 0;
+
+ error:
+ virDomainDefFree(def);
+ return -1;
+}
+
+static int
+libxlDomainMigratePerform3Params(virDomainPtr dom,
+ const char *dconnuri,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ libxlDriverPrivatePtr driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ const char *dom_xml = NULL;
+ const char *dname = NULL;
+ const char *uri = NULL;
+ int ret = -1;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+ if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
+ goto cleanup;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_XML,
+ &dom_xml) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME,
+ &dname) < 0 ||
+ virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_URI,
+ &uri) < 0)
+
+ goto cleanup;
+
+ if (!(vm = libxlDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainMigratePerform3ParamsEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (libxlDomainMigrationPerform(driver, vm, dom_xml, dconnuri,
+ uri, dname, flags) < 0) {
+ /* Job terminated and vm unlocked if MigrationPerform failed */
+ vm = NULL;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static virDomainPtr
+libxlDomainMigrateFinish3Params(virConnectPtr dconn,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned int flags,
+ int cancelled)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ virDomainObjPtr vm = NULL;
+ const char *dname = NULL;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
+ if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
+ return NULL;
+
+ if (virTypedParamsGetString(params, nparams,
+ VIR_MIGRATE_PARAM_DEST_NAME,
+ &dname) < 0)
+ return NULL;
+
+ if (!dname ||
+ !(vm = virDomainObjListFindByName(driver->domains, dname))) {
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching name '%s'"),
+ NULLSTR(dname));
+ return NULL;
+ }
+
+ if (virDomainMigrateFinish3ParamsEnsureACL(dconn, vm->def) < 0) {
+ virObjectUnlock(vm);
+ return NULL;
+ }
+
+ return libxlDomainMigrationFinish(dconn, vm, flags, cancelled);
+}
+
+static int
+libxlDomainMigrateConfirm3Params(virDomainPtr domain,
+ virTypedParameterPtr params,
+ int nparams,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ unsigned int flags,
+ int cancelled)
+{
+ libxlDriverPrivatePtr driver = domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+ if (virTypedParamsValidate(params, nparams, LIBXL_MIGRATION_PARAMETERS) < 0)
+ return -1;
+
+ if (!(vm = libxlDomObjFromDomain(domain)))
+ return -1;
+
+ if (virDomainMigrateConfirm3ParamsEnsureACL(domain->conn, vm->def) < 0) {
+ virObjectUnlock(vm);
+ return -1;
+ }
+
+ return libxlDomainMigrationConfirm(driver, vm, flags, cancelled);
+}
+
static virDriver libxlDriver = {
.no = VIR_DRV_LIBXL,
@@ -4394,6 +4613,11 @@ static virDriver libxlDriver = {
.nodeDeviceDetachFlags = libxlNodeDeviceDetachFlags, /* 1.2.3 */
.nodeDeviceReAttach = libxlNodeDeviceReAttach, /* 1.2.3 */
.nodeDeviceReset = libxlNodeDeviceReset, /* 1.2.3 */
+ .domainMigrateBegin3Params = libxlDomainMigrateBegin3Params, /* 1.2.3 */
+ .domainMigratePrepare3Params = libxlDomainMigratePrepare3Params, /* 1.2.3 */
+ .domainMigratePerform3Params = libxlDomainMigratePerform3Params, /* 1.2.3 */
+ .domainMigrateFinish3Params = libxlDomainMigrateFinish3Params, /* 1.2.3 */
+ .domainMigrateConfirm3Params = libxlDomainMigrateConfirm3Params, /* 1.2.3 */
};
static virStateDriver libxlStateDriver = {
diff --git a/src/libxl/libxl_migration.c b/src/libxl/libxl_migration.c
new file mode 100644
index 0000000..7776934
--- /dev/null
+++ b/src/libxl/libxl_migration.c
@@ -0,0 +1,978 @@
+/*
+ * libxl_migration.c: methods for handling migration with libxenlight
+ *
+ * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jim Fehlig <jfehlig(a)suse.com>
+ * Chunyan Liu <cyliu(a)suse.com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+#include "virlog.h"
+#include "virerror.h"
+#include "virconf.h"
+#include "datatypes.h"
+#include "virfile.h"
+#include "viralloc.h"
+#include "viruuid.h"
+#include "vircommand.h"
+#include "virstring.h"
+#include "rpc/virnetsocket.h"
+#include "libxl_domain.h"
+#include "libxl_driver.h"
+#include "libxl_conf.h"
+#include "libxl_migration.h"
+
+#define VIR_FROM_THIS VIR_FROM_LIBXL
+
+VIR_LOG_INIT("libxl.libxl_migration");
+
+#define LIBXL_MIGRATION_PROTO_VERSION 1
+#define LIBXL_MIGRATION_MSG_MAX_SIZE 512
+
+typedef struct _libxlMigrateReceiveArgs {
+ virConnectPtr conn;
+ virDomainObjPtr vm;
+
+ /* for freeing listen sockets */
+ virNetSocketPtr *socks;
+ size_t nsocks;
+} libxlMigrateReceiveArgs;
+
+/*
+ * For future extensibility, a simple messaging protocol used to send migration
+ * data between libxl hosts. The message is encapsulated in json and currently
+ * includes the following entries:
+ *
+ * {"libvirt-libxl-mig-msg" :
+ * {"version" : $ver},
+ * {"state" : $state},
+ * {"status" : $status}
+ * }
+ *
+ * Possible $state values are "negotiate-version",
"send-binary-data",
+ * "sending-binary-data", "received-binary-data", "done",
and "error".
+ *
+ * Migration between source and destination libxl hosts is performed with the
+ * following message exchange:
+ *
+ * - src->dst: connect
+ * - dst->src: state="negotiate-version",
version=LIBXL_MIGRATION_PROTO_VERSION
+ * - src->dst: state-"negotiate-version",
+ * version=min(dst ver, LIBXL_MIGRATION_PROTO_VERSION)
+ * - dst->src: state="send-binary-data", version=negotiated_version
+ * - src->dst: state="sending-binary-data", version=negotiated_version
+ * _ src->dst: binary migration data
+ * - dst->src: state="received-binary-data", version=negotiated_version
+ * - src->dst: state="done", version=negotiated_version
+ *
+ */
+
+static virJSONValuePtr
+libxlMigrationMsgNew(const char *state)
+{
+ virJSONValuePtr msg;
+ virJSONValuePtr body;
+
+ if (!(msg = virJSONValueNewObject()))
+ return NULL;
+
+ if (!(body = virJSONValueNewObject())) {
+ virJSONValueFree(msg);
+ return NULL;
+ }
+
+ virJSONValueObjectAppendNumberInt(body, "version",
+ LIBXL_MIGRATION_PROTO_VERSION);
+ virJSONValueObjectAppendString(body, "state", state);
+ virJSONValueObjectAppendNumberInt(body, "status", 0);
+
+ virJSONValueObjectAppend(msg, "libvirt-libxl-mig-msg", body);
+
+ return msg;
+}
+
+static bool
+libxlMigrationMsgCheckState(virJSONValuePtr message, const char *state)
+{
+ virJSONValuePtr body;
+ const char *msg_state;
+
+ if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg")))
+ return false;
+
+ msg_state = virJSONValueObjectGetString(body, "state");
+ if (!msg_state || STRNEQ(msg_state, state))
+ return false;
+
+ return true;
+}
+
+static void
+libxlMigrationMsgSetStringField(virJSONValuePtr message,
+ const char *field,
+ const char *val)
+{
+ virJSONValuePtr body;
+
+ if ((body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) {
+ virJSONValueObjectRemoveKey(body, field, NULL);
+ virJSONValueObjectAppendString(body, field, val);
+ }
+}
+
+static void
+libxlMigrationMsgSetIntField(virJSONValuePtr message,
+ const char *field,
+ int val)
+{
+ virJSONValuePtr body;
+
+ if ((body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg"))) {
+ virJSONValueObjectRemoveKey(body, field, NULL);
+ virJSONValueObjectAppendNumberInt(body, field, val);
+ }
+}
+
+static int
+libxlMigrationMsgGetIntField(virJSONValuePtr message,
+ const char *field,
+ int *val)
+{
+ virJSONValuePtr body;
+
+ if (!(body = virJSONValueObjectGet(message, "libvirt-libxl-mig-msg")))
+ return -1;
+
+ if (virJSONValueObjectGetNumberInt(body, field, val) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Send a migration message with 'status' field set to @status.
+ */
+static int
+libxlMigrationMsgSend(int fd, virJSONValuePtr message, int status)
+{
+ int ret = -1;
+ size_t len;
+ char *msgstr;
+
+ libxlMigrationMsgSetIntField(message, "status", status);
+ if (!(msgstr = virJSONValueToString(message, false))) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Malformed migration message"));
+ return -1;
+ }
+
+ VIR_DEBUG("Sending migration message %s", msgstr);
+ len = strlen(msgstr) + 1;
+ if (safewrite(fd, msgstr, len) != len) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to send migration message"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(msgstr);
+ return ret;
+}
+
+static virJSONValuePtr
+libxlMigrationMsgReceive(int fd)
+{
+ char buf[LIBXL_MIGRATION_MSG_MAX_SIZE];
+ int ret;
+ virJSONValuePtr msg = NULL;
+ size_t i;
+
+ /*
+ * Message is small and NULL-terminated. Read a byte at a time
+ * until encountering the null-termination.
+ */
+ for (i = 0; i < LIBXL_MIGRATION_MSG_MAX_SIZE;) {
+ ret = saferead(fd, &(buf[i]), 1);
+ if (ret == 1)
+ if (buf[i++] == '\0')
+ break;
+
+ /* bail out if eof or failure != EAGAIN */
+ if ((ret == 0) || (ret == -1 && errno != EAGAIN)) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive migration message"));
+ return NULL;
+ }
+ }
+
+ if (i == LIBXL_MIGRATION_MSG_MAX_SIZE) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Migration message exceeded max size"));
+ return NULL;
+ }
+
+ VIR_DEBUG("Received migration message %s", buf);
+ if (!(msg = virJSONValueFromString(buf)))
+ goto error;
+
+ /* Verify a libvirt-libxl-mig-msg was received */
+ if (!(virJSONValueObjectGet(msg, "libvirt-libxl-mig-msg")))
+ goto error;
+
+ return msg;
+
+ error:
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Malformed migration message"));
+ virJSONValueFree(msg);
+ return NULL;
+}
+
+static int
+libxlMigrationSrcNegotiate(int fd)
+{
+ int ret = -1;
+ virJSONValuePtr msg;
+ int dstver;
+
+ if (!(msg = libxlMigrationMsgReceive(fd)))
+ return -1;
+
+ if (!libxlMigrationMsgCheckState(msg, "negotiate-version")) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to negotiate libxl "
+ "migration protocol version"));
+ goto cleanup;
+ }
+
+ /*
+ * ACK by returning the message, with version set to
+ * min(dest ver, src ver)
+ */
+ if (libxlMigrationMsgGetIntField(msg, "version", &dstver) < 0)
+ goto cleanup;
+
+ if (dstver > LIBXL_MIGRATION_PROTO_VERSION)
+ libxlMigrationMsgSetIntField(msg,
+ "version",
+ LIBXL_MIGRATION_PROTO_VERSION);
+
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(msg);
+ return ret;
+}
+
+static int
+libxlMigrationDstNegotiate(int fd, virJSONValuePtr *message)
+{
+ virJSONValuePtr msg;
+
+ if (!(msg = libxlMigrationMsgNew("negotiate-version")))
+ return -1;
+
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0) {
+ virJSONValueFree(msg);
+ return -1;
+ }
+
+ /*
+ * Receive ACK. src set version=min(dst ver, highest src ver),
+ * which is the negotiated version. Use this message with
+ * negotiated version throughout the transaction.
+ */
+ virJSONValueFree(msg);
+ if (!(msg = libxlMigrationMsgReceive(fd)))
+ return -1;
+
+ *message = msg;
+ return 0;
+}
+
+static int
+libxlMigrationSrcReady(int fd)
+{
+ int ret = -1;
+ virJSONValuePtr msg;
+
+ if (!(msg = libxlMigrationMsgReceive(fd)))
+ return -1;
+
+ if (!libxlMigrationMsgCheckState(msg, "send-binary-data")) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive 'send-binary-data' message
"
+ "from destination libxl migration host"));
+ goto cleanup;
+ }
+
+ /* ACK by returning the message with state sending */
+ libxlMigrationMsgSetStringField(msg, "state",
"sending-binary-data");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(msg);
+ return ret;
+}
+
+static int
+libxlMigrationDstReady(int fd, virJSONValuePtr msg)
+{
+ int ret = -1;
+ virJSONValuePtr tmp = NULL;
+
+ libxlMigrationMsgSetStringField(msg, "state",
"send-binary-data");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ return -1;
+
+ /* Receive ACK, state sending */
+ if (!(tmp = libxlMigrationMsgReceive(fd)))
+ return -1;
+ if (!libxlMigrationMsgCheckState(tmp, "sending-binary-data")) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive 'sending-binary-data' message
"
+ "from source libxl migration host"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(tmp);
+ return ret;
+}
+
+static int
+libxlMigrationSrcDone(int fd)
+{
+ int ret = -1;
+ virJSONValuePtr msg;
+
+ if (!(msg = libxlMigrationMsgReceive(fd)))
+ return -1;
+
+ if (!libxlMigrationMsgCheckState(msg, "received-binary-data")) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive 'received-binary-data' message
"
+ "from destination libxl migration host"));
+ goto cleanup;
+ }
+
+ /* ACK by returning the message with state done */
+ libxlMigrationMsgSetStringField(msg, "state", "done");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(msg);
+ return ret;
+}
+
+static int
+libxlMigrationDestDone(int fd, virJSONValuePtr msg)
+{
+ int ret = -1;
+ virJSONValuePtr tmp;
+
+ libxlMigrationMsgSetStringField(msg, "state",
"received-binary-data");
+ if (libxlMigrationMsgSend(fd, msg, 0) < 0)
+ return -1;
+
+ /* Receive ACK, state done */
+ if (!(tmp = libxlMigrationMsgReceive(fd)))
+ return -1;
+ if (!libxlMigrationMsgCheckState(tmp, "done")) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to receive 'done' message "
+ "from source libxl migration host"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(tmp);
+ return ret;
+}
+
+static void
+libxlMigrationSrcFailed(int fd, int status)
+{
+ virJSONValuePtr msg;
+
+ if (!(msg = libxlMigrationMsgNew("error")))
+ return;
+ libxlMigrationMsgSend(fd, msg, status);
+ virJSONValueFree(msg);
+}
+
+static void
+libxlMigrationDstFailed(int fd, virJSONValuePtr msg, int status)
+{
+ libxlMigrationMsgSetStringField(msg, "state", "error");
+ libxlMigrationMsgSend(fd, msg, status);
+}
+
+static void
+libxlDoMigrateReceive(virNetSocketPtr sock,
+ int events ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ libxlMigrateReceiveArgs *data = opaque;
+ virConnectPtr conn = data->conn;
+ virDomainObjPtr vm = data->vm;
+ libxlDomainObjPrivatePtr priv = vm->privateData;
+ virNetSocketPtr *socks = data->socks;
+ size_t nsocks = data->nsocks;
+ libxlDriverPrivatePtr driver = conn->privateData;
+ virNetSocketPtr client_sock;
+ virJSONValuePtr mig_msg = NULL;
+ int recvfd;
+ size_t i;
+ int ret;
+
+ virNetSocketAccept(sock, &client_sock);
+ if (client_sock == NULL) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Fail to accept migration connection"));
+ goto cleanup;
+ }
+ VIR_DEBUG("Accepted migration connection\n");
+ recvfd = virNetSocketDupFD(client_sock, true);
+ virObjectUnref(client_sock);
+
+ if (libxlMigrationDstNegotiate(recvfd, &mig_msg) < 0)
+ goto cleanup;
+
+ if (libxlMigrationDstReady(recvfd, mig_msg) < 0)
+ goto cleanup;
+
+ virObjectLock(vm);
+ ret = libxlDomainStart(driver, vm, false, recvfd);
+ virObjectUnlock(vm);
+
+ if (ret < 0) {
+ libxlMigrationDstFailed(recvfd, mig_msg, ret);
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to restore domain with libxenlight"));
+ if (!vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+ goto cleanup;
+ }
+
+ if (libxlMigrationDestDone(recvfd, mig_msg) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to notify sender that migration completed"));
+ goto cleanup_dom;
+ }
+
+ /* Remove all listen socks from event handler, and close them. */
+ for (i = 0; i < nsocks; i++) {
+ virNetSocketUpdateIOCallback(socks[i], 0);
+ virNetSocketRemoveIOCallback(socks[i]);
+ virNetSocketClose(socks[i]);
+ virObjectUnref(socks[i]);
+ }
+ VIR_FREE(socks);
+ goto cleanup;
+
+ cleanup_dom:
+ libxl_domain_destroy(priv->ctx, vm->def->id, NULL);
+ vm->def->id = -1;
+ if (!vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+
+ cleanup:
+ VIR_FORCE_CLOSE(recvfd);
+ virJSONValueFree(mig_msg);
+ VIR_FREE(opaque);
+ return;
+}
+
+static int
+libxlDoMigrateSend(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ unsigned long flags,
+ int sockfd)
+{
+ libxlDomainObjPrivatePtr priv;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virObjectEventPtr event = NULL;
+ int xl_flags = 0;
+ int ret = -1;
+
+ if (flags & VIR_MIGRATE_LIVE)
+ xl_flags = LIBXL_SUSPEND_LIVE;
+
+ priv = vm->privateData;
+
+ if (libxlMigrationSrcNegotiate(sockfd) < 0)
+ goto cleanup;
+
+ if (libxlMigrationSrcReady(sockfd) < 0)
+ goto cleanup;
+
+ ret = libxl_domain_suspend(priv->ctx, vm->def->id, sockfd,
+ xl_flags, NULL);
+ if (ret != 0) {
+ libxlMigrationSrcFailed(sockfd, ret);
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to save domain '%d' with
libxenlight"),
+ vm->def->id);
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (libxlMigrationSrcDone(sockfd) < 0) {
+ if (libxl_domain_resume(priv->ctx, vm->def->id, 0, 0) != 0) {
+ VIR_DEBUG("Failed to resume domain '%d' with libxenlight",
+ vm->def->id);
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED,
+ VIR_DOMAIN_PAUSED_MIGRATION);
+ event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED);
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ goto cleanup;
+ }
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (event)
+ libxlDomainEventQueue(driver, event);
+ virObjectUnref(cfg);
+ return ret;
+}
+
+static bool
+libxlDomainMigrationIsAllowed(virDomainDefPtr def)
+{
+ /* Migration is not allowed if definition contains any hostdevs */
+ if (def->nhostdevs > 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has assigned host devices"));
+ return false;
+ }
+
+ return true;
+}
+
+char *
+libxlDomainMigrationBegin(virConnectPtr conn,
+ virDomainObjPtr vm,
+ const char *xmlin)
+{
+ libxlDriverPrivatePtr driver = conn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainDefPtr tmpdef = NULL;
+ virDomainDefPtr def;
+ char *xml = NULL;
+
+ if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (xmlin) {
+ if (!(tmpdef = virDomainDefParseString(xmlin, cfg->caps,
+ driver->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_XEN,
+ VIR_DOMAIN_XML_INACTIVE)))
+ goto endjob;
+
+ def = tmpdef;
+ } else {
+ def = vm->def;
+ }
+
+ if (!libxlDomainMigrationIsAllowed(def))
+ goto endjob;
+
+ xml = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE);
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+
+ virDomainDefFree(tmpdef);
+ virObjectUnref(cfg);
+ return xml;
+
+ endjob:
+ if (!libxlDomainObjEndJob(driver, vm))
+ vm = NULL;
+ goto cleanup;
+}
+
+virDomainDefPtr
+libxlDomainMigrationPrepareDef(libxlDriverPrivatePtr driver,
+ const char *dom_xml,
+ const char *dname)
+{
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainDefPtr def;
+ char *name = NULL;
+
+ if (!dom_xml) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("no domain XML passed"));
+ return NULL;
+ }
+
+ if (!(def = virDomainDefParseString(dom_xml, cfg->caps, driver->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_XEN,
+ VIR_DOMAIN_XML_INACTIVE)))
+ goto cleanup;
+
+ if (dname) {
+ name = def->name;
+ if (VIR_STRDUP(def->name, dname) < 0) {
+ virDomainDefFree(def);
+ def = NULL;
+ }
+ }
+
+ cleanup:
+ virObjectUnref(cfg);
+ VIR_FREE(name);
+ return def;
+}
+
+int
+libxlDomainMigrationPrepare(virConnectPtr dconn,
+ virDomainDefPtr def,
+ const char *uri_in,
+ char **uri_out)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ virDomainObjPtr vm = NULL;
+ char *hostname = NULL;
+ unsigned short port;
+ char portstr[100];
+ virURIPtr uri = NULL;
+ virNetSocketPtr *socks = NULL;
+ size_t nsocks = 0;
+ int nsocks_listen = 0;
+ libxlMigrateReceiveArgs *args;
+ size_t i;
+ int ret = -1;
+
+ if (!(vm = virDomainObjListAdd(driver->domains, def,
+ driver->xmlopt,
+ VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+ VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+ NULL)))
+ goto cleanup;
+
+ /* Create socket connection to receive migration data */
+ if (!uri_in) {
+ if ((hostname = virGetHostname()) == NULL)
+ goto cleanup;
+
+ if (STRPREFIX(hostname, "localhost")) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("hostname on destination resolved to localhost,"
+ " but migration requires an FQDN"));
+ goto cleanup;
+ }
+
+ if (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0)
+ goto cleanup;
+
+ if (virAsprintf(uri_out, "tcp://%s:%d", hostname, port) < 0)
+ goto cleanup;
+ } else {
+ if (!(STRPREFIX(uri_in, "tcp://"))) {
+ /* not full URI, add prefix tcp:// */
+ char *tmp;
+ if (virAsprintf(&tmp, "tcp://%s", uri_in) < 0)
+ goto cleanup;
+ uri = virURIParse(tmp);
+ VIR_FREE(tmp);
+ } else {
+ uri = virURIParse(uri_in);
+ }
+
+ if (uri == NULL) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("unable to parse URI: %s"),
+ uri_in);
+ goto cleanup;
+ }
+
+ if (uri->server == NULL) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("missing host in migration URI: %s"),
+ uri_in);
+ goto cleanup;
+ } else {
+ hostname = uri->server;
+ }
+
+ if (uri->port == 0) {
+ if (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0)
+ goto cleanup;
+
+ } else {
+ port = uri->port;
+ }
+
+ if (virAsprintf(uri_out, "tcp://%s:%d", hostname, port) < 0)
+ goto cleanup;
+ }
+
+ snprintf(portstr, sizeof(portstr), "%d", port);
+
+ if (virNetSocketNewListenTCP(hostname, portstr, &socks, &nsocks) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Fail to create socket for incoming migration"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC(args) < 0)
+ goto cleanup;
+
+ args->conn = dconn;
+ args->vm = vm;
+ args->socks = socks;
+ args->nsocks = nsocks;
+
+ for (i = 0; i < nsocks; i++) {
+ if (virNetSocketSetBlocking(socks[i], true) < 0)
+ continue;
+
+ if (virNetSocketListen(socks[i], 1) < 0)
+ continue;
+
+ if (virNetSocketAddIOCallback(socks[i],
+ 0,
+ libxlDoMigrateReceive,
+ args,
+ NULL) < 0) {
+ continue;
+ }
+
+ virNetSocketUpdateIOCallback(socks[i], VIR_EVENT_HANDLE_READABLE);
+ nsocks_listen++;
+ }
+
+ if (!nsocks_listen)
+ goto cleanup;
+
+ ret = 0;
+ goto done;
+
+ cleanup:
+ for (i = 0; i < nsocks; i++) {
+ virNetSocketClose(socks[i]);
+ virObjectUnref(socks[i]);
+ }
+ VIR_FREE(socks);
+
+ done:
+ virURIFree(uri);
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+int
+libxlDomainMigrationPerform(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ const char *dom_xml ATTRIBUTE_UNUSED,
+ const char *dconnuri ATTRIBUTE_UNUSED,
+ const char *uri_str,
+ const char *dname ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ char *hostname = NULL;
+ unsigned short port = 0;
+ char portstr[100];
+ virURIPtr uri = NULL;
+ virNetSocketPtr sock;
+ int sockfd = -1;
+ int saved_errno = EINVAL;
+ int ret = -1;
+
+ /* parse dst host:port from uri */
+ uri = virURIParse(uri_str);
+ if (uri == NULL || uri->server == NULL || uri->port == 0)
+ goto cleanup;
+
+ hostname = uri->server;
+ port = uri->port;
+ snprintf(portstr, sizeof(portstr), "%d", port);
+
+ /* socket connect to dst host:port */
+ if (virNetSocketNewConnectTCP(hostname, portstr, &sock) < 0) {
+ virReportSystemError(saved_errno,
+ _("unable to connect to '%s:%s'"),
+ hostname, portstr);
+ goto cleanup;
+ }
+
+ if (virNetSocketSetBlocking(sock, true) < 0) {
+ virObjectUnref(sock);
+ goto cleanup;
+ }
+
+ sockfd = virNetSocketDupFD(sock, true);
+ virObjectUnref(sock);
+
+ /* suspend vm and send saved data to dst through socket fd */
+ virObjectUnlock(vm);
+ ret = libxlDoMigrateSend(driver, vm, flags, sockfd);
+ virObjectLock(vm);
+
+ cleanup:
+ /* If failure, terminate the job started in MigrationBegin */
+ if (ret == -1) {
+ if (libxlDomainObjEndJob(driver, vm))
+ virObjectUnlock(vm);
+ }
+ VIR_FORCE_CLOSE(sockfd);
+ virURIFree(uri);
+ return ret;
+}
+
+virDomainPtr
+libxlDomainMigrationFinish(virConnectPtr dconn,
+ virDomainObjPtr vm,
+ unsigned int flags,
+ int cancelled)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ libxlDomainObjPrivatePtr priv = vm->privateData;
+ virObjectEventPtr event = NULL;
+ virDomainPtr dom = NULL;
+
+ virPortAllocatorRelease(driver->migrationPorts, priv->migrationPort);
+ priv->migrationPort = 0;
+
+ if (cancelled)
+ goto cleanup;
+
+ if (!(flags & VIR_MIGRATE_PAUSED)) {
+ if (libxl_domain_unpause(priv->ctx, vm->def->id) != 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to unpause domain"));
+ goto cleanup;
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
+ VIR_DOMAIN_RUNNING_MIGRATED);
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_RESUMED,
+ VIR_DOMAIN_EVENT_RESUMED_MIGRATED);
+ } else {
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
+ }
+
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ goto cleanup;
+
+ dom = virGetDomain(dconn, vm->def->name, vm->def->uuid);
+
+ if (dom == NULL) {
+ libxl_domain_destroy(priv->ctx, vm->def->id, NULL);
+ libxlDomainCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
+ event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_FAILED);
+ libxlDomainEventQueue(driver, event);
+ }
+
+ cleanup:
+ if (event)
+ libxlDomainEventQueue(driver, event);
+ if (vm)
+ virObjectUnlock(vm);
+ virObjectUnref(cfg);
+ return dom;
+}
+
+int
+libxlDomainMigrationConfirm(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ unsigned int flags,
+ int cancelled)
+{
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ libxlDomainObjPrivatePtr priv = vm->privateData;
+ virObjectEventPtr event = NULL;
+ int ret = -1;
+
+ if (cancelled) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("migration failed, attempting to resume on source
host"));
+ if (libxl_domain_resume(priv->ctx, vm->def->id, 1, 0) == 0) {
+ ret = 0;
+ } else {
+ VIR_DEBUG("Unable to resume domain '%s' after failed
migration",
+ vm->def->name);
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED,
+ VIR_DOMAIN_PAUSED_MIGRATION);
+ event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED);
+ ignore_value(virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm));
+ }
+ goto cleanup;
+ }
+
+ libxl_domain_destroy(priv->ctx, vm->def->id, NULL);
+ libxlDomainCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_MIGRATED);
+ event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_MIGRATED);
+
+ VIR_DEBUG("Domain '%s' successfully migrated",
vm->def->name);
+
+ if (flags & VIR_MIGRATE_UNDEFINE_SOURCE)
+ virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm);
+
+ if (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE))
+ virDomainObjListRemove(driver->domains, vm);
+
+ ret = 0;
+
+ cleanup:
+ if (!libxlDomainObjEndJob(driver, vm))
+ vm = NULL;
+ if (event)
+ libxlDomainEventQueue(driver, event);
+ if (vm)
+ virObjectUnlock(vm);
+ virObjectUnref(cfg);
+ return ret;
+}
diff --git a/src/libxl/libxl_migration.h b/src/libxl/libxl_migration.h
new file mode 100644
index 0000000..63d8bdc
--- /dev/null
+++ b/src/libxl/libxl_migration.h
@@ -0,0 +1,78 @@
+/*
+ * libxl_migration.h: methods for handling migration with libxenlight
+ *
+ * Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Jim Fehlig <jfehlig(a)suse.com>
+ */
+
+#ifndef LIBXL_MIGRATION_H
+# define LIBXL_MIGRATION_H
+
+# include "libxl_conf.h"
+
+# define LIBXL_MIGRATION_FLAGS \
+ (VIR_MIGRATE_LIVE | \
+ VIR_MIGRATE_UNDEFINE_SOURCE | \
+ VIR_MIGRATE_PAUSED)
+
+/* All supported migration parameters and their types. */
+# define LIBXL_MIGRATION_PARAMETERS \
+ VIR_MIGRATE_PARAM_URI, VIR_TYPED_PARAM_STRING, \
+ VIR_MIGRATE_PARAM_DEST_NAME, VIR_TYPED_PARAM_STRING, \
+ VIR_MIGRATE_PARAM_DEST_XML, VIR_TYPED_PARAM_STRING, \
+ NULL
+
+char *
+libxlDomainMigrationBegin(virConnectPtr conn,
+ virDomainObjPtr vm,
+ const char *xmlin);
+
+virDomainDefPtr
+libxlDomainMigrationPrepareDef(libxlDriverPrivatePtr driver,
+ const char *dom_xml,
+ const char *dname);
+
+int
+libxlDomainMigrationPrepare(virConnectPtr dconn,
+ virDomainDefPtr def,
+ const char *uri_in,
+ char **uri_out);
+
+int
+libxlDomainMigrationPerform(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ const char *dom_xml,
+ const char *dconnuri,
+ const char *uri_str,
+ const char *dname,
+ unsigned int flags);
+
+virDomainPtr
+libxlDomainMigrationFinish(virConnectPtr dconn,
+ virDomainObjPtr vm,
+ unsigned int flags,
+ int cancelled);
+
+int
+libxlDomainMigrationConfirm(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ unsigned int flags,
+ int cancelled);
+
+#endif /* LIBXL_DRIVER_H */
--
1.8.1.4