[libvirt] [PATCH v3 2/2] libxl: add tunnelled migration support

Joao Martins joao.m.martins at oracle.com
Wed Feb 15 11:17:38 UTC 2017


From: Bob Liu <bob.liu at oracle.com>

Tunnelled migration doesn't require any extra network connections
beside the libvirt daemon.  It's capable of strong encryption and the
default option of openstack-nova.

This patch adds the tunnelled migration(Tunnel3params) support to
libxl.  On the source side, the data flow is:

 * libxlDoMigrateSend() -> pipe libxlTunnel3MigrationFunc() polls pipe
 * out and then write to dest stream.

While on the destination side:
 * Stream -> pipe -> 'recvfd of libxlDomainStartRestore'

The usage is the same as p2p migration, execpt adding one extra
'--tunnelled' to the libvirt p2p migration command.

Signed-off-by: Bob Liu <bob.liu at oracle.com>
Signed-off-by: Joao Martins <joao.m.martins at oracle.com>
---
v3:
 * virStream{Send,Finish} and VIR_ALLOC_N already report errors,
 hence removing the virReportError calls from its error paths.
 * Revert top-level migration APIs to initial v1 approach.
v2:
 ( Comments from Jim )
 * Adjust common preparetunnel function reflecting v1 suggestions
   - Using common top-level Prepare function by adding is_tunnel variable
   - Using libxl_migration PrepareAny added in patch 1
   - virHook support in PrepareTunnel3
 * Move virThreadPtr from libxlTunnelMigrationThread into libxlTunnelControl
 * Rename function local variable from tmThreadPtr to arg
 * Remove redundant assignment "tmThreadPtr = &tc->tmThread;" always being not
 null.
 * Adjust debug message to "poll returned 0" as opposed to "poll got
 timeout" as the latter might not always be the case.
---
 src/libxl/libxl_driver.c    |  58 +++++++++-
 src/libxl/libxl_migration.c | 275 +++++++++++++++++++++++++++++++++++++++++---
 src/libxl/libxl_migration.h |   9 ++
 3 files changed, 325 insertions(+), 17 deletions(-)

diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index 74cb05a..e5b8f48 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -5934,6 +5934,61 @@ libxlDomainMigrateBegin3Params(virDomainPtr domain,
 }
 
 static int
