---
src/libxl/libxl_conf.h | 4 +
src/libxl/libxl_driver.c | 641 ++++++++++++++++++++++++++++++++++++++++++++++
src/libxl/libxl_driver.h | 5 +
3 files changed, 650 insertions(+), 0 deletions(-)
diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h
index 8ba0ee4..2041cc2 100644
--- a/src/libxl/libxl_conf.h
+++ b/src/libxl/libxl_conf.h
@@ -41,6 +41,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"
@@ -109,6 +112,7 @@ struct _libxlDriverPrivate {
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr reservedVNCPorts;
+ virPortAllocatorPtr reservedMigPorts;
/* Immutable pointer, lockless APIs*/
virSysinfoDefPtr hostsysinfo;
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index e2a6d44..93b7153 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -32,6 +32,12 @@
#include <libxl_utils.h>
#include <fcntl.h>
#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
#include "internal.h"
#include "virlog.h"
@@ -52,6 +58,7 @@
#include "virsysinfo.h"
#include "viraccessapicheck.h"
#include "viratomic.h"
+#include "rpc/virnetsocket.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
@@ -69,6 +76,20 @@
static libxlDriverPrivatePtr libxl_driver = NULL;
+typedef struct _libxlMigrateReceiveArgs {
+ virConnectPtr conn;
+ virDomainObjPtr vm;
+
+ /* for freeing listen sockets */
+ virNetSocketPtr *socks;
+ size_t nsocks;
+} libxlMigrateReceiveArgs;
+
+static const char libxlMigrateReceiverReady[]=
+ "libvirt libxl migration receiver ready, send binary domain data";
+static const char libxlMigrateReceiverFinish[]=
+ "domain received, ready to unpause";
+
/* Function declarations */
static int
libxlDomainManagedSaveLoad(virDomainObjPtr vm,
@@ -836,6 +857,12 @@ libxlStateInitialize(bool privileged,
LIBXL_VNC_PORT_MAX)))
goto error;
+ /* Allocate bitmap for migration port reservation */
+ if (!(libxl_driver->reservedMigPorts =
+ virPortAllocatorNew(LIBXL_MIGRATION_PORT_MIN,
+ LIBXL_MIGRATION_PORT_MAX)))
+ goto error;
+
if (!(libxl_driver->domains = virDomainObjListNew()))
goto error;
@@ -4175,11 +4202,620 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature)
switch (feature) {
case VIR_DRV_FEATURE_TYPED_PARAM_STRING:
return 1;
+ case VIR_DRV_FEATURE_MIGRATION_V3:
+ return 1;
default:
return 0;
}
}
+static int
+libxlCheckMessageBanner(int fd, const char *banner, int banner_sz)
+{
+ char buf[banner_sz];
+ int ret = 0;
+
+ do {
+ ret = saferead(fd, buf, banner_sz);
+ } while (ret == -1 && errno == EAGAIN);
+
+ if (ret != banner_sz || memcmp(buf, banner, banner_sz)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *
+libxlDomainMigrateBegin3(virDomainPtr domain,
+ const char *xmlin,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ unsigned long flags,
+ const char *dname ATTRIBUTE_UNUSED,
+ unsigned long resource ATTRIBUTE_UNUSED)
+{
+ libxlDriverPrivatePtr driver = domain->conn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainObjPtr vm;
+ virDomainDefPtr def = NULL;
+ char *xml = NULL;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
+
+ vm = virDomainObjListFindByUUID(driver->domains, domain->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ goto cleanup;
+ }
+
+ if (virDomainMigrateBegin3EnsureACL(domain->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (xmlin) {
+ if (!(def = virDomainDefParseString(xmlin, cfg->caps,
+ driver->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_XEN,
+ VIR_DOMAIN_XML_INACTIVE)))
+ goto cleanup;
+
+ xml = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE);
+ } else {
+ xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_SECURE);
+ }
+
+cleanup:
+ virDomainDefFree(def);
+ if (vm)
+ virObjectUnlock(vm);
+ virObjectUnref(cfg);
+ return xml;
+}
+
+static void
+doMigrateReceive(virNetSocketPtr sock,
+ int events ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ libxlMigrateReceiveArgs *data = opaque;
+ virConnectPtr conn = data->conn;
+ virDomainObjPtr vm = data->vm;
+ virNetSocketPtr *socks = data->socks;
+ size_t nsocks = data->nsocks;
+ libxlDriverPrivatePtr driver = conn->privateData;
+ virNetSocketPtr client_sock;
+ int recv_fd;
+ int len;
+ 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\n");
+ recv_fd = virNetSocketDupFD(client_sock, true);
+ virObjectUnref(client_sock);
+
+ len = sizeof(libxlMigrateReceiverReady);
+ if (safewrite(recv_fd, libxlMigrateReceiverReady, len) != len) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to write libxlMigrateReceiverReady"));
+ goto cleanup;
+ }
+
+ virObjectLock(vm);
+ ret = libxlVmStart(driver, vm, false, recv_fd);
+ virObjectUnlock(vm);
+
+ if (ret < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to restore domain with libxenlight"));
+ if (!vm->persistent) {
+ virDomainObjListRemove(driver->domains, vm);
+ vm = NULL;
+ }
+ goto cleanup;
+ }
+
+ len = sizeof(libxlMigrateReceiverFinish);
+ if (safewrite(recv_fd, libxlMigrateReceiverFinish, len) != len) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to write libxlMigrateReceiverFinish"));
+ }
+
+ /* Remove all listen socks from event handler, and close them. */
+ if (nsocks) {
+ for (i = 0; i < nsocks; i++) {
+ virNetSocketUpdateIOCallback(socks[i], 0);
+ virNetSocketRemoveIOCallback(socks[i]);
+ virNetSocketClose(socks[i]);
+ virObjectUnref(socks[i]);
+ }
+ VIR_FREE(socks);
+ }
+
+cleanup:
+ VIR_FORCE_CLOSE(recv_fd);
+ VIR_FREE(opaque);
+ return;
+}
+
+static int
+doMigrateSend(libxlDriverPrivatePtr driver,
+ virDomainObjPtr vm,
+ unsigned long flags,
+ int sockfd)
+{
+ libxlDomainObjPrivatePtr priv;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainEventPtr event = NULL;
+ int live = 0;
+ int ret = -1;
+
+ if (flags & VIR_MIGRATE_LIVE)
+ live = LIBXL_SUSPEND_LIVE;
+
+ priv = vm->privateData;
+
+ /* read fixed message from dest (ready to receive) */
+ if (libxlCheckMessageBanner(sockfd, libxlMigrateReceiverReady,
+ sizeof(libxlMigrateReceiverReady)))
+ goto cleanup;
+
+ if (libxl_domain_suspend(priv->ctx, vm->def->id, sockfd, live, NULL) != 0)
{
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to save domain '%d' with
libxenlight"),
+ vm->def->id);
+ goto cleanup;
+ }
+
+ /* read fixed message from dest (receive completed) */
+ if (libxlCheckMessageBanner(sockfd, libxlMigrateReceiverFinish,
+ sizeof(libxlMigrateReceiverFinish))) {
+ 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 = virDomainEventNewFromObj(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 int
+libxlDomainMigratePrepare3(virConnectPtr dconn,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ const char *uri_in,
+ char **uri_out,
+ unsigned long flags,
+ const char *dname,
+ unsigned long resource ATTRIBUTE_UNUSED,
+ const char *dom_xml)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainDefPtr def = NULL;
+ 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;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+
+ libxlDriverLock(driver);
+ if (!dom_xml) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("no domain XML passed"));
+ goto cleanup;
+ }
+ def = virDomainDefParseString(dom_xml, cfg->caps, driver->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_XEN,
+ VIR_DOMAIN_XML_INACTIVE);
+
+ /* Target domain name, maybe renamed. */
+ if (dname) {
+ if (VIR_STRDUP(def->name, dname) < 0)
+ goto cleanup;
+ }
+
+ if (!(vm = virDomainObjListAdd(driver->domains, def,
+ driver->xmlopt,
+ VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+ VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+ NULL)))
+ goto cleanup;
+
+ def = NULL;
+
+ if (virDomainMigratePrepare3EnsureACL(dconn, vm->def) < 0)
+ goto cleanup;
+
+ /* Create socket connection to receive migration data */
+ if (!uri_in) {
+ hostname = virGetHostname();
+ if (hostname == NULL)
+ goto cleanup;
+
+ if (virPortAllocatorAcquire(driver->reservedMigPorts, &port) < 0)
+ goto cleanup;
+
+ if (port == 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to find an unused migrate
port"));
+ goto cleanup;
+ }
+
+ if (virAsprintf(uri_out, "tcp://%s:%d", hostname, port) < 0)
+ goto cleanup;
+ } else {
+ if (!strstr(uri_in, "//")) {
+ /* 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->reservedMigPorts, &port) < 0)
+ goto cleanup;
+
+ if (port == 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to find an unused migrate port"));
+ 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,
+ doMigrateReceive,
+ args,
+ NULL) < 0) {
+ continue;
+ }
+
+ virNetSocketUpdateIOCallback(socks[i], VIR_EVENT_HANDLE_READABLE);
+ nsocks_listen ++;
+ }
+
+ if (!nsocks_listen)
+ goto cleanup;
+
+ ret = 0;
+ goto end;
+
+cleanup:
+ if (nsocks) {
+ for (i = 0 ; i < nsocks ; i++) {
+ virNetSocketClose(socks[i]);
+ virObjectUnref(socks[i]);
+ }
+ VIR_FREE(socks);
+ }
+
+end:
+ virURIFree(uri);
+ if (vm)
+ virObjectUnlock(vm);
+ virObjectUnref(cfg);
+ libxlDriverUnlock(driver);
+ return ret;
+}
+
+static int
+libxlDomainMigratePerform3(virDomainPtr dom,
+ const char *xmlin ATTRIBUTE_UNUSED,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ const char *dconnuri ATTRIBUTE_UNUSED,
+ const char *uri,
+ unsigned long flags,
+ const char *dname ATTRIBUTE_UNUSED,
+ unsigned long resource ATTRIBUTE_UNUSED)
+{
+ libxlDriverPrivatePtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ char *hostname = NULL;
+ unsigned short port = 0;
+ char portstr[100];
+ virURIPtr uri_p = NULL;
+ virNetSocketPtr sock;
+ int sockfd = -1;
+ int saved_errno = EINVAL;
+ int ret = -1;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+
+ vm = virDomainObjListFindByUUID(driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ if (virDomainMigratePerform3EnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ /* parse dst host:port from uri */
+ uri_p = virURIParse(uri);
+ if (uri_p == NULL || uri_p->server == NULL || uri_p->port == 0)
+ goto cleanup;
+
+ hostname = uri_p->server;
+ port = uri_p->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 */
+ ret = doMigrateSend(driver, vm, flags, sockfd);
+
+cleanup:
+ VIR_FORCE_CLOSE(sockfd);
+ virURIFree(uri_p);
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static virDomainPtr
+libxlDomainMigrateFinish3(virConnectPtr dconn,
+ const char *dname,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ char **cookieout ATTRIBUTE_UNUSED,
+ int *cookieoutlen ATTRIBUTE_UNUSED,
+ const char *dconnuri ATTRIBUTE_UNUSED,
+ const char *uri,
+ unsigned long flags,
+ int cancelled)
+{
+ libxlDriverPrivatePtr driver = dconn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ unsigned short port = 0;
+ virURIPtr uri_p = NULL;
+ virDomainObjPtr vm = NULL;
+ virDomainPtr dom = NULL;
+ libxlDomainObjPrivatePtr priv;
+ virDomainEventPtr event = NULL;
+ int rc;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, NULL);
+
+ uri_p = virURIParse(uri);
+ if (uri_p == NULL || uri_p->port == 0)
+ VIR_DEBUG("Fail to parse port from URI");
+ port = uri_p->port;
+ if (LIBXL_MIGRATION_PORT_MIN <= port && port <
LIBXL_MIGRATION_PORT_MAX) {
+ if (virPortAllocatorRelease(driver->reservedMigPorts, port) < 0)
+ VIR_DEBUG("Could not mark port %d as unused", port);
+ }
+
+ vm = virDomainObjListFindByName(driver->domains, dname);
+ if (!vm)
+ goto cleanup;
+
+ if (virDomainMigrateFinish3EnsureACL(dconn, vm->def) < 0)
+ goto cleanup;
+
+ if (!cancelled) {
+ if (!(flags & VIR_MIGRATE_PAUSED)) {
+ priv = vm->privateData;
+ rc = libxl_domain_unpause(priv->ctx, vm->def->id);
+ if (rc) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Failed to unpause domain"));
+ goto error;
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ goto error;
+ }
+
+ dom = virGetDomain(dconn, vm->def->name, vm->def->uuid);
+ goto cleanup;
+ }
+
+error:
+ if (libxlVmReap(driver, vm, VIR_DOMAIN_SHUTOFF_SAVED)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to destroy domain '%d'"),
vm->def->id);
+ goto cleanup;
+ }
+ event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SAVED);
+ if (!vm->persistent) {
+ virDomainObjListRemove(driver->domains, vm);
+ vm = NULL;
+ }
+
+cleanup:
+ virURIFree(uri_p);
+ if (vm)
+ virObjectUnlock(vm);
+ if (event)
+ libxlDomainEventQueue(driver, event);
+ virObjectUnref(cfg);
+ return dom;
+}
+
+static int
+libxlDomainMigrateConfirm3(virDomainPtr domain,
+ const char *cookiein ATTRIBUTE_UNUSED,
+ int cookieinlen ATTRIBUTE_UNUSED,
+ unsigned long flags,
+ int cancelled)
+{
+ libxlDriverPrivatePtr driver = domain->conn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
+ virDomainObjPtr vm;
+ libxlDomainObjPrivatePtr priv;
+ virDomainEventPtr event = NULL;
+ int ret = -1;
+
+ virCheckFlags(LIBXL_MIGRATION_FLAGS, -1);
+
+ vm = virDomainObjListFindByUUID(driver->domains, domain->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ if (virDomainMigrateConfirm3EnsureACL(domain->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (cancelled) {
+ priv = vm->privateData;
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("migration failed, try to resume on our end"));
+ if (!libxl_domain_resume(priv->ctx, vm->def->id, 0, 0)) {
+ ret = 0;
+ } else {
+ VIR_DEBUG("Failed to resume domain '%d' with libxenlight",
+ vm->def->id);
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_MIGRATION);
+ event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED);
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ goto cleanup;
+ }
+
+ goto cleanup;
+ }
+
+ if (libxlVmReap(driver, vm, VIR_DOMAIN_SHUTOFF_SAVED)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to destroy domain '%d'"),
vm->def->id);
+ goto cleanup;
+ }
+
+ event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SAVED);
+
+ if (flags & VIR_MIGRATE_UNDEFINE_SOURCE)
+ virDomainDeleteConfig(cfg->configDir, cfg->autostartDir, vm);
+
+ if (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE)) {
+ virDomainObjListRemove(driver->domains, vm);
+ vm = NULL;
+ }
+
+ VIR_DEBUG("Migration successful.\n");
+ ret = 0;
+
+cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ if (event)
+ libxlDomainEventQueue(driver, event);
+ virObjectUnref(cfg);
+ return ret;
+}
+
static virDriver libxlDriver = {
.no = VIR_DRV_LIBXL,
@@ -4249,6 +4885,11 @@ static virDriver libxlDriver = {
#ifdef LIBXL_HAVE_DOMAIN_NODEAFFINITY
.domainGetNumaParameters = libxlDomainGetNumaParameters, /* 1.1.1 */
#endif
+ .domainMigrateBegin3 = libxlDomainMigrateBegin3, /* 1.1.3 */
+ .domainMigratePrepare3 = libxlDomainMigratePrepare3, /* 1.1.3 */
+ .domainMigratePerform3 = libxlDomainMigratePerform3, /* 1.1.3 */
+ .domainMigrateFinish3 = libxlDomainMigrateFinish3, /* 1.1.3 */
+ .domainMigrateConfirm3 = libxlDomainMigrateConfirm3, /* 1.1.3 */
.nodeGetFreeMemory = libxlNodeGetFreeMemory, /* 0.9.0 */
.nodeGetCellsFreeMemory = libxlNodeGetCellsFreeMemory, /* 1.1.1 */
.connectDomainEventRegister = libxlConnectDomainEventRegister, /* 0.9.0 */
diff --git a/src/libxl/libxl_driver.h b/src/libxl/libxl_driver.h
index a33d60c..25ac2b8 100644
--- a/src/libxl/libxl_driver.h
+++ b/src/libxl/libxl_driver.h
@@ -24,6 +24,11 @@
#ifndef LIBXL_DRIVER_H
# define LIBXL_DRIVER_H
+# define LIBXL_MIGRATION_FLAGS \
+ (VIR_MIGRATE_LIVE | \
+ VIR_MIGRATE_UNDEFINE_SOURCE | \
+ VIR_MIGRATE_PAUSED)
+
int libxlRegister(void);
#endif /* LIBXL_DRIVER_H */
--
1.6.0.2