---
 src/ch/ch_conf.h                  |   4 +
 src/ch/ch_domain.h                |   2 +
 src/ch/ch_driver.c                | 362 +++++++++++++++++++++++++++++-
 src/ch/ch_monitor.c               | 156 +++++++++++++
 src/ch/ch_monitor.h               |   8 +
 src/ch/ch_process.c               | 136 ++++++++++-
 src/ch/ch_process.h               |   6 +
 src/hypervisor/domain_interface.c |   1 +
 src/libvirt-domain.c              |  15 +-
 9 files changed, 680 insertions(+), 10 deletions(-)

diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
index b08573476e..40d639eb2a 100644
--- a/src/ch/ch_conf.h
+++ b/src/ch/ch_conf.h
@@ -26,6 +26,7 @@
 #include "ch_capabilities.h"
 #include "virebtables.h"
 #include "object_event.h"
+#include "virportallocator.h"
 
 #define CH_DRIVER_NAME "CH"
 #define CH_CMD "cloud-hypervisor"
@@ -90,6 +91,9 @@ struct _virCHDriver
 
     /* Immutable pointer, self-locking APIs */
     virObjectEventState *domainEventState;
+
+    /* Immutable pointer, immutable object */
+    virPortAllocatorRange *migrationPorts;
 };
 
 #define CH_SAVE_MAGIC "libvirt-xml\n \0 \r"
diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h
index 69a657f6af..f9ad18b518 100644
--- a/src/ch/ch_domain.h
+++ b/src/ch/ch_domain.h
@@ -25,6 +25,7 @@
 #include "virchrdev.h"
 #include "vircgroup.h"
 #include "virdomainjob.h"
+#include "virthread.h"
 
 
 typedef struct _virCHDomainObjPrivate virCHDomainObjPrivate;
