---
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