+libxlDomainMigratePrepareTunnel3Params(virConnectPtr dconn,
+                                       virStreamPtr st,
+                                       virTypedParameterPtr params,
+                                       int nparams,
+                                       const char *cookiein,
+                                       int cookieinlen,
+                                       char **cookieout ATTRIBUTE_UNUSED,
+                                       int *cookieoutlen ATTRIBUTE_UNUSED,
+                                       unsigned int flags)
+{
+    libxlDriverPrivatePtr driver = dconn->privateData;
+    virDomainDefPtr def = NULL;
+    const char *dom_xml = NULL;
+    const char *dname = NULL;
+    const char *uri_in = NULL;
+
+#ifdef LIBXL_HAVE_NO_SUSPEND_RESUME
+    virReportUnsupportedError();
+    return -1;
+#endif
+
+    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 (virDomainMigratePrepareTunnel3ParamsEnsureACL(dconn, def) < 0)
+        goto error;
+
+    if (libxlDomainMigrationPrepareTunnel3(dconn, st, &def, cookiein,
+                                           cookieinlen, flags) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    virDomainDefFree(def);
+    return -1;
+}
+
+static int
 libxlDomainMigratePrepare3Params(virConnectPtr dconn,
                                  virTypedParameterPtr params,
                                  int nparams,
@@ -6033,7 +6088,7 @@ libxlDomainMigratePerform3Params(virDomainPtr dom,
     if (virDomainMigratePerform3ParamsEnsureACL(dom->conn, vm->def) < 0)
         goto cleanup;
 
-    if (flags & VIR_MIGRATE_PEER2PEER) {
+    if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) {
         if (libxlDomainMigrationPerformP2P(driver, vm, dom->conn, dom_xml,
                                            dconnuri, uri, dname, flags) < 0)
             goto cleanup;
@@ -6518,6 +6573,7 @@ static virHypervisorDriver libxlHypervisorDriver = {
     .nodeDeviceReset = libxlNodeDeviceReset, /* 1.2.3 */
     .domainMigrateBegin3Params = libxlDomainMigrateBegin3Params, /* 1.2.6 */
     .domainMigratePrepare3Params = libxlDomainMigratePrepare3Params, /* 1.2.6 */
+    .domainMigratePrepareTunnel3Params = libxlDomainMigratePrepareTunnel3Params, /* 3.1.0 */
     .domainMigratePerform3Params = libxlDomainMigratePerform3Params, /* 1.2.6 */
     .domainMigrateFinish3Params = libxlDomainMigrateFinish3Params, /* 1.2.6 */
     .domainMigrateConfirm3Params = libxlDomainMigrateConfirm3Params, /* 1.2.6 */
diff --git a/src/libxl/libxl_migration.c b/src/libxl/libxl_migration.c
index 7beabd2..6bd8983 100644
--- a/src/libxl/libxl_migration.c
+++ b/src/libxl/libxl_migration.c
@@ -44,6 +44,7 @@
 #include "libxl_migration.h"
 #include "locking/domain_lock.h"
 #include "virtypedparam.h"
+#include "fdstream.h"
 
 #define VIR_FROM_THIS VIR_FROM_LIBXL
 
@@ -554,6 +555,95 @@ libxlDomainMigrationPrepareAny(virConnectPtr dconn,
 }
 
 int
+libxlDomainMigrationPrepareTunnel3(virConnectPtr dconn,
+                                   virStreamPtr st,
+                                   virDomainDefPtr *def,
+                                   const char *cookiein,
+                                   int cookieinlen,
+                                   unsigned int flags)
+{
+    libxlMigrationCookiePtr mig = NULL;
+    libxlDriverPrivatePtr driver = dconn->privateData;
+    virDomainObjPtr vm = NULL;
+    libxlMigrationDstArgs *args = NULL;
+    virThread thread;
+    bool taint_hook = false;
+    libxlDomainObjPrivatePtr priv = NULL;
+    char *xmlout = NULL;
+    int dataFD[2] = { -1, -1 };
+    int ret = -1;
+
+    if (libxlDomainMigrationPrepareAny(dconn, def, cookiein, cookieinlen,
+                                       &mig, &xmlout, &taint_hook) < 0)
+        goto error;
+
+    if (!(vm = virDomainObjListAdd(driver->domains, *def,
+                                   driver->xmlopt,
+                                   VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+                                   VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+                                   NULL)))
+        goto error;
+
+    *def = NULL;
+    priv = vm->privateData;
+
+    if (taint_hook) {
+        /* Domain XML has been altered by a hook script. */
+        priv->hookRun = true;
+    }
+
+    /*
+     * The data flow of tunnel3 migration in the dest side:
+     * stream -> pipe -> recvfd of libxlDomainStartRestore
+     */
+    if (pipe(dataFD) < 0)
+        goto error;
+
+    /* Stream data will be written to pipeIn */
+    if (virFDStreamOpen(st, dataFD[1]) < 0)
+        goto error;
+    dataFD[1] = -1; /* 'st' owns the FD now & will close it */
+
+    if (libxlMigrationDstArgsInitialize() < 0)
+        goto error;
+
+    if (!(args = virObjectNew(libxlMigrationDstArgsClass)))
+        goto error;
+
+    args->conn = dconn;
+    args->vm = vm;
+    args->flags = flags;
+    args->migcookie = mig;
+    /* Receive from pipeOut */
+    args->recvfd = dataFD[0];
+    args->nsocks = 0;
+    if (virThreadCreate(&thread, false, libxlDoMigrateReceive, args) < 0) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("Failed to create thread for receiving migration data"));
+        goto error;
+    }
+
+    ret = 0;
+    goto done;
+
+ error:
+    VIR_FORCE_CLOSE(dataFD[1]);
+    VIR_FORCE_CLOSE(dataFD[0]);
+    virObjectUnref(args);
+    /* Remove virDomainObj from domain list */
+    if (vm) {
+        virDomainObjListRemove(driver->domains, vm);
+        vm = NULL;
+    }
+
+ done:
+    if (vm)
+        virObjectUnlock(vm);
+
+    return ret;
+}
+
+int
 libxlDomainMigrationPrepare(virConnectPtr dconn,
                             virDomainDefPtr *def,
                             const char *uri_in,
@@ -734,9 +824,143 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
     return ret;
 }
 
-/* This function is a simplification of virDomainMigrateVersion3Full
- * excluding tunnel support and restricting it to migration v3
- * with params since it was the first to be introduced in libxl.
+typedef struct _libxlTunnelMigrationThread libxlTunnelMigrationThread;
+struct _libxlTunnelMigrationThread {
+    virStreamPtr st;
+    int srcFD;
+};
+#define TUNNEL_SEND_BUF_SIZE 65536
+
+/*
+ * The data flow of tunnel3 migration in the src side:
+ * libxlDoMigrateSend() -> pipe
+ * libxlTunnel3MigrationFunc() polls pipe out and then write to dest stream.
+ */
+static void libxlTunnel3MigrationFunc(void *arg)
+{
+    libxlTunnelMigrationThread *data = (libxlTunnelMigrationThread *)arg;
+    char *buffer = NULL;
+    struct pollfd fds[1];
+    int timeout = -1;
+
+    if (VIR_ALLOC_N(buffer, TUNNEL_SEND_BUF_SIZE) < 0)
+        return;
+
+    fds[0].fd = data->srcFD;
+    for (;;) {
+        int ret;
+
+        fds[0].events = POLLIN;
+        fds[0].revents = 0;
+        ret = poll(fds, ARRAY_CARDINALITY(fds), timeout);
+        if (ret < 0) {
+            if (errno == EAGAIN || errno == EINTR)
+                continue;
+            virReportError(errno, "%s",
+                           _("poll failed in libxlTunnel3MigrationFunc"));
+            goto cleanup;
+        }
+
+        if (ret == 0) {
+            VIR_DEBUG("poll returned 0");
+            break;
+        }
+
+        if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
+            int nbytes;
+
+            nbytes = read(data->srcFD, buffer, TUNNEL_SEND_BUF_SIZE);
+            if (nbytes > 0) {
+                /* Write to dest stream */
+                if (virStreamSend(data->st, buffer, nbytes) < 0) {
+                    virStreamAbort(data->st);
+                    goto cleanup;
+                }
+            } else if (nbytes < 0) {
+                virReportError(errno, "%s",
+                               _("tunnelled migration failed to read from xen side"));
+                    virStreamAbort(data->st);
+                    goto cleanup;
+            } else {
+                /* EOF; transferred all data */
+                break;
+            }
+        }
+    }
+
+    ignore_value(virStreamFinish(data->st));
+
+ cleanup:
+    VIR_FREE(buffer);
+
+    return;
+}
+
+struct libxlTunnelControl {
+    libxlTunnelMigrationThread tmThread;
+    virThread thread;
+    int dataFD[2];
+};
+
+static int
+libxlMigrationStartTunnel(libxlDriverPrivatePtr driver,
+                          virDomainObjPtr vm,
+                          unsigned long flags,
+                          virStreamPtr st,
+                          struct libxlTunnelControl *tc)
+{
+    libxlTunnelMigrationThread *arg = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(tc) < 0)
+        goto out;
+
+    tc->dataFD[0] = -1;
+    tc->dataFD[1] = -1;
+    if (pipe(tc->dataFD) < 0) {
+        virReportError(errno, "%s", _("Unable to make pipes"));
+        goto out;
+    }
+
+    arg = &tc->tmThread;
+    /* Read from pipe */
+    arg->srcFD = tc->dataFD[0];
+    /* Write to dest stream */
+    arg->st = st;
+    if (virThreadCreate(&tc->thread, true,
+                        libxlTunnel3MigrationFunc, arg) < 0) {
+        virReportError(errno, "%s",
+                       _("Unable to create tunnel migration thread"));
+        goto out;
+    }
+
+    virObjectUnlock(vm);
+    /* Send data to pipe */
+    ret = libxlDoMigrateSend(driver, vm, flags, tc->dataFD[1]);
+    virObjectLock(vm);
+
+ out:
+    /* libxlMigrationStopTunnel will be called in libxlDoMigrateP2P to free
+     * all resources for us. */
+    return ret;
+}
+
+static void libxlMigrationStopTunnel(struct libxlTunnelControl *tc)
+{
+    if (!tc)
+        return;
+
+    virThreadCancel(&tc->thread);
+    virThreadJoin(&tc->thread);
+
+    VIR_FORCE_CLOSE(tc->dataFD[0]);
+    VIR_FORCE_CLOSE(tc->dataFD[1]);
+    VIR_FREE(tc);
+}
+
+/* This function is a simplification of virDomainMigrateVersion3Full and
+ * restricting it to migration v3 with params since it was the first to be
+ * introduced in libxl.
  */
 static int
 libxlDoMigrateP2P(libxlDriverPrivatePtr driver,
@@ -761,6 +985,9 @@ libxlDoMigrateP2P(libxlDriverPrivatePtr driver,
     bool cancelled = true;
     virErrorPtr orig_err = NULL;
     int ret = -1;
+    /* For tunnel migration */
+    virStreamPtr st = NULL;
+    struct libxlTunnelControl *tc = NULL;
 
     dom_xml = libxlDomainMigrationBegin(sconn, vm, xmlin,
                                         &cookieout, &cookieoutlen);
@@ -788,29 +1015,40 @@ libxlDoMigrateP2P(libxlDriverPrivatePtr driver,
 
     VIR_DEBUG("Prepare3");
     virObjectUnlock(vm);
-    ret = dconn->driver->domainMigratePrepare3Params
-        (dconn, params, nparams, cookieout, cookieoutlen, NULL, NULL, &uri_out, destflags);
+    if (flags & VIR_MIGRATE_TUNNELLED) {
+        if (!(st = virStreamNew(dconn, 0)))
+            goto cleanup;
+        ret = dconn->driver->domainMigratePrepareTunnel3Params
+            (dconn, st, params, nparams, cookieout, cookieoutlen, NULL, NULL, destflags);
+    } else {
+        ret = dconn->driver->domainMigratePrepare3Params
+            (dconn, params, nparams, cookieout, cookieoutlen, NULL, NULL, &uri_out, destflags);
+    }
     virObjectLock(vm);
 
     if (ret == -1)
         goto cleanup;
 
-    if (uri_out) {
-        if (virTypedParamsReplaceString(&params, &nparams,
-                                        VIR_MIGRATE_PARAM_URI, uri_out) < 0) {
-            orig_err = virSaveLastError();
+    if (!(flags & VIR_MIGRATE_TUNNELLED)) {
+        if (uri_out) {
+            if (virTypedParamsReplaceString(&params, &nparams,
+                                            VIR_MIGRATE_PARAM_URI, uri_out) < 0) {
+                orig_err = virSaveLastError();
+                goto finish;
+            }
+        } else {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("domainMigratePrepare3 did not set uri"));
             goto finish;
         }
-    } else {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("domainMigratePrepare3 did not set uri"));
-        goto finish;
     }
 
     VIR_DEBUG("Perform3 uri=%s", NULLSTR(uri_out));
-    ret = libxlDomainMigrationPerform(driver, vm, NULL, NULL,
-                                      uri_out, NULL, flags);
-
+    if (flags & VIR_MIGRATE_TUNNELLED)
+        ret = libxlMigrationStartTunnel(driver, vm, flags, st, tc);
+    else
+        ret = libxlDomainMigrationPerform(driver, vm, NULL, NULL,
+                                          uri_out, NULL, flags);
     if (ret < 0)
         orig_err = virSaveLastError();
 
@@ -848,6 +1086,11 @@ libxlDoMigrateP2P(libxlDriverPrivatePtr driver,
                  vm->def->name);
 
  cleanup:
+    if (flags & VIR_MIGRATE_TUNNELLED) {
+        libxlMigrationStopTunnel(tc);
+        virObjectUnref(st);
+    }
+
     if (ddomain) {
         virObjectUnref(ddomain);
         ret = 0;
diff --git a/src/libxl/libxl_migration.h b/src/libxl/libxl_migration.h
index 8a074a0..fcea558 100644
--- a/src/libxl/libxl_migration.h
+++ b/src/libxl/libxl_migration.h
@@ -29,6 +29,7 @@
 # define LIBXL_MIGRATION_FLAGS                  \
     (VIR_MIGRATE_LIVE |                         \
      VIR_MIGRATE_PEER2PEER |                    \
+     VIR_MIGRATE_TUNNELLED |                    \
      VIR_MIGRATE_PERSIST_DEST |                 \
      VIR_MIGRATE_UNDEFINE_SOURCE |              \
      VIR_MIGRATE_PAUSED)
@@ -53,6 +54,14 @@ libxlDomainMigrationPrepareDef(libxlDriverPrivatePtr driver,
                                const char *dname);
 
 int
+libxlDomainMigrationPrepareTunnel3(virConnectPtr dconn,
+                                   virStreamPtr st,
+                                   virDomainDefPtr *def,
+                                   const char *cookiein,
+                                   int cookieinlen,
+                                   unsigned int flags);
+
+int
 libxlDomainMigrationPrepare(virConnectPtr dconn,
                             virDomainDefPtr *def,
                             const char *uri_in,
-- 
2.1.4




More information about the libvir-list mailing list