@@ -32,6 +33,7 @@ struct _virCHDomainObjPrivate {
     virChrdevs *chrdevs;
     virCHDriver *driver;
     virCHMonitor *monitor;
+    virThread *migrationDstReceiveThr;
     char *machineName;
     virBitmap *autoCpuset;
     virBitmap *autoNodeset;
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index 3bdcf66ebd..c53607251b 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -29,20 +29,25 @@
 #include "ch_process.h"
 #include "domain_cgroup.h"
 #include "domain_event.h"
+#include "domain_interface.h"
 #include "datatypes.h"
 #include "driver.h"
+#include "viralloc.h"
 #include "viraccessapicheck.h"
 #include "virchrdev.h"
 #include "virerror.h"
 #include "virlog.h"
 #include "virobject.h"
 #include "virfile.h"
+#include "virtime.h"
 #include "virtypedparam.h"
 #include "virutil.h"
 #include "viruuid.h"
 #include "virnuma.h"
 #include "virhostmem.h"
 
+#include "util/virportallocator.h"
+
 #define VIR_FROM_THIS VIR_FROM_CH
 
 VIR_LOG_INIT("ch.ch_driver");
@@ -1453,6 +1458,13 @@ chStateInitialize(bool privileged,
     if (!(ch_driver->domainEventState = virObjectEventStateNew()))
         goto cleanup;
 
+        /* Allocate bitmap for migration port reservation */
+    if (!(ch_driver->migrationPorts =
+          virPortAllocatorRangeNew(_("migration"),
+                                   49152,
+                                   49216)))
+        goto cleanup;
+
     if ((rv = chExtractVersion(ch_driver)) < 0) {
         if (rv == -2)
             ret = VIR_DRV_STATE_INIT_SKIPPED;
@@ -1495,8 +1507,9 @@ chConnectSupportsFeature(virConnectPtr conn,
                            _("Global feature %1$d should have already been 
handled"),
                            feature);
             return -1;
-        case VIR_DRV_FEATURE_MIGRATION_V2:
         case VIR_DRV_FEATURE_MIGRATION_V3:
+            return 1;
+        case VIR_DRV_FEATURE_MIGRATION_V2:
         case VIR_DRV_FEATURE_MIGRATION_P2P:
         case VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION:
         case VIR_DRV_FEATURE_XML_MIGRATABLE:
@@ -2338,6 +2351,348 @@ chDomainInterfaceAddresses(virDomain *dom,
     return ret;
 }
 
+/*******************************************************************
+ * Migration Protocol Version 3
+ *******************************************************************/
+
+static char *
+chDomainMigrateBegin3(virDomainPtr domain,
+                      const char *xmlin,
+                      char **cookieout,
+                      int *cookieoutlen,
+                      unsigned long flags,
+                      const char *dname,
+                      unsigned long resource G_GNUC_UNUSED)
+{
+    virDomainObj *vm;
+    char *xml = NULL;
+    virCHDriver *driver = domain->conn->privateData;
+
+    VIR_WARN("chDomainMigrateBegin3 %p %s %p %p %lu %s",
+              domain, xmlin, cookieout, cookieoutlen, flags, dname);
+    if (!(vm = virCHDomainObjFromDomain(domain)))
+        return NULL;
+
+    if (virDomainMigrateBegin3EnsureACL(domain->conn, vm->def) < 0) {
+        virDomainObjEndAPI(&vm);
+        return NULL;
+    }
+
+    // Copied from libxl_migration.c:386
+    if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    xml = virDomainDefFormat(vm->def, driver->xmlopt, 
VIR_DOMAIN_DEF_FORMAT_SECURE);
+
+    if (xml) {
+        VIR_WARN("chDomainMigrateBegin3 success. xml: %s", xml);
+        goto cleanup;
+    }
+
+    return NULL;
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return xml;
+}
+
+static
+virDomainDef *
+chMigrationAnyPrepareDef(virCHDriver *driver,
+                           const char *dom_xml,
+                           const char *dname)
+{
+    virDomainDef *def;
+    char *name = NULL;
+
+    if (!dom_xml) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("no domain XML passed"));
+        return NULL;
+    }
+
+    if (!(def = virDomainDefParseString(dom_xml, driver->xmlopt,
+                                        NULL,
+                                        VIR_DOMAIN_DEF_PARSE_INACTIVE)))
+        goto cleanup;
+
+    if (dname) {
+        VIR_FREE(name);
+        def->name = g_strdup(dname);
+    }
+
+ cleanup:
+    return def;
+}
+
+typedef struct _chMigrationDstArgs {
+    unsigned int port;
+    virCHDomainObjPrivate *priv;
+} chMigrationDstArgs;
+
+static void
+chDoMigrateDstReceive(void *opaque)
+{
+    chMigrationDstArgs *args = opaque;
+    virCHDomainObjPrivate *priv = args->priv;
+    g_autofree char* rcv_uri = NULL;
+
+    VIR_WARN("In thread. %u %p", args->port, args->priv);
+    if (!priv->monitor) {
+        VIR_ERROR(_("VMs monitor not initialized"));
+    }
+
+    rcv_uri = g_strdup_printf("tcp:0.0.0.0:%d", args->port);
+
+    if (virCHMonitorMigrationReceive(priv->monitor, rcv_uri) < 0) {
+        VIR_WARN("Migration receive failed.");
+    }
+
+    VIR_WARN("Migration thread finished its duty");
+}
+
+/**
+ * Runs on the destination and prepares the empty cloud hypervisor process to
+ * receive the migration.
+ * Allocates some tcp port number to use as a migration channel.
+ */
+static int
+chDomainMigratePrepare3(virConnectPtr dconn,
+                        const char *cookiein,
+                        int cookieinlen,
+                        char **cookieout,
+                        int *cookieoutlen,
+                        const char *uri_in,
+                        char **uri_out,
+                        unsigned long flags,
+                        const char *dname,
+                        unsigned long resource G_GNUC_UNUSED,
+                        const char *dom_xml)
+{
+    virCHDriver *driver = dconn->privateData;
+    virDomainObj *vm = NULL;
+    virCHDomainObjPrivate *priv = NULL;
+    chMigrationDstArgs *args = g_new0(chMigrationDstArgs, 1);
+    unsigned short port = 0;
+    g_autofree char *hostname = NULL;
+    const char *threadname = "mig-ch";
+    g_autoptr(virDomainDef) def = NULL;
+    int rc = 0;
+    const char *incFormat = "%s:%s:%d"; // seems to differ for AF_INET6
+
+    VIR_WARN("chDomainMigratePrepare3 %p %s %u %p %p %s %p %lu %s %s",
+              dconn, cookiein, cookieinlen, cookieout, cookieoutlen, uri_in, 
uri_out, flags, dname, dom_xml);
+
+    if (virDomainMigratePrepare3EnsureACL(dconn, def) < 0)
+        return -1;
+
+    if (!(def = chMigrationAnyPrepareDef(driver, dom_xml, dname)))
+        return -1;
+
+    VIR_WARN("Got DomainDef prepared successfully");
+
+    if (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0) {
+        rc = -1;
+        goto cleanup;
+    }
+    VIR_WARN("Got port %i", port);
+
+    if ((hostname = virGetHostname()) == NULL) {
+        rc = -1;
+        goto cleanup;
+    }
+
+    *uri_out = g_strdup_printf(incFormat, "tcp", hostname, port);
+    VIR_WARN("uri out %s", *uri_out);
+
+    if (!(vm = virDomainObjListAdd(driver->domains, &def,
+                                   driver->xmlopt,
+                                   VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+                                   VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+                                   NULL)))
+    {
+        rc = -1;
+        VIR_WARN("Could not add Domain Obj to List");
+        goto cleanup;
+    }
+
+    if (virCHProcessInit(driver, vm) < 0) {
+        rc = -1;
+        VIR_WARN("Could not init process");
+        goto cleanup;
+    }
+
+    VIR_WARN("Try creating migration thread");
+    priv = vm->privateData;
+    args->port = port;
+    args->priv = priv;
+
+    // VM receiving is blocking which we cannot do here, because it would block
+    // the Libvirt migration protocol.
+    // Prepare a thread to receive the migration data
+    // VIR_FREE(priv->migrationDstReceiveThr);
+    priv->migrationDstReceiveThr = g_new0(virThread, 1);
+    if (virThreadCreateFull(priv->migrationDstReceiveThr, true,
+                            chDoMigrateDstReceive,
+                            threadname,
+                            false,
+                            args) < 0) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("Failed to create thread for receiving migration 
data"));
+        goto cleanup;
+    }
+
+    VIR_WARN("Finished creating migration thread");
+
+
+    VIR_WARN("Fin migrationPrepare");
+
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return rc;
+}
+
+static int
+chDomainMigratePerform3(virDomainPtr dom,
+                        const char *xmlin,
+                        const char *cookiein,
+                        int cookieinlen,
+                        char **cookieout,
+                        int *cookieoutlen,
+                        const char *dconnuri,
+                        const char *uri,
+                        unsigned long flags,
+                        const char *dname,
+                        unsigned long resource)
+{
+    size_t i;
+    virDomainObj *vm;
+    g_autoptr(virCHDriverConfig) cfg = 
virCHDriverGetConfig(dom->conn->privateData);
+    virCHDomainObjPrivate *priv = NULL;
+    g_autofree char *id = g_strdup_printf("bla");
+    VIR_WARN("chDomainMigratePerform3 %p %s %s %u %p %p %s %s %lu %s %lu",
+              dom, xmlin, cookiein, cookieinlen, cookieout, cookieoutlen, 
dconnuri, uri, flags, dname, resource);
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        return -1;
+
+    priv = vm->privateData;
+
+    if (!priv->monitor) {
+        VIR_ERROR(_("VMs monitor not initialized"));
+        goto cleanup;
+    }
+
+    if (virDomainMigratePerform3EnsureACL(dom->conn, vm->def) < 0) {
+        goto cleanup;
+    }
+
+    // Net device id hardcoded currently
+    // We need to remove network devices from the VM before live migration.
+    // Libvirt pre-allocates network devices and passes only the FD to CHV. CHV
+    // is not able to migrate those devices.
+    // See following CHV issue: 
https://github.com/cloud-hypervisor/cloud-hypervisor/issues/7054
+        /* de-activate netdevs after stopping vm */
+    ignore_value(virDomainInterfaceStopDevices(vm->def));
+    for (i = 0; i < vm->def->nnets; i++) {
+        virDomainInterfaceDeleteDevice(vm->def, vm->def->nets[i], false, 
cfg->stateDir);
+        id = g_strdup_printf("net_%lu", i);
+        if (virCHMonitorRemoveDevice(priv->monitor, id) < 0) {
+            VIR_WARN("Could not remove net device. Continue to migrate 
regardless.");
+        }
+    }
+
+    if (virCHMonitorMigrationSend(priv->monitor, uri) < 0) {
+        VIR_WARN("Migration send failed.");
+        goto cleanup;
+    }
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return 0;
+}
+
+static virDomainPtr
+chDomainMigrateFinish3(virConnectPtr dconn,
+                       const char *dname,
+                       const char *cookiein,
+                       int cookieinlen,
+                       char **cookieout,
+                       int *cookieoutlen,
+                       const char *dconnuri G_GNUC_UNUSED,
+                       const char *uri G_GNUC_UNUSED,
+                       unsigned long flags,
+                       int cancelled)
+{
+    virCHDriver *driver = dconn->privateData;
+    virDomainObj *vm = NULL;
+    virDomainPtr dom = NULL;
+
+    VIR_WARN("chDomainMigrateFinish3 %p %s %s %d %p %p %lu %d",
+              dconn, dname, cookiein, cookieinlen, cookieout, cookieoutlen, 
flags, cancelled);
+
+    vm = virDomainObjListFindByName(driver->domains, dname);
+    if (!vm) {
+        virReportError(VIR_ERR_NO_DOMAIN,
+                       _("no domain with matching name '%1$s'"), dname);
+        return NULL;
+    }
+
+    if (virDomainMigrateFinish3EnsureACL(dconn, vm->def) < 0) {
+        virDomainObjEndAPI(&vm);
+        return NULL;
+    }
+    if (!(dom = virGetDomain(dconn, vm->def->name, vm->def->uuid, 
vm->def->id))) {
+        virDomainObjEndAPI(&vm);
+        VIR_WARN("virGetDomain failed.");
+        return NULL;
+
+    }
+    if (virCHProcessUpdateInfo(vm) < 0) {
+        VIR_WARN("Could not update console info. Consider that non-fatal.");
+    }
+
+    if (virCHProcessInitNetwork(driver, vm) < 0) {
+        VIR_WARN("Could not updatenetwork info. Consider that non-fatal.");
+    }
+
+    virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_MIGRATED);
+
+    virDomainObjEndAPI(&vm);
+    return dom;
+}
+
+static int
+chDomainMigrateConfirm3(virDomainPtr domain,
+                        const char *cookiein,
+                        int cookieinlen,
+                        unsigned long flags,
+                        int cancelled)
+{
+    virCHDriver *driver = domain->conn->privateData;
+    virObjectEvent *event = NULL;
+    virDomainObj *vm;
+
+    VIR_WARN("chDomainMigrateConfirm3 %p %s %d %lu %d",
+              domain, cookiein, cookieinlen, flags, cancelled);
+
+    if (!(vm = virCHDomainObjFromDomain(domain)))
+        return -1;
+
+    if (virDomainMigrateConfirm3EnsureACL(domain->conn, vm->def) < 0) {
+        virDomainObjEndAPI(&vm);
+        return -1;
+    }
+
+    // Code from chDestroyFlags
+    virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_MIGRATED);
+    virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
+    virCHDomainRemoveInactive(driver, vm);
+    virDomainObjEndAPI(&vm);
+
+    virObjectEventStateQueue(driver->domainEventState, event);
+    return 0;
+}
 
 /* Function Tables */
 static virHypervisorDriver chHypervisorDriver = {
@@ -2400,6 +2755,11 @@ static virHypervisorDriver chHypervisorDriver = {
     .connectDomainEventRegisterAny = chConnectDomainEventRegisterAny,       /* 
10.10.0 */
     .connectDomainEventDeregisterAny = chConnectDomainEventDeregisterAny,   /* 
10.10.0 */
     .domainInterfaceAddresses = chDomainInterfaceAddresses, /* 11.0.0 */
+    .domainMigrateBegin3 = chDomainMigrateBegin3, /* 11.4.0 */
+    .domainMigratePrepare3 = chDomainMigratePrepare3, /* 11.4.0 */
+    .domainMigratePerform3 = chDomainMigratePerform3, /* 11.4.0 */
+    .domainMigrateFinish3 = chDomainMigrateFinish3, /* 11.4.0 */
+    .domainMigrateConfirm3 = chDomainMigrateConfirm3, /* 11.4.0 */
 };
 
 static virConnectDriver chConnectDriver = {
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index 3d3b4cb87d..595fa30be0 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -548,6 +548,7 @@ virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef 
*vmdef,
     if (!(*jsonstr = virJSONValueToString(content, false)))
         return -1;
 
+    VIR_WARN("Build VM JSON: \n %s \n", *jsonstr);
     return 0;
 }
 
@@ -684,6 +685,8 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, 
int logfile)
         return NULL;
     }
 
+    VIR_WARN("Start emulator with cmd: %s", vm->def->emulator);
+
     cmd = virCommandNew(vm->def->emulator);
     virCommandSetOutputFD(cmd, &logfile);
     virCommandSetErrorFD(cmd, &logfile);
@@ -1163,6 +1166,159 @@ virCHMonitorSaveVM(virCHMonitor *mon,
     return ret;
 }
 
+int virCHMonitorRemoveDevice(virCHMonitor *mon,
+                             const char* device_id)
+{
+    g_autofree char *url = NULL;
+    int responseCode = 0;
+    int ret = -1;
+    g_autofree char *payload = NULL;
+    struct curl_slist *headers = NULL;
+    struct curl_data data = {0};
+
+    url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_REMOVE_DEVICE);
+
+    headers = curl_slist_append(headers, "Accept: application/json");
+    headers = curl_slist_append(headers, "Content-Type: application/json");
+
+    if (virCHMonitorBuildKeyValueStringJson(&payload, "id", device_id) != 0)
+        return -1;
+
+    VIR_WARN("Remove device id %s json %s", device_id, payload);
+
+    VIR_WITH_OBJECT_LOCK_GUARD(mon) {
+        /* reset all options of a libcurl session handle at first */
+        curl_easy_reset(mon->handle);
+
+        curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, 
mon->socketpath);
+        curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+        curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT");
+        curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers);
+        curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data);
+
+        responseCode = virCHMonitorCurlPerform(mon->handle);
+    }
+
+    if (responseCode == 200 || responseCode == 204) {
+        ret = 0;
+    } else {
+        data.content = g_realloc(data.content, data.size + 1);
+        data.content[data.size] = 0;
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       data.content);
+        g_free(data.content);
+    }
+
+    /* reset the libcurl handle to avoid leaking a stack pointer to data */
+    curl_easy_reset(mon->handle);
+    curl_slist_free_all(headers);
+    return ret;
+}
+
+int virCHMonitorMigrationSend(virCHMonitor *mon,
+                              const char *dst_uri)
+{
+    g_autofree char *url = NULL;
+    int responseCode = 0;
+    int ret = -1;
+    g_autofree char *payload = NULL;
+    struct curl_slist *headers = NULL;
+    struct curl_data data = {0};
+
+    url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_SEND_MIGRATION);
+
+    headers = curl_slist_append(headers, "Accept: application/json");
+    headers = curl_slist_append(headers, "Content-Type: application/json");
+
+    if (virCHMonitorBuildKeyValueStringJson(&payload, "destination_url", 
dst_uri) != 0)
+        return -1;
+
+    VIR_WARN("Send VM to url %s json %s", dst_uri, payload);
+
+    VIR_WITH_OBJECT_LOCK_GUARD(mon) {
+        /* reset all options of a libcurl session handle at first */
+        curl_easy_reset(mon->handle);
+
+        curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, 
mon->socketpath);
+        curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+        curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT");
+        curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers);
+        curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data);
+
+        responseCode = virCHMonitorCurlPerform(mon->handle);
+    }
+
+    if (responseCode == 200 || responseCode == 204) {
+        ret = 0;
+    } else {
+        data.content = g_realloc(data.content, data.size + 1);
+        data.content[data.size] = 0;
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("Error sending VM: '%1$s'"),
+                       data.content);
+        g_free(data.content);
+    }
+
+    /* reset the libcurl handle to avoid leaking a stack pointer to data */
+    curl_easy_reset(mon->handle);
+    curl_slist_free_all(headers);
+    return ret;
+}
+
+int virCHMonitorMigrationReceive(virCHMonitor *mon,
+                                 const char *rcv_uri)
+{
+    g_autofree char *url = NULL;
+    int responseCode = 0;
+    int ret = -1;
+    g_autofree char *payload = NULL;
+    struct curl_slist *headers = NULL;
+    struct curl_data data = {0};
+
+    url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_RECEIVE_MIGRATION);
+
+    headers = curl_slist_append(headers, "Accept: application/json");
+    headers = curl_slist_append(headers, "Content-Type: application/json");
+
+    if (virCHMonitorBuildKeyValueStringJson(&payload, "receiver_url", rcv_uri) 
!= 0)
+        return -1;
+
+    VIR_WARN("Receive VM from url %s json: %s", rcv_uri, payload);
+
+    VIR_WITH_OBJECT_LOCK_GUARD(mon) {
+        /* reset all options of a libcurl session handle at first */
+        curl_easy_reset(mon->handle);
+
+        curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, 
mon->socketpath);
+        curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+        curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT");
+        curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers);
+        curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data);
+
+        responseCode = virCHMonitorCurlPerform(mon->handle);
+    }
+
+    if (responseCode == 200 || responseCode == 204) {
+        ret = 0;
+    } else {
+        data.content = g_realloc(data.content, data.size + 1);
+        data.content[data.size] = 0;
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("Error receiving VM: '%1$s'"),
+                       data.content);
+        g_free(data.content);
+    }
+
+    /* reset the libcurl handle to avoid leaking a stack pointer to data */
+    curl_easy_reset(mon->handle);
+    curl_slist_free_all(headers);
+    return ret;
+}
+
 int
 virCHMonitorBuildRestoreJson(virDomainDef *vmdef,
                              const char *from,
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index ffac9e938e..256ab0d8a1 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -40,6 +40,9 @@
 #define URL_VM_INFO "vm.info"
 #define URL_VM_SAVE "vm.snapshot"
 #define URL_VM_RESTORE "vm.restore"
+#define URL_VM_RECEIVE_MIGRATION "vm.receive-migration"
+#define URL_VM_SEND_MIGRATION "vm.send-migration"
+#define URL_VM_REMOVE_DEVICE "vm.remove-device"
 
 #define VIRCH_THREAD_NAME_LEN   16
 
@@ -128,6 +131,11 @@ int virCHMonitorSuspendVM(virCHMonitor *mon);
 int virCHMonitorResumeVM(virCHMonitor *mon);
 int virCHMonitorSaveVM(virCHMonitor *mon,
                        const char *to);
+int virCHMonitorMigrationSend(virCHMonitor *mon,
+                              const char *dst_uri);
+int virCHMonitorMigrationReceive(virCHMonitor *mon,
+                                 const char *rcv_uri);
+int virCHMonitorRemoveDevice(virCHMonitor *mon, const char* device_id);
 int virCHMonitorGetInfo(virCHMonitor *mon, virJSONValue **info);
 
 size_t virCHMonitorGetThreadInfo(virCHMonitor *mon, bool refresh,
diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c
index 95c808cb41..4d2bbc2b3d 100644
--- a/src/ch/ch_process.c
+++ b/src/ch/ch_process.c
@@ -135,6 +135,7 @@ int
 virCHProcessUpdateInfo(virDomainObj *vm)
 {
     g_autoptr(virJSONValue) info = NULL;
+
     virCHDomainObjPrivate *priv = vm->privateData;
     if (virCHMonitorGetInfo(priv->monitor, &info) < 0)
         return -1;
@@ -643,7 +644,7 @@ chProcessAddNetworkDevices(virCHDriver *driver,
                            int **nicindexes,
                            size_t *nnicindexes)
 {
-    size_t i;
+    size_t i, j;
     VIR_AUTOCLOSE mon_sockfd = -1;
     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     g_auto(virBuffer) http_headers = VIR_BUFFER_INITIALIZER;
@@ -654,8 +655,10 @@ chProcessAddNetworkDevices(virCHDriver *driver,
         return -1;
     }
 
-    if ((mon_sockfd = chMonitorSocketConnect(mon)) < 0)
+    if ((mon_sockfd = chMonitorSocketConnect(mon)) < 0) {
+        VIR_WARN("chProcessAddNetworkDevices failed");
         return -1;
+    }
 
     virBufferAddLit(&http_headers, "PUT /api/v1/vm.add-net HTTP/1.1\r\n");
     virBufferAddLit(&http_headers, "Host: localhost\r\n");
@@ -681,24 +684,33 @@ chProcessAddNetworkDevices(virCHDriver *driver,
         if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("net definition failed validation"));
+            VIR_WARN("virCHDomainValidateActualNetDef failed.");
             return -1;
         }
 
         tapfds = g_new0(int, tapfd_len);
         memset(tapfds, -1, (tapfd_len) * sizeof(int));
 
+        VIR_WARN("net type: %u", vmdef->nets[i]->type);
         /* Connect Guest interfaces */
         if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds,
-                                         nicindexes, nnicindexes) < 0)
+                                         nicindexes, nnicindexes) < 0) {
+            VIR_WARN("chProcessAddNetworkDevices failed.");
             return -1;
+        }
 
         if (virCHMonitorBuildNetJson(vmdef->nets[i], i, &payload) < 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("Failed to build net json"));
+            VIR_WARN("virCHMonitorBuildNetJson failed.");
             return -1;
         }
 
-        VIR_DEBUG("payload sent with net-add request to CH = %s", payload);
+        VIR_WARN("payload sent with net-add request to CH = %s", payload);
+
+        for (j = 0; j < tapfd_len; j++) {
+            VIR_WARN("tapfd %lu : %d", j, tapfds[j]);
+        }
 
         virBufferAsprintf(&buf, "%s", virBufferCurrentContent(&http_headers));
         virBufferAsprintf(&buf, "Content-Length: %zu\r\n\r\n", 
strlen(payload));
@@ -892,6 +904,122 @@ virCHProcessPrepareDomain(virDomainObj *vm)
     return 0;
 }
 
+int virCHProcessInitNetwork(virCHDriver *driver,
+                            virDomainObj *vm)
+{
+    int ret = -1;
+    virCHDomainObjPrivate *priv = vm->privateData;
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(priv->driver);
+    g_autofree int *nicindexes = NULL;
+    size_t nnicindexes = 0;
+
+    if (chProcessAddNetworkDevices(driver, priv->monitor, vm->def,
+                                   &nicindexes, &nnicindexes) < 0) {
+        VIR_WARN("Failed chProcessAddNetworkDevices");
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed while adding guest interfaces"));
+        goto cleanup;
+    }
+
+    /* Bring up netdevs before starting CPUs */
+    if (virDomainInterfaceStartDevices(vm->def) < 0) {
+        VIR_WARN("Failed virDomainInterfaceStartDevices");
+        return -1;
+    }
+
+    return 0;
+
+ cleanup:
+
+    return ret;
+}
+
+/**
+ * A variant of virCHProcessStart that does not start the vCPU threads and the
+ * VM. Sets up the CH process along most configuration.
+ * Is used to setup CH in order to receive a live migration afterwards.
+ */
+int
+virCHProcessInit(virCHDriver *driver,
+                 virDomainObj *vm)
+{
+    int ret = -1;
+    virCHDomainObjPrivate *priv = vm->privateData;
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(priv->driver);
+    g_autofree int *nicindexes = NULL;
+    size_t nnicindexes = 0;
+    g_autoptr(domainLogContext) logCtxt = NULL;
+    int logfile = -1;
+
+    if (virDomainObjIsActive(vm)) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("VM is already active"));
+        return -1;
+    }
+
+    if (virCHProcessStartValidate(driver, vm) < 0) {
+        return -1;
+    }
+
+    VIR_WARN("Creating domain log file for %s domain", vm->def->name);
+    if (!(logCtxt = domainLogContextNew(cfg->stdioLogD, cfg->logDir,
+                                        CH_DRIVER_NAME,
+                                        vm, driver->privileged,
+                                        vm->def->name))) {
+        virLastErrorPrefixMessage("%s", _("can't connect to virtlogd"));
+        return -1;
+    }
+    logfile = domainLogContextGetWriteFD(logCtxt);
+
+    if (virCHProcessPrepareDomain(vm) < 0) {
+        return -1;
+    }
+
+    if (virCHProcessPrepareHost(driver, vm) < 0)
+        return -1;
+
+    if (!priv->monitor) {
+        /* And we can get the first monitor connection now too */
+        if (!(priv->monitor = virCHProcessConnectMonitor(driver, vm, 
logfile))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to create connection to CH socket"));
+            goto cleanup;
+        }
+
+        if (virCHMonitorCreateVM(driver, priv->monitor) < 0) {
+            VIR_WARN("Failed virCHMonitorCreateVM");
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to create guest VM"));
+            goto cleanup;
+        }
+    }
+
+    vm->def->id = vm->pid;
+    priv->machineName = virCHDomainGetMachineName(vm);
+
+    if (virDomainCgroupSetupCgroup("ch", vm,
+                                   nnicindexes, nicindexes,
+                                   &priv->cgroup,
+                                   cfg->cgroupControllers,
+                                   0, /*maxThreadsPerProc*/
+                                   priv->driver->privileged,
+                                   priv->machineName) < 0)
+    {
+        VIR_WARN("Failed virDomainCgroupSetupCgroup");
+        goto cleanup;
+    }
+
+    virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_MIGRATION);
+
+    return 0;
+
+ cleanup:
+    if (ret)
+        virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
+
+    return ret;
+}
+
 /**
  * virCHProcessStart:
  * @driver: pointer to driver structure
diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h
index 7a6995b7cf..3a5a2d17d7 100644
--- a/src/ch/ch_process.h
+++ b/src/ch/ch_process.h
@@ -23,6 +23,9 @@
 #include "ch_conf.h"
 #include "internal.h"
 
+int virCHProcessInit(virCHDriver *driver,
+                     virDomainObj *vm);
+
 int virCHProcessStart(virCHDriver *driver,
                       virDomainObj *vm,
                       virDomainRunningReason reason);
@@ -38,3 +41,6 @@ int virCHProcessStartRestore(virCHDriver *driver,
                          const char *from);
 
 int virCHProcessUpdateInfo(virDomainObj *vm);
+
+int virCHProcessInitNetwork(virCHDriver *driver,
+                            virDomainObj *vm);
diff --git a/src/hypervisor/domain_interface.c 
b/src/hypervisor/domain_interface.c
index 5bc698d272..a13fcfb7d2 100644
--- a/src/hypervisor/domain_interface.c
+++ b/src/hypervisor/domain_interface.c
@@ -82,6 +82,7 @@ virDomainInterfaceEthernetConnect(virDomainDef *def,
     bool template_ifname = false;
     const char *tunpath = "/dev/net/tun";
     const char *auditdev = tunpath;
+    VIR_WARN("virDomainInterfaceEthernetConnect %s", net->ifname);
 
     if (net->backend.tap) {
         tunpath = net->backend.tap;
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index 93e8f5b853..867876b23f 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -3307,6 +3307,11 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
                      "params=%p, nparams=%d, useParams=%d, flags=0x%x",
                      dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri),
                      bandwidth, params, nparams, useParams, flags);
+    VIR_WARN(
+                     "dconn=%p, xmlin=%s, dname=%s, uri=%s, bandwidth=%llu, "
+                     "params=%p, nparams=%d, useParams=%d, flags=0x%x",
+                     dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri),
+                     bandwidth, params, nparams, useParams, flags);
     VIR_TYPED_PARAMS_DEBUG(params, nparams);
 
     virCheckNonEmptyOptStringArgReturn(dname, NULL);
@@ -3337,7 +3342,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
     if (ret)
         protection = VIR_MIGRATE_CHANGE_PROTECTION;
 
-    VIR_DEBUG("Begin3 %p", domain->conn);
+    VIR_WARN("Begin3 %p", domain->conn);
     if (useParams) {
         dom_xml = domain->conn->driver->domainMigrateBegin3Params
             (domain, params, nparams, &cookieout, &cookieoutlen,
@@ -3366,7 +3371,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
     destflags = flags & ~(VIR_MIGRATE_ABORT_ON_ERROR |
                           VIR_MIGRATE_AUTO_CONVERGE);
 
-    VIR_DEBUG("Prepare3 %p flags=0x%x", dconn, destflags);
+    VIR_WARN("Prepare3 %p flags=0x%x", dconn, destflags);
     cookiein = g_steal_pointer(&cookieout);
     cookieinlen = cookieoutlen;
     cookieoutlen = 0;
@@ -3427,7 +3432,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
      * running, but in paused state until the destination can
      * confirm migration completion.
      */
-    VIR_DEBUG("Perform3 %p uri=%s", domain->conn, uri);
+    VIR_WARN("Perform3 %p uri=%s", domain->conn, uri);
     VIR_FREE(cookiein);
     cookiein = g_steal_pointer(&cookieout);
     cookieinlen = cookieoutlen;
@@ -3465,7 +3470,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
      * send all migration data. Returns NULL for ddomain if
      * the dest was unable to complete migration.
      */
-    VIR_DEBUG("Finish3 %p ret=%d", dconn, ret);
+    VIR_WARN("Finish3 %p ret=%d", dconn, ret);
     VIR_FREE(cookiein);
     cookiein = g_steal_pointer(&cookieout);
     cookieinlen = cookieoutlen;
@@ -3540,7 +3545,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain,
      * cancelled there.
      */
     if (notify_source) {
-        VIR_DEBUG("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain);
+        VIR_WARN("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain);
         VIR_FREE(cookiein);
         cookiein = g_steal_pointer(&cookieout);
         cookieinlen = cookieoutlen;
-- 
2.49.0

Reply via email to